-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathtemplate.js
154 lines (138 loc) · 3.86 KB
/
template.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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**
* This is an implementation-in-progress of a WHATWG/HTML proposal #2254
* Standardize <template> variables and event handlers
* https://github.com/whatwg/html/issues/2254
*
* MIT License
*
* Mev-Rael ([email protected])
*
* Please provide your feedback, PRs should be submitted here
* https://github.com/Mevrael/html-template
*
* Adds a .parse(data) method to a Template prototype
*
* Currently doest not support any statements like if/endif blocks
*/
Object.assign(HTMLTemplateElement.prototype, {
tokenTypes: {
UNDEFINED: 0,
VAR: 1,
NESTED_VAR: 2,
IF: 3,
ENDIF: 4
},
parse(data) {
let html = this.getRootNodeAsHtml();
const tokens = this.getTokens(html);
let delta = 0; // when replacing tokens, increase/decrease delta length so next token would be replaced in correct position of html
tokens.forEach(token => {
const replaceWith = this.parseToken(token, data);
html = html.substr(0, token.startsAt - delta) + replaceWith + html.substr(token.endsAt - delta);
delta += token.length - replaceWith.length;
});
return this.htmlToNode(html);
},
htmlToNode(html) {
return document.createRange().createContextualFragment(html).firstChild;
},
getRootNode() {
const nodes = this.content.childNodes;
const l = nodes.length;
for (let k = 0; k < l; k++) {
const node = nodes[k];
if (node.nodeType === Node.ELEMENT_NODE) {
return node;
}
}
throw new SyntaxError('Template has no root element node');
},
getRootNodeAsHtml() {
return this.getRootNode().outerHTML;
},
// get all the strings within {{ }} in template root node
getTokens(html) {
let tokens = [];
let startAt = 0;
while(token = this.getNextToken(html, startAt)) {
tokens.push(token);
startAt = token.endsAt;
}
return tokens;
},
// gets next token from an HTML string starting at startAt position
// if no more tokens found - returns false
// if token is not closed with }} - throws a SyntaxError
// if token is found - returns an object:
// {
// value: string - contents of expression between {{ }},
// startsAt: position of {{
// endsAt: position of }}
// length: total length of expression starting from the first "{" and ending with last "}"
// }
getNextToken(html, startAt = 0) {
let startPos = html.indexOf('{{', startAt);
if (startPos === -1) {
return false;
}
let endPos = html.indexOf('}}', startPos);
if (endPos === -1) {
throw new SyntaxError('Template expression is not closed with }}');
}
startPos += 2;
const value = html.substr(startPos, endPos - startPos).trim();
startPos -= 2;
endPos += 2;
return {
type: this.getTokenTypeByValue(value),
value: value,
startsAt: startPos,
endsAt: endPos,
length: endPos - startPos
}
},
getTokenTypeByValue(value) {
if (value.indexOf('if') === 0) {
return this.tokenTypes.IF;
} else if (value === 'endif') {
return this.tokenTypes.ENDIF;
} else if (value.indexOf('.') !== -1) {
return this.tokenTypes.NESTED_VAR;
} else {
return this.tokenTypes.VAR;
}
},
parseToken(token, data) {
return this['parseToken' + token.type](token.value, data);
},
// VAR
parseToken1(value, data) {
if (data[value] === undefined) {
return '';
} else {
return data[value].toString();
}
},
// NESTED_VAR
parseToken2(value, data) {
let parts = value.split('.');
const l = parts.length;
let curNestData = data;
for (let k = 0; k < l; k++) {
if (curNestData[parts[k]] === undefined) {
return '';
} else {
curNestData = curNestData[parts[k]];
}
}
return curNestData;
},
// IF
parseToken3(value, data) {
return 'if';
},
// ENDIF
parseToken4(value, data) {
return 'endif';
},
});