mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-28 18:49:57 +02:00
much better sketch resetting using DOM rebuild
This commit is contained in:
@@ -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 <img>
|
|
||||||
- extract custom elements
|
|
||||||
- ammend HTML markup
|
|
||||||
- generate offline image
|
|
||||||
- compile markdown into a single HTML file using normal HTML templating
|
|
||||||
- localization
|
|
@@ -1,5 +1,4 @@
|
|||||||
import { enrich } from "../lib/enrich.js";
|
import { enrich } from "../lib/enrich.js";
|
||||||
import { create } from "../lib/create.js";
|
|
||||||
import { Bezier } from "./types/bezier.js";
|
import { Bezier } from "./types/bezier.js";
|
||||||
import { BSpline } from "./types/bspline.js";
|
import { BSpline } from "./types/bspline.js";
|
||||||
import { Vector } from "./types/vector.js";
|
import { Vector } from "./types/vector.js";
|
||||||
@@ -7,6 +6,7 @@ import { Matrix } from "./types/matrix.js";
|
|||||||
import { Shape } from "./util/shape.js";
|
import { Shape } from "./util/shape.js";
|
||||||
import binomial from "./util/binomial.js";
|
import binomial from "./util/binomial.js";
|
||||||
import { BaseAPI } from "./base-api.js";
|
import { BaseAPI } from "./base-api.js";
|
||||||
|
import impartSliderLogic from "./impart-slider-logic.js";
|
||||||
|
|
||||||
const MOUSE_PRECISION_ZONE = 5;
|
const MOUSE_PRECISION_ZONE = 5;
|
||||||
const TOUCH_PRECISION_ZONE = 30;
|
const TOUCH_PRECISION_ZONE = 30;
|
||||||
@@ -182,154 +182,6 @@ class GraphicsAPI extends BaseAPI {
|
|||||||
this.line(0, 0, 0, this.height);
|
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
|
* 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 };
|
export { GraphicsAPI, Bezier, BSpline, Vector, Matrix, Shape };
|
||||||
|
172
docs/js/custom-element/api/impart-slider-logic.js
Normal file
172
docs/js/custom-element/api/impart-slider-logic.js
Normal file
@@ -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;
|
||||||
|
}
|
@@ -256,7 +256,15 @@ class GraphicsElement extends CustomElement {
|
|||||||
* Reload this graphics element the brute force way.
|
* Reload this graphics element the brute force way.
|
||||||
*/
|
*/
|
||||||
async reset() {
|
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.
|
// If we get here, there were no source code errors: undo the scheduled error print.
|
||||||
clearTimeout(this.errorPrintTimeout);
|
clearTimeout(this.errorPrintTimeout);
|
||||||
this.render();
|
this.render();
|
||||||
|
this.dispatchEvent(new CustomEvent(`loaded`));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -10,17 +10,15 @@
|
|||||||
* grabs your document.referrer value, which (unless Do
|
* grabs your document.referrer value, which (unless Do
|
||||||
* Not Track is enabled) will contain the location of
|
* Not Track is enabled) will contain the location of
|
||||||
* the page you were on before you clicked a link to this
|
* the page you were on before you clicked a link to this
|
||||||
* page, and GETs that to my logger. That GET operation
|
* page, and GETs that to my logger.
|
||||||
* comes from your computer, so will have your IP as part
|
|
||||||
* of the HTTP headers.
|
|
||||||
*
|
*
|
||||||
* And that's all I really care about, because I want to
|
* If you want to know what gets logged, have a look
|
||||||
* know how many people visit this page, and roughly where
|
* at the ./src/logger/logger.php file on github.
|
||||||
* they're from (gasp! IPs can be turned into rough
|
|
||||||
* geographical location O_O).
|
|
||||||
*
|
*
|
||||||
* If you want to know what logger.php looks like, hit up
|
* If that's too much effort:
|
||||||
* github. It's in referrer/logger.php
|
* - the request URL
|
||||||
|
* - the referrer URL (if there is one)
|
||||||
|
* - the user agent
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
(function referrer(l) {
|
(function referrer(l) {
|
||||||
@@ -32,20 +30,7 @@
|
|||||||
if (loc.indexOf("localhost") !== -1) return;
|
if (loc.indexOf("localhost") !== -1) return;
|
||||||
// right, continue
|
// right, continue
|
||||||
var url = "https://pomax.nihongoresources.com/pages/bezierinfo/logger.php";
|
var url = "https://pomax.nihongoresources.com/pages/bezierinfo/logger.php";
|
||||||
var xhr = new XMLHttpRequest();
|
fetch(`${url}?referrer=${encodeURIComponent(document.referrer)}&for=${page}`)
|
||||||
xhr.open(
|
.then((_) => {})
|
||||||
"GET",
|
.catch((_) => {});
|
||||||
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? */
|
|
||||||
}
|
|
||||||
})(window.location.toString());
|
})(window.location.toString());
|
||||||
|
29
docs/news/2020-08-28.md
Normal file
29
docs/news/2020-08-28.md
Normal file
@@ -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 <img src="svg">
|
||||||
|
- 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 <fallback-image>
|
||||||
|
- link in exported image
|
||||||
|
- each section into the larger HTML file (baesd on `toc` order)
|
||||||
|
- localization!
|
@@ -24,7 +24,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
--note-block-color: rgb(255, 255, 246);
|
--note-block-color: rgb(255, 255, 246);
|
||||||
--code-block-color: lightyellow;
|
--code-block-color: rgba(0,0,0,0.03);
|
||||||
--heading-color: #cfe6ff;
|
--heading-color: #cfe6ff;
|
||||||
--heading-text: #333;
|
--heading-text: #333;
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ div.print {
|
|||||||
div.howtocode {
|
div.howtocode {
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
background: rgb(246, 255, 255);
|
background: #FCFCFD;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
padding: 1em 1em 0 1em;
|
padding: 1em 1em 0 1em;
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@ pre {
|
|||||||
|
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: monospace;
|
font-family: Courier;
|
||||||
}
|
}
|
||||||
|
|
||||||
li code, p code {
|
li code, p code {
|
||||||
|
59
src/logger/logger.php
Normal file
59
src/logger/logger.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Alright, what does this do: it gets the client's IP and the
|
||||||
|
* referring URL they communicated. Then it logs those, timestamped,
|
||||||
|
* to a gzipped file. That's it. Why would I log anything else...?
|
||||||
|
*
|
||||||
|
* This lets me see roughly where in the world people are reading
|
||||||
|
* the article (which tells me where it might make sense to tell
|
||||||
|
* people about its existence), and it lets me see which other pages
|
||||||
|
* on the web link to it, so I can check those pages out and maybe
|
||||||
|
* find information that lets me improve it (like forum comments
|
||||||
|
* about the article that people left in that forum, rather than
|
||||||
|
* as a comment on my article).
|
||||||
|
*
|
||||||
|
* Lastly, it tells me how many people visit the page at all.
|
||||||
|
* Github's gh-pages system does not do any kind of stats, so the
|
||||||
|
* only way to find out how many daily visitors I get is by using
|
||||||
|
* a tracking service. I don't know about you, but I'd rather have
|
||||||
|
* a website do that itself than rely on google analytics (or
|
||||||
|
* another third party 'you have no idea what happens with the
|
||||||
|
* data we collect' company).
|
||||||
|
*
|
||||||
|
* - Pomax
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Allow the bezier article and legendre-gauss table page to call us.
|
||||||
|
header('Access-Control-Allow-Origin: https://pomax.github.io');
|
||||||
|
header('Access-Control-Allow-Headers: *');
|
||||||
|
|
||||||
|
echo "accepted\n";
|
||||||
|
// Form timestamps
|
||||||
|
$time = microtime(true);
|
||||||
|
$stamp = date("Y-m-d H:i:s");
|
||||||
|
|
||||||
|
// Get the original page. This'll be a string like 'bezier' or 'legendre'.
|
||||||
|
$for = isset($_GET["for"]) ? $_GET["for"] : '';
|
||||||
|
|
||||||
|
// Get referrer URL.
|
||||||
|
$ref = isset($_GET["referrer"]) ? $_GET["referrer"] : '';
|
||||||
|
|
||||||
|
// Get the User Agent
|
||||||
|
$ua = isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : '';
|
||||||
|
|
||||||
|
// Convert the data to a single line of JSON
|
||||||
|
$json_line = '{"for": "'.$for.'",'.
|
||||||
|
' "time": '.$time.','.
|
||||||
|
' "stamp": "'.$stamp.'",'.
|
||||||
|
' "referrer": "'.$ref.'",'.
|
||||||
|
' "ua": "'.$ua.'"}' . "\n";
|
||||||
|
|
||||||
|
// Finally, write the data to the log file.
|
||||||
|
// (there's an .htaccess rule that prevents public access to the log files)
|
||||||
|
$gz = gzopen('referral_log_' . date("Y-m-d") . '.gz','a9');
|
||||||
|
gzwrite($gz, $json_line);
|
||||||
|
gzclose($gz);
|
||||||
|
|
||||||
|
// That's all there is.
|
||||||
|
|
||||||
|
echo "success";
|
Reference in New Issue
Block a user