solarite 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -5
- package/benchmarks/naive/Solarite.min.js +0 -4
- package/benchmarks/naive/index.html +0 -14
- package/benchmarks/naive/main.js +0 -339
- package/benchmarks/naive/package-lock.json +0 -13
- package/benchmarks/naive/package.json +0 -23
- package/benchmarks/readme.md +0 -48
- package/build/build.bat +0 -4
- package/build/build.js +0 -139
- package/build/lib/rollup.min.js +0 -11
- package/build/lib/source-map.min.js +0 -1
- package/build/lib/terser.min.js +0 -1
- package/docs/index.md +0 -883
- package/docs/js/Playground.js +0 -184
- package/docs/js/codemirror/codemirror6.js +0 -32513
- package/docs/js/codemirror/themeSolarIce.js +0 -312
- package/docs/js/documentation.js +0 -32
- package/docs/js/ui/CodeEditor.js +0 -978
- package/docs/js/ui/DarkToggle.js +0 -52
- package/docs/js/ui/FlexResizer.js +0 -152
- package/docs/js/util/Draggable2.js +0 -151
- package/docs/js/util/Errors.js +0 -20
- package/docs/js/util/Html.js +0 -147
- package/docs/js/util/Icons.js +0 -623
- package/docs/js/util/Input.js +0 -253
- package/docs/js/util/Util.js +0 -88
- package/docs/js/util/delve.js +0 -43
- package/docs/media/FiraCode400.woff2 +0 -0
- package/docs/media/cabin-latin-700.woff2 +0 -0
- package/docs/media/documentation.css +0 -96
- package/docs/media/eternium.css +0 -1123
- package/docs/media/solarite-machine.webp +0 -0
- package/index.html +0 -761
- package/tests/Benchmark.test.js +0 -319
- package/tests/Solarite.test.js +0 -3838
- package/tests/Testimony.js +0 -809
- package/tests/index.html +0 -46
- package/tests/run.bat +0 -2
package/docs/js/ui/DarkToggle.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// Add this from a regular script tag in the head, not a module!
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/** TODO: Use slot for custom content? */
|
|
5
|
-
class DarkToggle extends HTMLElement {
|
|
6
|
-
|
|
7
|
-
constructor() {
|
|
8
|
-
super();
|
|
9
|
-
this.render();
|
|
10
|
-
|
|
11
|
-
this.addEventListener('click', () => {
|
|
12
|
-
let h=document.documentElement;
|
|
13
|
-
let isDark = !h.hasAttribute('dark');
|
|
14
|
-
h.toggleAttribute('dark', isDark);
|
|
15
|
-
localStorage.setItem('dark', h.hasAttribute('dark')+'');
|
|
16
|
-
|
|
17
|
-
for (let iframe of this.ownerDocument.body.querySelectorAll('iframe'))
|
|
18
|
-
iframe.contentWindow.document.documentElement.toggleAttribute('dark', isDark);
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Add a dark attibute to the head tag if local storage or prefers-color-scheme says we're in dark mode. */
|
|
24
|
-
static init() {
|
|
25
|
-
let saved = localStorage.getItem('dark');
|
|
26
|
-
let isDark = saved
|
|
27
|
-
? saved==='true'
|
|
28
|
-
: (document.querySelector('html[light]') ?
|
|
29
|
-
false
|
|
30
|
-
: (document.querySelector('html[dark]') || window?.matchMedia('(prefers-color-scheme: dark)').matches));
|
|
31
|
-
|
|
32
|
-
document.documentElement.toggleAttribute('dark', isDark);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
render() {
|
|
37
|
-
this.setAttribute('style', 'user-select: none');
|
|
38
|
-
this.setAttribute('title', 'Toggle Dark Mode');
|
|
39
|
-
this.innerHTML = `
|
|
40
|
-
<svg width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512">
|
|
41
|
-
<path fill="currentColor" d="M152.62 126.77c0-33 4.85-66.35 17.23-94.77C87.54 67.83 32 151.89 32 247.38C32
|
|
42
|
-
375.85 136.15 480 264.62 480c95.49 0 179.55-55.54 215.38-137.85c-28.42 12.38-61.8 17.23-94.77 17.23c-128.47
|
|
43
|
-
0-232.61-104.14-232.61-232.61Z"/></svg>`;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
customElements.define('dark-toggle', DarkToggle);
|
|
48
|
-
DarkToggle.init();
|
|
49
|
-
document.documentElement.classList.add('eternium');
|
|
50
|
-
|
|
51
|
-
// "Export"
|
|
52
|
-
window.DarkToggle = DarkToggle;
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import {Solarite, r, getArg} from "../../../src/solarite/Solarite.js";
|
|
2
|
-
import Draggable2 from "../util/Draggable2.js";
|
|
3
|
-
import Util from "../util/Util.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Put this element between flex children to allow resizing them. */
|
|
7
|
-
export default class FlexResizer extends Solarite {
|
|
8
|
-
|
|
9
|
-
onStart = Util.callback();
|
|
10
|
-
onMove = Util.callback();
|
|
11
|
-
onStop = Util.callback();
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @param unit {string} Can be 'px' or '%'
|
|
15
|
-
* @param thickness {int} Width of the resize drag area.*/
|
|
16
|
-
constructor({unit='px', thickness=10}={}) {
|
|
17
|
-
super();
|
|
18
|
-
this.thickness = parseFloat(getArg(this, 'thickness', thickness));
|
|
19
|
-
this.unit = getArg(this, 'unit', unit, ['px', '%']) || 'px';
|
|
20
|
-
|
|
21
|
-
let prevEl, startSize, isVertical;
|
|
22
|
-
let iframePointerEvents = new WeakMap(); // Store values of iframe pointer events.
|
|
23
|
-
let draggable = new Draggable2(this, {
|
|
24
|
-
onStart: e => {
|
|
25
|
-
isVertical = this.isVertical();
|
|
26
|
-
prevEl = this.previousElementSibling;
|
|
27
|
-
startSize = isVertical ? prevEl.offsetWidth : prevEl.offsetHeight;
|
|
28
|
-
|
|
29
|
-
// Disable iframes
|
|
30
|
-
for (let iframe of this.ownerDocument.querySelectorAll('iframe')) {
|
|
31
|
-
iframePointerEvents.set(iframe, iframe.style.pointerEvents);
|
|
32
|
-
iframe.style.pointerEvents = 'none';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
this.onStart(draggable, e);
|
|
36
|
-
},
|
|
37
|
-
onMove: e => {
|
|
38
|
-
let parentSizePx = isVertical
|
|
39
|
-
? this.parentNode.offsetWidth
|
|
40
|
-
: this.parentNode.offsetHeight;
|
|
41
|
-
let dragDist = isVertical
|
|
42
|
-
? draggable.totalDist.x
|
|
43
|
-
: draggable.totalDist.y;
|
|
44
|
-
let parentSize = this.unit==='%' ? 100 : parentSizePx;
|
|
45
|
-
let newSize = this.unit==='%'
|
|
46
|
-
? (startSize + dragDist) / parentSizePx * 100
|
|
47
|
-
: (startSize + dragDist);
|
|
48
|
-
if (newSize > parentSize)
|
|
49
|
-
newSize = parentSize;
|
|
50
|
-
this.setSize(newSize);
|
|
51
|
-
this.onMove(draggable, e);
|
|
52
|
-
},
|
|
53
|
-
onStop: e => {
|
|
54
|
-
|
|
55
|
-
// Re-enable iframes.
|
|
56
|
-
for (let iframe of this.ownerDocument.querySelectorAll('iframe'))
|
|
57
|
-
iframe.style.pointerEvents = iframePointerEvents.get(iframe); // restore prev value
|
|
58
|
-
|
|
59
|
-
this.onStop(draggable, e);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* It's vertical if the user drags the resizer vertically to resize.
|
|
66
|
-
* When vertical, the resizer will be stretched horizontally to fill the available space.
|
|
67
|
-
* @returns {boolean} */
|
|
68
|
-
isVertical() {
|
|
69
|
-
if (this.parentNode) {
|
|
70
|
-
return getComputedStyle(this.parentNode).flexDirection === 'row';
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** @return {Number} */
|
|
75
|
-
getSize() {
|
|
76
|
-
let prev;
|
|
77
|
-
let prev2 = this;
|
|
78
|
-
while (prev2 = prev2.previousElementSibling) {
|
|
79
|
-
if (prev2.style.display !== 'none' && prev2.tagName !== 'flex-resizer') {
|
|
80
|
-
prev = prev2;
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return (this.isVertical() ? prev?.offsetWidth : prev?.offsetHeight) || 0;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @param size {Number} Width in pixels or percent. */
|
|
90
|
-
setSize(size) {
|
|
91
|
-
let prevEl = this.previousElementSibling;
|
|
92
|
-
let nextEl = this.nextElementSibling;
|
|
93
|
-
if (size < 0)
|
|
94
|
-
size = 0;
|
|
95
|
-
|
|
96
|
-
if (prevEl) {
|
|
97
|
-
if (this.isVertical()) {
|
|
98
|
-
if (nextEl) {
|
|
99
|
-
let totalWidth = this.unit === '%'
|
|
100
|
-
? (prevEl.offsetWidth + nextEl.offsetWidth) / this.parentNode.offsetWidth * 100
|
|
101
|
-
: (prevEl.offsetWidth + nextEl.offsetWidth);
|
|
102
|
-
if (totalWidth - size < 0)
|
|
103
|
-
size = totalWidth;
|
|
104
|
-
|
|
105
|
-
nextEl.style.minWidth = (totalWidth - size) + this.unit;
|
|
106
|
-
nextEl.style.maxWidth = (totalWidth - size) + this.unit;
|
|
107
|
-
}
|
|
108
|
-
prevEl.style.minWidth = size + this.unit;
|
|
109
|
-
prevEl.style.maxWidth = size + this.unit;
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
if (nextEl) {
|
|
113
|
-
let totalHeight = this.unit === '%'
|
|
114
|
-
? (prevEl.offsetHeight + nextEl.offsetHeight) / this.parentNode.offsetHeight * 100
|
|
115
|
-
: (prevEl.offsetHeight + nextEl.offsetHeight);
|
|
116
|
-
if (totalHeight - size < 0)
|
|
117
|
-
size = totalHeight;
|
|
118
|
-
nextEl.style.minHeight = (totalHeight - size) + this.unit;
|
|
119
|
-
nextEl.style.maxHeight = (totalHeight - size) + this.unit;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
prevEl.style.minHeight = size + this.unit;
|
|
123
|
-
prevEl.style.maxHeight = size + this.unit;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
render() {
|
|
129
|
-
r(this)`
|
|
130
|
-
<flex-resizer>
|
|
131
|
-
<style>
|
|
132
|
-
:host {
|
|
133
|
-
display: block; z-index: 1;
|
|
134
|
-
|
|
135
|
-
/* We put the resize region (the margin thickness) all to the right / top bc vertical scrollbars
|
|
136
|
-
* are often to the left, and tab bars below the top.*/
|
|
137
|
-
${this.isVertical()
|
|
138
|
-
|
|
139
|
-
/* Stretch horizontally */
|
|
140
|
-
? `min-width: ${this.thickness-1}px !important; max-width: ${this.thickness-1}px !important;
|
|
141
|
-
margin-left: -1px; margin-right: -${this.thickness-1}px; cursor: ew-resize`
|
|
142
|
-
|
|
143
|
-
/* Stretch vertically */
|
|
144
|
-
: `min-height: ${this.thickness}px !important; max-height: ${this.thickness}px !important;
|
|
145
|
-
margin-top: -${this.thickness/2}px; margin-bottom: -${this.thickness/2}px; cursor: ns-resize`
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
</style>
|
|
149
|
-
</flex-resizer>
|
|
150
|
-
`}
|
|
151
|
-
}
|
|
152
|
-
customElements.define('flex-resizer', FlexResizer);
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Add events for drag start, drag, and drag stop to an element.
|
|
3
|
-
* When callback functions are called, the "this" variable will be the HTMLElement being dragged, when mouseDown first occurred.
|
|
4
|
-
*
|
|
5
|
-
* @param options {object}
|
|
6
|
-
* @param options.onStart {function(e:MouseEvent, draggable:Draggable):bool} Called on mousedown.
|
|
7
|
-
* If this function returns false, dragging will be canceled.
|
|
8
|
-
* @param options.onMove {function(e:MouseEvent, draggable:Draggable)} Called with the distance the mouse has moved while dragging.
|
|
9
|
-
* @param options.onStop {function(e:MouseEvent, draggable:Draggable)} Called when dragging stops.
|
|
10
|
-
*
|
|
11
|
-
* @param options.selector {string=} If set, apply drag events to children that match this selector,
|
|
12
|
-
* instead of the element itself.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* new Draggable(th, {
|
|
16
|
-
* onMove: (draggable, e) => {
|
|
17
|
-
* console.log(draggable.totalDist)
|
|
18
|
-
* }
|
|
19
|
-
* });
|
|
20
|
-
*/
|
|
21
|
-
export default class Draggable {
|
|
22
|
-
|
|
23
|
-
/** @type {HTMLElement} */
|
|
24
|
-
el;
|
|
25
|
-
options = {};
|
|
26
|
-
|
|
27
|
-
startPosition;
|
|
28
|
-
clientStart;
|
|
29
|
-
|
|
30
|
-
/** @type {{x:int, y:int}} Distance the mouse moved since the last onMove() call. */
|
|
31
|
-
dist;
|
|
32
|
-
|
|
33
|
-
/** @type {{x:int, y:int}} Total distance the mouse moved since dragging started. */
|
|
34
|
-
totalDist;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/** @type {HTMLElement} The node clicked when dragging starts. Can be el or a child of el.*/
|
|
38
|
-
target;
|
|
39
|
-
|
|
40
|
-
/** @type {boolean} */
|
|
41
|
-
moved;
|
|
42
|
-
|
|
43
|
-
/** @type {HTMLElement} */
|
|
44
|
-
mask;
|
|
45
|
-
|
|
46
|
-
constructor(el, options) {
|
|
47
|
-
this.el = el;
|
|
48
|
-
this.options = options;
|
|
49
|
-
|
|
50
|
-
// Mask is needed to still grab move events when over iframe.
|
|
51
|
-
let div = document.createElement('div');
|
|
52
|
-
div.innerHTML = `
|
|
53
|
-
<div class="mask" style="position: absolute; inset: 0; z-index: 32767; user-select: none; pointer-events: none"></div>`;
|
|
54
|
-
this.mask = div.children[0];
|
|
55
|
-
//this.mask.style.height = document.body.scrollHeight + 'px';
|
|
56
|
-
this.mask.remove(); // so it will be appended on the next move.
|
|
57
|
-
|
|
58
|
-
// Make sure the "this" object is always the Draggable and not the event target.
|
|
59
|
-
this.mousedown = this.mousedown.bind(this);
|
|
60
|
-
this.mousemove = this.mousemove.bind(this);
|
|
61
|
-
this.mouseup = this.mouseup.bind(this);
|
|
62
|
-
this.scroll = this.scroll.bind(this);
|
|
63
|
-
|
|
64
|
-
this.el.addEventListener('mousedown', this.mousedown);
|
|
65
|
-
|
|
66
|
-
// this.trace = new Error().stack
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
mousedown(e) {
|
|
70
|
-
// Prevent selecting text when dragging.
|
|
71
|
-
this.moved = false;
|
|
72
|
-
this.startPosition = {x: e.pageX, y: e.pageY};
|
|
73
|
-
this.clientStart = {x: e.clientX, y: e.clientY};
|
|
74
|
-
this.totalDist = {x:0, y:0};
|
|
75
|
-
|
|
76
|
-
this.lastScroll = {x: document.documentElement.scrollLeft, y: document.documentElement.scrollTop};
|
|
77
|
-
|
|
78
|
-
if (!this.options.onStart || this.options.onStart(e, this) !== false) {
|
|
79
|
-
e.preventDefault(); // Required after Chromium 124.
|
|
80
|
-
|
|
81
|
-
this.target = this.options.selector ? e.target.closest(this.options.selector) : this.el.contains(e.target) ? this.el : null;
|
|
82
|
-
if (this.target) {
|
|
83
|
-
this.mask.style.cursor = getComputedStyle(e.target).cursor;
|
|
84
|
-
document.addEventListener('mousemove', this.mousemove);
|
|
85
|
-
document.addEventListener('mouseup', this.mouseup);
|
|
86
|
-
document.addEventListener('scroll', this.scroll, true);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
mousemove(e=null) {
|
|
93
|
-
if (e) {
|
|
94
|
-
this.dist = {
|
|
95
|
-
x: e.pageX - this.startPosition.x - this.totalDist.x,
|
|
96
|
-
y: e.pageY - this.startPosition.y - this.totalDist.y
|
|
97
|
-
};
|
|
98
|
-
this.totalDist = {
|
|
99
|
-
x: e.pageX - this.startPosition.x,
|
|
100
|
-
y: e.pageY - this.startPosition.y
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
this.moved = this.moved || this.totalDist.x !== 0 || this.totalDist.y !== 0;
|
|
104
|
-
if (this.moved && this.options.onMove)
|
|
105
|
-
this.options.onMove(e||{}, this); // TODO: reorder or only pass mouseDown?
|
|
106
|
-
|
|
107
|
-
if (!this.mask.parentNode)
|
|
108
|
-
document.body.append(this.mask);
|
|
109
|
-
if (e)
|
|
110
|
-
e.preventDefault();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
mouseup(e) {
|
|
114
|
-
this.cleanup();
|
|
115
|
-
|
|
116
|
-
// TODO: Capture the drop target, instead of it being the mask.
|
|
117
|
-
if (this.moved && this.options.onStop)
|
|
118
|
-
this.options.onStop(e, this);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
scroll(e) {
|
|
122
|
-
let newScroll = {x: document.documentElement.scrollLeft, y: document.documentElement.scrollTop};
|
|
123
|
-
this.dist = {
|
|
124
|
-
x: newScroll.x - this.lastScroll.x,
|
|
125
|
-
y: newScroll.y - this.lastScroll.y
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
this.totalDist.x += newScroll.x - this.lastScroll.x;
|
|
129
|
-
this.totalDist.y += newScroll.y - this.lastScroll.y;
|
|
130
|
-
this.mousemove();
|
|
131
|
-
|
|
132
|
-
this.lastScroll = newScroll;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
cleanup() {
|
|
136
|
-
document.removeEventListener('mousemove', this.mousemove);
|
|
137
|
-
document.removeEventListener('mouseup', this.mouseup);
|
|
138
|
-
document.removeEventListener('scroll', this.scroll, true);
|
|
139
|
-
this.mask.remove();
|
|
140
|
-
this.target = null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/** @deprecated */
|
|
145
|
-
function pauseEvent(e){ // stackoverflow.com/a/5432363
|
|
146
|
-
if(e.stopPropagation) e.stopPropagation();
|
|
147
|
-
if(e.preventDefault) e.preventDefault();
|
|
148
|
-
e.cancelBubble=true; // deprecated?
|
|
149
|
-
e.returnValue=false; // deprecated?
|
|
150
|
-
return false;
|
|
151
|
-
}
|
package/docs/js/util/Errors.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
//#IFDEV
|
|
2
|
-
/*@__NO_SIDE_EFFECTS__*/
|
|
3
|
-
export function assert(val) {
|
|
4
|
-
if (!val) {
|
|
5
|
-
debugger;
|
|
6
|
-
throw new Error('Assertion failed: ' + val);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
var Errors = {
|
|
10
|
-
getStack(ignoreTop=0, ignoreRegex) {
|
|
11
|
-
var e = new Error();
|
|
12
|
-
if (!e.stack) return []; // There is no stack in IE.
|
|
13
|
-
return e.stack
|
|
14
|
-
.split('\n')
|
|
15
|
-
.slice(ignoreTop+2)
|
|
16
|
-
.map(line => line.trim().replace(/^at /, ''))
|
|
17
|
-
.filter(line => !ignoreRegex || !line.match(ignoreRegex));
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
//#ENDIF
|
package/docs/js/util/Html.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
var div = document.createElement('div');
|
|
2
|
-
var decodeCache_ = {};
|
|
3
|
-
|
|
4
|
-
// TODO: Move all of this to StringUtil?
|
|
5
|
-
// Or move createEl here, along with htmlToText and textToHtml?
|
|
6
|
-
export default {
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Convert html entities like < to their literal values like <.
|
|
10
|
-
* @param {string} html
|
|
11
|
-
* @return {string} */
|
|
12
|
-
decode(html) {
|
|
13
|
-
if (!html)
|
|
14
|
-
return '';
|
|
15
|
-
|
|
16
|
-
return html // Fast solution inspired by https://stackoverflow.com/a/43282001
|
|
17
|
-
.replace(/&[#A-Z0-9]+;/gi, entity => {
|
|
18
|
-
let result = decodeCache_[entity];
|
|
19
|
-
if (result)
|
|
20
|
-
return result;
|
|
21
|
-
|
|
22
|
-
div.innerHTML = entity; // create and cache new entity
|
|
23
|
-
return decodeCache_[entity] = div.textContent;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
encode(text, quotes='"') {
|
|
29
|
-
text = ((text === null || text === undefined) ? '' : text+'')
|
|
30
|
-
.replace(/&/g, '&')
|
|
31
|
-
.replace(/</g, '<')
|
|
32
|
-
.replace(/>/g, '>')
|
|
33
|
-
.replace(/\a0/g, ' ')
|
|
34
|
-
if (quotes.includes("'"))
|
|
35
|
-
text = text.replace(/'/g, ''');
|
|
36
|
-
if (quotes.includes('"'))
|
|
37
|
-
text = text.replace(/"/g, '"');
|
|
38
|
-
return text;
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Convert a template tag with embedded html elements into a complete DOM tree,
|
|
46
|
-
* without recreating the embedded elements.
|
|
47
|
-
*
|
|
48
|
-
* Caveats:
|
|
49
|
-
* Will fail if elements are inserted as expressions inside <tags> instead of as children.
|
|
50
|
-
*
|
|
51
|
-
* See also createEl()
|
|
52
|
-
*
|
|
53
|
-
* @param strings {string[]}
|
|
54
|
-
* @param exprs {*[]}
|
|
55
|
-
* @return {Node[]} asdf
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* Html.create`<div>${document.createElement('a')}hi</div>` */
|
|
59
|
-
create(strings, ...exprs) {
|
|
60
|
-
|
|
61
|
-
// 1. Append html strings together with placeholders for Nodes.
|
|
62
|
-
let html = [];
|
|
63
|
-
let placeholders = [];
|
|
64
|
-
for (let i in strings) {
|
|
65
|
-
html.push(strings[i])
|
|
66
|
-
let val = exprs[i];
|
|
67
|
-
if (val) {
|
|
68
|
-
if (val instanceof Node) {
|
|
69
|
-
html.push('') // U+E000, part of private unicode range for custom use.
|
|
70
|
-
placeholders.push(val)
|
|
71
|
-
}
|
|
72
|
-
else if (Array.isArray(val)) {
|
|
73
|
-
val = val.flat();
|
|
74
|
-
for (let item of val)
|
|
75
|
-
if (item instanceof Node) {
|
|
76
|
-
html.push('')
|
|
77
|
-
placeholders.push(item)
|
|
78
|
-
}
|
|
79
|
-
else
|
|
80
|
-
html.push(item+'');
|
|
81
|
-
}
|
|
82
|
-
else
|
|
83
|
-
html.push(val+'');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 2. Create the element.
|
|
88
|
-
let htmlJoined = html.join('');
|
|
89
|
-
div.innerHTML = htmlJoined;
|
|
90
|
-
|
|
91
|
-
// Why does it sometimes fail but work on the second try? Why is this remarkably stupid code necessary?
|
|
92
|
-
if (div.innerHTML !== htmlJoined)
|
|
93
|
-
div.innerHTML = htmlJoined;
|
|
94
|
-
|
|
95
|
-
// 3. Replace the placeholders:
|
|
96
|
-
let node;
|
|
97
|
-
let toRemove = [];
|
|
98
|
-
const walker = document.createTreeWalker(div, NodeFilter.SHOW_TEXT);
|
|
99
|
-
while (node = walker.nextNode()) {
|
|
100
|
-
let parts = node.textContent.split(//g)
|
|
101
|
-
if (parts.length > 1) {
|
|
102
|
-
let next = node.nextElementSibling;
|
|
103
|
-
for (let i = 0; i < parts.length; i++) {
|
|
104
|
-
let text = parts[i];
|
|
105
|
-
if (i > 0)
|
|
106
|
-
node.parentNode.insertBefore(placeholders.shift(), next);
|
|
107
|
-
if (text.length)
|
|
108
|
-
node.parentNode.insertBefore(document.createTextNode(text), next);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Removing them here will mess up the treeWalker.
|
|
112
|
-
toRemove.push(node);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (placeholders.length)
|
|
117
|
-
debugger;
|
|
118
|
-
|
|
119
|
-
toRemove.map(el => el.remove())
|
|
120
|
-
|
|
121
|
-
// 4. Get the result
|
|
122
|
-
let result = [];
|
|
123
|
-
while (div.firstChild)
|
|
124
|
-
result.push(div.removeChild(div.firstChild));
|
|
125
|
-
return result;
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Create and return one element.
|
|
130
|
-
* If the string starts with whitespace before an element, trim that whitespace.
|
|
131
|
-
* @param strings {string|string[]}
|
|
132
|
-
* @param exprs {*[]=}
|
|
133
|
-
* @returns {Node} */
|
|
134
|
-
createOne(strings, ...exprs) {
|
|
135
|
-
if (!Array.isArray(strings))
|
|
136
|
-
strings = [strings];
|
|
137
|
-
|
|
138
|
-
if (strings.length && strings[0].match(/^\s*</)) {
|
|
139
|
-
strings = strings.slice();
|
|
140
|
-
strings[0] = strings[0].trimStart();
|
|
141
|
-
}
|
|
142
|
-
return this.create(strings, ...exprs)[0];
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export {div};
|
|
147
|
-
|