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:
parent
b74921341c
commit
0567da019f
@ -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 <glen@archematics.app>",
|
||||
"homepage_url": "https://archematics.app",
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["*://*/*"],
|
||||
|
@ -6,7 +6,54 @@
|
||||
</head>
|
||||
|
||||
<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>
|
||||
Write to the JavaScript console: <br/>
|
||||
<label for="vrml97">Generated VRML97 specifications</label>
|
||||
|
BIN
public/assets/archIcon128.png
Normal file
BIN
public/assets/archIcon128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
public/assets/archIcon256.png
Normal file
BIN
public/assets/archIcon256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 161 KiB |
BIN
public/assets/archIcon48.png
Normal file
BIN
public/assets/archIcon48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
BIN
public/assets/joyceExample.png
Normal file
BIN
public/assets/joyceExample.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 119 KiB |
BIN
public/assets/vrmlExample.png
Normal file
BIN
public/assets/vrmlExample.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@ -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
|
||||
|
@ -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<Record<FlagType, boolean>>
|
||||
|
||||
|
@ -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 := $('<span>👁</span>')
|
||||
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 := $('<span>👁</span>')
|
||||
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 := $('<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
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user