Please stop sending x-frame-options:SAMEORIGIN

Modern study methods include active recall (guess-and-check) and spaced repetition (answering correctly makes the question come back later, answering incorrectly makes the question come back sooner). Board game puzzles are inherently active recall (as opposed to passively reading someone else’s game’s moves), but don’t inherently use spaced repetition. Spaced repetition works really well for languages, and I suspect that doing puzzles from easiest to hardest with the scheduler (approximating after how many months you may forget a certain strategy) works better than attempting random puzzles at your current elo rating, like most people do.

I personally use Anki (an active recall/spaced repetition/digital flash card app) with FSRS (an open-source review scheduler using the Difficulty-Stability-Retrievability memory model similar to how SuperMemo does).

In order to individually schedule reviews for each puzzle, I need to access each individual puzzle, by A) somehow copy the entire scripting/move list/ai player/database into local offline html flash cards or B) link to the puzzle’s url or C) the text of the card tells me where to manually go find the puzzle, even if it’s in a hardcover book. B is the easiest option.

A quirk of Anki is that, while each flash card is an individual html/css/js page generated from text fields stored in an sql database combined with an html template, when you are reviewing and switching from one flash card to the next, the internal pyqt6 web browser’s hidden address bar url isn’t changed. Instead, the content of the page is swapped out using rust/python/typescript/javascript to render the next page. So in order to render external content I need to A) embed it in an iframe unless that fails because of x-frame-options, or B) use javascript to change window.location.href which opens the puzzle externally in the default web browser and I have to manually (or with greasemonkey/autohotkey) close the tab and switch windows when I’m done, or C) write an addon to change the url and back, and addons only work on a pc, not android/iphone because of play store sandboxing rules (I wrote one incorrectly, so sometimes when flipping to the next card it can get stuck on a blank page and I need to leave/re-enter the study session to see the next card)

As far as I can tell, there is no rhyme nor reason as to whether a site will disallow iframe embedding.
refused to connect: checkers, sign language dictionary, rna folding
connects: blackbox, chinese/japanese/korean/laser/normal chess, connect four, draughts/frisian, spanish dictionary, dna alignment, go, hashi-o-kakero, nonograms, rush hour
checkers, dictionaries, and biochem are on both sides of the fence.

