diff --git a/etc/manifest.json b/etc/manifest.json index 4b286d4..7b61060 100644 --- a/etc/manifest.json +++ b/etc/manifest.json @@ -1,10 +1,16 @@ { "manifest_version": 3, "name": "archematics", - "version": "0.1", - - "description": "unearths mathematical treasures lost in the web.", + "version": "1.0", + "description": "unearths mathematical treasures lost in the web", + "icons": { + "48": "assets/archIcon48.png", + "128": "assets/archIcon128.png", + "256": "assets/archIcon256.png" + }, + "author": "Glen Whitney ", + "homepage_url": "https://archematics.app", "content_scripts": [ { "matches": ["*://*/*"], diff --git a/etc/options.html b/etc/options.html index af3606f..b9f6eae 100644 --- a/etc/options.html +++ b/etc/options.html @@ -6,7 +6,54 @@ -

Debugging

+

archematics

+

This plug-in currently has two main capabilities that can be activated + independently:

+

Embedded VRML/X3D display

+
+ +
+
+ +
+

Scans web pages for links (in text or images) to VRML or X3D files + (and other file formats compatible with the + + X_ITE X3D Browser, 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.

+

Be sure to visit George Hart's + + Encyclopedia of Polyhedra with this module enabled for a trove of + beautiful models to play with.

+

JavaScript reinterpretation of Geometry Applets

+
+ +
+
+ +
+

In the late 1990s, + David Joyce 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 + + GeoGebra JavaScript module, bringing the diagrams back to life.

+

What's more, Joyce posted an + + on-line version of the entire + Euclid's Elements 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 + + his site.

+
+

Debugging-only options

Embedded VRML/X3D display

Write to the JavaScript console:
diff --git a/public/assets/archIcon128.png b/public/assets/archIcon128.png new file mode 100644 index 0000000..2ed40d5 Binary files /dev/null and b/public/assets/archIcon128.png differ diff --git a/public/assets/archIcon256.png b/public/assets/archIcon256.png new file mode 100644 index 0000000..9cd6ff8 Binary files /dev/null and b/public/assets/archIcon256.png differ diff --git a/public/assets/archIcon48.png b/public/assets/archIcon48.png new file mode 100644 index 0000000..029fbf4 Binary files /dev/null and b/public/assets/archIcon48.png differ diff --git a/public/assets/joyceExample.png b/public/assets/joyceExample.png new file mode 100644 index 0000000..8fd4170 Binary files /dev/null and b/public/assets/joyceExample.png differ diff --git a/public/assets/vrmlExample.png b/public/assets/vrmlExample.png new file mode 100644 index 0000000..aafb52c Binary files /dev/null and b/public/assets/vrmlExample.png differ diff --git a/src/adapptext.civet b/src/adapptext.civet index 661a250..8995710 100644 --- a/src/adapptext.civet +++ b/src/adapptext.civet @@ -26,9 +26,9 @@ obs := new MutationObserver (mutationList) => width: parseInt(newNode.getAttribute('width') ?? '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) return new Promise (resolve, reject) => @@ -41,6 +41,8 @@ function addScriptTag(url: string, params = '', module = false) document.head.appendChild script document.addEventListener "DOMContentLoaded", async => + config := await browser.storage.local.get(flags) as ConfigType + unless config.joyce return finalJoyceApplets := document.querySelectorAll "applet[code='Geometry']" if finalJoyceApplets.length joyceApplets = [] @@ -58,7 +60,6 @@ document.addEventListener "DOMContentLoaded", async => width: parseInt(jApp.getAttribute('width') ?? '200'), height: parseInt(jApp.getAttribute('height') ?? '200') } if joyceApplets.length - config := await browser.storage.local.get(flags) as ConfigType use3d .= false for each jApp of joyceApplets if contains3d jApp.params diff --git a/src/adapptypes.civet b/src/adapptypes.civet index ef20292..afdfc63 100644 --- a/src/adapptypes.civet +++ b/src/adapptypes.civet @@ -1,7 +1,10 @@ // This file is a bit misnamed, as it has options for giveAwrl, too. 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 ConfigType = Partial> diff --git a/src/giveAwrl.civet b/src/giveAwrl.civet index f867667..8c3a844 100644 --- a/src/giveAwrl.civet +++ b/src/giveAwrl.civet @@ -1,12 +1,15 @@ import ./deps/jquery.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)$/ certainlyHandled := 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_url .= './' + x_ite_rel unless typeof browser is 'undefined' @@ -43,141 +46,149 @@ function makeBrowser(url: string, width: string, height: string) text .= await response.text() if /#\s*VRML\s*V?1[.]/i.test text text = convert text - maybeDebug text + maybeDebug text, config browser3D.baseURL = url scene := await browser3D.createX3DFromString text browser3D.replaceWorld scene {canvas, browser3D} -// Put eye icons after all of the eligible links -links := $('a').filter -> knownExtensions.test @.getAttribute('href') ?? '' -links.after -> - newSpan := $('👁') - newSpan.css 'overflow', 'visible' - floatLike := this.lastElementChild as HTMLElement - float .= '' - if floatLike - float = (floatLike.getAttribute('align') ?? '') - or floatLike.style.getPropertyValue 'float' - or window.getComputedStyle(floatLike).getPropertyValue 'float' - switch float - /left/i - float = 'left' - newSpan.css {float, position: 'relative'} - /right/i - float = 'right' - newSpan.css {float, position: 'relative'} - else float = '' - newSpan.hover - (-> $(@).css 'background-color', 'lightblue'), - (-> $(@).css 'background-color', 'inherit') - newSpan.on 'click', @, - (e) => - eye := e.target - state .= eye.getAttribute 'data' - unless state - state = 'off' - url := e.data.getAttribute('href') ?? '' - overImg := floatLike and floatLike.tagName is 'IMG' - width := overImg ? ($(floatLike).width() + 'px') : '150px' - height := overImg ? ($(floatLike).height() + 'px') : '150px' - {canvas} := await makeBrowser url, width, height - if float - canvas.style.float = float - if overImg - canvas.style.position = 'absolute' - imgSty := window.getComputedStyle floatLike - canvas.style.marginTop = imgSty.getPropertyValue 'margin-top' - canvas.style.marginLeft = imgSty.getPropertyValue 'margin-left' - canvas.style.marginRight = imgSty.getPropertyValue 'margin-right' - if float is 'right' - canvas.style.left = $(eye).width() + 'px' - else if float is 'left' - canvas.style.right = $(eye).width() + 'px' - else canvas.style.left = floatLike.offsetLeft - $(eye).append canvas - if state is 'off' - eye.setAttribute 'data', 'on' - $(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() +configPromise.then((config) => + // Put eye icons after all of the eligible links + links := $('a').filter -> knownExtensions.test @.getAttribute('href') ?? '' + links.after -> + unless config.vrmlview return '' + newSpan := $('👁') + newSpan.css 'overflow', 'visible' + floatLike := this.lastElementChild as HTMLElement + float .= '' + if floatLike + float = (floatLike.getAttribute('align') ?? '') + or floatLike.style.getPropertyValue 'float' + or window.getComputedStyle(floatLike).getPropertyValue 'float' + switch float + /left/i + float = 'left' + newSpan.css {float, position: 'relative'} + /right/i + float = 'right' + newSpan.css {float, position: 'relative'} + else float = '' + newSpan.hover + (-> $(@).css 'background-color', 'lightblue'), + (-> $(@).css 'background-color', 'inherit') + newSpan.on 'click', @, + (e) => + eye := e.target + state .= eye.getAttribute 'data' + unless state + state = 'off' + url := e.data.getAttribute('href') ?? '' + overImg := floatLike and floatLike.tagName is 'IMG' + width := overImg ? ($(floatLike).width() + 'px') : '150px' + height := overImg ? ($(floatLike).height() + 'px') : '150px' + {canvas} := await makeBrowser url, width, height, config + if float + canvas.style.float = float + if overImg + canvas.style.position = 'absolute' + imgSty := window.getComputedStyle floatLike + canvas.style.marginTop = + imgSty.getPropertyValue 'margin-top' + canvas.style.marginLeft = + imgSty.getPropertyValue 'margin-left' + canvas.style.marginRight = + imgSty.getPropertyValue 'margin-right' + if float is 'right' + canvas.style.left = $(eye).width() + 'px' + else if float is 'left' + canvas.style.right = $(eye).width() + 'px' + else canvas.style.left = floatLike.offsetLeft + $(eye).append canvas + if state is 'off' + eye.setAttribute 'data', 'on' + $(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) - config := await browser.storage.local.get(['vrml97']) as ConfigType + 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 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 := $('
') + $('head').after $('') + $('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 - -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 := $('
') - $('head').after $('') - $('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 diff --git a/src/options.civet b/src/options.civet index dcf6809..bf48c35 100644 --- a/src/options.civet +++ b/src/options.civet @@ -5,7 +5,7 @@ cache := await browser.storage.local.get flags for each box of flags checkbox := document.getElementById(box) as HTMLInputElement 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) -> elt := event.target as HTMLInputElement diff --git a/tools/makePlugin.bash b/tools/makePlugin.bash index f70be10..191afc9 100644 --- a/tools/makePlugin.bash +++ b/tools/makePlugin.bash @@ -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 node_modules/webextension-polyfill/dist/browser-polyfill.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