Debugging Console on iPhone / Loading Javascript on iPhone

Hello!

I’m trying to get cm-chessboard to run in Anki, a lightweight javascript chessboard. (There’s also the excellent Chessli deck, but it doesn’t quite do what I want.)

I can get it to work on the desktop version. But on mobile I’m just drawing a blank. Nothing.

Is there a way to get a debugging console on iPhone? I tried to load a javascript console, but again: no luck.

As for cm-chess, I’m suspecting, Anki on iPhone doesn’t find the javascript resources, but I can’t know. For what it’s worth, accessing cm-chessboard’s example page over the internet with Safari yields the expected and desired results.

The assets for cm-board in my current configuration reside in a directory cm inside collection.media. I’m trying the following code (in the card template):

<!-- <script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script> -->

<h2>{{Vorderseite}}</h2>

<div id="board"></div>

<script type="module">
    import {COLOR, Chessboard, BORDER_TYPE} from "./cm/src/Chessboard.js"
		
    new Chessboard(document.getElementById("board"), {
        assetsUrl: "/cm/assets/",
        assetsCache: false,
        position: "rnbqkbnr/pp1p1ppp/4p3/2p5/4P3/2N3P1/PPPP1P1P/R1BQKBNR b KQkq - 0 3",
    })
</script>

As I wrote, this works splendidly on the desktop, but on iPhone: nothing. For comparision, I successfully tried to load the older HtmlTTChess library as external resource file residing in collection.media:

<!-- <script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script> -->

<h2>{{Vorderseite}}</h2>

<div id="board"></div>

<c:chess mode="bw" class="cdig">r1b1kb1r/1pqp1ppp/p1n1pn2/8/3NP3/2N1B3/PPP1BPPP/R2Q1RK1</c:chess>

<script type="module">
	import parseChess from "./_html_tt_chess.js"
	parseChess()
</script>

This works both on desktop and on iPhone. Assuming that there are no javascript incompabilities with the iPhone, the only difference that I can see is that HtmlTTChess is a single file.

Any help getting this to run or at least understanding the problem would be much appreciated.

I’m aware of this guide for loading external files on these forums. I’m extremely grateful for it, but unfortunately it doesn’t cover this problem.

2 Likes

It occurred to me that I could try to redirect the console output to the page itself and at least get an error message. (It’s a simple idea, but catching/listening to errors from imports unfortunately turned out to be far from simple.)

This is what I have now in the card template.

<h2>{{Vorderseite}}</h2>

<!-- Put console messages here on the page: -->
 <pre id="myterminal" style="width:100%;height:30em;overflow:scroll;color:lightgrey;background-color:black;">
 </pre>

<div id="board"></div>

<script type="module">
function send_to_terminal(m) {
// Append our "messages" to our "terminal".
// This could do a better job, for instance by accepting
//  objects as an argument.
    var myterminal=document.getElementById("myterminal");
    var mymessage=document.createElement("div");
    mymessage.appendChild(document.createTextNode(m));
    myterminal.appendChild(mymessage);
}

// This could also be done better. For instance, by also
//  catching errors in promisses (somehow?). But frankly,
//  reading javascript documentation feels like a
// day-trip to Gormenghast. 
(function(){
    var oldError = console.error;
    var oldWarn = console.warn;
    var oldLog = console.log;

    window.onerror = function(message, source, lineno, colno, error) {
        send_to_terminal(message);
    };

    console.error = function(message) {
        send_to_terminal(message);
        oldError.apply(console, arguments);
    }

    console.warn = function(message) {
        send_to_terminal(message);
        oldWarn.apply(console, arguments);
    }

    console.log = function(message) {
        send_to_terminal(message);
        oldLog.apply(console, arguments);
    }
})();

// Import the library. This is the only way I found where I was 
// able to catch errors.
const {
    Chessboard,
    COLOR,
    BORDER_TYPE,
} = await import("./cm/src/Chessboard.js")
          .catch((err) => {
	           send_to_terminal(err.message);
          });


new Chessboard(document.getElementById("board"), {
    assetsUrl: "../assets/",
    position: "rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR w Gkq - 4 11",
    })
</script>

This works somewhat and on desktop, the chessboard appears as intended, too. (Error added just to demonstrate the “terminal” for the screenshot.)

But on my iPhone, I just get the error message: Importing a module script failed. The javascript interpreter, whichever it is, being very helpful here.

