| 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 23 matching lines...) Expand all Loading... |
| 34 * the attribute name and the value is the function that gets called for every | 34 * the attribute name and the value is the function that gets called for every |
| 35 * single node that has this attribute. | 35 * single node that has this attribute. |
| 36 * @type {!Object} | 36 * @type {!Object} |
| 37 */ | 37 */ |
| 38 var handlers = { | 38 var handlers = { |
| 39 /** | 39 /** |
| 40 * This handler sets the textContent of the element. | 40 * This handler sets the textContent of the element. |
| 41 * @param {!HTMLElement} element The node to modify. | 41 * @param {!HTMLElement} element The node to modify. |
| 42 * @param {string} key The name of the value in |data|. | 42 * @param {string} key The name of the value in |data|. |
| 43 * @param {!LoadTimeData} data The data source to draw from. | 43 * @param {!LoadTimeData} data The data source to draw from. |
| 44 * @param {!Array<ProcessingRoot>} visited | 44 * @param {!Set<ProcessingRoot>} visited |
| 45 */ | 45 */ |
| 46 'i18n-content': function(element, key, data, visited) { | 46 'i18n-content': function(element, key, data, visited) { |
| 47 element.textContent = data.getString(key); | 47 element.textContent = data.getString(key); |
| 48 }, | 48 }, |
| 49 | 49 |
| 50 /** | 50 /** |
| 51 * This handler adds options to a <select> element. | 51 * This handler adds options to a <select> element. |
| 52 * @param {!HTMLElement} select The node to modify. | 52 * @param {!HTMLElement} select The node to modify. |
| 53 * @param {string} key The name of the value in |data|. It should | 53 * @param {string} key The name of the value in |data|. It should |
| 54 * identify an array of values to initialize an <option>. Each value, | 54 * identify an array of values to initialize an <option>. Each value, |
| 55 * if a pair, represents [content, value]. Otherwise, it should be a | 55 * if a pair, represents [content, value]. Otherwise, it should be a |
| 56 * content string with no value. | 56 * content string with no value. |
| 57 * @param {!LoadTimeData} data The data source to draw from. | 57 * @param {!LoadTimeData} data The data source to draw from. |
| 58 * @param {!Array<ProcessingRoot>} visited | 58 * @param {!Set<ProcessingRoot>} visited |
| 59 */ | 59 */ |
| 60 'i18n-options': function(select, key, data, visited) { | 60 'i18n-options': function(select, key, data, visited) { |
| 61 var options = data.getValue(key); | 61 var options = data.getValue(key); |
| 62 options.forEach(function(optionData) { | 62 options.forEach(function(optionData) { |
| 63 var option = typeof optionData == 'string' ? | 63 var option = typeof optionData == 'string' ? |
| 64 new Option(optionData) : | 64 new Option(optionData) : |
| 65 new Option(optionData[1], optionData[0]); | 65 new Option(optionData[1], optionData[0]); |
| 66 select.appendChild(option); | 66 select.appendChild(option); |
| 67 }); | 67 }); |
| 68 }, | 68 }, |
| 69 | 69 |
| 70 /** | 70 /** |
| 71 * This is used to set HTML attributes and DOM properties. The syntax is: | 71 * This is used to set HTML attributes and DOM properties. The syntax is: |
| 72 * attributename:key; | 72 * attributename:key; |
| 73 * .domProperty:key; | 73 * .domProperty:key; |
| 74 * .nested.dom.property:key | 74 * .nested.dom.property:key |
| 75 * @param {!HTMLElement} element The node to modify. | 75 * @param {!HTMLElement} element The node to modify. |
| 76 * @param {string} attributeAndKeys The path of the attribute to modify | 76 * @param {string} attributeAndKeys The path of the attribute to modify |
| 77 * followed by a colon, and the name of the value in |data|. | 77 * followed by a colon, and the name of the value in |data|. |
| 78 * Multiple attribute/key pairs may be separated by semicolons. | 78 * Multiple attribute/key pairs may be separated by semicolons. |
| 79 * @param {!LoadTimeData} data The data source to draw from. | 79 * @param {!LoadTimeData} data The data source to draw from. |
| 80 * @param {!Array<ProcessingRoot>} visited | 80 * @param {!Set<ProcessingRoot>} visited |
| 81 */ | 81 */ |
| 82 'i18n-values': function(element, attributeAndKeys, data, visited) { | 82 'i18n-values': function(element, attributeAndKeys, data, visited) { |
| 83 var parts = attributeAndKeys.replace(/\s/g, '').split(/;/); | 83 var parts = attributeAndKeys.replace(/\s/g, '').split(/;/); |
| 84 parts.forEach(function(part) { | 84 parts.forEach(function(part) { |
| 85 if (!part) | 85 if (!part) |
| 86 return; | 86 return; |
| 87 | 87 |
| 88 var attributeAndKeyPair = part.match(/^([^:]+):(.+)$/); | 88 var attributeAndKeyPair = part.match(/^([^:]+):(.+)$/); |
| 89 if (!attributeAndKeyPair) | 89 if (!attributeAndKeyPair) |
| 90 throw new Error('malformed i18n-values: ' + attributeAndKeys); | 90 throw new Error('malformed i18n-values: ' + attributeAndKeys); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 var selector = prefixes.map(function(prefix) { | 130 var selector = prefixes.map(function(prefix) { |
| 131 return prefix + '[' + attributeNames.join('], ' + prefix + '[') + ']'; | 131 return prefix + '[' + attributeNames.join('], ' + prefix + '[') + ']'; |
| 132 }).join(', '); | 132 }).join(', '); |
| 133 | 133 |
| 134 /** | 134 /** |
| 135 * Processes a DOM tree using a |data| source to populate template values. | 135 * Processes a DOM tree using a |data| source to populate template values. |
| 136 * @param {!ProcessingRoot} root The root of the DOM tree to process. | 136 * @param {!ProcessingRoot} root The root of the DOM tree to process. |
| 137 * @param {!LoadTimeData} data The data to draw from. | 137 * @param {!LoadTimeData} data The data to draw from. |
| 138 */ | 138 */ |
| 139 function process(root, data) { | 139 function process(root, data) { |
| 140 processWithoutCycles(root, data, [], true); | 140 processWithoutCycles(root, data, new Set(), true); |
| 141 } | 141 } |
| 142 | 142 |
| 143 /** | 143 /** |
| 144 * Internal process() method that stops cycles while processing. | 144 * Internal process() method that stops cycles while processing. |
| 145 * @param {!ProcessingRoot} root | 145 * @param {!ProcessingRoot} root |
| 146 * @param {!LoadTimeData} data | 146 * @param {!LoadTimeData} data |
| 147 * @param {!Array<ProcessingRoot>} visited Already visited roots. | 147 * @param {!Set<ProcessingRoot>} visited Already visited roots. |
| 148 * @param {boolean} mark Whether nodes should be marked processed. | 148 * @param {boolean} mark Whether nodes should be marked processed. |
| 149 */ | 149 */ |
| 150 function processWithoutCycles(root, data, visited, mark) { | 150 function processWithoutCycles(root, data, visited, mark) { |
| 151 if (visited.indexOf(root) >= 0) { | 151 if (visited.has(root)) { |
| 152 // Found a cycle. Stop it. | 152 // Found a cycle. Stop it. |
| 153 return; | 153 return; |
| 154 } | 154 } |
| 155 | 155 |
| 156 // Mark the node as visited before recursing. | 156 // Mark the node as visited before recursing. |
| 157 visited.push(root); | 157 visited.add(root); |
| 158 | 158 |
| 159 var importLinks = root.querySelectorAll('link[rel=import]'); | 159 var importLinks = root.querySelectorAll('link[rel=import]'); |
| 160 for (var i = 0; i < importLinks.length; ++i) { | 160 for (var i = 0; i < importLinks.length; ++i) { |
| 161 var importLink = /** @type {!HTMLLinkElement} */(importLinks[i]); | 161 var importLink = /** @type {!HTMLLinkElement} */(importLinks[i]); |
| 162 if (!importLink.import) { | 162 if (!importLink.import) { |
| 163 // Happens when a <link rel=import> is inside a <template>. | 163 // Happens when a <link rel=import> is inside a <template>. |
| 164 // TODO(dbeam): should we log an error if we detect that here? | 164 // TODO(dbeam): should we log an error if we detect that here? |
| 165 continue; | 165 continue; |
| 166 } | 166 } |
| 167 processWithoutCycles(importLink.import, data, visited, mark); | 167 processWithoutCycles(importLink.import, data, visited, mark); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 191 processed[i].setAttribute('i18n-processed', ''); | 191 processed[i].setAttribute('i18n-processed', ''); |
| 192 } | 192 } |
| 193 } | 193 } |
| 194 } | 194 } |
| 195 } | 195 } |
| 196 | 196 |
| 197 /** | 197 /** |
| 198 * Run through various [i18n-*] attributes and populate. | 198 * Run through various [i18n-*] attributes and populate. |
| 199 * @param {!Element} element | 199 * @param {!Element} element |
| 200 * @param {!LoadTimeData} data | 200 * @param {!LoadTimeData} data |
| 201 * @param {!Array<ProcessingRoot>} visited | 201 * @param {!Set<ProcessingRoot>} visited |
| 202 */ | 202 */ |
| 203 function processElement(element, data, visited) { | 203 function processElement(element, data, visited) { |
| 204 for (var i = 0; i < attributeNames.length; i++) { | 204 for (var i = 0; i < attributeNames.length; i++) { |
| 205 var name = attributeNames[i]; | 205 var name = attributeNames[i]; |
| 206 var attribute = element.getAttribute(name); | 206 var attribute = element.getAttribute(name); |
| 207 if (attribute != null) | 207 if (attribute != null) |
| 208 handlers[name](element, attribute, data, visited); | 208 handlers[name](element, attribute, data, visited); |
| 209 } | 209 } |
| 210 } | 210 } |
| 211 | 211 |
| 212 return { | 212 return { |
| 213 process: process | 213 process: process |
| 214 }; | 214 }; |
| 215 }()); | 215 }()); |
| OLD | NEW |