OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** @typedef {Document|DocumentFragment|Element} */ | 5 /** @typedef {Document|DocumentFragment|Element} */ |
6 var ProcessingRoot; | 6 var ProcessingRoot; |
7 | 7 |
8 /** | 8 /** |
9 * @fileoverview This is a simple template engine inspired by JsTemplates | 9 * @fileoverview This is a simple template engine inspired by JsTemplates |
10 * optimized for i18n. | 10 * optimized for i18n. |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
106 targetObject[path] = value; | 106 targetObject[path] = value; |
107 // In case we set innerHTML (ignoring others) we need to recursively | 107 // In case we set innerHTML (ignoring others) we need to recursively |
108 // check the content. | 108 // check the content. |
109 if (path == 'innerHTML') { | 109 if (path == 'innerHTML') { |
110 for (var i = 0; i < element.children.length; ++i) { | 110 for (var i = 0; i < element.children.length; ++i) { |
111 processWithoutCycles(element.children[i], data, visited, false); | 111 processWithoutCycles(element.children[i], data, visited, false); |
112 } | 112 } |
113 } | 113 } |
114 } | 114 } |
115 } else { | 115 } else { |
116 element.setAttribute(propName, /** @type {string} */(value)); | 116 element.setAttribute(propName, /** @type {string} */ (value)); |
117 } | 117 } |
118 }); | 118 }); |
119 } | 119 } |
120 }; | 120 }; |
121 | 121 |
122 var prefixes = ['']; | 122 var prefixes = ['']; |
123 | 123 |
124 // Only look through shadow DOM when it's supported. As of April 2015, iOS | 124 // Only look through shadow DOM when it's supported. As of April 2015, iOS |
125 // Chrome doesn't support shadow DOM. | 125 // Chrome doesn't support shadow DOM. |
126 if (Element.prototype.createShadowRoot) | 126 if (Element.prototype.createShadowRoot) |
127 prefixes.push('* /deep/ '); | 127 prefixes.push('* /deep/ '); |
128 | 128 |
129 var attributeNames = Object.keys(handlers); | 129 var attributeNames = Object.keys(handlers); |
130 var selector = prefixes.map(function(prefix) { | 130 var selector = prefixes |
131 return prefix + '[' + attributeNames.join('], ' + prefix + '[') + ']'; | 131 .map(function(prefix) { |
132 }).join(', '); | 132 return prefix + '[' + |
| 133 attributeNames.join('], ' + prefix + '[') + ']'; |
| 134 }) |
| 135 .join(', '); |
133 | 136 |
134 /** | 137 /** |
135 * Processes a DOM tree using a |data| source to populate template values. | 138 * Processes a DOM tree using a |data| source to populate template values. |
136 * @param {!ProcessingRoot} root The root of the DOM tree to process. | 139 * @param {!ProcessingRoot} root The root of the DOM tree to process. |
137 * @param {!LoadTimeData} data The data to draw from. | 140 * @param {!LoadTimeData} data The data to draw from. |
138 */ | 141 */ |
139 function process(root, data) { | 142 function process(root, data) { |
140 processWithoutCycles(root, data, new Set(), true); | 143 processWithoutCycles(root, data, new Set(), true); |
141 } | 144 } |
142 | 145 |
143 /** | 146 /** |
144 * Internal process() method that stops cycles while processing. | 147 * Internal process() method that stops cycles while processing. |
145 * @param {!ProcessingRoot} root | 148 * @param {!ProcessingRoot} root |
146 * @param {!LoadTimeData} data | 149 * @param {!LoadTimeData} data |
147 * @param {!Set<ProcessingRoot>} visited Already visited roots. | 150 * @param {!Set<ProcessingRoot>} visited Already visited roots. |
148 * @param {boolean} mark Whether nodes should be marked processed. | 151 * @param {boolean} mark Whether nodes should be marked processed. |
149 */ | 152 */ |
150 function processWithoutCycles(root, data, visited, mark) { | 153 function processWithoutCycles(root, data, visited, mark) { |
151 if (visited.has(root)) { | 154 if (visited.has(root)) { |
152 // Found a cycle. Stop it. | 155 // Found a cycle. Stop it. |
153 return; | 156 return; |
154 } | 157 } |
155 | 158 |
156 // Mark the node as visited before recursing. | 159 // Mark the node as visited before recursing. |
157 visited.add(root); | 160 visited.add(root); |
158 | 161 |
159 var importLinks = root.querySelectorAll('link[rel=import]'); | 162 var importLinks = root.querySelectorAll('link[rel=import]'); |
160 for (var i = 0; i < importLinks.length; ++i) { | 163 for (var i = 0; i < importLinks.length; ++i) { |
161 var importLink = /** @type {!HTMLLinkElement} */(importLinks[i]); | 164 var importLink = /** @type {!HTMLLinkElement} */ (importLinks[i]); |
162 if (!importLink.import) { | 165 if (!importLink.import) { |
163 // Happens when a <link rel=import> is inside a <template>. | 166 // Happens when a <link rel=import> is inside a <template>. |
164 // TODO(dbeam): should we log an error if we detect that here? | 167 // TODO(dbeam): should we log an error if we detect that here? |
165 continue; | 168 continue; |
166 } | 169 } |
167 processWithoutCycles(importLink.import, data, visited, mark); | 170 processWithoutCycles(importLink.import, data, visited, mark); |
168 } | 171 } |
169 | 172 |
170 var templates = root.querySelectorAll('template'); | 173 var templates = root.querySelectorAll('template'); |
171 for (var i = 0; i < templates.length; ++i) { | 174 for (var i = 0; i < templates.length; ++i) { |
172 var template = /** @type {HTMLTemplateElement} */(templates[i]); | 175 var template = /** @type {HTMLTemplateElement} */ (templates[i]); |
173 if (!template.content) | 176 if (!template.content) |
174 continue; | 177 continue; |
175 processWithoutCycles(template.content, data, visited, mark); | 178 processWithoutCycles(template.content, data, visited, mark); |
176 } | 179 } |
177 | 180 |
178 var isElement = root instanceof Element; | 181 var isElement = root instanceof Element; |
179 if (isElement && root.webkitMatchesSelector(selector)) | 182 if (isElement && root.webkitMatchesSelector(selector)) |
180 processElement(/** @type {!Element} */(root), data, visited); | 183 processElement(/** @type {!Element} */ (root), data, visited); |
181 | 184 |
182 var elements = root.querySelectorAll(selector); | 185 var elements = root.querySelectorAll(selector); |
183 for (var i = 0; i < elements.length; ++i) { | 186 for (var i = 0; i < elements.length; ++i) { |
184 processElement(elements[i], data, visited); | 187 processElement(elements[i], data, visited); |
185 } | 188 } |
186 | 189 |
187 if (mark) { | 190 if (mark) { |
188 var processed = isElement ? [root] : root.children; | 191 var processed = isElement ? [root] : root.children; |
189 if (processed) { | 192 if (processed) { |
190 for (var i = 0; i < processed.length; ++i) { | 193 for (var i = 0; i < processed.length; ++i) { |
(...skipping 11 matching lines...) Expand all Loading... |
202 */ | 205 */ |
203 function processElement(element, data, visited) { | 206 function processElement(element, data, visited) { |
204 for (var i = 0; i < attributeNames.length; i++) { | 207 for (var i = 0; i < attributeNames.length; i++) { |
205 var name = attributeNames[i]; | 208 var name = attributeNames[i]; |
206 var attribute = element.getAttribute(name); | 209 var attribute = element.getAttribute(name); |
207 if (attribute != null) | 210 if (attribute != null) |
208 handlers[name](element, attribute, data, visited); | 211 handlers[name](element, attribute, data, visited); |
209 } | 212 } |
210 } | 213 } |
211 | 214 |
212 return { | 215 return {process: process}; |
213 process: process | |
214 }; | |
215 }()); | 216 }()); |
OLD | NEW |