Any ideas?

1 Like

It’s just a random guess, but have you tried splitting this import statement into three separate ones?

At the very least this should allow independent testing for each member to see, which ones fail to be imported.

1 Like

Good idea. Just to see what happens, I tried to do that with three static import declarations.

But I don’t know how to redirect errors from that to the page. I was only able to catch errors from a dynamic import() and with those I just get a Namespace object which I destructuring bind to three variables.

However, while reading documentation, again, I noticed that my first attempt at dynamic import with error message was wrong. If I understand things correctly, you can only call await from within an async function. I believe, this is the right way to do it:

import('./cm/src/Chessboard.js')
        .then(module => {
            new module.Chessboard(document.getElementById("board"), {
                assetsUrl: "./assets/",
                position: "rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR w Gkq - 4 11",
            })
        })
        .catch((err) => {
            send_to_terminal(err.message);
        });

Still, on the iPhone: Importing a module script failed.

Anki serves requested files through a port, right? Also on iPhone? Is there some way to access a server log, to see if the files are served that Chessboard.js requests as dependencies?

Have you considered copying the contents of the Chessboard.js to your script wholesale? It’s more of a workaround rather than a proper solution, but still

1 Like

I see 3 possible reasons for failure:

  1. Try using an absolute path instead of relative path for module
  2. collection.media may not support subdirectory
  3. media files that no cards reference should lead with an underscore ‘_’

References:

https://docs.ankiweb.net/media.html#checking-media

2 Likes

Yeah, good idea, too. At least, that might yield some information. The file Chessboard.js mostly contains the imports and the definition of the Chessboard class.

Naively copying it (changing the file paths) doesn’t work even on desktop. Will investigate later when I have more time.

If that doesn’t work, I suppose another step would be to bundle the whole cm-chessboard package into a single file and include that? I’m not all that familiar with Javascript. Maybe with webpack? I’d have to learn how to use that, though.

I think the underscores are just for protection from Anki’s media manager; I learned about them only very recently while doing web-searches for this cm-chessboard project. For years and until recently, I had a dummy card with e.g.

<img src="common.css">

The thing about subfolders looks like a hot trail, though. It does work on Desktop (LInux), but that might not be the case with iOS. If that’s the case, a web bundler might indeed help. Have to investigate later.

1 Like

It’s probably the subfolders.

For what it’s worth, a minimal dynamic import test appears to work correctly on AnkiMobile for me, so they are supported.

1 Like

Indeed, apparently Anki on iPhone doesn’t access subfolders in collection.media.

Reporting a partial success. Still have to solve a couple of problems for my goal, but I’m reporting this here as the solution to this thread, since that part is probably most helpful to others in a similar situation as this or something similar might work with other libraries, too.

I went straight to bundling the javascript into a single file and then loading that. After fiddling around with webpack with increasing frustration, I figured that it tries to do too many things that I don’t understand and went looking for a simpler javascript bundler. The first one that I tried, esbuild was functional and simple enough. I created a build directory, including the source code of cm-chessboard, and put a file myboard.js into it:

import { COLOR, Chessboard, BORDER_TYPE } from "./src/Chessboard";

new Chessboard(document.getElementById("board"), {
    assetsUrl: "./", // No subdirectory!
    position: "rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR w Gkq - 4 11",
});

Then following the instruction on esbuild - Getting started, I then used esbuild to create a javascript file that bundles my file and all the parts of cm-chessboard that it needs together. In a unix shell:

./node_modules/.bin/esbuild myboard.js --bundle --outfile=cmboard-test.js

That file I put into collection.media and used this card template:

<!-- No point in working with card data at this stage. We put that here to keep Anki's card editor happy. -->
<small style="display:none">{{Vorderseite}}</small>

<style>
	svg { width: 100% }
</style>

<div id="board"></div>

<!-- This was used for testing, whether Anki finds the ressource files (standard.svg is a collection of the chess pieces). Indeed, on iOS subfolders don't seem to work at all. -->

<iframe src="./standard.svg"></iframe>

<script src="./cmboard-test.js"></script>

Besides the javascript, cm-chessboard also needs two files: a css-file that I just copied and an SVG containing the pieces. Unfortunately, the path to the latter is partially hardcoded. For now I changed this manually and put the file standard.svg into the root of collection.media.

Result on the iPhone:

Thank you, all!

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.