\n\t\t\t`);\n\t\t\t$(\"#axes\").append(axis);\n\t\t\taxis.on(\"input\", this.setVariations.bind(this))\n\t\t\taxis.on(\"change\", this.updateWords.bind(this))\n\t\t}\n\t}\n\n\tprogress_callback(message) {\n\t\tconsole.log(\"Got json \", message)\n\t\tif (\"type\" in message && message.type == \"ready\") {\n\t\t\t$(\"#bigLoadingModal\").hide()\n\t\t\t$(\"#startModal\").show()\n\t\t} else if (message.type == \"axes\") {\n\t\t\tthis.setupAxes(message.axes)\n\t\t} else if (message.type == \"tables\") {\n\t\t\tconsole.log(\"Hiding spinner\")\n\t\t\t$(\"#spinnerModal\").hide();\n\t\t\tlet table_diff = message.tables;\n\t\t\t$(\"#difftable\").empty();\n\t\t\t$(\"#difftable\").append(this.renderTableDiff({\"tables\":table_diff}, true).children())\n\t\t} else if (message.type == \"glyphs\") {\n\t\t\t$(\"#spinnerModal\").hide();\n\t\t\tlet glyph_diff = message.glyphs;\n\t\t\tthis.renderGlyphDiff(glyph_diff);\n\t\t\t$(\".node\").on(\"click\", function (event) { $(this).children().toggle(); event.stopPropagation() })\n\t\t} else if (message.type == \"words\") {\n\t\t\t$(\"#spinnerModal\").hide();\n\t\t\t$(\"#wordspinner\").hide();\n\t\t\tlet diffs = message.words;\n\t\t\tfor (var [script, words] of Object.entries(diffs)) {\n\t\t\t\tthis.renderWordDiff(script, words);\n\t\t\t}\n\t\t}\n\t}\n\n\tvariationLocation() {\n\t\t// Return the current axis location as a string of the form\n\t\t// tag=value,tag=value\n\t\treturn $(\"#axes input\").map(function () {\n\t\t\treturn `${this.id.replace(\"axis-\", \"\")}=${this.value}`\n\t\t}).get().join(\",\")\n\t}\n\n\n\tletsDoThis() {\n\t\t$(\"#startModal\").hide();\n\t\t$(\"#spinnerModal\").show();\n\t\tconsole.log(\"Current location = \", location)\n\t\tdiffWorker.postMessage({ command: \"axes\", beforeFont: this.beforeFont, afterFont: this.afterFont });\n\t\tdiffWorker.postMessage({ command: \"tables\", beforeFont: this.beforeFont, afterFont: this.afterFont });\n\t\tthis.updateGlyphs();\n\t\tthis.updateWords();\n\t}\n\n\tupdateGlyphs() {\n\t\tlet location = this.variationLocation();\n\t\tdiffWorker.postMessage({ command: \"glyphs\", beforeFont: this.beforeFont, afterFont: this.afterFont, location });\n\t}\n\n\tupdateWords() {\n\t\t$(\"#wordspinner\").show();\n\t\t$(\"#worddiff\").empty();\n\t\tlet location = this.variationLocation();\n\t\tdiffWorker.postMessage({ command: \"words\", beforeFont: this.beforeFont, afterFont: this.afterFont, location });\n\t}\n\n\taddAGlyph(glyph, where) {\n\t\twhere.append(`\n\t\t\t
\n\t\t`);\n\t}\n\trenderGlyphDiff(glyph_diff) {\n\t\t$(\"#glyphdiff\").empty();\n\t\tfor (var [key, glyphs] of Object.entries(glyph_diff)) {\n\t\t\tlet title = key.charAt(0).toUpperCase() + key.slice(1);\n\t\t\tif (glyphs.length > 0) {\n\t\t\t\tlet that = this;\n\t\t\t\t$(\"#glyphdiff\").append($(`
')\n\t\t\t\t$(\"#glyphdiff\").append(place);\n\t\t\t\tglyphs.forEach((glyph) => {\n\t\t\t\t\tthat.addAGlyph(glyph, place)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\t$('[data-toggle=\"tooltip\"]').tooltip()\n\t}\n\n\n\trenderWordDiff(script, diffs) {\n\t\t$(\"#worddiff\").append($(`
${script} words
`));\n\t\tlet place = $('
')\n\t\t$(\"#worddiff\").append(place);\n\t\tdiffs.forEach((glyph) => {\n\t\t\tthis.addAWord(glyph, place)\n\t\t})\n\t\t$('[data-toggle=\"tooltip\"]').tooltip()\n\t}\n\n}\n\n$(function () {\n\twindow.diffenator = new Diffenator();\n\tdiffWorker.onmessage = (e) => window.diffenator.progress_callback(e.data);\n\t$(\"#bigLoadingModal\").show()\n\n\t$('.fontdrop').on(\n\t\t'dragover dragenter',\n\t\tfunction (e) {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\t$(this).addClass(\"dragging\");\n\t\t}\n\t)\n\t$('.fontdrop').on(\n\t\t'dragleave dragend',\n\t\tfunction (e) {\n\t\t\t$(this).removeClass(\"dragging\");\n\t\t}\n\t);\n\n\t$('.fontdrop').on(\n\t\t'drop',\n\t\tfunction (e) {\n\t\t\t$(this).removeClass(\"dragging\");\n\t\t\tif (e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.files.length) {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t\tdiffenator.dropFile(e.originalEvent.dataTransfer.files, this);\n\t\t\t}\n\t\t}\n\t);\n\n\t$(\"#fonttoggle\").click(function () {\n\t\tif ($(this).text() == \"Old\") {\n\t\t\t$(this).text(\"New\");\n\t\t\t$(\".font-before\").removeClass(\"font-before\").addClass(\"font-after\");\n\t\t} else {\n\t\t\t$(this).text(\"Old\");\n\t\t\t$(\".font-after\").removeClass(\"font-after\").addClass(\"font-before\");\n\t\t}\n\t})\n\n})\n\n\n//# sourceURL=webpack:///./index.js?");
+
+/***/ }),
+
+/***/ "./node_modules/worker-loader/dist/cjs.js!./webworker.js":
+/*!***************************************************************!*\
+ !*** ./node_modules/worker-loader/dist/cjs.js!./webworker.js ***!
+ \***************************************************************/
+/*! exports provided: default */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Worker_fn; });\nfunction Worker_fn() {\n return new Worker(__webpack_require__.p + \"bootstrap.worker.js\");\n}\n\n\n//# sourceURL=webpack:///./webworker.js?./node_modules/worker-loader/dist/cjs.js");
+
+/***/ })
+
+}]);
\ No newline at end of file
diff --git a/docs/0.bootstrap.worker.js b/docs/0.bootstrap.worker.js
new file mode 100644
index 0000000..334bc3a
--- /dev/null
+++ b/docs/0.bootstrap.worker.js
@@ -0,0 +1,49 @@
+self["webpackChunk"]([0],{
+
+/***/ "../pkg/diffenator3.js":
+/*!*****************************!*\
+ !*** ../pkg/diffenator3.js ***!
+ \*****************************/
+/*! exports provided: __wbg_set_wasm, diff_words, diff_glyphs, diff_tables, diff, axes, debugging, __wbindgen_object_drop_ref, __wbindgen_string_new, __wbg_call_b3ca7c6051f9bec1, __wbg_new_abda76e883ba8a5f, __wbg_stack_658279fe44541cf6, __wbg_error_f851667af71bcfc6, __wbindgen_debug_string, __wbindgen_throw */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _diffenator3_bg_wasm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./diffenator3_bg.wasm */ \"../pkg/diffenator3_bg.wasm\");\n/* harmony import */ var _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./diffenator3_bg.js */ \"../pkg/diffenator3_bg.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbg_set_wasm\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbg_set_wasm\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"diff_words\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"diff_words\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"diff_glyphs\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"diff_glyphs\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"diff_tables\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"diff_tables\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"diff\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"diff\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axes\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"axes\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"debugging\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"debugging\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_object_drop_ref\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbindgen_object_drop_ref\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_string_new\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbindgen_string_new\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbg_call_b3ca7c6051f9bec1\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbg_call_b3ca7c6051f9bec1\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbg_new_abda76e883ba8a5f\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbg_new_abda76e883ba8a5f\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbg_stack_658279fe44541cf6\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbg_stack_658279fe44541cf6\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbg_error_f851667af71bcfc6\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbg_error_f851667af71bcfc6\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_debug_string\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbindgen_debug_string\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_throw\", function() { return _diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbindgen_throw\"]; });\n\n\n\nObject(_diffenator3_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbg_set_wasm\"])(_diffenator3_bg_wasm__WEBPACK_IMPORTED_MODULE_0__);\n\n\n\n//# sourceURL=webpack:///../pkg/diffenator3.js?");
+
+/***/ }),
+
+/***/ "../pkg/diffenator3_bg.js":
+/*!********************************!*\
+ !*** ../pkg/diffenator3_bg.js ***!
+ \********************************/
+/*! exports provided: __wbg_set_wasm, diff_words, diff_glyphs, diff_tables, diff, axes, debugging, __wbindgen_object_drop_ref, __wbindgen_string_new, __wbg_call_b3ca7c6051f9bec1, __wbg_new_abda76e883ba8a5f, __wbg_stack_658279fe44541cf6, __wbg_error_f851667af71bcfc6, __wbindgen_debug_string, __wbindgen_throw */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+eval("__webpack_require__.r(__webpack_exports__);\n/* WEBPACK VAR INJECTION */(function(module) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbg_set_wasm\", function() { return __wbg_set_wasm; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"diff_words\", function() { return diff_words; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"diff_glyphs\", function() { return diff_glyphs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"diff_tables\", function() { return diff_tables; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"diff\", function() { return diff; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axes\", function() { return axes; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"debugging\", function() { return debugging; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_object_drop_ref\", function() { return __wbindgen_object_drop_ref; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_string_new\", function() { return __wbindgen_string_new; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbg_call_b3ca7c6051f9bec1\", function() { return __wbg_call_b3ca7c6051f9bec1; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbg_new_abda76e883ba8a5f\", function() { return __wbg_new_abda76e883ba8a5f; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbg_stack_658279fe44541cf6\", function() { return __wbg_stack_658279fe44541cf6; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbg_error_f851667af71bcfc6\", function() { return __wbg_error_f851667af71bcfc6; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_debug_string\", function() { return __wbindgen_debug_string; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_throw\", function() { return __wbindgen_throw; });\nlet wasm;\nfunction __wbg_set_wasm(val) {\n wasm = val;\n}\n\n\nconst heap = new Array(128).fill(undefined);\n\nheap.push(undefined, null, true, false);\n\nfunction getObject(idx) { return heap[idx]; }\n\nlet heap_next = heap.length;\n\nfunction dropObject(idx) {\n if (idx < 132) return;\n heap[idx] = heap_next;\n heap_next = idx;\n}\n\nfunction takeObject(idx) {\n const ret = getObject(idx);\n dropObject(idx);\n return ret;\n}\n\nconst lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;\n\nlet cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true });\n\ncachedTextDecoder.decode();\n\nlet cachedUint8Memory0 = null;\n\nfunction getUint8Memory0() {\n if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) {\n cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);\n }\n return cachedUint8Memory0;\n}\n\nfunction getStringFromWasm0(ptr, len) {\n ptr = ptr >>> 0;\n return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));\n}\n\nfunction addHeapObject(obj) {\n if (heap_next === heap.length) heap.push(heap.length + 1);\n const idx = heap_next;\n heap_next = heap[idx];\n\n heap[idx] = obj;\n return idx;\n}\n\nfunction debugString(val) {\n // primitive types\n const type = typeof val;\n if (type == 'number' || type == 'boolean' || val == null) {\n return `${val}`;\n }\n if (type == 'string') {\n return `\"${val}\"`;\n }\n if (type == 'symbol') {\n const description = val.description;\n if (description == null) {\n return 'Symbol';\n } else {\n return `Symbol(${description})`;\n }\n }\n if (type == 'function') {\n const name = val.name;\n if (typeof name == 'string' && name.length > 0) {\n return `Function(${name})`;\n } else {\n return 'Function';\n }\n }\n // objects\n if (Array.isArray(val)) {\n const length = val.length;\n let debug = '[';\n if (length > 0) {\n debug += debugString(val[0]);\n }\n for(let i = 1; i < length; i++) {\n debug += ', ' + debugString(val[i]);\n }\n debug += ']';\n return debug;\n }\n // Test for built-in\n const builtInMatches = /\\[object ([^\\]]+)\\]/.exec(toString.call(val));\n let className;\n if (builtInMatches.length > 1) {\n className = builtInMatches[1];\n } else {\n // Failed to match the standard '[object ClassName]'\n return toString.call(val);\n }\n if (className == 'Object') {\n // we're a user defined class or Object\n // JSON.stringify avoids problems with cycles, and is generally much\n // easier than looping through ownProperties of `val`.\n try {\n return 'Object(' + JSON.stringify(val) + ')';\n } catch (_) {\n return 'Object';\n }\n }\n // errors\n if (val instanceof Error) {\n return `${val.name}: ${val.message}\\n${val.stack}`;\n }\n // TODO we could test for more things here, like `Set`s and `Map`s.\n return className;\n}\n\nlet WASM_VECTOR_LEN = 0;\n\nconst lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder;\n\nlet cachedTextEncoder = new lTextEncoder('utf-8');\n\nconst encodeString = (typeof cachedTextEncoder.encodeInto === 'function'\n ? function (arg, view) {\n return cachedTextEncoder.encodeInto(arg, view);\n}\n : function (arg, view) {\n const buf = cachedTextEncoder.encode(arg);\n view.set(buf);\n return {\n read: arg.length,\n written: buf.length\n };\n});\n\nfunction passStringToWasm0(arg, malloc, realloc) {\n\n if (realloc === undefined) {\n const buf = cachedTextEncoder.encode(arg);\n const ptr = malloc(buf.length, 1) >>> 0;\n getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);\n WASM_VECTOR_LEN = buf.length;\n return ptr;\n }\n\n let len = arg.length;\n let ptr = malloc(len, 1) >>> 0;\n\n const mem = getUint8Memory0();\n\n let offset = 0;\n\n for (; offset < len; offset++) {\n const code = arg.charCodeAt(offset);\n if (code > 0x7F) break;\n mem[ptr + offset] = code;\n }\n\n if (offset !== len) {\n if (offset !== 0) {\n arg = arg.slice(offset);\n }\n ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;\n const view = getUint8Memory0().subarray(ptr + offset, ptr + len);\n const ret = encodeString(arg, view);\n\n offset += ret.written;\n ptr = realloc(ptr, len, offset, 1) >>> 0;\n }\n\n WASM_VECTOR_LEN = offset;\n return ptr;\n}\n\nlet cachedInt32Memory0 = null;\n\nfunction getInt32Memory0() {\n if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) {\n cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);\n }\n return cachedInt32Memory0;\n}\n\nfunction passArray8ToWasm0(arg, malloc) {\n const ptr = malloc(arg.length * 1, 1) >>> 0;\n getUint8Memory0().set(arg, ptr / 1);\n WASM_VECTOR_LEN = arg.length;\n return ptr;\n}\n\nlet stack_pointer = 128;\n\nfunction addBorrowedObject(obj) {\n if (stack_pointer == 1) throw new Error('out of js stack');\n heap[--stack_pointer] = obj;\n return stack_pointer;\n}\n/**\n* @param {Uint8Array} font_a\n* @param {Uint8Array} font_b\n* @param {string} location\n* @param {Function} f\n*/\nfunction diff_words(font_a, font_b, location, f) {\n try {\n const ptr0 = passArray8ToWasm0(font_a, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArray8ToWasm0(font_b, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n const ptr2 = passStringToWasm0(location, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n const len2 = WASM_VECTOR_LEN;\n wasm.diff_words(ptr0, len0, ptr1, len1, ptr2, len2, addBorrowedObject(f));\n } finally {\n heap[stack_pointer++] = undefined;\n }\n}\n\n/**\n* @param {Uint8Array} font_a\n* @param {Uint8Array} font_b\n* @param {string} location\n* @param {Function} f\n*/\nfunction diff_glyphs(font_a, font_b, location, f) {\n try {\n const ptr0 = passArray8ToWasm0(font_a, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArray8ToWasm0(font_b, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n const ptr2 = passStringToWasm0(location, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n const len2 = WASM_VECTOR_LEN;\n wasm.diff_glyphs(ptr0, len0, ptr1, len1, ptr2, len2, addBorrowedObject(f));\n } finally {\n heap[stack_pointer++] = undefined;\n }\n}\n\n/**\n* @param {Uint8Array} font_a\n* @param {Uint8Array} font_b\n* @param {Function} f\n*/\nfunction diff_tables(font_a, font_b, f) {\n try {\n const ptr0 = passArray8ToWasm0(font_a, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArray8ToWasm0(font_b, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n wasm.diff_tables(ptr0, len0, ptr1, len1, addBorrowedObject(f));\n } finally {\n heap[stack_pointer++] = undefined;\n }\n}\n\n/**\n* @param {Uint8Array} font_a\n* @param {Uint8Array} font_b\n* @returns {string}\n*/\nfunction diff(font_a, font_b) {\n let deferred3_0;\n let deferred3_1;\n try {\n const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);\n const ptr0 = passArray8ToWasm0(font_a, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArray8ToWasm0(font_b, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n wasm.diff(retptr, ptr0, len0, ptr1, len1);\n var r0 = getInt32Memory0()[retptr / 4 + 0];\n var r1 = getInt32Memory0()[retptr / 4 + 1];\n deferred3_0 = r0;\n deferred3_1 = r1;\n return getStringFromWasm0(r0, r1);\n } finally {\n wasm.__wbindgen_add_to_stack_pointer(16);\n wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);\n }\n}\n\n/**\n* @param {Uint8Array} font_a\n* @param {Uint8Array} font_b\n* @returns {string}\n*/\nfunction axes(font_a, font_b) {\n let deferred3_0;\n let deferred3_1;\n try {\n const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);\n const ptr0 = passArray8ToWasm0(font_a, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArray8ToWasm0(font_b, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n wasm.axes(retptr, ptr0, len0, ptr1, len1);\n var r0 = getInt32Memory0()[retptr / 4 + 0];\n var r1 = getInt32Memory0()[retptr / 4 + 1];\n deferred3_0 = r0;\n deferred3_1 = r1;\n return getStringFromWasm0(r0, r1);\n } finally {\n wasm.__wbindgen_add_to_stack_pointer(16);\n wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);\n }\n}\n\n/**\n*/\nfunction debugging() {\n wasm.debugging();\n}\n\nfunction handleError(f, args) {\n try {\n return f.apply(this, args);\n } catch (e) {\n wasm.__wbindgen_exn_store(addHeapObject(e));\n }\n}\n\nfunction __wbindgen_object_drop_ref(arg0) {\n takeObject(arg0);\n};\n\nfunction __wbindgen_string_new(arg0, arg1) {\n const ret = getStringFromWasm0(arg0, arg1);\n return addHeapObject(ret);\n};\n\nfunction __wbg_call_b3ca7c6051f9bec1() { return handleError(function (arg0, arg1, arg2) {\n const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));\n return addHeapObject(ret);\n}, arguments) };\n\nfunction __wbg_new_abda76e883ba8a5f() {\n const ret = new Error();\n return addHeapObject(ret);\n};\n\nfunction __wbg_stack_658279fe44541cf6(arg0, arg1) {\n const ret = getObject(arg1).stack;\n const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n const len1 = WASM_VECTOR_LEN;\n getInt32Memory0()[arg0 / 4 + 1] = len1;\n getInt32Memory0()[arg0 / 4 + 0] = ptr1;\n};\n\nfunction __wbg_error_f851667af71bcfc6(arg0, arg1) {\n let deferred0_0;\n let deferred0_1;\n try {\n deferred0_0 = arg0;\n deferred0_1 = arg1;\n console.error(getStringFromWasm0(arg0, arg1));\n } finally {\n wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);\n }\n};\n\nfunction __wbindgen_debug_string(arg0, arg1) {\n const ret = debugString(getObject(arg1));\n const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);\n const len1 = WASM_VECTOR_LEN;\n getInt32Memory0()[arg0 / 4 + 1] = len1;\n getInt32Memory0()[arg0 / 4 + 0] = ptr1;\n};\n\nfunction __wbindgen_throw(arg0, arg1) {\n throw new Error(getStringFromWasm0(arg0, arg1));\n};\n\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../www/node_modules/webpack/buildin/harmony-module.js */ \"./node_modules/webpack/buildin/harmony-module.js\")(module)))\n\n//# sourceURL=webpack:///../pkg/diffenator3_bg.js?");
+
+/***/ }),
+
+/***/ "../pkg/diffenator3_bg.wasm":
+/*!**********************************!*\
+ !*** ../pkg/diffenator3_bg.wasm ***!
+ \**********************************/
+/*! exports provided: memory, diff_words, diff_glyphs, diff_tables, diff, axes, debugging, __wbindgen_malloc, __wbindgen_realloc, __wbindgen_add_to_stack_pointer, __wbindgen_free, __wbindgen_exn_store */
+/***/ (function(module, exports, __webpack_require__) {
+
+eval("\"use strict\";\n// Instantiate WebAssembly module\nvar wasmExports = __webpack_require__.w[module.i];\n__webpack_require__.r(exports);\n// export exports from WebAssembly module\nfor(var name in wasmExports) if(name != \"__webpack_init__\") exports[name] = wasmExports[name];\n// exec imports from WebAssembly module (for esm order)\n/* harmony import */ var m0 = __webpack_require__(/*! ./diffenator3_bg.js */ \"../pkg/diffenator3_bg.js\");\n\n\n// exec wasm module\nwasmExports[\"__webpack_init__\"]()\n\n//# sourceURL=webpack:///../pkg/diffenator3_bg.wasm?");
+
+/***/ }),
+
+/***/ "./node_modules/webpack/buildin/harmony-module.js":
+/*!*******************************************!*\
+ !*** (webpack)/buildin/harmony-module.js ***!
+ \*******************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+eval("module.exports = function(originalModule) {\n\tif (!originalModule.webpackPolyfill) {\n\t\tvar module = Object.create(originalModule);\n\t\t// module.parent = undefined by default\n\t\tif (!module.children) module.children = [];\n\t\tObject.defineProperty(module, \"loaded\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.l;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"id\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.i;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"exports\", {\n\t\t\tenumerable: true\n\t\t});\n\t\tmodule.webpackPolyfill = 1;\n\t}\n\treturn module;\n};\n\n\n//# sourceURL=webpack:///(webpack)/buildin/harmony-module.js?");
+
+/***/ })
+
+});
\ No newline at end of file
diff --git a/docs/1.bootstrap.js b/docs/1.bootstrap.js
deleted file mode 100644
index 4ec60a0..0000000
--- a/docs/1.bootstrap.js
+++ /dev/null
@@ -1 +0,0 @@
-(window.webpackJsonp=window.webpackJsonp||[]).push([[1],[,function(e,t,r){"use strict";r.r(t);const n=new function(){return new Worker(r.p+"bootstrap.worker.js")};jQuery.fn.shake=function(e,t,r){e=void 0===e?100:e,t=void 0===t?10:t,r=void 0===r?3:r;var n=$(this);n.css("position","relative");for(var o=0;o
");if(!e)return r;if(Array.isArray(e)){var n=$("
");n.addClass("attr-before"),n.html(" "+e[0]+" ");var o=$("
");return o.addClass("attr-after"),o.append(this.renderTableDiff(e[1],!0).children()),r.append(n),r.append(o),r}if(e.constructor!=Object){var a=$("
");return a.html(e),r.append(a),r}for(const[n,o]of Object.entries(e)){var s=$("
");s.addClass("node"),t||s.hide(),s.append(n),s.append(this.renderTableDiff(o,!1).children()),r.append(s)}return r}progress_callback(e){try{let n=JSON.parse(e);if(console.log("Hiding spinner"),$("#spinnerModal").hide(0),console.log(n),n.tables){let e=n.tables;$("#difftable").empty(),$("#difftable").append(this.renderTableDiff(e,!0).children())}else if(n.glyphs){let e=n.glyphs;this.renderGlyphDiff(e),$(".node").on("click",(function(e){$(this).children().toggle(),e.stopPropagation()}))}else if(n.words)for(var[t,r]of(console.log(t),Object.entries(n.words)))this.renderWordDiff(t,r)}catch(e){console.error(e)}}letsDoThis(){$("#startModal").hide(),$("#spinnerModal").show(),$("#wordspinner").show(),console.log("Showing spinner"),n.onmessage=e=>this.progress_callback(e.data),n.postMessage({beforeFont:this.beforeFont,afterFont:this.afterFont})}addAGlyph(e,t){t.append(`\n\t\t\t
\n\t\t \n\t ${e.string}\n\t \n\t\t\t
\n\t\t`)}addAWord(e,t){t.append(`\n\t\t\t
\n\t\t \n\t ${e.word}\n\t \n\t\t\t
\n\t\t`)}renderGlyphDiff(e){for(var[t,r]of($("#glyphdiff").empty(),Object.entries(e))){let e=t.charAt(0).toUpperCase()+t.slice(1);if(r.length>0){let t=this;$("#glyphdiff").append($(`
${e} glyphs
`));let n=$('
');$("#glyphdiff").append(n),r.forEach(e=>{t.addAGlyph(e,n)})}}$('[data-toggle="tooltip"]').tooltip()}renderWordDiff(e,t){$("#wordspinner").hide(),$("#worddiff").append($(`
${e} words
`));let r=$('
');$("#worddiff").append(r),t.forEach(e=>{this.addAWord(e,r)}),$('[data-toggle="tooltip"]').tooltip()}}$((function(){window.diffenator=new o,$("#startModal").show(),$(".fontdrop").on("dragover dragenter",(function(e){e.preventDefault(),e.stopPropagation(),$(this).addClass("dragging")})),$(".fontdrop").on("dragleave dragend",(function(e){$(this).removeClass("dragging")})),$(".fontdrop").on("drop",(function(e){$(this).removeClass("dragging"),e.originalEvent.dataTransfer&&e.originalEvent.dataTransfer.files.length&&(e.preventDefault(),e.stopPropagation(),diffenator.dropFile(e.originalEvent.dataTransfer.files,this))})),$("#fonttoggle").click((function(){"Old"==$(this).text()?($(this).text("New"),$(".font-before").removeClass("font-before").addClass("font-after")):($(this).text("Old"),$(".font-after").removeClass("font-after").addClass("font-before"))}))}))}]]);
\ No newline at end of file
diff --git a/docs/1.bootstrap.worker.js b/docs/1.bootstrap.worker.js
deleted file mode 100644
index 0560ad6..0000000
--- a/docs/1.bootstrap.worker.js
+++ /dev/null
@@ -1 +0,0 @@
-self.webpackChunk([1],[,function(n,t,e){"use strict";e.r(t);var r=e(4),o=e(2);e.d(t,"__wbg_set_wasm",(function(){return o.d})),e.d(t,"progressive_diff",(function(){return o.l})),e.d(t,"diff",(function(){return o.k})),e.d(t,"debugging",(function(){return o.j})),e.d(t,"__wbindgen_object_drop_ref",(function(){return o.g})),e.d(t,"__wbindgen_string_new",(function(){return o.h})),e.d(t,"__wbg_call_b3ca7c6051f9bec1",(function(){return o.a})),e.d(t,"__wbg_new_abda76e883ba8a5f",(function(){return o.c})),e.d(t,"__wbg_stack_658279fe44541cf6",(function(){return o.e})),e.d(t,"__wbg_error_f851667af71bcfc6",(function(){return o.b})),e.d(t,"__wbindgen_debug_string",(function(){return o.f})),e.d(t,"__wbindgen_throw",(function(){return o.i})),Object(o.d)(r)},function(n,t,e){"use strict";(function(n){let r;function o(n){r=n}e.d(t,"d",(function(){return o})),e.d(t,"l",(function(){return j})),e.d(t,"k",(function(){return v})),e.d(t,"j",(function(){return x})),e.d(t,"g",(function(){return E})),e.d(t,"h",(function(){return A})),e.d(t,"a",(function(){return T})),e.d(t,"c",(function(){return $})),e.d(t,"e",(function(){return P})),e.d(t,"b",(function(){return S})),e.d(t,"f",(function(){return D})),e.d(t,"i",(function(){return I}));const i=new Array(128).fill(void 0);function c(n){return i[n]}i.push(void 0,null,!0,!1);let u=i.length;function f(n){const t=c(n);return function(n){n<132||(i[n]=u,u=n)}(n),t}let l=new("undefined"==typeof TextDecoder?(0,n.require)("util").TextDecoder:TextDecoder)("utf-8",{ignoreBOM:!0,fatal:!0});l.decode();let _=null;function d(){return null!==_&&0!==_.byteLength||(_=new Uint8Array(r.memory.buffer)),_}function a(n,t){return n>>>=0,l.decode(d().subarray(n,n+t))}function s(n){u===i.length&&i.push(i.length+1);const t=u;return u=i[t],i[t]=n,t}let b=0;let g=new("undefined"==typeof TextEncoder?(0,n.require)("util").TextEncoder:TextEncoder)("utf-8");const w="function"==typeof g.encodeInto?function(n,t){return g.encodeInto(n,t)}:function(n,t){const e=g.encode(n);return t.set(e),{read:n.length,written:e.length}};function y(n,t,e){if(void 0===e){const e=g.encode(n),r=t(e.length,1)>>>0;return d().subarray(r,r+e.length).set(e),b=e.length,r}let r=n.length,o=t(r,1)>>>0;const i=d();let c=0;for(;c
127)break;i[o+c]=t}if(c!==r){0!==c&&(n=n.slice(c)),o=e(o,r,r=c+3*n.length,1)>>>0;const t=d().subarray(o+c,o+r);c+=w(n,t).written,o=e(o,r,c,1)>>>0}return b=c,o}let h=null;function p(){return null!==h&&0!==h.byteLength||(h=new Int32Array(r.memory.buffer)),h}function m(n,t){const e=t(1*n.length,1)>>>0;return d().set(n,e/1),b=n.length,e}let k=128;function j(n,t,e){try{const o=m(n,r.__wbindgen_malloc),c=b,u=m(t,r.__wbindgen_malloc),f=b;r.progressive_diff(o,c,u,f,function(n){if(1==k)throw new Error("out of js stack");return i[--k]=n,k}(e))}finally{i[k++]=void 0}}function v(n,t){let e,o;try{const u=r.__wbindgen_add_to_stack_pointer(-16),f=m(n,r.__wbindgen_malloc),l=b,_=m(t,r.__wbindgen_malloc),d=b;r.diff(u,f,l,_,d);var i=p()[u/4+0],c=p()[u/4+1];return e=i,o=c,a(i,c)}finally{r.__wbindgen_add_to_stack_pointer(16),r.__wbindgen_free(e,o,1)}}function x(){r.debugging()}function O(n,t){try{return n.apply(this,t)}catch(n){r.__wbindgen_exn_store(s(n))}}function E(n){f(n)}function A(n,t){return s(a(n,t))}function T(){return O((function(n,t,e){return s(c(n).call(c(t),c(e)))}),arguments)}function $(){return s(new Error)}function P(n,t){const e=y(c(t).stack,r.__wbindgen_malloc,r.__wbindgen_realloc),o=b;p()[n/4+1]=o,p()[n/4+0]=e}function S(n,t){let e,o;try{e=n,o=t,console.error(a(n,t))}finally{r.__wbindgen_free(e,o,1)}}function D(n,t){const e=y(function n(t){const e=typeof t;if("number"==e||"boolean"==e||null==t)return""+t;if("string"==e)return`"${t}"`;if("symbol"==e){const n=t.description;return null==n?"Symbol":`Symbol(${n})`}if("function"==e){const n=t.name;return"string"==typeof n&&n.length>0?`Function(${n})`:"Function"}if(Array.isArray(t)){const e=t.length;let r="[";e>0&&(r+=n(t[0]));for(let o=1;o1))return toString.call(t);if(o=r[1],"Object"==o)try{return"Object("+JSON.stringify(t)+")"}catch(n){return"Object"}return t instanceof Error?`${t.name}: ${t.message}\n${t.stack}`:o}(c(t)),r.__wbindgen_malloc,r.__wbindgen_realloc),o=b;p()[n/4+1]=o,p()[n/4+0]=e}function I(n,t){throw new Error(a(n,t))}}).call(this,e(3)(n))},function(n,t){n.exports=function(n){if(!n.webpackPolyfill){var t=Object.create(n);t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),Object.defineProperty(t,"exports",{enumerable:!0}),t.webpackPolyfill=1}return t}},function(n,t,e){"use strict";var r=e.w[n.i];for(var o in e.r(t),r)"__webpack_init__"!=o&&(t[o]=r[o]);e(2);r.__webpack_init__()}]);
\ No newline at end of file
diff --git a/docs/b595417675cfd0908ff2.module.wasm b/docs/4e63794b39b83b4a3b88.module.wasm
similarity index 93%
rename from docs/b595417675cfd0908ff2.module.wasm
rename to docs/4e63794b39b83b4a3b88.module.wasm
index ef8e9e4..32f6860 100644
Binary files a/docs/b595417675cfd0908ff2.module.wasm and b/docs/4e63794b39b83b4a3b88.module.wasm differ
diff --git a/docs/bootstrap.js b/docs/bootstrap.js
index 8ead0bc..371d42c 100644
--- a/docs/bootstrap.js
+++ b/docs/bootstrap.js
@@ -1 +1,215 @@
-!function(e){function r(r){for(var t,o,u=r[0],i=r[1],a=0,l=[];aconsole.error("Error importing `index.js`:",e))}]);
\ No newline at end of file
+/******/ (function(modules) { // webpackBootstrap
+/******/ // install a JSONP callback for chunk loading
+/******/ function webpackJsonpCallback(data) {
+/******/ var chunkIds = data[0];
+/******/ var moreModules = data[1];
+/******/
+/******/
+/******/ // add "moreModules" to the modules object,
+/******/ // then flag all "chunkIds" as loaded and fire callback
+/******/ var moduleId, chunkId, i = 0, resolves = [];
+/******/ for(;i < chunkIds.length; i++) {
+/******/ chunkId = chunkIds[i];
+/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
+/******/ resolves.push(installedChunks[chunkId][0]);
+/******/ }
+/******/ installedChunks[chunkId] = 0;
+/******/ }
+/******/ for(moduleId in moreModules) {
+/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
+/******/ modules[moduleId] = moreModules[moduleId];
+/******/ }
+/******/ }
+/******/ if(parentJsonpFunction) parentJsonpFunction(data);
+/******/
+/******/ while(resolves.length) {
+/******/ resolves.shift()();
+/******/ }
+/******/
+/******/ };
+/******/
+/******/
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // object to store loaded and loading chunks
+/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
+/******/ // Promise = chunk loading, 0 = chunk loaded
+/******/ var installedChunks = {
+/******/ "main": 0
+/******/ };
+/******/
+/******/
+/******/
+/******/ // script path function
+/******/ function jsonpScriptSrc(chunkId) {
+/******/ return __webpack_require__.p + "" + chunkId + ".bootstrap.js"
+/******/ }
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/ // This file contains only the entry chunk.
+/******/ // The chunk loading function for additional chunks
+/******/ __webpack_require__.e = function requireEnsure(chunkId) {
+/******/ var promises = [];
+/******/
+/******/
+/******/ // JSONP chunk loading for javascript
+/******/
+/******/ var installedChunkData = installedChunks[chunkId];
+/******/ if(installedChunkData !== 0) { // 0 means "already installed".
+/******/
+/******/ // a Promise means "currently loading".
+/******/ if(installedChunkData) {
+/******/ promises.push(installedChunkData[2]);
+/******/ } else {
+/******/ // setup Promise in chunk cache
+/******/ var promise = new Promise(function(resolve, reject) {
+/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
+/******/ });
+/******/ promises.push(installedChunkData[2] = promise);
+/******/
+/******/ // start chunk loading
+/******/ var script = document.createElement('script');
+/******/ var onScriptComplete;
+/******/
+/******/ script.charset = 'utf-8';
+/******/ script.timeout = 120;
+/******/ if (__webpack_require__.nc) {
+/******/ script.setAttribute("nonce", __webpack_require__.nc);
+/******/ }
+/******/ script.src = jsonpScriptSrc(chunkId);
+/******/
+/******/ // create error before stack unwound to get useful stacktrace later
+/******/ var error = new Error();
+/******/ onScriptComplete = function (event) {
+/******/ // avoid mem leaks in IE.
+/******/ script.onerror = script.onload = null;
+/******/ clearTimeout(timeout);
+/******/ var chunk = installedChunks[chunkId];
+/******/ if(chunk !== 0) {
+/******/ if(chunk) {
+/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
+/******/ var realSrc = event && event.target && event.target.src;
+/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
+/******/ error.name = 'ChunkLoadError';
+/******/ error.type = errorType;
+/******/ error.request = realSrc;
+/******/ chunk[1](error);
+/******/ }
+/******/ installedChunks[chunkId] = undefined;
+/******/ }
+/******/ };
+/******/ var timeout = setTimeout(function(){
+/******/ onScriptComplete({ type: 'timeout', target: script });
+/******/ }, 120000);
+/******/ script.onerror = script.onload = onScriptComplete;
+/******/ document.head.appendChild(script);
+/******/ }
+/******/ }
+/******/ return Promise.all(promises);
+/******/ };
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
+/******/ }
+/******/ };
+/******/
+/******/ // define __esModule on exports
+/******/ __webpack_require__.r = function(exports) {
+/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ }
+/******/ Object.defineProperty(exports, '__esModule', { value: true });
+/******/ };
+/******/
+/******/ // create a fake namespace object
+/******/ // mode & 1: value is a module id, require it
+/******/ // mode & 2: merge all properties of value into the ns
+/******/ // mode & 4: return value when already ns object
+/******/ // mode & 8|1: behave like require
+/******/ __webpack_require__.t = function(value, mode) {
+/******/ if(mode & 1) value = __webpack_require__(value);
+/******/ if(mode & 8) return value;
+/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/ var ns = Object.create(null);
+/******/ __webpack_require__.r(ns);
+/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/ return ns;
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+/******/
+/******/ // on error function for async loading
+/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
+/******/
+/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
+/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
+/******/ jsonpArray.push = webpackJsonpCallback;
+/******/ jsonpArray = jsonpArray.slice();
+/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
+/******/ var parentJsonpFunction = oldJsonpFunction;
+/******/
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = "./bootstrap.js");
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ "./bootstrap.js":
+/*!**********************!*\
+ !*** ./bootstrap.js ***!
+ \**********************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+eval("// A dependency graph that contains any wasm must all be imported\n// asynchronously. This `bootstrap.js` file does the single async import, so\n// that no one else needs to worry about it again.\n__webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./index.js */ \"./index.js\"))\n .catch(e => console.error(\"Error importing `index.js`:\", e));\n\n\n//# sourceURL=webpack:///./bootstrap.js?");
+
+/***/ })
+
+/******/ });
\ No newline at end of file
diff --git a/docs/bootstrap.worker.js b/docs/bootstrap.worker.js
index f33e2ae..9810a6f 100644
--- a/docs/bootstrap.worker.js
+++ b/docs/bootstrap.worker.js
@@ -1 +1,199 @@
-!function(e){self.webpackChunk=function(n,r){for(var o in r)e[o]=r[o];for(;n.length;)t[n.pop()]=1};var n={},t={0:1},r={};var o={4:function(){return{"./diffenator3_bg.js":{__wbindgen_object_drop_ref:function(e){return n[2].exports.g(e)},__wbindgen_string_new:function(e,t){return n[2].exports.h(e,t)},__wbg_call_b3ca7c6051f9bec1:function(e,t,r){return n[2].exports.a(e,t,r)},__wbg_new_abda76e883ba8a5f:function(){return n[2].exports.c()},__wbg_stack_658279fe44541cf6:function(e,t){return n[2].exports.e(e,t)},__wbg_error_f851667af71bcfc6:function(e,t){return n[2].exports.b(e,t)},__wbindgen_debug_string:function(e,t){return n[2].exports.f(e,t)},__wbindgen_throw:function(e,t){return n[2].exports.i(e,t)}}}}};function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.e=function(e){var n=[];return n.push(Promise.resolve().then((function(){t[e]||importScripts(i.p+""+e+".bootstrap.worker.js")}))),({1:[4]}[e]||[]).forEach((function(e){var t=r[e];if(t)n.push(t);else{var f,u=o[e](),s=fetch(i.p+""+{4:"b595417675cfd0908ff2"}[e]+".module.wasm");if(u instanceof Promise&&"function"==typeof WebAssembly.compileStreaming)f=Promise.all([WebAssembly.compileStreaming(s),u]).then((function(e){return WebAssembly.instantiate(e[0],e[1])}));else if("function"==typeof WebAssembly.instantiateStreaming)f=WebAssembly.instantiateStreaming(s,u);else{f=s.then((function(e){return e.arrayBuffer()})).then((function(e){return WebAssembly.instantiate(e,u)}))}n.push(r[e]=f.then((function(n){return i.w[e]=(n.instance||n).exports})))}})),Promise.all(n)},i.m=e,i.c=n,i.d=function(e,n,t){i.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,n){if(1&n&&(e=i(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)i.d(t,r,function(n){return e[n]}.bind(null,r));return t},i.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(n,"a",n),n},i.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},i.p="",i.w={},i(i.s=0)}([function(e,n,t){e=t.e(1).then(t.bind(null,1));!async function(){let n=await e;self.onmessage=async e=>{const{beforeFont:t,afterFont:r}=e.data;n.progressive_diff(t,r,e=>{self.postMessage(e)})},self}()}]);
\ No newline at end of file
+/******/ (function(modules) { // webpackBootstrap
+/******/ self["webpackChunk"] = function webpackChunkCallback(chunkIds, moreModules) {
+/******/ for(var moduleId in moreModules) {
+/******/ modules[moduleId] = moreModules[moduleId];
+/******/ }
+/******/ while(chunkIds.length)
+/******/ installedChunks[chunkIds.pop()] = 1;
+/******/ };
+/******/
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // object to store loaded chunks
+/******/ // "1" means "already loaded"
+/******/ var installedChunks = {
+/******/ "webworker": 1
+/******/ };
+/******/
+/******/ // object to store loaded and loading wasm modules
+/******/ var installedWasmModules = {};
+/******/
+/******/ function promiseResolve() { return Promise.resolve(); }
+/******/
+/******/ var wasmImportObjects = {
+/******/ "../pkg/diffenator3_bg.wasm": function() {
+/******/ return {
+/******/ "./diffenator3_bg.js": {
+/******/ "__wbindgen_object_drop_ref": function(p0i32) {
+/******/ return installedModules["../pkg/diffenator3_bg.js"].exports["__wbindgen_object_drop_ref"](p0i32);
+/******/ },
+/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
+/******/ return installedModules["../pkg/diffenator3_bg.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
+/******/ },
+/******/ "__wbg_call_b3ca7c6051f9bec1": function(p0i32,p1i32,p2i32) {
+/******/ return installedModules["../pkg/diffenator3_bg.js"].exports["__wbg_call_b3ca7c6051f9bec1"](p0i32,p1i32,p2i32);
+/******/ },
+/******/ "__wbg_new_abda76e883ba8a5f": function() {
+/******/ return installedModules["../pkg/diffenator3_bg.js"].exports["__wbg_new_abda76e883ba8a5f"]();
+/******/ },
+/******/ "__wbg_stack_658279fe44541cf6": function(p0i32,p1i32) {
+/******/ return installedModules["../pkg/diffenator3_bg.js"].exports["__wbg_stack_658279fe44541cf6"](p0i32,p1i32);
+/******/ },
+/******/ "__wbg_error_f851667af71bcfc6": function(p0i32,p1i32) {
+/******/ return installedModules["../pkg/diffenator3_bg.js"].exports["__wbg_error_f851667af71bcfc6"](p0i32,p1i32);
+/******/ },
+/******/ "__wbindgen_debug_string": function(p0i32,p1i32) {
+/******/ return installedModules["../pkg/diffenator3_bg.js"].exports["__wbindgen_debug_string"](p0i32,p1i32);
+/******/ },
+/******/ "__wbindgen_throw": function(p0i32,p1i32) {
+/******/ return installedModules["../pkg/diffenator3_bg.js"].exports["__wbindgen_throw"](p0i32,p1i32);
+/******/ }
+/******/ }
+/******/ };
+/******/ },
+/******/ };
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/ // This file contains only the entry chunk.
+/******/ // The chunk loading function for additional chunks
+/******/ __webpack_require__.e = function requireEnsure(chunkId) {
+/******/ var promises = [];
+/******/ promises.push(Promise.resolve().then(function() {
+/******/ // "1" is the signal for "already loaded"
+/******/ if(!installedChunks[chunkId]) {
+/******/ importScripts(__webpack_require__.p + "" + chunkId + ".bootstrap.worker.js");
+/******/ }
+/******/ }));
+/******/
+/******/ // Fetch + compile chunk loading for webassembly
+/******/
+/******/ var wasmModules = {"0":["../pkg/diffenator3_bg.wasm"]}[chunkId] || [];
+/******/
+/******/ wasmModules.forEach(function(wasmModuleId) {
+/******/ var installedWasmModuleData = installedWasmModules[wasmModuleId];
+/******/
+/******/ // a Promise means "currently loading" or "already loaded".
+/******/ if(installedWasmModuleData)
+/******/ promises.push(installedWasmModuleData);
+/******/ else {
+/******/ var importObject = wasmImportObjects[wasmModuleId]();
+/******/ var req = fetch(__webpack_require__.p + "" + {"../pkg/diffenator3_bg.wasm":"4e63794b39b83b4a3b88"}[wasmModuleId] + ".module.wasm");
+/******/ var promise;
+/******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') {
+/******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {
+/******/ return WebAssembly.instantiate(items[0], items[1]);
+/******/ });
+/******/ } else if(typeof WebAssembly.instantiateStreaming === 'function') {
+/******/ promise = WebAssembly.instantiateStreaming(req, importObject);
+/******/ } else {
+/******/ var bytesPromise = req.then(function(x) { return x.arrayBuffer(); });
+/******/ promise = bytesPromise.then(function(bytes) {
+/******/ return WebAssembly.instantiate(bytes, importObject);
+/******/ });
+/******/ }
+/******/ promises.push(installedWasmModules[wasmModuleId] = promise.then(function(res) {
+/******/ return __webpack_require__.w[wasmModuleId] = (res.instance || res).exports;
+/******/ }));
+/******/ }
+/******/ });
+/******/ return Promise.all(promises);
+/******/ };
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
+/******/ }
+/******/ };
+/******/
+/******/ // define __esModule on exports
+/******/ __webpack_require__.r = function(exports) {
+/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ }
+/******/ Object.defineProperty(exports, '__esModule', { value: true });
+/******/ };
+/******/
+/******/ // create a fake namespace object
+/******/ // mode & 1: value is a module id, require it
+/******/ // mode & 2: merge all properties of value into the ns
+/******/ // mode & 4: return value when already ns object
+/******/ // mode & 8|1: behave like require
+/******/ __webpack_require__.t = function(value, mode) {
+/******/ if(mode & 1) value = __webpack_require__(value);
+/******/ if(mode & 8) return value;
+/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/ var ns = Object.create(null);
+/******/ __webpack_require__.r(ns);
+/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/ return ns;
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+/******/
+/******/ // object with all WebAssembly.instance exports
+/******/ __webpack_require__.w = {};
+/******/
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = "./webworker.js");
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ "./webworker.js":
+/*!**********************!*\
+ !*** ./webworker.js ***!
+ \**********************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+eval("var module = __webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ../pkg/diffenator3.js */ \"../pkg/diffenator3.js\"));\nasync function init() {\n let wasm = await module;\n self.postMessage({ type: \"ready\" })\n console.log(\"Got wasm module\", wasm);\n wasm.debugging();\n self.onmessage = async (event) => {\n console.log(\"Worker received message\");\n console.log(event);\n const { command, beforeFont, location, afterFont } = event.data;\n if (command == \"axes\") {\n self.postMessage({\n \"type\": \"axes\",\n \"axes\": JSON.parse(wasm.axes(beforeFont, afterFont))[\"axes\"]\n });\n } else if (command == \"tables\") {\n wasm.diff_tables(beforeFont, afterFont, (tables) => {\n self.postMessage({\n \"type\": \"tables\",\n \"tables\": JSON.parse(tables)[\"tables\"]\n });\n });\n } else if (command == \"glyphs\") {\n wasm.diff_glyphs(beforeFont, afterFont, location, (glyphs) => {\n self.postMessage({\n \"type\": \"glyphs\",\n \"glyphs\": JSON.parse(glyphs)[\"glyphs\"]\n });\n });\n } else if (command == \"words\") {\n wasm.diff_words(beforeFont, afterFont, location, (words) => {\n self.postMessage({\n \"type\": \"words\",\n \"words\": JSON.parse(words)[\"words\"]\n });\n });\n }\n\n }\n return self;\n}\n\ninit();\n\n\n//# sourceURL=webpack:///./webworker.js?");
+
+/***/ })
+
+/******/ });
\ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
index c77a7a8..53f31fe 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -16,9 +16,8 @@
src: url();
}
- @font-face {
- font-family: "Adobe Notdef";
- src: url("AND-Regular.ttf");
+ .font-before, .font-after {
+ font-variation-settings: "wght" 400, "wdth" 100;
}
.font-before {
@@ -28,6 +27,7 @@
.font-after {
font-family: "Font After", "Adobe Notdef";
}
+
@@ -233,6 +233,8 @@ Font after
Table diff