forked from WebReflection/hyperHTML
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwire.js
83 lines (75 loc) · 3.31 KB
/
wire.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
'use strict';
const tta = (m => /* c8 ignore start */ m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m /* c8 ignore stop */)(require('@ungap/template-tag-arguments'));
const Wire = (m => /* c8 ignore start */ m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m /* c8 ignore stop */)(require('hyperhtml-wire'));
const {Tagger} = require('../objects/Updates.js');
// all wires used per each context
const wires = new WeakMap;
// A wire is a callback used as tag function
// to lazily relate a generic object to a template literal.
// hyper.wire(user)`<div id=user>${user.name}</div>`; => the div#user
// This provides the ability to have a unique DOM structure
// related to a unique JS object through a reusable template literal.
// A wire can specify a type, as svg or html, and also an id
// via html:id or :id convention. Such :id allows same JS objects
// to be associated to different DOM structures accordingly with
// the used template literal without losing previously rendered parts.
const wire = (obj, type) => obj == null ?
content(type || 'html') :
weakly(obj, type || 'html');
// A wire content is a virtual reference to one or more nodes.
// It's represented by either a DOM node, or an Array.
// In both cases, the wire content role is to simply update
// all nodes through the list of related callbacks.
// In few words, a wire content is like an invisible parent node
// in charge of updating its content like a bound element would do.
const content = type => {
let wire, tagger, template;
return function () {
const args = tta.apply(null, arguments);
if (template !== args[0]) {
template = args[0];
tagger = new Tagger(type);
wire = wireContent(tagger.apply(tagger, args));
} else {
tagger.apply(tagger, args);
}
return wire;
};
};
// wires are weakly created through objects.
// Each object can have multiple wires associated
// and this is thanks to the type + :id feature.
const weakly = (obj, type) => {
const i = type.indexOf(':');
let wire = wires.get(obj);
let id = type;
if (-1 < i) {
id = type.slice(i + 1);
type = type.slice(0, i) || 'html';
}
if (!wire)
wires.set(obj, wire = {});
return wire[id] || (wire[id] = content(type));
};
// A document fragment loses its nodes
// as soon as it is appended into another node.
// This has the undesired effect of losing wired content
// on a second render call, because (by then) the fragment would be empty:
// no longer providing access to those sub-nodes that ultimately need to
// stay associated with the original interpolation.
// To prevent hyperHTML from forgetting about a fragment's sub-nodes,
// fragments are instead returned as an Array of nodes or, if there's only one entry,
// as a single referenced node which, unlike fragments, will indeed persist
// wire content throughout multiple renderings.
// The initial fragment, at this point, would be used as unique reference to this
// array of nodes or to this single referenced node.
const wireContent = node => {
const childNodes = node.childNodes;
const {length} = childNodes;
return length === 1 ?
childNodes[0] :
(length ? new Wire(childNodes) : node);
};
exports.content = content;
exports.weakly = weakly;
Object.defineProperty(exports, '__esModule', {value: true}).default = wire;