make tairu work better with noscript

This commit is contained in:
りき萌 2024-02-20 23:30:36 +01:00
parent 90de54c359
commit a92ae02454
22 changed files with 404 additions and 238 deletions

View file

@ -19,16 +19,19 @@ function getLiterateProgram(name) {
return literatePrograms.get(name);
}
function getLiterateProgramWorkerCommands(name) {
function getLiterateProgramWorkerCommands(name, count) {
let commands = [];
let literateProgram = getLiterateProgram(name);
for (let frame of literateProgram.frames) {
for (let i = 0; i < count; ++i) {
let frame = literateProgram.frames[i];
if (frame.mode == "input") {
commands.push({ kind: "module", source: frame.textContent });
} else if (frame.mode == "output") {
commands.push({ kind: "output" });
}
}
return commands;
}
@ -180,6 +183,8 @@ class OutputMode {
}
});
this.frame.placeholderImage.classList.add("loading");
this.frame.program.onChanged.push(_ => this.evaluate());
}
@ -187,7 +192,7 @@ class OutputMode {
this.requestConsoleClear();
this.iframe.contentWindow.postMessage({
action: "eval",
input: getLiterateProgramWorkerCommands(this.frame.programName),
input: getLiterateProgramWorkerCommands(this.frame.programName, this.frame.frameIndex + 1),
});
}
@ -229,6 +234,15 @@ class OutputMode {
// iframe cannot be `display: none` to get its scrollWidth/scrollHeight.
this.iframe.classList.remove("hidden");
if (this.frame.placeholderImage != null) {
// Fade the iframe in after it becomes visible, and remove the image.
setTimeout(() => this.iframe.classList.add("loaded"), 0);
this.frame.removeChild(this.frame.placeholderImage);
} else {
// If there is no image, don't do the fade in.
this.iframe.classList.add("loaded");
}
let width = this.iframe.contentDocument.body.scrollWidth;
let height = this.iframe.contentDocument.body.scrollHeight;
@ -244,8 +258,11 @@ class OutputMode {
class LiterateProgram extends HTMLElement {
connectedCallback() {
this.programName = this.getAttribute("data-program");
this.frameIndex = this.program.frames.length;
this.program.frames.push(this);
this.placeholderImage = this.getElementsByClassName("placeholder")[0];
this.mode = this.getAttribute("data-mode");
if (this.mode == "input") {
this.modeImpl = new InputMode(this);

View file

@ -1,87 +0,0 @@
// A frameworking class assigning some CSS classes to the canvas to make it integrate nicer with CSS.
export class Frame extends HTMLCanvasElement {
static fontFace = "RecVar";
static monoFontFace = "RecVarMono";
constructor() {
super();
}
async connectedCallback() {
this.style.cssText = `
margin-top: 8px;
margin-bottom: 4px;
border-radius: 4px;
max-width: 100%;
`;
this.ctx = this.getContext("2d");
requestAnimationFrame(this.#drawLoop.bind(this));
}
#drawLoop() {
this.ctx.font = "14px RecVar";
this.draw();
requestAnimationFrame(this.#drawLoop.bind(this));
}
// Override this!
draw() {
throw new ReferenceError("draw() must be overridden");
}
getTextPositionInBox(text, x, y, width, height, hAlign, vAlign) {
let measurements = this.ctx.measureText(text);
let leftX;
switch (hAlign) {
case "left":
leftX = x;
break;
case "center":
leftX = x + width / 2 - measurements.width / 2;
break;
case "right":
leftX = x + width - measurements.width;
break;
}
let textHeight = measurements.fontBoundingBoxAscent;
let baselineY;
switch (vAlign) {
case "top":
baselineY = y + textHeight;
break;
case "center":
baselineY = y + height / 2 + textHeight / 2;
break;
case "bottom":
baselineY = y + height;
break;
}
return { leftX, baselineY };
}
get scaleInViewportX() {
return this.clientWidth / this.width;
}
get scaleInViewportY() {
return this.clientHeight / this.height;
}
getMousePositionFromEvent(event) {
return {
x: event.offsetX / this.scaleInViewportX,
y: event.offsetY / this.scaleInViewportY,
};
}
}
export function defineFrame(elementName, claß) { // because `class` is a keyword.
customElements.define(elementName, claß, { extends: "canvas" });
}
defineFrame("tairu--frame", Frame);

View file

@ -1,76 +0,0 @@
import { TileEditor } from 'tairu/editor.js';
export function alignTextInRectangle(ctx, text, x, y, width, height, hAlign, vAlign) {
let measurements = ctx.measureText(text);
let leftX;
switch (hAlign) {
case "left":
leftX = x;
break;
case "center":
leftX = x + width / 2 - measurements.width / 2;
break;
case "right":
leftX = x + width - measurements.width;
break;
}
let textHeight = measurements.fontBoundingBoxAscent;
let baselineY;
switch (vAlign) {
case "top":
baselineY = y + textHeight;
break;
case "center":
baselineY = y + height / 2 + textHeight / 2;
break;
case "bottom":
baselineY = y + height;
break;
}
return { leftX, baselineY };
}
export function shouldConnect(a, b) {
return a == b;
}
export class TileEditorWithCardinalDirections extends TileEditor {
constructor(options) {
super(options);
this.colorScheme.tiles[1] = "#f96565";
}
drawConnectionText(text, enabled, tileX, tileY, hAlign, vAlign) {
this.ctx.beginPath();
this.ctx.fillStyle = enabled ? "#6c023e" : "#d84161";
this.ctx.font = `800 14px ${Frame.monoFontFace}`;
const padding = 2;
let topLeftX = tileX * this.tileSize + padding;
let topLeftY = tileY * this.tileSize + padding;
let rectSize = this.tileSize - padding * 2;
let { leftX, baselineY } = this.getTextPositionInBox(text, topLeftX, topLeftY, rectSize, rectSize, hAlign, vAlign);
this.ctx.fillText(text, leftX, baselineY);
}
drawTiles() {
super.drawTiles();
for (let y = 0; y < this.tilemap.height; ++y) {
for (let x = 0; x < this.tilemap.width; ++x) {
let tile = this.tilemap.at(x, y);
if (canConnect(tile)) {
let connectedWithEast = shouldConnect(tile, this.tilemap.at(x + 1, y));
let connectedWithSouth = shouldConnect(tile, this.tilemap.at(x, y + 1));
let connectedWithNorth = shouldConnect(tile, this.tilemap.at(x, y - 1));
let connectedWithWest = shouldConnect(tile, this.tilemap.at(x - 1, y));
this.drawConnectionText("E", connectedWithEast, x, y, "right", "center");
this.drawConnectionText("S", connectedWithSouth, x, y, "center", "bottom");
this.drawConnectionText("N", connectedWithNorth, x, y, "center", "top");
this.drawConnectionText("W", connectedWithWest, x, y, "left", "center");
}
}
}
}
}

View file

@ -1,47 +0,0 @@
import { Tilemap } from './tilemap.js';
const alphabet = " x";
function parseTilemap(lineArray) {
let tilemap = new Tilemap(lineArray[0].length, lineArray.length);
for (let y in lineArray) {
let line = lineArray[y];
for (let x = 0; x < line.length; ++x) {
let char = line.charAt(x);
tilemap.setAt(x, y, alphabet.indexOf(char));
}
}
return tilemap;
}
export default {
bitwiseAutotiling: parseTilemap([
" ",
" xxx ",
" xxx ",
" xxx ",
" ",
]),
bitwiseAutotilingChapter2: parseTilemap([
" ",
" x ",
" x ",
" xxx ",
" ",
]),
bitwiseAutotilingCorners: parseTilemap([
" ",
" x x ",
" x ",
" x x ",
" ",
]),
bitwiseAutotiling47: parseTilemap([
" x ",
" x ",
" xx xx ",
" xxxx ",
" x ",
]),
};

View file

@ -7,3 +7,12 @@ document.addEventListener("click", event => {
event.preventDefault();
}
})
// Certain words don't make sense if scripts are disabled.
class YesScript extends HTMLElement {
connectedCallback() {
this.classList.add("yes-indeed");
}
}
customElements.define("th-yesscript", YesScript);