Please stop sending x-frame-options:SAMEORIGIN, at least for …/problems/*

Testing: To see whether that header is sent:
Firefox or Chrome→right-click→Inspect (Q)→Network tab→press F5 to reload the page→scroll up and select the first GET/document/html row→right sidebar→Headers→scroll down→Response Headers→scroll down→x-frame-options:SAMEORIGIN
or save a .html file containing <iframe width="100%" height="100%" src="https://www.checkercruncher.com/problems/755"></iframe> and see if it loads

links:
why spaced repetition? ncase.me/remember/
Anki (and not its counterfeits) apps.ankiweb net/
FSRS (the new) www.reddit com/r/Anki/comments/18jvyun/some_posts_and_articles_about_fsrs/
SuperMemo (the original) supermemopedia com/wiki/SuperMemo_15_Freeware
how not to write bad cards www.supermemo com/en/blog/twenty-rules-of-formulating-knowledge
list of board game puzzle websites I use, and how compatible they are with hotlinking versus scraping github.com/ankitects/anki/issues/2007#issuecomment-1962902817

That’s an interesting use case, not one I thought of. I hope you find a work around that works for you,
but this is not something I can turn off. The Same-origin policy - Wikipedia is an important security layer for problem comments and anything else that allows user generated content.

For the problem comments, I’m pretty sure they don’t allow weird html or JavaScript shenanigans but in case I missed something, I’m leaving the same-origin policy on.


iframe blocking is a coin flip masquerading as security, and both you and qtwebkit flipped tails.
I need to do one of:

  • get checkercruncher to stop blocking iframes x-frame-options:SAMEORIGINContent-Security-Policy: frame-ancestors 'self' http://127.0.0.1;
  • find an alternative checkers (not draughts nor frisian) website or database+js
  • get official anki desktop & ankidroid builds to always compile with a custom fork of qtwebkit that has a modified FrameLoader::shouldInterruptLoadForXFrameOptions() to at least have the option to load it that firefox and chrome have
  • get qtwebkit to support browser.frames.enabled
  • switch to a qtwebkit alternative
  • use an internet proxy that strips the X-Frame-Options header (though my internet hotspot app already uses the VPN feature slot on my phone)
  • study checkers separately on pc with my custom iframe2internal addon instead of mixing board game decks on the phone
  • track down why the card template’s javascript no longer opens a new browser window per puzzle in both ankidroid’s old and new reviewer
  • modify anki desktop and ankidroid to use setURL() to visit the website directly (like the addon does) while still being able to access the flip/grade grade buttons through gesture/key/screen button, and then reloading the reviewer instance to load the back of the card from localhost.

A proxy doesn’t work. It can strip x-frame-options and content-security-policy headers but only for websites that don’t force https. https://stackoverflow.com/a/62055479www.checkercruncher.com is most likely a safe site, but a secure connection could not be established. This issue is caused by mitmproxy, which is either software on your computer or your network. www.checkercruncher.com has a security policy called HTTP Strict Transport Security (HSTS), which means that Firefox can only connect to it securely. You can’t add an exception to visit this site. MOZILLA_PKIX_ERROR_MITM_DETECTED”

I’m sorry, I know it’s frustrating, but I believe this to be operating as intended.

Now it works on the phone (was missing a quote), newer desktop anki 25.07.5 (_page broke), and with the last function I can now return to a local card after doing a remote one without it getting stuck, so I can mix different games in the same review session (mw.reviewer.web.setUrl() then mw.moveToState("review")). Also checkercruncher requires me to be logged in before I can move with one tap, and that applies on the second card because I didn’t get the timing right yet.
This is how I have it as-is, less user cookies, without cleaning up dead code/comments:
iframe2open_link_internally/__init__.py anki addon

from aqt import gui_hooks, mw
from aqt.qt import QUrl
import aqt#.reviewer.Reviewer
from aqt.qt import QWebEngineScript
import _thread, time

iframe2open_link_internally_oldurl = QUrl("unset")
def reviewer_did_show_question_open_link_internally(card):
	if "iframe2internal::front" in card._note.tags:
		open_link_internally(card)
gui_hooks.reviewer_did_show_question.append(reviewer_did_show_question_open_link_internally)

def reviewer_did_show_answer_open_link_internally(card):
	if "iframe2internal::back" in card._note.tags:
		open_link_internally(card)
gui_hooks.reviewer_did_show_answer.append(reviewer_did_show_answer_open_link_internally)

def open_link_internally(card):
	global iframe2open_link_internally_oldurl
	iframe2open_link_internally_oldurl=mw.reviewer.web.url(); #py, copy this url #console.log(oldurl=document.location.href); //js, copy this url
	print(iframe2open_link_internally_oldurl, " → ", card._note.fields[0])
	
#	###SCARY SECURITY RISK STUFF### that doesn't fix signingsavvy, and only makes the chat work in eternagame
#	mw.web.page().settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
#	mw.web.page().settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
#	mw.web.page().settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
#	mw.web.page().settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.XSSAuditingEnabled, False)
#	#mw.web.page().settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.webSecurityEnabled, False)
#	mw.web.page().settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
#	mw.reviewer.web._page.settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
#	mw.reviewer.web._page.settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
#	mw.reviewer.web._page.settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
#	mw.reviewer.web._page.settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.XSSAuditingEnabled, False)
#	#mw.reviewer.web._page.settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.webSecurityEnabled, False)
#	mw.reviewer.web._page.settings().setAttribute(aqt.QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)

#   following commented lines are old broken code
#	aqt.QWebEngineSettings.globalSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
#	aqt.QWebEngineSettings.globalSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
#	aqt.QWebEngineSettings.globalSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
#	aqt.QWebEngineSettings.globalSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.XSSAuditingEnabled, False)
#	aqt.QWebEngineSettings.globalSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.webSecurityEnabled, False)
#	aqt.QWebEngineSettings.globalSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
#	aqt.QWebEngineSettings.defaultSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
#	aqt.QWebEngineSettings.defaultSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
#	aqt.QWebEngineSettings.defaultSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
#	aqt.QWebEngineSettings.defaultSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.XSSAuditingEnabled, False)
#	aqt.QWebEngineSettings.defaultSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.webSecurityEnabled, False)
#	aqt.QWebEngineSettings.defaultSettings().setAttribute(aqt.QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
#	aqt.QWebEngineSettings.setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, True)
#	aqt.QWebEngineSettings.setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
#	aqt.QWebEngineSettings.setAttribute(aqt.QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
#	aqt.QWebEngineSettings.setAttribute(aqt.QWebEngineSettings.WebAttribute.XSSAuditingEnabled, False)
#	aqt.QWebEngineSettings.setAttribute(aqt.QWebEngineSettings.WebAttribute.webSecurityEnabled, False)
#	aqt.QWebEngineSettings.setAttribute(aqt.QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True)
#	###SCARY SECURITY RISK STUFF###
	
	mw.reviewer.web.set_open_links_externally(False) #py, allow loading the link directly without an iframe that can fail with x-frame-options example.com refused to connect
	#document.location.href="https://www.checkercruncher.com/problems/1"; //js or click <a>, use actual link from {{iframe url}} field
	#mw.reviewer.web.setUrl(aqt.qt.QUrl("https://www.checkercruncher.com/problems/1051")) #js or click <a>, use actual link from {{iframe url}} field
	
	
	
	
	#inject javascript for sign language front side to show ASL sign video WITHOUT english word
	aslhidewordscript = QWebEngineScript()
	aslhidewordscript.setName("aslhideword.js");
	#aslhidewordscript.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentCreation) #does nothing
	aslhidewordscript.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentReady) #shows word then hides word
	#aslhidewordscript.setInjectionPoint(QWebEngineScript.InjectionPoint.Deferred)
	#mw.reviewer.web._page.runJavaScript("""
	#document-start#caught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.    at observeDOM (
	#document-end too late
	aslhidewordscript.setSourceCode("""
// ==UserScript==
// @run-at document-start
// ==/UserScript==

function waitfordocumentElement() {
	//if(typeof document.head !== "undefined" && typeof document.head.appendChild === 'function'){
	try {
		(function() {css=document.createElement('style');css.type='text/css';css.id='aslhideword';document.head.appendChild(css);css.innerText="#page_signs #searchField, .signing_header h2, .signing_header h3, .signing_caption #video-1-captions, .signing_details .desc {display:none;}";})();
	} catch (TypeError) {
		setTimeout(waitfordocumentElement,100);
	}
}
waitfordocumentElement();
	""")
	#mw.reviewer.web._page.scripts().insert(aslhidewordscript) #anki 25.07: AttributeError: 'MainWebView' object has no attribute '_page'. Did you mean: 'page'?
	#mw.reviewer.web.page.scripts().insert(aslhidewordscript) #AttributeError: 'function' object has no attribute 'scripts'
	#mw.reviewer.web.scripts().insert(aslhidewordscript)
	
	mw.reviewer.web.setUrl(QUrl(card._note.fields[0])) #js or click <a>, use actual link from {{iframe url}} field
	
	#mw.reviewer.web.page().runJavaScript('alert("hello");')
	mw.reviewer.web.page().runJavaScript('document.cookie = "_checker_cruncher_session=REDACTED";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "_csrf_token=REDACTED";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "board_drag=touch";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "board_selection=Vinyl";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "board_size=Normal";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "difficulty=normal";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "enable_quick_move=true";')#https://www.checkercruncher.com click to move piece when there is only one move
	mw.reviewer.web.page().runJavaScript('document.cookie = "feedback_colors=green-red";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "move_delay=250";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "piece_selection=Original";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "remember_user_token=REDACTED";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "show_algebraic_notation=true";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "show_board_perspective=true";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "show_numbered_notation=true";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "signed_in=1";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "sound_volume=80";')
	mw.reviewer.web.page().runJavaScript('document.cookie = "user_id=REDACTED";')

#do puzzle on website

def ShowAnsweragain_thread(mw):
	time.sleep(5) #seconds
	#mw.reviewer.web.eval('pycmd("ans");') #crashes anki the second time?? but manually clicking the button works
    #needed a semicolon? works in ctrl-shift-; twice?

def webview_did_receive_js_message_restore_internal_state(handled, message, context):
	global iframe2open_link_internally_oldurl
	if (
		mw.reviewer.card # mw?.reviewer?.card?._note?.tags
		#and mw.reviewer.card._note
		#and mw.reviewer.card._note.tags
		and iframe2open_link_internally_oldurl.url() != "unset"
		and (
			(message == "ans" and "iframe2internal::front" in mw.reviewer.card._note.tags)
			or (message.startswith("ease") and "iframe2internal::back" in mw.reviewer.card._note.tags)
		)
	):
		print(mw.reviewer.card._note.fields[0], " → ", iframe2open_link_internally_oldurl)
		#document.location.href="http://127.0.0.1:55431/_anki/legacyPageData?id=3054436143888" //js, paste the copied url
		#mw.reviewer.web.setUrl(aqt.qt.QUrl("https://www.checkercruncher.com/problems/1051"))
		#if(iframe2open_link_internally_oldurl.url() != "unset")
		mw.reviewer.web.setUrl(iframe2open_link_internally_oldurl) #py, paste the copied url
		mw.reviewer.web.set_open_links_externally(True) #py, optional, reset normal link click handling
		iframe2open_link_internally_oldurl = QUrl("unset")
		
		_thread.start_new_thread(ShowAnsweragain_thread, (mw,) ) #give it time to load the internal url, then automatically click Show Answer again to flip the card
		
		#time.sleep(5) #delays first click actually changing the url back
		return (True, None) #press Show Answer once to reset url, again to flip card. Press answer button once to reset url, again to answer card.
	return handled
gui_hooks.webview_did_receive_js_message.append(webview_did_receive_js_message_restore_internal_state)


#mw.reviewer.nextCard()























#front iframe to front url to oldurl
#############################
#back iframe to back url to oldurl


















###########failed code below here
def reviewer_will_init_answer_buttons_restore_internal_state(buttons_tuple, reviewer, card):
	global iframe2open_link_internally_oldurl
	#print(iframe2open_link_internally_oldurl, " ← ", card._note.fields[0])
	print(card._note.fields[0], " → ", iframe2open_link_internally_oldurl)
	#document.location.href="http://127.0.0.1:55431/_anki/legacyPageData?id=3054436143888" //js, paste the copied url
	#mw.reviewer.web.setUrl(aqt.qt.QUrl("https://www.checkercruncher.com/problems/1051"))
	mw.reviewer.web.setUrl(iframe2open_link_internally_oldurl) #py, paste the copied url
	mw.reviewer.web.set_open_links_externally(True) #py, optional, reset normal link click handling
	#click show answer
#	iframe2open_link_internally_oldurl = QUrl("unset")
	return buttons_tuple
#gui_hooks.reviewer_will_init_answer_buttons.prepend(reviewer_will_init_answer_buttons_restore_internal_state)

def reviewer_will_play_answer_sounds_restore_internal_state(card, tags):
	global iframe2open_link_internally_oldurl
	#print(iframe2open_link_internally_oldurl, " ← ", card._note.fields[0])
	print(card._note.fields[0], " → ", iframe2open_link_internally_oldurl)
	#document.location.href="http://127.0.0.1:55431/_anki/legacyPageData?id=3054436143888" //js, paste the copied url
	#mw.reviewer.web.setUrl(aqt.qt.QUrl("https://www.checkercruncher.com/problems/1051"))
	mw.reviewer.web.setUrl(iframe2open_link_internally_oldurl) #py, paste the copied url
	mw.reviewer.web.set_open_links_externally(True) #py, optional, reset normal link click handling
	#click show answer
#	iframe2open_link_internally_oldurl = QUrl("unset")
	#return buttons_tuple
#gui_hooks.reviewer_will_play_answer_sounds.prepend(reviewer_will_play_answer_sounds_restore_internal_state)

def card_will_show_restore_internal_state(text, card, kind):
	global iframe2open_link_internally_oldurl
	#print(iframe2open_link_internally_oldurl, " ← ", card._note.fields[0])
	print(card._note.fields[0], " → ", iframe2open_link_internally_oldurl)
	#document.location.href="http://127.0.0.1:55431/_anki/legacyPageData?id=3054436143888" //js, paste the copied url
	#mw.reviewer.web.setUrl(aqt.qt.QUrl("https://www.checkercruncher.com/problems/1051"))
	#if(iframe2open_link_internally_oldurl.url() != "unset")
	mw.reviewer.web.setUrl(iframe2open_link_internally_oldurl) #py, paste the copied url
	mw.reviewer.web.set_open_links_externally(True) #py, optional, reset normal link click handling
	#click show answer
#	iframe2open_link_internally_oldurl = QUrl("unset")
	#return buttons_tuple
	return text
#gui_hooks.card_will_show.prepend(card_will_show_restore_internal_state)


###chatgpt attempt 1 failure
def card_will_show_fix(text, card, kind):
    global iframe2open_link_internally_oldurl
    if iframe2open_link_internally_oldurl.url() != "unset":	#if was on checkercruncher
        if not ("iframe2internal::front" in card._note.tags or "iframe2internal::back" in card._note.tags):	#and moving to not checkercruncher. attempt 3 SUCCESS!
            mw.reviewer.web.setUrl(iframe2open_link_internally_oldurl)
            mw.reviewer.web.set_open_links_externally(True)
            iframe2open_link_internally_oldurl = QUrl("unset")
            
            #mw.moveToState("deckBrowser")
            mw.moveToState("review") # reloads front side checkercruncher on back side review buttons #failure 2
    return text

gui_hooks.card_will_show.append(card_will_show_fix)
###chatgpt attempt 1 failure

iframe note type, card’s front-side template

<script>
// baseurl::{{Tags}} + {{baseurl tag + iframe url}}
// use iframeurl or $("iframe#my_iframe").attr("src") instead of `{{baseurl tag + iframe url}}`
baseurl="";
iframeurl=`{{baseurl tag + iframe url}}`;
baseurl=`{{Tags}}`.split(' ').filter(tag => tag.startsWith("baseurl::"))[0].slice("baseurl::".length);
//alert(baseurl)
iframeurl=baseurl+iframeurl;
//alert(iframeurl)
$("iframe#my_iframe").attr("src",iframeurl);
$("div#context a").attr("href",iframeurl);
</script>

<!--mw.reviewer.web._page.open_links_externally=False-->
{{#Context}}<div id="context" style="font-family:Babelstone Flags"><a href="{{baseurl tag + iframe url}}">{{Context}}</a> 📶{{#Do whole collection instead of single puzzle?}}<!--🧩🧩🧩Do whole collection-->{{/Do whole collection instead of single puzzle?}}<span id="elo2kyudan"></span></div>{{/Context}}

<script>
//Cookie monster https://ankiweb.net/shared/info/1501583548
document.cookie = "pieceSet=western;SameSite=None;Secure";//domain=.lishogi.org";
document.cookie = "pieceStyle=HIDETCHI;SameSite=None;Secure;domain=playshogi.com";
document.cookie = "sid=REDACTED;SameSite=None;Secure;domain=playshogi.com";
document.cookie = "pieceStyle=HIDETCHI;SameSite=None;Secure";//domain=playshogi.com";
document.cookie = "pieceStyle=HIDETCHI;SameSite=None;Secure;domain=127.0.0.1";
localStorage.setItem('xiangqi.pieceLocale', "\"en\""); document.cookie = "xiangqi.pieceLocale=\"en\";SameSite=None;Secure";//domain=play.xiangqi.com";

document.cookie="sid=;Domain=playshogi.com;Secure=false;HostOnly=true;HttpOnly=false;Path=/;SameSite=None";
document.cookie = "janggi-piece=2;Domain=pychess.com;Secure=false;HostOnly=true;HttpOnly=false;Path=/;SameSite=None";
document.cookie="ogs.preference.language=en;Domain=online-go.com";
/*
Creación:"Sun, 08 Jan 2023 01:54:43 GMT"
Expira / Tiempo máximo:"Sun, 22 Jan 2023 01:54:43 GMT"
Tamaño:39
Último acceso:"Sun, 08 Jan 2023 01:54:43 GMT"
*/
</script>

