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,141 +46,149 @@ 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}
// Put eye icons after all of the eligible links configPromise.then((config) =>
links := $('a').filter -> knownExtensions.test @.getAttribute('href') ?? '' // Put eye icons after all of the eligible links
links.after -> links := $('a').filter -> knownExtensions.test @.getAttribute('href') ?? ''
newSpan := $('<span>👁</span>') links.after ->
newSpan.css 'overflow', 'visible' unless config.vrmlview return ''
floatLike := this.lastElementChild as HTMLElement newSpan := $('<span>👁</span>')
float .= '' newSpan.css 'overflow', 'visible'
if floatLike floatLike := this.lastElementChild as HTMLElement
float = (floatLike.getAttribute('align') ?? '') float .= ''
or floatLike.style.getPropertyValue 'float' if floatLike
or window.getComputedStyle(floatLike).getPropertyValue 'float' float = (floatLike.getAttribute('align') ?? '')
switch float or floatLike.style.getPropertyValue 'float'
/left/i or window.getComputedStyle(floatLike).getPropertyValue 'float'
float = 'left' switch float
newSpan.css {float, position: 'relative'} /left/i
/right/i float = 'left'
float = 'right' newSpan.css {float, position: 'relative'}
newSpan.css {float, position: 'relative'} /right/i
else float = '' float = 'right'
newSpan.hover newSpan.css {float, position: 'relative'}
(-> $(@).css 'background-color', 'lightblue'), else float = ''
(-> $(@).css 'background-color', 'inherit') newSpan.hover
newSpan.on 'click', @, (-> $(@).css 'background-color', 'lightblue'),
(e) => (-> $(@).css 'background-color', 'inherit')
eye := e.target newSpan.on 'click', @,
state .= eye.getAttribute 'data' (e) =>
unless state eye := e.target
state = 'off' state .= eye.getAttribute 'data'
url := e.data.getAttribute('href') ?? '' unless state
overImg := floatLike and floatLike.tagName is 'IMG' state = 'off'
width := overImg ? ($(floatLike).width() + 'px') : '150px' url := e.data.getAttribute('href') ?? ''
height := overImg ? ($(floatLike).height() + 'px') : '150px' overImg := floatLike and floatLike.tagName is 'IMG'
{canvas} := await makeBrowser url, width, height width := overImg ? ($(floatLike).width() + 'px') : '150px'
if float height := overImg ? ($(floatLike).height() + 'px') : '150px'
canvas.style.float = float {canvas} := await makeBrowser url, width, height, config
if overImg if float
canvas.style.position = 'absolute' canvas.style.float = float
imgSty := window.getComputedStyle floatLike if overImg
canvas.style.marginTop = imgSty.getPropertyValue 'margin-top' canvas.style.position = 'absolute'
canvas.style.marginLeft = imgSty.getPropertyValue 'margin-left' imgSty := window.getComputedStyle floatLike
canvas.style.marginRight = imgSty.getPropertyValue 'margin-right' canvas.style.marginTop =
if float is 'right' imgSty.getPropertyValue 'margin-top'
canvas.style.left = $(eye).width() + 'px' canvas.style.marginLeft =
else if float is 'left' imgSty.getPropertyValue 'margin-left'
canvas.style.right = $(eye).width() + 'px' canvas.style.marginRight =
else canvas.style.left = floatLike.offsetLeft imgSty.getPropertyValue 'margin-right'
$(eye).append canvas if float is 'right'
if state is 'off' canvas.style.left = $(eye).width() + 'px'
eye.setAttribute 'data', 'on' else if float is 'left'
$(eye).css 'text-decoration', 'line-through 3px' canvas.style.right = $(eye).width() + 'px'
$(eye.lastElementChild as Element).show() else canvas.style.left = floatLike.offsetLeft
else $(eye).append canvas
eye.setAttribute 'data', 'off' if state is 'off'
$(eye).css 'text-decoration', 'none' eye.setAttribute 'data', 'on'
$(eye.lastElementChild as Element).hide() $(eye).css 'text-decoration', 'line-through 3px'
$(eye.lastElementChild as Element).show()
else
eye.setAttribute 'data', 'off'
$(eye).css 'text-decoration', 'none'
$(eye.lastElementChild as Element).hide()
function maybeDebug(vrml: string) let conwayBrowser: any
config := await browser.storage.local.get(['vrml97']) as ConfigType madeConway .= false
// See if we are on George Hart's Conway-notation generator page
inputs := $('input[type="button"][value="Generate"][onclick="viewVRML()"]')
if config.vrmlview and inputs.length is 1
// Seems so, fix the generator
// Note that modifying the onclick prop is not the recommended way to
// change button click functionality, but we need to clear out the old
// behavior so I wasn't sure how else to do it
inputs.prop 'onclick', (i, val) => () =>
import(browser.runtime.getURL('conway.js')).then (conway) =>
notation := $('input[name="notation"]').val()
unless notation then return
vrml := conway.generateVRML notation.toString()
maybeDebug vrml, config
unless madeConway
{canvas, browser3D} :=
await makeBrowser '', '250px', '250px', config
conwayBrowser = browser3D
canvas.style.float = 'left'
canvas.style.marginRight = '1em'
$('form[name="input"]').first().before canvas
madeConway = true
scene := await conwayBrowser.createX3DFromString vrml
conwayBrowser.replaceWorld scene
// See if we are on George Hart's prism generator page
panelFrame := $('frame[name="panel"][src="prism-maker-subpanel.html"]')
if config.vrmlview and panelFrame.length is 1
// Seems so, fix the generator
panelFrame.on "load", =>
panelDoc := frames[1].document
vrmlDoc := frames[0].document
vrmlBody := $('body', vrmlDoc)
// Grab the initial text while it is still easy to get
textNode := vrmlBody.contents()[0]
initialVrml1: string := textNode.textContent or ''
// Now build up the vrml frame as we want it
viewerDiv := $('<div></div>')
$('head').after $('<body></body>')
$('body').append viewerDiv
// We are presuming here that the body just contains a single
// text node. That should stay true unless GWH changes the page.
initialVrml97 := convert initialVrml1
{canvas, browser3D: prismBrowser} :=
await makeBrowser '', '300px', '300px', config
viewerDiv.append canvas
initialScene := await prismBrowser.createX3DFromString initialVrml97
prismBrowser.replaceWorld initialScene
$(textNode).remove()
$('frame[name="vrml"]').remove()
// OK, finally have the layout cleaned up. Now we can set up our
// replacement generator:
prismBtn :=
$('input[type="button"][value="View"][onclick="ViewVRML()"]',
panelDoc)
unless prismBtn.length is 1 return
prismBtn.prop 'onclick', (i, val) => =>
import(browser.runtime.getURL('prism.js')).then (prism) =>
numerator := parseInt(
$('input[name="numerator"]', panelDoc).val() as string)
denominator := parseInt(
$('input[name="denominator"]', panelDoc).val() as string)
checks: boolean[] := []
$('input[name="what"]', panelDoc).each ->
checks.push (@ as HTMLInputElement).checked
return
{vrml, err} := prism.generateVRML numerator, denominator, checks
maybeDebug vrml, config
if err then alert err
if vrml
scene := await prismBrowser.createX3DFromString vrml
prismBrowser.replaceWorld scene
) // end of then for configPromise
function maybeDebug(vrml: string, config: ConfigType)
if config.vrml97 then console.log 'Generated VRML97', vrml if config.vrml97 then console.log 'Generated VRML97', vrml
let conwayBrowser: any
madeConway .= false
// See if we are on George Hart's Conway-notation generator page
inputs := $('input[type="button"][value="Generate"][onclick="viewVRML()"]')
if inputs.length is 1
// Seems so, fix the generator
// Note that modifying the onclick prop is not the recommended way to
// change button click functionality, but we need to clear out the old
// behavior so I wasn't sure how else to do it
inputs.prop 'onclick', (i, val) => () =>
import(browser.runtime.getURL('conway.js')).then (conway) =>
notation := $('input[name="notation"]').val()
unless notation then return
vrml := conway.generateVRML notation.toString()
maybeDebug vrml
unless madeConway
{canvas, browser3D} := await makeBrowser '', '250px', '250px'
conwayBrowser = browser3D
canvas.style.float = 'left'
canvas.style.marginRight = '1em'
$('form[name="input"]').first().before canvas
madeConway = true
scene := await conwayBrowser.createX3DFromString vrml
conwayBrowser.replaceWorld scene
// See if we are on George Hart's prism generator page
panelFrame := $('frame[name="panel"][src="prism-maker-subpanel.html"]')
if panelFrame.length is 1
// Seems so, fix the generator
panelFrame.on "load", =>
panelDoc := frames[1].document
vrmlDoc := frames[0].document
vrmlBody := $('body', vrmlDoc)
// Grab the initial text while it is still easy to get
textNode := vrmlBody.contents()[0]
initialVrml1: string := textNode.textContent or ''
// Now build up the vrml frame as we want it
viewerDiv := $('<div></div>')
$('head').after $('<body></body>')
$('body').append viewerDiv
// We are presuming here that the body just contains a single
// text node. That should stay true unless GWH changes the page.
initialVrml97 := convert initialVrml1
{canvas, browser3D: prismBrowser} := await makeBrowser '', '300px', '300px'
viewerDiv.append canvas
initialScene := await prismBrowser.createX3DFromString initialVrml97
prismBrowser.replaceWorld initialScene
$(textNode).remove()
$('frame[name="vrml"]').remove()
// OK, finally have the layout cleaned up. Now we can set up our
// replacement generator:
prismBtn := $('input[type="button"][value="View"][onclick="ViewVRML()"]',
panelDoc)
unless prismBtn.length is 1 return
prismBtn.prop 'onclick', (i, val) => =>
import(browser.runtime.getURL('prism.js')).then (prism) =>
numerator :=
parseInt $('input[name="numerator"]', panelDoc).val() as string
denominator :=
parseInt $('input[name="denominator"]', panelDoc).val() as string
checks: boolean[] := []
$('input[name="what"]', panelDoc).each ->
checks.push (@ as HTMLInputElement).checked
return
{vrml, err} := prism.generateVRML numerator, denominator, checks
maybeDebug vrml
if err then alert err
if vrml
scene := await prismBrowser.createX3DFromString vrml
prismBrowser.replaceWorld scene

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