2019-07-03 01:42:17 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
class Architype {
|
|
|
|
|
constructor(container) {
|
|
|
|
|
this.container_ = container;
|
|
|
|
|
|
|
|
|
|
this.container_.classList.add('architype');
|
|
|
|
|
// TODO: make theme selectable
|
|
|
|
|
this.container_.classList.add('dark');
|
|
|
|
|
|
|
|
|
|
addEventListener('resize', (e) => { this.onResize(e); });
|
|
|
|
|
|
|
|
|
|
let editorElem = document.createElement('ul');
|
|
|
|
|
this.container_.appendChild(editorElem);
|
|
|
|
|
this.editor_ = new Editor(editorElem);
|
|
|
|
|
|
|
|
|
|
this.grid_ = document.createElement('div');
|
|
|
|
|
this.grid_.classList.add('grid');
|
|
|
|
|
this.container_.appendChild(this.grid_);
|
|
|
|
|
|
2019-07-03 22:10:36 +00:00
|
|
|
this.generation_ = 0;
|
2019-07-03 22:23:41 +00:00
|
|
|
this.renderGeneration_ = -1;
|
2019-07-03 22:10:36 +00:00
|
|
|
this.drawGeneration_ = -1;
|
|
|
|
|
|
2019-07-03 22:23:41 +00:00
|
|
|
this.render_ = [];
|
|
|
|
|
for (let i = 0; i < navigator.hardwareConcurrency; ++i) {
|
|
|
|
|
let render = new Worker('render.js');
|
|
|
|
|
render.addEventListener('message', (e) => { this.onRender(e); });
|
|
|
|
|
this.render_.push(render);
|
|
|
|
|
}
|
2019-07-03 22:10:36 +00:00
|
|
|
|
2019-07-03 01:42:17 +00:00
|
|
|
this.unserialize(JSON.parse(localStorage.getItem('currentState')));
|
|
|
|
|
|
2019-07-03 01:51:13 +00:00
|
|
|
this.observer_ = new MutationObserver(e => { this.onChange(e); });
|
|
|
|
|
this.observer_.observe(editorElem, {
|
2019-07-03 01:42:17 +00:00
|
|
|
attributes: true,
|
|
|
|
|
attributeFilter: ['data-arch-value'],
|
2019-07-03 01:51:13 +00:00
|
|
|
childList: true,
|
2019-07-03 01:42:17 +00:00
|
|
|
subtree: true,
|
|
|
|
|
});
|
|
|
|
|
|
2019-07-03 01:51:13 +00:00
|
|
|
this.onChange();
|
2019-07-03 01:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
serialize() {
|
|
|
|
|
return {
|
|
|
|
|
version: 1,
|
2019-07-03 22:23:41 +00:00
|
|
|
generation: ++this.generation_,
|
2019-07-03 01:42:17 +00:00
|
|
|
editor: this.editor_.serialize(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unserialize(ser) {
|
|
|
|
|
if (!ser) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (ser.version) {
|
|
|
|
|
case 1:
|
2019-07-03 22:10:36 +00:00
|
|
|
this.generation_ = ser.generation;
|
2019-07-03 01:42:17 +00:00
|
|
|
this.editor_.unserialize(ser.editor);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
console.log('unrecognized localStorage.currentState version', ser);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-03 01:51:13 +00:00
|
|
|
onChange(e) {
|
2019-07-03 22:10:36 +00:00
|
|
|
this.serialized_ = this.serialize();
|
2019-07-03 22:23:41 +00:00
|
|
|
this.startRender();
|
2019-07-03 22:10:36 +00:00
|
|
|
localStorage.setItem('currentState', JSON.stringify(this.serialized_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onRender(e) {
|
2019-07-03 22:23:41 +00:00
|
|
|
this.render_.push(e.target);
|
2019-07-03 22:10:36 +00:00
|
|
|
|
|
|
|
|
if (e.data.generation > this.drawGeneration_) {
|
2019-07-03 22:23:41 +00:00
|
|
|
// Received newer than we've drawn; redraw
|
|
|
|
|
this.drawGeneration_ = e.data.generation;
|
2019-07-03 22:10:36 +00:00
|
|
|
this.draw(e.data.steps);
|
|
|
|
|
this.fixSizes();
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-03 22:23:41 +00:00
|
|
|
this.startRender();
|
2019-07-03 22:10:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startRender() {
|
2019-07-03 22:23:41 +00:00
|
|
|
if (this.generation_ == this.renderGeneration_) {
|
|
|
|
|
// Already sent this generation for rendering
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let render = this.render_.pop();
|
|
|
|
|
if (!render) {
|
|
|
|
|
// Ran out of workers
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.renderGeneration_ = this.serialized_.generation;
|
|
|
|
|
render.postMessage(this.serialized_);
|
2019-07-03 01:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
2019-07-10 04:09:54 +00:00
|
|
|
// TODO: factor out draw/grid code
|
2019-07-03 01:42:17 +00:00
|
|
|
onResize(e) {
|
2019-07-03 20:00:05 +00:00
|
|
|
this.fixSizes();
|
2019-07-03 01:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
2019-07-03 18:27:32 +00:00
|
|
|
draw(steps) {
|
|
|
|
|
this.grid_.innerHTML = '';
|
2019-07-05 04:09:24 +00:00
|
|
|
this.toSize_ = [];
|
2019-07-03 01:42:17 +00:00
|
|
|
|
2019-07-03 18:27:32 +00:00
|
|
|
for (let step of steps) {
|
|
|
|
|
switch (step.type) {
|
|
|
|
|
case 'size':
|
|
|
|
|
this.drawGrid(step.size);
|
|
|
|
|
break;
|
2019-07-03 01:42:17 +00:00
|
|
|
|
2019-07-09 04:19:09 +00:00
|
|
|
case 'arrow':
|
|
|
|
|
this.drawArrow(step.pos, step.cls);
|
|
|
|
|
break;
|
|
|
|
|
|
2019-07-04 06:15:39 +00:00
|
|
|
case 'group':
|
|
|
|
|
this.drawGroup(step.min, step.max, step.label);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'line':
|
|
|
|
|
this.drawLine(step.pos, step.cls);
|
|
|
|
|
break;
|
|
|
|
|
|
2019-07-03 18:27:32 +00:00
|
|
|
case 'node':
|
2019-07-04 06:15:39 +00:00
|
|
|
this.drawNode(step.label, step.pos);
|
2019-07-03 18:27:32 +00:00
|
|
|
break;
|
2019-07-03 01:42:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-03 18:27:32 +00:00
|
|
|
drawGrid(size) {
|
2019-07-03 01:42:17 +00:00
|
|
|
this.grid_.style.gridTemplateColumns =
|
2019-07-03 18:27:32 +00:00
|
|
|
'repeat(' + size[0] + ',1fr)';
|
2019-07-03 01:42:17 +00:00
|
|
|
this.grid_.style.gridTemplateRows =
|
2019-07-03 18:27:32 +00:00
|
|
|
'repeat(' + size[1] +
|
2019-07-03 01:42:17 +00:00
|
|
|
',minmax(0, calc((100vw - var(--editor-width)) / ' +
|
2019-07-03 18:27:32 +00:00
|
|
|
size[0] + ')))';
|
|
|
|
|
}
|
2019-07-03 01:42:17 +00:00
|
|
|
|
2019-07-09 04:19:09 +00:00
|
|
|
drawArrow(pos, cls) {
|
|
|
|
|
let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
|
|
|
svg.classList.add('gridArrow');
|
|
|
|
|
svg.classList.add(cls);
|
|
|
|
|
svg.style.gridColumn = pos[0] + 1;
|
|
|
|
|
svg.style.gridRow = pos[1] + 1;
|
|
|
|
|
this.grid_.appendChild(svg);
|
|
|
|
|
|
|
|
|
|
let use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
|
|
|
|
svg.appendChild(use);
|
|
|
|
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#' + cls);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-04 06:15:39 +00:00
|
|
|
drawGroup(min, max, label) {
|
|
|
|
|
let group = document.createElement('div');
|
|
|
|
|
this.grid_.appendChild(group);
|
2019-07-05 04:09:24 +00:00
|
|
|
group.classList.add('gridGroup');
|
2019-07-04 06:15:39 +00:00
|
|
|
group.style.gridColumn = (min[0] + 1) + ' / ' + (max[0] + 2);
|
|
|
|
|
group.style.gridRow = (min[1] + 1) + ' / ' + (max[1] + 2);
|
2019-07-05 04:09:24 +00:00
|
|
|
|
|
|
|
|
if (label != '') {
|
|
|
|
|
let labelNode = document.createElement('div');
|
|
|
|
|
this.grid_.appendChild(labelNode);
|
|
|
|
|
labelNode.classList.add('gridGroupLabel');
|
|
|
|
|
labelNode.innerText = label;
|
|
|
|
|
labelNode.style.gridColumn = (min[0] + 1) + ' / ' + (max[0] + 2);
|
|
|
|
|
labelNode.style.gridRow = min[1] + 1;
|
|
|
|
|
this.toSize_.push(labelNode);
|
|
|
|
|
}
|
2019-07-04 06:15:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drawLine(pos, cls) {
|
|
|
|
|
let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
|
|
|
svg.classList.add('gridLines');
|
|
|
|
|
svg.style.gridColumn = pos[0] + 1;
|
|
|
|
|
svg.style.gridRow = pos[1] + 1;
|
|
|
|
|
this.grid_.appendChild(svg);
|
|
|
|
|
|
|
|
|
|
let use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
|
|
|
|
svg.appendChild(use);
|
|
|
|
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#' + cls);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drawNode(label, pos) {
|
2019-07-03 18:27:32 +00:00
|
|
|
let node = document.createElement('div');
|
|
|
|
|
node.classList.add('gridNode');
|
|
|
|
|
this.grid_.appendChild(node);
|
|
|
|
|
node.innerText = label;
|
|
|
|
|
node.style.gridColumn = pos[0] + 1;
|
|
|
|
|
node.style.gridRow = pos[1] + 1;
|
2019-07-05 04:09:24 +00:00
|
|
|
this.toSize_.push(node);
|
2019-07-03 01:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
2019-07-03 18:27:32 +00:00
|
|
|
fixSizes() {
|
2019-07-05 04:09:24 +00:00
|
|
|
for (let node of this.toSize_) {
|
2019-07-03 18:27:32 +00:00
|
|
|
node.style.fontSize = null;
|
|
|
|
|
for (let size = 20;
|
|
|
|
|
size && (node.scrollWidth > node.clientWidth ||
|
|
|
|
|
node.scrollHeight > node.clientHeight);
|
|
|
|
|
--size) {
|
|
|
|
|
node.style.fontSize = size + 'px';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-03 01:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
<!--# include file="Editor.js" -->
|
|
|
|
|
|
|
|
|
|
<!--# include file="utils.js" -->
|
|
|
|
|
|
|
|
|
|
new Architype(document.getElementById('architype'));
|