<!-- https://www.webcomponents.org/element/x-frame-bypass -- >
<script crossorigin="anonymous" src="//unpkg.com/@ungap/custom-elements"></script>
<script crossorigin="anonymous" type="module" src="https://unpkg.com/x-frame-bypass"></script>
<iframe crossorigin="anonymous" is="x-frame-bypass" src="https://www.checkercruncher.com/problems/17"></iframe>-->
<a id="nogestures" class="tappable">
<iframe id="my_iframe" is="x-frame-bypass" src="{{baseurl tag + iframe url}}" src2="_iframe.php?url={{baseurl tag + iframe url}}" style="height: 100vh; width:100%;" seamless="seamless"><a href="{{baseurl tag + iframe url}}">{{baseurl tag + iframe url}}</a></iframe>
</a>

<script>
return;
//import from https://online-go.com/puzzles by opening a collection, pasting this into the console, copying the output message into a file, then importing
var collection=$("dt:contains('Col')").next().text(); var s=""; $("#selected_puzzle option").each(function() { s+="https://online-go.com/puzzle/"+$(this).val()+"	Go "+collection+"\n"; }); console.log(s);

//import multiple lishogi.org puzzles with $(".study__chapters div").each(function(e){c=document.URL.split('/');c.pop();c=c.join('/')+"/"+$(this).attr("data-id");console.log(c)})

