Skip to content
This repository was archived by the owner on Sep 20, 2019. It is now read-only.

Commit 1428b00

Browse files
author
Steven Orvell
committed
Fix implementations with broken template.clone/importNode
1 parent 1782720 commit 1428b00

File tree

1 file changed

+88
-63
lines changed

1 file changed

+88
-63
lines changed

src/Template/Template.js

Lines changed: 88 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,39 @@
99
*/
1010

1111
// minimal template polyfill
12-
if (typeof HTMLTemplateElement === 'undefined') {
13-
(function() {
12+
(function() {
13+
var needsTemplate = (typeof HTMLTemplateElement === 'undefined');
14+
15+
// returns true if nested templates can be cloned (they cannot be on
16+
// some impl's like Safari 8)
17+
var needsCloning = (function() {
18+
if (!needsTemplate) {
19+
var t = document.createElement('template');
20+
t.innerHTML = '<div></div>';
21+
var clone = t.cloneNode(true);
22+
return (clone.content.childNodes.length === 0);
23+
}
24+
})();
25+
26+
var TEMPLATE_TAG = 'template';
27+
var TemplateImpl = function() {};
1428

15-
var TEMPLATE_TAG = 'template';
29+
if (needsTemplate) {
1630

1731
var contentDoc = document.implementation.createHTMLDocument('template');
1832
var canDecorate = true;
1933

2034
/**
2135
Provides a minimal shim for the <template> element.
2236
*/
23-
HTMLTemplateElement = function() {};
24-
HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
37+
38+
TemplateImpl.prototype = Object.create(HTMLElement.prototype);
2539

2640
/**
2741
The `decorate` method moves element children to the template's `content`.
2842
NOTE: there is no support for dynamically adding elements to templates.
2943
*/
30-
HTMLTemplateElement.decorate = function(template) {
44+
TemplateImpl.decorate = function(template) {
3145
// if the template is decorated, return fast
3246
if (template.content) {
3347
return;
@@ -51,7 +65,7 @@ if (typeof HTMLTemplateElement === 'undefined') {
5165
},
5266
set: function(text) {
5367
contentDoc.body.innerHTML = text;
54-
HTMLTemplateElement.bootstrap(contentDoc);
68+
TemplateImpl.bootstrap(contentDoc);
5569
while (this.content.firstChild) {
5670
this.content.removeChild(this.content.firstChild);
5771
}
@@ -63,22 +77,73 @@ if (typeof HTMLTemplateElement === 'undefined') {
6377
});
6478

6579
template.cloneNode = function(deep) {
66-
return HTMLTemplateElement.cloneNode(this, deep);
80+
return TemplateImpl.cloneNode(this, deep);
6781
};
6882

6983
} catch (err) {
7084
canDecorate = false;
7185
}
7286
}
7387
// bootstrap recursively
74-
HTMLTemplateElement.bootstrap(template.content);
88+
TemplateImpl.bootstrap(template.content);
89+
};
90+
91+
/**
92+
The `bootstrap` method is called automatically and "fixes" all
93+
<template> elements in the document referenced by the `doc` argument.
94+
*/
95+
TemplateImpl.bootstrap = function(doc) {
96+
var templates = doc.querySelectorAll(TEMPLATE_TAG);
97+
for (var i=0, l=templates.length, t; (i<l) && (t=templates[i]); i++) {
98+
TemplateImpl.decorate(t);
99+
}
75100
};
76101

102+
// auto-bootstrapping for main document
103+
document.addEventListener('DOMContentLoaded', function() {
104+
TemplateImpl.bootstrap(document);
105+
});
106+
107+
// Patch document.createElement to ensure newly created templates have content
108+
var createElement = document.createElement;
109+
document.createElement = function() {
110+
'use strict';
111+
var el = createElement.apply(document, arguments);
112+
if (el.localName == 'template') {
113+
TemplateImpl.decorate(el);
114+
}
115+
return el;
116+
};
117+
118+
var escapeDataRegExp = /[&\u00A0<>]/g;
119+
120+
function escapeReplace(c) {
121+
switch (c) {
122+
case '&':
123+
return '&amp;';
124+
case '<':
125+
return '&lt;';
126+
case '>':
127+
return '&gt;';
128+
case '\u00A0':
129+
return '&nbsp;';
130+
}
131+
}
132+
133+
function escapeData(s) {
134+
return s.replace(escapeDataRegExp, escapeReplace);
135+
}
136+
}
137+
138+
// make cloning/importing work!
139+
if (needsTemplate || needsCloning) {
77140
var nativeCloneNode = Node.prototype.cloneNode;
78141

79-
HTMLTemplateElement.cloneNode = function(template, deep) {
142+
TemplateImpl.cloneNode = function(template, deep) {
80143
var clone = nativeCloneNode.call(template);
81-
this.decorate(clone);
144+
if (this.decorate) {
145+
this.decorate(clone);
146+
}
82147
if (deep) {
83148
// NOTE: use native clone node to make sure CE's wrapped
84149
// cloneNode does not cause elements to upgrade.
@@ -90,13 +155,15 @@ if (typeof HTMLTemplateElement === 'undefined') {
90155
return clone;
91156
};
92157

93-
HTMLTemplateElement.fixClonedDom = function(clone, source) {
158+
TemplateImpl.fixClonedDom = function(clone, source) {
94159
var s$ = source.querySelectorAll(TEMPLATE_TAG);
95160
var t$ = clone.querySelectorAll(TEMPLATE_TAG);
96161
for (var i=0, l=t$.length, t, s; i<l; i++) {
97162
s = s$[i];
98163
t = t$[i];
99-
this.decorate(s);
164+
if (this.decorate) {
165+
this.decorate(s);
166+
}
100167
t.parentNode.replaceChild(s.cloneNode(true), t);
101168
}
102169
};
@@ -106,69 +173,27 @@ if (typeof HTMLTemplateElement === 'undefined') {
106173
Node.prototype.cloneNode = function(deep) {
107174
var dom = nativeCloneNode.call(this, deep);
108175
if (deep) {
109-
HTMLTemplateElement.fixClonedDom(dom, this);
176+
TemplateImpl.fixClonedDom(dom, this);
110177
}
111178
return dom;
112179
};
113180

114181
// clone instead of importing <template>
115182
document.importNode = function(element, deep) {
116183
if (element.localName === TEMPLATE_TAG) {
117-
return HTMLTemplateElement.cloneNode(element, deep);
184+
return TemplateImpl.cloneNode(element, deep);
118185
} else {
119186
var dom = originalImportNode.call(document, element, deep);
120187
if (deep) {
121-
HTMLTemplateElement.fixClonedDom(dom, element);
188+
TemplateImpl.fixClonedDom(dom, element);
122189
}
123190
return dom;
124191
}
125192
};
193+
}
126194

127-
/**
128-
The `bootstrap` method is called automatically and "fixes" all
129-
<template> elements in the document referenced by the `doc` argument.
130-
*/
131-
HTMLTemplateElement.bootstrap = function(doc) {
132-
var templates = doc.querySelectorAll(TEMPLATE_TAG);
133-
for (var i=0, l=templates.length, t; (i<l) && (t=templates[i]); i++) {
134-
HTMLTemplateElement.decorate(t);
135-
}
136-
};
137-
138-
// auto-bootstrapping for main document
139-
document.addEventListener('DOMContentLoaded', function() {
140-
HTMLTemplateElement.bootstrap(document);
141-
});
142-
143-
// Patch document.createElement to ensure newly created templates have content
144-
var createElement = document.createElement;
145-
document.createElement = function() {
146-
'use strict';
147-
var el = createElement.apply(document, arguments);
148-
if (el.localName == 'template') {
149-
HTMLTemplateElement.decorate(el);
150-
}
151-
return el;
152-
};
153-
154-
var escapeDataRegExp = /[&\u00A0<>]/g;
155-
156-
function escapeReplace(c) {
157-
switch (c) {
158-
case '&':
159-
return '&amp;';
160-
case '<':
161-
return '&lt;';
162-
case '>':
163-
return '&gt;';
164-
case '\u00A0':
165-
return '&nbsp;';
166-
}
167-
}
168-
169-
function escapeData(s) {
170-
return s.replace(escapeDataRegExp, escapeReplace);
171-
}
195+
if (needsTemplate) {
196+
HTMLTemplateElement = TemplateImpl;
197+
}
172198

173-
})();
174-
}
199+
})();

0 commit comments

Comments
 (0)