solarite 0.3.0 → 0.3.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/dist/Solarite-debug.js +411 -524
- package/dist/Solarite.js +394 -507
- package/dist/Solarite.min.js +2 -2
- package/package.json +5 -6
- package/readme.md +2 -2
- package/src/{solarite/ExprPath.js → ExprPath.js} +43 -58
- package/src/{solarite/Globals.js → Globals.js} +4 -4
- package/src/{util/MultiValueMap.js → MultiValueMap.js} +1 -32
- package/src/{solarite/NodeGroup.js → NodeGroup.js} +30 -26
- package/src/{solarite/Shell.js → Shell.js} +8 -7
- package/src/Solarite.d.ts +62 -0
- package/src/{solarite/Solarite.js → Solarite.js} +13 -15
- package/src/{solarite/Template.js → Template.js} +1 -1
- package/src/{solarite/Util.js → Util.js} +49 -45
- package/src/{solarite/createSolarite.js → createSolarite.js} +12 -10
- package/src/{solarite/getArg.js → getArg.js} +24 -3
- package/src/{solarite/h.js → h.js} +4 -12
- package/src/unused/FastLookupArray.js +54 -0
- package/src/unused/Hashes.js +339 -0
- package/src/unused/InUse.test.js +92 -0
- package/src/unused/InUseMap.js +98 -0
- package/src/unused/LinkedList.js +117 -0
- package/src/unused/LinkedList.test.js +115 -0
- package/src/unused/Misc.js +13 -0
- package/src/unused/Perf.js +47 -0
- package/src/unused/TrackedArray.js +54 -0
- package/src/unused/WeakArray.js +33 -0
- package/src/{solarite/watch.js → watch.js} +4 -4
- package/src/util/Util.js +0 -101
- /package/src/{solarite/HtmlParser.js → HtmlParser.js} +0 -0
- /package/src/{util/Errors.js → assert.js} +0 -0
- /package/src/{util/delve.js → delve.js} +0 -0
- /package/src/{solarite/hash.js → hash.js} +0 -0
- /package/src/{solarite/udomdiff.js → udomdiff.js} +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solarite JavasCript UI library.
|
|
3
|
+
* MIT License
|
|
4
|
+
* https://vorticode.github.io/solarite/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export default function h(htmlStrings?: HTMLElement | string | string[] | Function | {render: Function}, ...exprs: any[]): Node | HTMLElement | Template | Function;
|
|
8
|
+
export { default as h, default as r } from './h.js';
|
|
9
|
+
|
|
10
|
+
export const ArgType: {
|
|
11
|
+
Bool: string;
|
|
12
|
+
Int: string;
|
|
13
|
+
Float: string;
|
|
14
|
+
String: string;
|
|
15
|
+
Json: string;
|
|
16
|
+
Eval: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getArg(el:HTMLElement, attributeName:string, defaultValue?:any,
|
|
20
|
+
type?:typeof ArgType[keyof typeof ArgType] | Function | any[], fallback?:any): any;
|
|
21
|
+
|
|
22
|
+
export interface RenderOptions {
|
|
23
|
+
styles?: boolean;
|
|
24
|
+
scripts?: boolean;
|
|
25
|
+
ids?: boolean;
|
|
26
|
+
render?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class Template {
|
|
30
|
+
exprs: (Template|string|Function)[];
|
|
31
|
+
html: string[];
|
|
32
|
+
constructor(htmlStrings: string[], exprs: any[]);
|
|
33
|
+
render(el?: HTMLElement, options?: RenderOptions): DocumentFragment | HTMLElement | null;
|
|
34
|
+
getExactKey(): string;
|
|
35
|
+
getCloseKey(): string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function delve(obj: object, path: string[], createVal?: any): any;
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
// Experimental:
|
|
43
|
+
//--------------
|
|
44
|
+
|
|
45
|
+
// Globals object
|
|
46
|
+
export const Globals: {
|
|
47
|
+
componentArgsHash: WeakMap<any, any>;
|
|
48
|
+
connected: WeakSet<HTMLElement>;
|
|
49
|
+
currentExprPath: any;
|
|
50
|
+
div: HTMLDivElement;
|
|
51
|
+
elementClasses: {[key: string]: typeof Node};
|
|
52
|
+
htmlProps: {[key: string]: boolean};
|
|
53
|
+
nodeEvents: WeakMap<Node, {[eventName: string]: [Function, Function, any[]]}>;
|
|
54
|
+
nodeGroups: WeakMap<HTMLElement, any>;
|
|
55
|
+
objToEl: WeakMap<any, any>;
|
|
56
|
+
rendered: WeakSet<HTMLElement>;
|
|
57
|
+
rendering: WeakSet<HTMLElement>;
|
|
58
|
+
shells: WeakMap<string[], any>;
|
|
59
|
+
reset: Function;
|
|
60
|
+
count: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
@@ -3,36 +3,34 @@
|
|
|
3
3
|
* MIT License
|
|
4
4
|
* https://vorticode.github.io/solarite/
|
|
5
5
|
*/
|
|
6
|
-
/** @jsx r */
|
|
7
|
-
/** @jsxFrag null */
|
|
8
6
|
|
|
7
|
+
import h from './h.js';
|
|
8
|
+
export default h;
|
|
9
|
+
export {default as h, default as r} from './h.js'; //Named exports for h() are deprecated.
|
|
10
|
+
export {default as delve} from './delve.js';
|
|
11
|
+
export {getArg, ArgType} from './getArg.js';
|
|
12
|
+
export {default as Template} from './Template.js';
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// Experimental:
|
|
17
|
+
//--------------
|
|
18
|
+
export {setArgs} from './getArg.js';
|
|
9
19
|
|
|
10
20
|
import createSolarite from "./createSolarite.js";
|
|
11
21
|
|
|
12
22
|
/**
|
|
13
23
|
* TODO: The Proxy and the multiple base classes mess up 'instanceof Solarite'
|
|
14
24
|
* @type {Node|Class<HTMLElement>|function(tagName:string):Node|Class<HTMLElement>} */
|
|
15
|
-
|
|
25
|
+
const Solarite = new Proxy(createSolarite(), {
|
|
16
26
|
apply(self, _, args) {
|
|
17
27
|
return createSolarite(...args)
|
|
18
28
|
}
|
|
19
29
|
});
|
|
20
30
|
|
|
21
|
-
import h from './h.js';
|
|
22
|
-
export default h;
|
|
23
|
-
|
|
24
31
|
/** @type {HTMLElement|Class} */
|
|
25
32
|
export {Solarite}
|
|
26
|
-
export {default as h, default as r} from './h.js';
|
|
27
|
-
export {getArg, ArgType} from './getArg.js';
|
|
28
|
-
export {default as Template} from './Template.js';
|
|
29
33
|
export {default as Globals} from './Globals.js';
|
|
30
|
-
|
|
31
|
-
import Util from './Util.js';
|
|
32
|
-
let getInputValue = Util.getInputValue;
|
|
33
|
-
export {getInputValue};
|
|
34
|
-
export {default as delve} from '../util/delve.js';
|
|
35
34
|
export {default as SolariteUtil} from './Util.js';
|
|
36
35
|
|
|
37
|
-
//Experimental:
|
|
38
36
|
//export {default as watch, renderWatched} from './watch.js'; // unfinished
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import Globals from "./Globals.js";
|
|
2
|
-
import delve from "
|
|
2
|
+
import delve from "./delve.js";
|
|
3
3
|
|
|
4
4
|
let Util = {
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Returns true if they're the same.
|
|
8
|
+
* @param a
|
|
9
|
+
* @param b
|
|
10
|
+
* @returns {boolean} */
|
|
11
|
+
arraySame(a, b) {
|
|
12
|
+
let aLength = a.length;
|
|
13
|
+
if (aLength !== b.length)
|
|
14
|
+
return false;
|
|
15
|
+
for (let i=0; i<aLength; i++)
|
|
16
|
+
if (a[i] !== b[i])
|
|
17
|
+
return false;
|
|
18
|
+
return true; // the same.
|
|
19
|
+
},
|
|
20
|
+
|
|
6
21
|
bindId(root, el) {
|
|
7
22
|
let id = el.getAttribute('data-id') || el.getAttribute('id');
|
|
8
23
|
if (id) { // If something hasn't removed the id.
|
|
@@ -43,7 +58,6 @@ let Util = {
|
|
|
43
58
|
}
|
|
44
59
|
},
|
|
45
60
|
|
|
46
|
-
|
|
47
61
|
/**
|
|
48
62
|
* Convert a Proper Case name to a name with dashes.
|
|
49
63
|
* Dashes will be placed between letters and numbers.
|
|
@@ -111,17 +125,17 @@ let Util = {
|
|
|
111
125
|
* for (const item of flatten(complexArray))
|
|
112
126
|
* console.log(item); // Outputs: 1, 2, 3, 4, 5, 6, { a: 'object' }, 7, 8, 9
|
|
113
127
|
*/
|
|
114
|
-
*flatten(value) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
},
|
|
128
|
+
// *flatten(value) {
|
|
129
|
+
// if (Array.isArray(value)) {
|
|
130
|
+
// for (const item of value) {
|
|
131
|
+
// yield* Util.flatten(item); // Recursively flatten arrays
|
|
132
|
+
// }
|
|
133
|
+
// } else if (typeof value === 'function') {
|
|
134
|
+
// const result = value();
|
|
135
|
+
// yield* Util.flatten(result); // Recursively flatten the result of a function
|
|
136
|
+
// } else
|
|
137
|
+
// yield value; // Yield primitive values as is
|
|
138
|
+
// },
|
|
125
139
|
|
|
126
140
|
/**
|
|
127
141
|
* Get the value of an input as the most appropriate JavaScript type.
|
|
@@ -143,6 +157,10 @@ let Util = {
|
|
|
143
157
|
return node.value; // String
|
|
144
158
|
},
|
|
145
159
|
|
|
160
|
+
isEvent(attrName) {
|
|
161
|
+
return attrName.startsWith('on') && attrName in Globals.div;
|
|
162
|
+
},
|
|
163
|
+
|
|
146
164
|
/**
|
|
147
165
|
* @param el {HTMLElement}
|
|
148
166
|
* @param prop {string}
|
|
@@ -182,9 +200,10 @@ let Util = {
|
|
|
182
200
|
return val === undefined || val === false || val === null;
|
|
183
201
|
},
|
|
184
202
|
|
|
203
|
+
/*
|
|
185
204
|
isPrimitive(val) {
|
|
186
205
|
return typeof val === 'string' || typeof val === 'number'
|
|
187
|
-
}
|
|
206
|
+
},*/
|
|
188
207
|
|
|
189
208
|
/**
|
|
190
209
|
* If val is a function, evaluate it recursively until the result is not a function.
|
|
@@ -202,6 +221,22 @@ let Util = {
|
|
|
202
221
|
return val;
|
|
203
222
|
},
|
|
204
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Use an array as the value of a map, appending to it when we add.
|
|
226
|
+
* Used by watch.js.
|
|
227
|
+
* @param map {Map|WeakMap|Object}
|
|
228
|
+
* @param key
|
|
229
|
+
* @param value */
|
|
230
|
+
mapArrayAdd(map, key, value) {
|
|
231
|
+
let result = map.get(key);
|
|
232
|
+
if (!result) {
|
|
233
|
+
result = [value];
|
|
234
|
+
map.set(key, result);
|
|
235
|
+
}
|
|
236
|
+
else
|
|
237
|
+
result.push(value);
|
|
238
|
+
},
|
|
239
|
+
|
|
205
240
|
/**
|
|
206
241
|
* Remove nodes from the beginning and end that are not:
|
|
207
242
|
* 1. Elements.
|
|
@@ -232,37 +267,6 @@ export default Util;
|
|
|
232
267
|
|
|
233
268
|
|
|
234
269
|
|
|
235
|
-
let isEvent = attrName => attrName.startsWith('on') && attrName in Globals.div;
|
|
236
|
-
export {isEvent};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
export function dashesToCamel(str) {
|
|
240
|
-
return str.replace(/-([a-z])/g, g => g[1].toUpperCase());
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Returns true if they're the same.
|
|
249
|
-
* @param a
|
|
250
|
-
* @param b
|
|
251
|
-
* @returns {boolean} */
|
|
252
|
-
export function arraySame(a, b) {
|
|
253
|
-
let aLength = a.length;
|
|
254
|
-
if (aLength !== b.length)
|
|
255
|
-
return false;
|
|
256
|
-
for (let i=0; i<aLength; i++)
|
|
257
|
-
if (a[i] !== b[i])
|
|
258
|
-
return false;
|
|
259
|
-
return true; // the same.
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
270
|
// For debugging only
|
|
267
271
|
//#IFDEV
|
|
268
272
|
export function setIndent(items, level=1) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import Util from
|
|
2
|
-
import SolariteUtil from './Util.js';
|
|
1
|
+
import Util from './Util.js';
|
|
3
2
|
import Globals from "./Globals.js";
|
|
4
3
|
|
|
5
4
|
function defineClass(Class, tagName, extendsTag) {
|
|
6
5
|
if (!customElements[getName](Class)) { // If not previously defined.
|
|
7
|
-
tagName = tagName ||
|
|
6
|
+
tagName = tagName || Util.camelToDashes(Class.name)
|
|
8
7
|
if (!tagName.includes('-'))
|
|
9
8
|
tagName += '-element';
|
|
10
9
|
|
|
@@ -70,10 +69,10 @@ export default function createSolarite(extendsTag=null) {
|
|
|
70
69
|
* TODO: Make these standalone functions.
|
|
71
70
|
* Callbacks.
|
|
72
71
|
* Use onConnect.push(() => ...); to add new callbacks. */
|
|
73
|
-
onConnect
|
|
72
|
+
onConnect;
|
|
74
73
|
|
|
75
|
-
onFirstConnect
|
|
76
|
-
onDisconnect
|
|
74
|
+
onFirstConnect;
|
|
75
|
+
onDisconnect;
|
|
77
76
|
|
|
78
77
|
/**
|
|
79
78
|
* @param options {RenderOptions} */
|
|
@@ -89,7 +88,7 @@ export default function createSolarite(extendsTag=null) {
|
|
|
89
88
|
|
|
90
89
|
// Add slot children before constructor code executes.
|
|
91
90
|
// This breaks the styleStaticNested test.
|
|
92
|
-
// PendingChildren is setup in NodeGroup.
|
|
91
|
+
// PendingChildren is setup in NodeGroup.instantiateComponent()
|
|
93
92
|
// TODO: Match named slots.
|
|
94
93
|
//let ch = Globals.pendingChildren.pop();
|
|
95
94
|
//if (ch) // TODO: how could there be a slot before render is called?
|
|
@@ -131,13 +130,16 @@ export default function createSolarite(extendsTag=null) {
|
|
|
131
130
|
this.renderFirstTime();
|
|
132
131
|
if (!Globals.connected.has(this)) {
|
|
133
132
|
Globals.connected.add(this);
|
|
134
|
-
this.onFirstConnect
|
|
133
|
+
if (this.onFirstConnect)
|
|
134
|
+
this.onFirstConnect();
|
|
135
135
|
}
|
|
136
|
-
this.onConnect
|
|
136
|
+
if (this.onConnect)
|
|
137
|
+
this.onConnect();
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
disconnectedCallback() {
|
|
140
|
-
this.onDisconnect
|
|
141
|
+
if (this.onDisconnect)
|
|
142
|
+
this.onDisconnect();
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
|
|
@@ -26,7 +26,7 @@ import Util from "./Util.js";
|
|
|
26
26
|
* @param el {HTMLElement}
|
|
27
27
|
* @param attributeName {string} Attribute name. Not case-sensitive.
|
|
28
28
|
* @param defaultValue {*} Default value to use if attribute doesn't exist.
|
|
29
|
-
* @param type {ArgType|function|*[]}
|
|
29
|
+
* @param type {ArgType|function|Class|*[]}
|
|
30
30
|
* If an array, use the value if it's in the array, otherwise return undefined.
|
|
31
31
|
* If it's a function, pass the value to the function and return the result.
|
|
32
32
|
* @param fallback {*} If the defaultValue is undefiend and type can't be parsed as the given type, use this value.
|
|
@@ -41,8 +41,11 @@ export function getArg(el, attributeName, defaultValue=undefined, type=ArgType.S
|
|
|
41
41
|
if (Array.isArray(type))
|
|
42
42
|
return type.includes(val) ? val : fallback;
|
|
43
43
|
|
|
44
|
-
if (typeof type === 'function')
|
|
45
|
-
return type
|
|
44
|
+
if (typeof type === 'function') {
|
|
45
|
+
return type.constructor
|
|
46
|
+
? new type(val) // arg type is custom Class
|
|
47
|
+
: type(val); // arg type is custom function
|
|
48
|
+
}
|
|
46
49
|
|
|
47
50
|
// If bool, it's true as long as it exists and its value isn't falsey.
|
|
48
51
|
if (type===ArgType.Bool) {
|
|
@@ -84,6 +87,24 @@ export function getArg(el, attributeName, defaultValue=undefined, type=ArgType.S
|
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
89
|
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Experimental. Set multiple arguments/attributes all at once.
|
|
93
|
+
* @param el {HTMLElement}
|
|
94
|
+
* @param args {Record<string, any>}
|
|
95
|
+
* @param types {Record<string, ArgType|function|Class>}
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* constructor({user, path}={}) {
|
|
99
|
+
* setArgs(this, arguments[0], {user: User, path: ArgType.String});
|
|
100
|
+
* }
|
|
101
|
+
*/
|
|
102
|
+
export function setArgs(el, args, types) {
|
|
103
|
+
for (let name in args)
|
|
104
|
+
this[name] = getArg(el, name, args[name], types[name] || ArgType.String);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
87
108
|
/**
|
|
88
109
|
* @enum */
|
|
89
110
|
var ArgType = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Template from "./Template.js";
|
|
2
2
|
import Util from "./Util.js";
|
|
3
3
|
import Globals from "./Globals.js";
|
|
4
|
-
import {assert} from "
|
|
4
|
+
import {assert} from "./assert.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Convert strings to HTMLNodes.
|
|
@@ -35,6 +35,8 @@ import {assert} from "../util/Errors.js";
|
|
|
35
35
|
* @return {Node|HTMLElement|Template} */
|
|
36
36
|
export default function h(htmlStrings=undefined, ...exprs) {
|
|
37
37
|
|
|
38
|
+
if (htmlStrings === undefined && !exprs.length && arguments.length)
|
|
39
|
+
throw new Error('h() cannot be called with undefined.');
|
|
38
40
|
|
|
39
41
|
// TODO: Make this a more flat if/else and call other functions for the logic.
|
|
40
42
|
if (htmlStrings instanceof Node) {
|
|
@@ -57,7 +59,7 @@ export default function h(htmlStrings=undefined, ...exprs) {
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
// 2. Render template created by #4 to element.
|
|
60
|
-
else
|
|
62
|
+
else { // instanceof Template
|
|
61
63
|
let options = exprs[1];
|
|
62
64
|
template.render(parent, options);
|
|
63
65
|
|
|
@@ -68,16 +70,6 @@ export default function h(htmlStrings=undefined, ...exprs) {
|
|
|
68
70
|
parent.append(this.rootNg.getParentNode());
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// null for expr[0], remove whole element.
|
|
75
|
-
// This path never happens?
|
|
76
|
-
else {
|
|
77
|
-
throw new Error('unsupported');
|
|
78
|
-
//let ngm = NodeGroupManager.get(parent);
|
|
79
|
-
//ngm.render(null, exprs[1])
|
|
80
|
-
}
|
|
81
73
|
}
|
|
82
74
|
|
|
83
75
|
// 3. Path if used as a template tag.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* partialUpdate test is 20% slower when using this, even before I override any methods!*/
|
|
3
|
+
export default class FastLookupArray extends Array {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments)
|
|
6
|
+
this.indexMap = new Map(); // You can replace with WeakMap() but ensure only objects are added.
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
push(item) { // doesn't support pushing multiple
|
|
11
|
+
if (!this.indexMap.has(item)) {
|
|
12
|
+
this[this.length] = item;
|
|
13
|
+
this.indexMap.set(item, this.length - 1);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
indexOf(item) {
|
|
18
|
+
return this.indexMap.get(item) || -1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
remove(item) {
|
|
22
|
+
const index = this.indexOf(item);
|
|
23
|
+
if (index !== -1) {
|
|
24
|
+
this.splice(index, 1);
|
|
25
|
+
this.indexMap.delete(item);
|
|
26
|
+
|
|
27
|
+
// Adjust the map indices for the items after the removed one
|
|
28
|
+
for (let i = index; i < this.length; i++)
|
|
29
|
+
this.indexMap.set(this[i], i);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
insertBefore(referenceItem, newItem) {
|
|
34
|
+
const refIndex = this.indexOf(referenceItem);
|
|
35
|
+
if (refIndex !== -1) {
|
|
36
|
+
this.splice(refIndex, 0, newItem);
|
|
37
|
+
this.indexMap.set(newItem, refIndex);
|
|
38
|
+
|
|
39
|
+
// Adjust the map indices for the items after the inserted one
|
|
40
|
+
for (let i = refIndex + 1; i < this.length; i++)
|
|
41
|
+
this.indexMap.set(this[i], i);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
set(index, item) {
|
|
46
|
+
const currentItem = this[index];
|
|
47
|
+
if (currentItem !== undefined)
|
|
48
|
+
this.indexMap.delete(currentItem);
|
|
49
|
+
this[index] = item;
|
|
50
|
+
this.indexMap.set(item, index);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// TODO: Override splice?
|
|
54
|
+
}
|