//import from eterna using Array.from(document.querySelectorAll('a:not(.dropdown-item):not(.nav-link)[href^="/puzzles/"][created]')).map(e=>'https://eternagame.org'+e.getAttribute('href')+'/play');
</script>

<!--<script>
return;
//document.location.href="{{iframe url}}";
//alert(document.location.href);
//var iframe = $('iframe')[0];
var iframe = document.getElementById('my_iframe');
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;

var scriptSource = ```
	window.addEventListener('DOMContentLoaded', function() {
		{{inject-script}}
	});
```;
var script = iframeDocument.createElement("script");
var source = iframeDocument.createTextNode(scriptSource);
script.appendChild(source);
iframeDocument.body.appendChild(script);
</script>-->

<style>@import url("_global_front.css");</style>

<script>
//Anki-shogi-tsume-international.ahk
//Click 298, 395
//Sleep 500
//Click 725, 444
//Sleep 100
//Click 950, 698

//if (document.getElementById('context')?.innerText.match(/Shogi/)) {
//	$('#context').prepend("[<s>click G16</s>] "); //set pieces to international style
//}

if (iframeurl.includes("checkercruncher.com")) {
	//Refused to display 'https://www.checkercruncher.com/problems/1' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
//if($(".win")[0])
if(!$(".firefox")[0])
	window.location=iframeurl; //open in external browser
	//document.domain = 'checkercruncher.com';
}

