diff --git a/blog/2020-08-28.md b/blog/2020-08-28.md
deleted file mode 100644
index 323f39bd..00000000
--- a/blog/2020-08-28.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Rewriting the tech stack
-
-- started in 2011 as simple webpage with some Processing.js
-- complete rewrite to React in 2016
-- web's caught up, and there is no reason to keep things React. This content should work even without JS.
-
-
-- markdown
- - extract latex
- - build using actual xelatex
- - replace with
- - extract custom elements
- - ammend HTML markup
- - generate offline image
-- compile markdown into a single HTML file using normal HTML templating
-- localization
diff --git a/docs/js/custom-element/api/graphics-api.js b/docs/js/custom-element/api/graphics-api.js
index 1ecc7f6e..3437ed5a 100644
--- a/docs/js/custom-element/api/graphics-api.js
+++ b/docs/js/custom-element/api/graphics-api.js
@@ -1,5 +1,4 @@
import { enrich } from "../lib/enrich.js";
-import { create } from "../lib/create.js";
import { Bezier } from "./types/bezier.js";
import { BSpline } from "./types/bspline.js";
import { Vector } from "./types/vector.js";
@@ -7,6 +6,7 @@ import { Matrix } from "./types/matrix.js";
import { Shape } from "./util/shape.js";
import binomial from "./util/binomial.js";
import { BaseAPI } from "./base-api.js";
+import impartSliderLogic from "./impart-slider-logic.js";
const MOUSE_PRECISION_ZONE = 5;
const TOUCH_PRECISION_ZONE = 30;
@@ -182,154 +182,6 @@ class GraphicsAPI extends BaseAPI {
this.line(0, 0, 0, this.height);
}
- /**
- * Dynamically add a slider
- */
- addSlider(classes, propname, min, max, step, value, transform) {
- if (this.element) {
- let slider = create(`input`);
- slider.type = `range`;
- slider.min = min;
- slider.max = max;
- slider.step = step;
- slider.setAttribute(`value`, value);
- slider.setAttribute(`class`, classes);
- this.element.append(slider);
- this.setSlider(slider, propname, value, transform);
- }
- }
-
- /**
- * Update a slider with new min/max/step parameters, and value.
- *
- * @param {*} propname
- * @param {*} min
- * @param {*} max
- * @param {*} step
- * @param {*} value
- */
- updateSlider(propname, min, max, step, value) {
- let slider = this._sliders[propname];
-
- if (!slider) {
- throw new Error(`this.${propname} has no associated slider.`);
- }
-
- slider.setAttribute(`min`, min);
- slider.setAttribute(`max`, max);
- slider.setAttribute(`step`, step);
- slider.setAttribute(`value`, value);
- slider.updateProperty(value);
- }
-
- /**
- * Set up a slider to control a named, numerical property in the sketch.
- *
- * @param {String} local query selector for the type=range element.
- * @param {String} propname the name of the property to control.
- * @param {float} initial the initial value for this property.
- * @param {boolean} redraw whether or not to redraw after updating the value from the slider.
- */
- setSlider(qs, propname, initial, transform) {
- if (propname !== false && typeof this[propname] !== `undefined`) {
- throw new Error(`this.${propname} already exists: cannot bind slider.`);
- }
-
- this._sliders = this._sliders || {};
-
- let propLabel = propname.replace(`!`, ``);
- propname = propLabel === propname ? propname : false;
-
- let slider = typeof qs === `string` ? this.find(qs) : qs;
-
- // create a slider row in the table of sliders
- let ui = (() => {
- if (!this.element) {
- return { update: (v) => {} };
- }
-
- let table = find(`table.slider-wrapper`);
-
- if (!table) {
- table = slider.parentNode.querySelector(`table.slider-wrapper`);
- if (!table) {
- table = create(`table`);
- table.classList.add(`slider-wrapper`);
- slider.parentNode.replaceChild(table, slider);
- }
- }
-
- let tr = create(`tr`);
-
- let td = create(`td`);
- let label = create(`label`);
- label.classList.add(`slider-label`);
- label.innerHTML = propLabel;
- td.append(label);
- tr.append(td);
-
- td = create(`td`);
- slider.classList.add(`slider`);
- this._sliders[propname] = slider;
- td.append(slider);
- tr.append(td);
-
- td = create(`td`);
- let valueField = create(`label`);
- valueField.classList.add(`slider-value`);
- valueField.textContent;
- td.append(valueField);
- tr.append(td);
-
- table.append(tr);
- return { update: (v) => (valueField.textContent = v) };
- })();
-
- if (!slider) {
- console.warn(`Warning: no slider found for query selector "${qs}"`);
- if (propname) this[propname] = initial;
- return undefined;
- }
-
- let step = slider.getAttribute(`step`) || "1";
- let res = !step.includes(`.`)
- ? 0
- : step.substring(step.indexOf(`.`) + 1).length;
-
- slider.updateProperty = (evt) => {
- let value = parseFloat(slider.value);
- ui.update(value.toFixed(res));
- try {
- let checked = transform ? transform(value) ?? value : value;
- if (propname) this[propname] = checked;
- } catch (e) {
- if (evt instanceof Event) {
- evt.preventDefault();
- evt.stopPropagation();
- }
- ui.update(e.value.toFixed(res));
- slider.value = e.value;
- slider.setAttribute(`value`, e.value);
- }
- if (!this.redrawing) this.redraw();
- };
-
- slider.value = initial;
- slider.updateProperty({ target: { value: initial } });
- slider.listen(`input`, (evt) => slider.updateProperty(evt));
-
- return slider;
- }
-
- /**
- * remove all sliders from this element
- */
- removeSliders() {
- this.findAll(`.slider-wrapper`).forEach((s) => {
- s.parentNode.removeChild(s);
- });
- }
-
/**
* Convert the canvas to an image
*/
@@ -936,4 +788,12 @@ class GraphicsAPI extends BaseAPI {
}
}
+/**
+ * Multiple inheritance is probably a good thing to not have in JS,
+ * but traits would have been nice, because a 1000+ LoC class is kind
+ * of silly, so to keep things easy to maintain, you end up writing
+ * custom trait-adding functions like this...
+ */
+impartSliderLogic(GraphicsAPI);
+
export { GraphicsAPI, Bezier, BSpline, Vector, Matrix, Shape };
diff --git a/docs/js/custom-element/api/impart-slider-logic.js b/docs/js/custom-element/api/impart-slider-logic.js
new file mode 100644
index 00000000..b91820db
--- /dev/null
+++ b/docs/js/custom-element/api/impart-slider-logic.js
@@ -0,0 +1,172 @@
+import { create } from "../lib/create.js";
+
+export default function impartSliderLogic(GraphicsAPI) {
+ /**
+ * Dynamically add a slider
+ */
+ GraphicsAPI.prototype.addSlider = function addSlider(
+ classes,
+ propname,
+ min,
+ max,
+ step,
+ value,
+ transform
+ ) {
+ if (this.element) {
+ let slider = create(`input`);
+ slider.type = `range`;
+ slider.min = min;
+ slider.max = max;
+ slider.step = step;
+ slider.setAttribute(`value`, value);
+ slider.setAttribute(`class`, classes);
+ this.element.append(slider);
+ this.setSlider(slider, propname, value, transform);
+ }
+ };
+
+ /**
+ * Update a slider with new min/max/step parameters, and value.
+ *
+ * @param {*} propname
+ * @param {*} min
+ * @param {*} max
+ * @param {*} step
+ * @param {*} value
+ */
+ GraphicsAPI.prototype.updateSlider = function updateSlider(
+ propname,
+ min,
+ max,
+ step,
+ value
+ ) {
+ let slider = this._sliders[propname];
+
+ if (!slider) {
+ throw new Error(`this.${propname} has no associated slider.`);
+ }
+
+ slider.setAttribute(`min`, min);
+ slider.setAttribute(`max`, max);
+ slider.setAttribute(`step`, step);
+ slider.setAttribute(`value`, value);
+ slider.updateProperty(value);
+ };
+
+ /**
+ * Set up a slider to control a named, numerical property in the sketch.
+ *
+ * @param {String} local query selector for the type=range element.
+ * @param {String} propname the name of the property to control.
+ * @param {float} initial the initial value for this property.
+ * @param {boolean} redraw whether or not to redraw after updating the value from the slider.
+ */
+ GraphicsAPI.prototype.setSlider = function setSlider(
+ qs,
+ propname,
+ initial,
+ transform
+ ) {
+ if (propname !== false && typeof this[propname] !== `undefined`) {
+ throw new Error(`this.${propname} already exists: cannot bind slider.`);
+ }
+
+ this._sliders = this._sliders || {};
+
+ let propLabel = propname.replace(`!`, ``);
+ propname = propLabel === propname ? propname : false;
+
+ let slider = typeof qs === `string` ? this.find(qs) : qs;
+
+ // create a slider row in the table of sliders
+ let ui = (() => {
+ if (!this.element) {
+ return { update: (v) => {} };
+ }
+
+ let table = find(`table.slider-wrapper`);
+
+ if (!table) {
+ table = slider.parentNode.querySelector(`table.slider-wrapper`);
+ if (!table) {
+ table = create(`table`);
+ table.classList.add(`slider-wrapper`);
+ slider.parentNode.replaceChild(table, slider);
+ }
+ }
+
+ let tr = create(`tr`);
+
+ let td = create(`td`);
+ let label = create(`label`);
+ label.classList.add(`slider-label`);
+ label.innerHTML = propLabel;
+ td.append(label);
+ tr.append(td);
+
+ td = create(`td`);
+ slider.classList.add(`slider`);
+ this._sliders[propname] = slider;
+ td.append(slider);
+ tr.append(td);
+
+ td = create(`td`);
+ let valueField = create(`label`);
+ valueField.classList.add(`slider-value`);
+ valueField.textContent;
+ td.append(valueField);
+ tr.append(td);
+
+ table.append(tr);
+ return { update: (v) => (valueField.textContent = v) };
+ })();
+
+ if (!slider) {
+ console.warn(`Warning: no slider found for query selector "${qs}"`);
+ if (propname) this[propname] = initial;
+ return undefined;
+ }
+
+ let step = slider.getAttribute(`step`) || "1";
+ let res = !step.includes(`.`)
+ ? 0
+ : step.substring(step.indexOf(`.`) + 1).length;
+
+ slider.updateProperty = (evt) => {
+ let value = parseFloat(slider.value);
+ ui.update(value.toFixed(res));
+ try {
+ let checked = transform ? transform(value) ?? value : value;
+ if (propname) this[propname] = checked;
+ } catch (e) {
+ if (evt instanceof Event) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+ ui.update(e.value.toFixed(res));
+ slider.value = e.value;
+ slider.setAttribute(`value`, e.value);
+ }
+ if (!this.redrawing) this.redraw();
+ };
+
+ slider.value = initial;
+ slider.updateProperty({ target: { value: initial } });
+ slider.listen(`input`, (evt) => slider.updateProperty(evt));
+
+ return slider;
+ };
+
+ /**
+ * remove all sliders from this element
+ */
+ GraphicsAPI.prototype.removeSliders = function removeSliders() {
+ this.findAll(`.slider-wrapper`).forEach((s) => {
+ s.parentNode.removeChild(s);
+ });
+ };
+
+ return GraphicsAPI;
+}
diff --git a/docs/js/custom-element/graphics-element.js b/docs/js/custom-element/graphics-element.js
index 7d666087..223b16e0 100644
--- a/docs/js/custom-element/graphics-element.js
+++ b/docs/js/custom-element/graphics-element.js
@@ -256,7 +256,15 @@ class GraphicsElement extends CustomElement {
* Reload this graphics element the brute force way.
*/
async reset() {
- this.outerHTML = this.originalHTML;
+ const parent = this.parentNode;
+ const offDOM = document.createElement(`div`);
+ offDOM.innerHTML = this.originalHTML;
+ const newElement = offDOM.querySelector(`graphics-element`);
+ offDOM.style.display = `none`;
+ document.body.append(offDOM);
+ newElement.addEventListener(`loaded`, () =>
+ parent.replaceChild(newElement, this)
+ );
}
/**
@@ -284,6 +292,7 @@ class GraphicsElement extends CustomElement {
// If we get here, there were no source code errors: undo the scheduled error print.
clearTimeout(this.errorPrintTimeout);
this.render();
+ this.dispatchEvent(new CustomEvent(`loaded`));
}
/**
diff --git a/docs/js/site/referrer.js b/docs/js/site/referrer.js
index 4d3bbdb0..cbf6f133 100644
--- a/docs/js/site/referrer.js
+++ b/docs/js/site/referrer.js
@@ -10,17 +10,15 @@
* grabs your document.referrer value, which (unless Do
* Not Track is enabled) will contain the location of
* the page you were on before you clicked a link to this
- * page, and GETs that to my logger. That GET operation
- * comes from your computer, so will have your IP as part
- * of the HTTP headers.
+ * page, and GETs that to my logger.
*
- * And that's all I really care about, because I want to
- * know how many people visit this page, and roughly where
- * they're from (gasp! IPs can be turned into rough
- * geographical location O_O).
+ * If you want to know what gets logged, have a look
+ * at the ./src/logger/logger.php file on github.
*
- * If you want to know what logger.php looks like, hit up
- * github. It's in referrer/logger.php
+ * If that's too much effort:
+ * - the request URL
+ * - the referrer URL (if there is one)
+ * - the user agent
*
*/
(function referrer(l) {
@@ -32,20 +30,7 @@
if (loc.indexOf("localhost") !== -1) return;
// right, continue
var url = "https://pomax.nihongoresources.com/pages/bezierinfo/logger.php";
- var xhr = new XMLHttpRequest();
- xhr.open(
- "GET",
- url +
- "?" +
- "referrer=" +
- encodeURIComponent(document.referrer) +
- "&for=" +
- page,
- true
- );
- try {
- xhr.send(null);
- } catch (e) {
- /* you don't care about this error, and I can't see it, so why would we do anything with it? */
- }
+ fetch(`${url}?referrer=${encodeURIComponent(document.referrer)}&for=${page}`)
+ .then((_) => {})
+ .catch((_) => {});
})(window.location.toString());
diff --git a/docs/news/2020-08-28.md b/docs/news/2020-08-28.md
new file mode 100644
index 00000000..4eb517fb
--- /dev/null
+++ b/docs/news/2020-08-28.md
@@ -0,0 +1,29 @@
+# Rewriting the tech stack
+
+- started in 2011 as simple webpage with some Processing.js
+- complete rewrite to React in 2016
+- web's caught up, and there is no reason to keep things React. This content should work even without JS.
+
+- progressive enhancement: it should work, JS should make it work better.
+ - generate "a finished page"
+ - html + css
+ - load it with JS that "makes it better", not "makes it work"
+
+- start with markdown
+ - convert to HTML
+ - index.template.html
+ - nunjucks, like Django/Jinja/Mustache
+ - extract latex
+ - build using actual `xelatex`
+ - convert TeX to SVG
+ - xelatex, pdfcrop, pdf2svg, svgo
+ - replace with
+ - extract custom elements
+ - extract .scr, load in node, _actually run_
+ - run single frame, export to `.png` image
+ - ammend HTML markup
+ - fill in missing width/height attributes
+ - add
+ - link in exported image
+- each section into the larger HTML file (baesd on `toc` order)
+- localization!
diff --git a/docs/placeholder-style.css b/docs/placeholder-style.css
index abbaa8a4..e7da1eff 100644
--- a/docs/placeholder-style.css
+++ b/docs/placeholder-style.css
@@ -24,7 +24,7 @@
margin: 0;
--note-block-color: rgb(255, 255, 246);
- --code-block-color: lightyellow;
+ --code-block-color: rgba(0,0,0,0.03);
--heading-color: #cfe6ff;
--heading-text: #333;
}
@@ -135,7 +135,7 @@ div.print {
div.howtocode {
position: relative;
border: 1px solid black;
- background: rgb(246, 255, 255);
+ background: #FCFCFD;
margin: 1em;
padding: 1em 1em 0 1em;
}
@@ -217,7 +217,7 @@ pre {
code {
- font-family: monospace;
+ font-family: Courier;
}
li code, p code {
diff --git a/src/logger/logger.php b/src/logger/logger.php
new file mode 100644
index 00000000..528c26a6
--- /dev/null
+++ b/src/logger/logger.php
@@ -0,0 +1,59 @@
+