feat: Improve options page and add toggles for the two main features. (#68)

Resolves #43.

Should leave archematics ready to submit.

Reviewed-on: #68
Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Co-committed-by: Glen Whitney <glen@studioinfinity.org>
This commit is contained in:
Glen Whitney 2024-02-20 04:14:44 +00:00 committed by Glen Whitney
parent b74921341c
commit 0567da019f
12 changed files with 215 additions and 142 deletions

View File

@ -1,10 +1,16 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "archematics", "name": "archematics",
"version": "0.1", "version": "1.0",
"description": "unearths mathematical treasures lost in the web.",
"description": "unearths mathematical treasures lost in the web",
"icons": {
"48": "assets/archIcon48.png",
"128": "assets/archIcon128.png",
"256": "assets/archIcon256.png"
},
"author": "Glen Whitney <glen@archematics.app>",
"homepage_url": "https://archematics.app",
"content_scripts": [ "content_scripts": [
{ {
"matches": ["*://*/*"], "matches": ["*://*/*"],

View File

@ -6,7 +6,54 @@
</head> </head>
<body> <body>
<h3>Debugging</h3> <h2>archematics</h2>
<p>This plug-in currently has two main capabilities that can be activated
independently:</p>
<h2>Embedded VRML/X3D display</h2>
<div style="float: right;margin: 1.5em;">
<label for="vrmlview">Enable</label>
<input type="checkbox" id="vrmlview"></div>
<div style="float: left;margin: 1em;">
<a href="http://www.wiley.com/legacy/compbooks/vrml2sbk/ch14/14fig04a.htm">
<img src="assets/vrmlExample.png" width="160px"></a></div>
<p>Scans web pages for links (in text or images) to VRML or X3D files
(and other file formats compatible with the
<a href="https://create3000.github.io/x_ite/tutorials/how-to-navigate-in-a-scene/">
X_ITE X3D Browser</a>, such as glTF, obj, and STL) and inserts
"eye-cons" (👁) next to each of them. When you click the eye, it embeds
a viewer and displays the model in 3D within the page. Right-click on an
active viewer for additional options. Click the eye again
to close the viewer. Supports multiple simultaneous viewers on the same
page.</p>
<p> Be sure to visit George Hart's
<a href="http://georgehart.com/virtual-polyhedra/vp.html">
Encyclopedia of Polyhedra</a> with this module enabled for a trove of
beautiful models to play with.</p>
<h2>JavaScript reinterpretation of Geometry Applets</h2>
<div style="float: right;margin: 1.5em;">
<label for="joyce">Enable</label><input type="checkbox" id="joyce">
</div>
<div style="float: left;margin: 1em;">
<a href="https://mathcs.clarku.edu/~djoyce/java/Geometry/eulerline.html">
<img src="assets/joyceExample.png" width="150px"></a></div>
<p>In the late 1990s, <a href="https://mathcs.clarku.edu/~djoyce/">
David Joyce</a> wrote a web applet in Java that allowed for interactive
draggable 2D and 3D geometry diagrams within a page. Nearly 30 years on,
Java is no longer supported for embedded applets by any major browser. This
module scans web pages for invocations of that Java applet, and translates
them into calls to the
<a href="https://wiki.geogebra.org/en/Reference:GeoGebra_Apps_Embedding">
GeoGebra JavaScript module</a>, bringing the diagrams back to life.</p>
<p>What's more, Joyce posted an
<a href="https://mathcs.clarku.edu/~djoyce/java/elements/elements.html">
on-line version</a> of the <strong>entire</strong>
Euclid's <em>Elements</em> with all of the traditional diagrams, and many
new ones of his own creation, rendered in fully interactive versions.
Be sure to give archematics a try on
<a href="https://mathcs.clarku.edu/~djoyce/java/elements/bookI/propI47.html">
his site</a>.</p>
<hr/>
<h3>Debugging-only options</h3>
<h4>Embedded VRML/X3D display</h4> <h4>Embedded VRML/X3D display</h4>
Write to the JavaScript console: <br/> Write to the JavaScript console: <br/>
<label for="vrml97">Generated VRML97 specifications</label> <label for="vrml97">Generated VRML97 specifications</label>

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -26,9 +26,9 @@ obs := new MutationObserver (mutationList) =>
width: parseInt(newNode.getAttribute('width') ?? '200'), width: parseInt(newNode.getAttribute('width') ?? '200'),
height: parseInt(newNode.getAttribute('height') ?? '200') } height: parseInt(newNode.getAttribute('height') ?? '200') }
config := childList: true, subtree: true observerConfig := childList: true, subtree: true
obs.observe document.documentElement, config obs.observe document.documentElement, observerConfig
function addScriptTag(url: string, params = '', module = false) function addScriptTag(url: string, params = '', module = false)
return new Promise (resolve, reject) => return new Promise (resolve, reject) =>
@ -41,6 +41,8 @@ function addScriptTag(url: string, params = '', module = false)
document.head.appendChild script document.head.appendChild script
document.addEventListener "DOMContentLoaded", async => document.addEventListener "DOMContentLoaded", async =>
config := await browser.storage.local.get(flags) as ConfigType
unless config.joyce return
finalJoyceApplets := document.querySelectorAll "applet[code='Geometry']" finalJoyceApplets := document.querySelectorAll "applet[code='Geometry']"
if finalJoyceApplets.length if finalJoyceApplets.length
joyceApplets = [] joyceApplets = []
@ -58,7 +60,6 @@ document.addEventListener "DOMContentLoaded", async =>
width: parseInt(jApp.getAttribute('width') ?? '200'), width: parseInt(jApp.getAttribute('width') ?? '200'),
height: parseInt(jApp.getAttribute('height') ?? '200') } height: parseInt(jApp.getAttribute('height') ?? '200') }
if joyceApplets.length if joyceApplets.length
config := await browser.storage.local.get(flags) as ConfigType
use3d .= false use3d .= false
for each jApp of joyceApplets for each jApp of joyceApplets
if contains3d jApp.params if contains3d jApp.params

View File

@ -1,7 +1,10 @@
// This file is a bit misnamed, as it has options for giveAwrl, too. // This file is a bit misnamed, as it has options for giveAwrl, too.
export const flags = [ export const flags = [
'color', 'commands', 'showall', 'showaux', 'algebra', 'vrml97'] as const 'vrmlview', 'joyce',
// debugging
'color', 'commands', 'showall', 'showaux', 'algebra', 'vrml97'
] as const
export type FlagType = (typeof flags)[number] export type FlagType = (typeof flags)[number]
export type ConfigType = Partial<Record<FlagType, boolean>> export type ConfigType = Partial<Record<FlagType, boolean>>

View File

@ -1,12 +1,15 @@
import ./deps/jquery.js import ./deps/jquery.js
{convert} from ./deps/vrml1to97/index.js {convert} from ./deps/vrml1to97/index.js
{ConfigType} from ./adapptypes.ts import type {ConfigType} from ./adapptypes.ts
configPromise := browser.storage.local.get(['vrmlview', 'vrml97'])
knownExtensions := /[.](?:wrl|x3d|gltf|glb|obj|stl|ply)$/ knownExtensions := /[.](?:wrl|x3d|gltf|glb|obj|stl|ply)$/
certainlyHandled := certainlyHandled :=
knownExtensions.source.slice(0, -2).split('wrl|')[1].split '|' knownExtensions.source.slice(0, -2).split('wrl|')[1].split '|'
function makeBrowser(url: string, width: string, height: string) function makeBrowser(url: string, width: string, height: string,
config: ConfigType)
x_ite_rel := 'deps/x_ite/x_ite.mjs' x_ite_rel := 'deps/x_ite/x_ite.mjs'
x_ite_url .= './' + x_ite_rel x_ite_url .= './' + x_ite_rel
unless typeof browser is 'undefined' unless typeof browser is 'undefined'
@ -43,15 +46,17 @@ function makeBrowser(url: string, width: string, height: string)
text .= await response.text() text .= await response.text()
if /#\s*VRML\s*V?1[.]/i.test text if /#\s*VRML\s*V?1[.]/i.test text
text = convert text text = convert text
maybeDebug text maybeDebug text, config
browser3D.baseURL = url browser3D.baseURL = url
scene := await browser3D.createX3DFromString text scene := await browser3D.createX3DFromString text
browser3D.replaceWorld scene browser3D.replaceWorld scene
{canvas, browser3D} {canvas, browser3D}
configPromise.then((config) =>
// Put eye icons after all of the eligible links // Put eye icons after all of the eligible links
links := $('a').filter -> knownExtensions.test @.getAttribute('href') ?? '' links := $('a').filter -> knownExtensions.test @.getAttribute('href') ?? ''
links.after -> links.after ->
unless config.vrmlview return ''
newSpan := $('<span>👁</span>') newSpan := $('<span>👁</span>')
newSpan.css 'overflow', 'visible' newSpan.css 'overflow', 'visible'
floatLike := this.lastElementChild as HTMLElement floatLike := this.lastElementChild as HTMLElement
@ -81,15 +86,18 @@ links.after ->
overImg := floatLike and floatLike.tagName is 'IMG' overImg := floatLike and floatLike.tagName is 'IMG'
width := overImg ? ($(floatLike).width() + 'px') : '150px' width := overImg ? ($(floatLike).width() + 'px') : '150px'
height := overImg ? ($(floatLike).height() + 'px') : '150px' height := overImg ? ($(floatLike).height() + 'px') : '150px'
{canvas} := await makeBrowser url, width, height {canvas} := await makeBrowser url, width, height, config
if float if float
canvas.style.float = float canvas.style.float = float
if overImg if overImg
canvas.style.position = 'absolute' canvas.style.position = 'absolute'
imgSty := window.getComputedStyle floatLike imgSty := window.getComputedStyle floatLike
canvas.style.marginTop = imgSty.getPropertyValue 'margin-top' canvas.style.marginTop =
canvas.style.marginLeft = imgSty.getPropertyValue 'margin-left' imgSty.getPropertyValue 'margin-top'
canvas.style.marginRight = imgSty.getPropertyValue 'margin-right' canvas.style.marginLeft =
imgSty.getPropertyValue 'margin-left'
canvas.style.marginRight =
imgSty.getPropertyValue 'margin-right'
if float is 'right' if float is 'right'
canvas.style.left = $(eye).width() + 'px' canvas.style.left = $(eye).width() + 'px'
else if float is 'left' else if float is 'left'
@ -105,16 +113,12 @@ links.after ->
$(eye).css 'text-decoration', 'none' $(eye).css 'text-decoration', 'none'
$(eye.lastElementChild as Element).hide() $(eye.lastElementChild as Element).hide()
function maybeDebug(vrml: string)
config := await browser.storage.local.get(['vrml97']) as ConfigType
if config.vrml97 then console.log 'Generated VRML97', vrml
let conwayBrowser: any let conwayBrowser: any
madeConway .= false madeConway .= false
// See if we are on George Hart's Conway-notation generator page // See if we are on George Hart's Conway-notation generator page
inputs := $('input[type="button"][value="Generate"][onclick="viewVRML()"]') inputs := $('input[type="button"][value="Generate"][onclick="viewVRML()"]')
if inputs.length is 1 if config.vrmlview and inputs.length is 1
// Seems so, fix the generator // Seems so, fix the generator
// Note that modifying the onclick prop is not the recommended way to // Note that modifying the onclick prop is not the recommended way to
// change button click functionality, but we need to clear out the old // change button click functionality, but we need to clear out the old
@ -124,9 +128,10 @@ if inputs.length is 1
notation := $('input[name="notation"]').val() notation := $('input[name="notation"]').val()
unless notation then return unless notation then return
vrml := conway.generateVRML notation.toString() vrml := conway.generateVRML notation.toString()
maybeDebug vrml maybeDebug vrml, config
unless madeConway unless madeConway
{canvas, browser3D} := await makeBrowser '', '250px', '250px' {canvas, browser3D} :=
await makeBrowser '', '250px', '250px', config
conwayBrowser = browser3D conwayBrowser = browser3D
canvas.style.float = 'left' canvas.style.float = 'left'
canvas.style.marginRight = '1em' canvas.style.marginRight = '1em'
@ -137,7 +142,7 @@ if inputs.length is 1
// See if we are on George Hart's prism generator page // See if we are on George Hart's prism generator page
panelFrame := $('frame[name="panel"][src="prism-maker-subpanel.html"]') panelFrame := $('frame[name="panel"][src="prism-maker-subpanel.html"]')
if panelFrame.length is 1 if config.vrmlview and panelFrame.length is 1
// Seems so, fix the generator // Seems so, fix the generator
panelFrame.on "load", => panelFrame.on "load", =>
panelDoc := frames[1].document panelDoc := frames[1].document
@ -153,7 +158,8 @@ if panelFrame.length is 1
// We are presuming here that the body just contains a single // We are presuming here that the body just contains a single
// text node. That should stay true unless GWH changes the page. // text node. That should stay true unless GWH changes the page.
initialVrml97 := convert initialVrml1 initialVrml97 := convert initialVrml1
{canvas, browser3D: prismBrowser} := await makeBrowser '', '300px', '300px' {canvas, browser3D: prismBrowser} :=
await makeBrowser '', '300px', '300px', config
viewerDiv.append canvas viewerDiv.append canvas
initialScene := await prismBrowser.createX3DFromString initialVrml97 initialScene := await prismBrowser.createX3DFromString initialVrml97
prismBrowser.replaceWorld initialScene prismBrowser.replaceWorld initialScene
@ -162,22 +168,27 @@ if panelFrame.length is 1
// OK, finally have the layout cleaned up. Now we can set up our // OK, finally have the layout cleaned up. Now we can set up our
// replacement generator: // replacement generator:
prismBtn := $('input[type="button"][value="View"][onclick="ViewVRML()"]', prismBtn :=
$('input[type="button"][value="View"][onclick="ViewVRML()"]',
panelDoc) panelDoc)
unless prismBtn.length is 1 return unless prismBtn.length is 1 return
prismBtn.prop 'onclick', (i, val) => => prismBtn.prop 'onclick', (i, val) => =>
import(browser.runtime.getURL('prism.js')).then (prism) => import(browser.runtime.getURL('prism.js')).then (prism) =>
numerator := numerator := parseInt(
parseInt $('input[name="numerator"]', panelDoc).val() as string $('input[name="numerator"]', panelDoc).val() as string)
denominator := denominator := parseInt(
parseInt $('input[name="denominator"]', panelDoc).val() as string $('input[name="denominator"]', panelDoc).val() as string)
checks: boolean[] := [] checks: boolean[] := []
$('input[name="what"]', panelDoc).each -> $('input[name="what"]', panelDoc).each ->
checks.push (@ as HTMLInputElement).checked checks.push (@ as HTMLInputElement).checked
return return
{vrml, err} := prism.generateVRML numerator, denominator, checks {vrml, err} := prism.generateVRML numerator, denominator, checks
maybeDebug vrml maybeDebug vrml, config
if err then alert err if err then alert err
if vrml if vrml
scene := await prismBrowser.createX3DFromString vrml scene := await prismBrowser.createX3DFromString vrml
prismBrowser.replaceWorld scene prismBrowser.replaceWorld scene
) // end of then for configPromise
function maybeDebug(vrml: string, config: ConfigType)
if config.vrml97 then console.log 'Generated VRML97', vrml

View File

@ -5,7 +5,7 @@ cache := await browser.storage.local.get flags
for each box of flags for each box of flags
checkbox := document.getElementById(box) as HTMLInputElement checkbox := document.getElementById(box) as HTMLInputElement
unless checkbox then continue unless checkbox then continue
checkbox.checked = cache[box] ?? false checkbox.checked = cache[box] ?? (box is 'vrmlview' or box is 'joyce')
document.body.addEventListener 'click', (event) -> document.body.addEventListener 'click', (event) ->
elt := event.target as HTMLInputElement elt := event.target as HTMLInputElement

View File

@ -25,4 +25,9 @@ cp public/js/options.js public/js/adapptypes.js public/js/conway.js $1
cp public/js/prism.js $1 cp public/js/prism.js $1
cp node_modules/webextension-polyfill/dist/browser-polyfill.js $1 cp node_modules/webextension-polyfill/dist/browser-polyfill.js $1
cp node_modules/@webcomponents/custom-elements/custom-elements.min.js $1 cp node_modules/@webcomponents/custom-elements/custom-elements.min.js $1
# Images etc
mkdir -p $1/assets
cp public/assets/arch*.png $1/assets
cp public/assets/*Example.png $1/assets
# Wrap it all up
zip -r $1 $1 zip -r $1 $1