if (iframeurl.includes("square-root-calculator")) {
	$('#context').text("Calculate √"+parseInt(Math.random()*1000)+" by hand to three decimal places"); //sqrt(random)
	//$('#context').html("Calculate <anki-mathjax>\sqrt {"+parseInt(Math.random()*1000)+"}</anki-mathjax> by hand to three decimal places"); //sqrt(random)
//MathJax.Hub.Typeset();
}

if (iframeurl.includes("eternagame.org")) {
	//document.getElementById('my_iframe').src='_iframe.php?url='+iframeurl;
//document.getElementById('my_iframe').src='http://localhost:80'; //ngix
	$("#context a").html("Eterna: "+$("#context a").html()+" (click here to open)");
//https://eternagame.org/puzzles/11098074/play
//document.getElementById('my_iframe').src="https://eternagame.org/eternajs/dist/prod/index.html?puzzle=11098074";
//document.getElementById('my_iframe').src="http://eternagame.org";
window.location.href=iframeurl; // automatically open in external browser
}

if (/biblegateway.com\/passage\/.*version=.*[,;].*/.test(iframeurl)) {
	window.location=iframeurl.replace("&amp;","&");
/*
// ==UserScript==
// @name        biblegateway.com verse merger
// @namespace   Violentmonkey Scripts
// @match       https://www.biblegateway.com/passage/*&version=*;*
// @match       https://www.biblegateway.com/passage/*&version=*,*
// @grant       none
// @version     1.0
// @author      -
// @description 3/4/2024, 09:15:15
// ==/UserScript==

(function() {
//https://www.biblegateway.com/passage/?search=G%C3%A9nesis%201&version=NVI,NIV
console.log("verse merger running");

colors=["black,grey"];//["orange,purple"]
var left=document.querySelector(".version-NVI .text-html").innerHTML.split(/(?=<sup class="versenum">)/);//split before versenum
var right=document.querySelector(".version-NIV .text-html").innerHTML.split(/(?=<sup class="versenum">)/);
right[0]=right[0].replace('<span class="chapternum">','<span style="color:'+colors[1]+'"><span class="chapternum">');//verse 1 english wants to be orange
left.unshift(...left.shift().split(/(?<=<\/h3>)/));//split after h3
right.unshift(...right.shift().split(/(?<=<\/h3>)/));
left.push(...left.pop().split(/(?=<div class="footnotes">)/));//split before footnotes
right.push(...right.pop().split(/(?=<div class="footnotes">)/));
var output="";
for(var v=0;v<left.length;v++){
  output+='<span style="color:'+colors[0]+'">'+left[v]+'<span style="color:'+colors[1]+'">'+right[v];
}
//output=output.replace(/<\/h3>.*<h3>/g," ");//merge headers
document.querySelector(".version-NVI .text-html").innerHTML=output;
document.querySelector('div.passage-col-tools:nth-child(3)').remove();
console.log("verse merger ran");
})();
*/
}
</script>

<script>
if(`{{Tags}}`.includes("::go")) {
	elo=`{{elo}}`.split("-")[0];
	document.querySelector("#elo2kyudan").innerText=30-elo<=0?1-(30-elo)+" Dan":30-elo+" Kyu";
	//00-29, 30-36 → 30kyu-1kyu, 1dan-7dan
}
</script>

<style>
html {
	background-image:url("{{background-image}}");
	background-size:cover;
}
</style>

<script>
//autoburyonmobile - this card may require a pc app, skip it. This allows studying a parent deck that contains both pc-only cards and android-compatible cards.
if(`{{Tags}}`.match("autoburyonmobile") && $(".mobile")[0]) {
	var jsApiContract = { "version": "0.0.3", "developer": "Khonkhortisan" };
	var api = new AnkiDroidJS(jsApiContract);
	api.ankiBuryCard();
}
</script>

<!--
<script type="module" ssrc="https://unpkg.com/x-frame-bypass"></script>
<a href="https://www.checkercruncher.com/problems/1" target='_top'>https://www.checkercruncher.com/problems/1 target='_top'</a>
<a href="https://www.checkercruncher.com/problems/1" target='_blank'>https://www.checkercruncher.com/problems/1 target='_blank'</a>
-->
<script src="__persistentlocalstorage.js"></script>

This should be good until the next thing breaks.