Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(481)

Side by Side Diff: appengine/config_service/ui/bower_components/polymer/lib/mixins/template-stamp.html

Issue 2923973003: Added base template for config ui. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!--
2 @license
3 Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 -->
10
11 <link rel="import" href="../utils/boot.html">
12 <link rel="import" href="../utils/mixin.html">
13
14 <script>
15 (function() {
16
17 'use strict';
18
19 // 1.x backwards-compatible auto-wrapper for template type extensions
20 // This is a clear layering violation and gives favored-nation status to
21 // dom-if and dom-repeat templates. This is a conceit we're choosing to keep
22 // a.) to ease 1.x backwards-compatibility due to loss of `is`, and
23 // b.) to maintain if/repeat capability in parser-constrained elements
24 // (e.g. table, select) in lieu of native CE type extensions without
25 // massive new invention in this space (e.g. directive system)
26 const templateExtensions = {
27 'dom-if': true,
28 'dom-repeat': true
29 };
30 function wrapTemplateExtension(node) {
31 let is = node.getAttribute('is');
32 if (is && templateExtensions[is]) {
33 let t = node;
34 t.removeAttribute('is');
35 node = t.ownerDocument.createElement(is);
36 t.parentNode.replaceChild(node, t);
37 node.appendChild(t);
38 while(t.attributes.length) {
39 node.setAttribute(t.attributes[0].name, t.attributes[0].value);
40 t.removeAttribute(t.attributes[0].name);
41 }
42 }
43 return node;
44 }
45
46 function findTemplateNode(root, nodeInfo) {
47 // recursively ascend tree until we hit root
48 let parent = nodeInfo.parentInfo && findTemplateNode(root, nodeInfo.parentIn fo);
49 // unwind the stack, returning the indexed node at each level
50 if (parent) {
51 // note: marginally faster than indexing via childNodes
52 // (http://jsperf.com/childnodes-lookup)
53 for (let n=parent.firstChild, i=0; n; n=n.nextSibling) {
54 if (nodeInfo.parentIndex === i++) {
55 return n;
56 }
57 }
58 } else {
59 return root;
60 }
61 }
62
63 // construct `$` map (from id annotations)
64 function applyIdToMap(inst, map, node, nodeInfo) {
65 if (nodeInfo.id) {
66 map[nodeInfo.id] = node;
67 }
68 }
69
70 // install event listeners (from event annotations)
71 function applyEventListener(inst, node, nodeInfo) {
72 if (nodeInfo.events && nodeInfo.events.length) {
73 for (let j=0, e$=nodeInfo.events, e; (j<e$.length) && (e=e$[j]); j++) {
74 inst._addMethodEventListenerToNode(node, e.name, e.value, inst);
75 }
76 }
77 }
78
79 // push configuration references at configure time
80 function applyTemplateContent(inst, node, nodeInfo) {
81 if (nodeInfo.templateInfo) {
82 node._templateInfo = nodeInfo.templateInfo;
83 }
84 }
85
86 function createNodeEventHandler(context, eventName, methodName) {
87 // Instances can optionally have a _methodHost which allows redirecting wher e
88 // to find methods. Currently used by `templatize`.
89 context = context._methodHost || context;
90 let handler = function(e) {
91 if (context[methodName]) {
92 context[methodName](e, e.detail);
93 } else {
94 console.warn('listener method `' + methodName + '` not defined');
95 }
96 };
97 return handler;
98 }
99
100 /**
101 * Element mixin that provides basic template parsing and stamping, including
102 * the following template-related features for stamped templates:
103 *
104 * - Declarative event listeners (`on-eventname="listener"`)
105 * - Map of node id's to stamped node instances (`this.$.id`)
106 * - Nested template content caching/removal and re-installation (performance
107 * optimization)
108 *
109 * @polymerMixin
110 * @memberof Polymer
111 * @summary Element class mixin that provides basic template parsing and stamp ing
112 */
113 Polymer.TemplateStamp = Polymer.dedupingMixin(superClass => {
114
115 /**
116 * @polymerMixinClass
117 * @implements {Polymer_TemplateStamp}
118 */
119 class TemplateStamp extends superClass {
120
121 /**
122 * Scans a template to produce template metadata.
123 *
124 * Template-specific metadata are stored in the object returned, and node-
125 * specific metadata are stored in objects in its flattened `nodeInfoList`
126 * array. Only nodes in the template that were parsed as nodes of
127 * interest contain an object in `nodeInfoList`. Each `nodeInfo` object
128 * contains an `index` (`childNodes` index in parent) and optionally
129 * `parent`, which points to node info of its parent (including its index) .
130 *
131 * The template metadata object returned from this method has the followin g
132 * structure (many fields optional):
133 *
134 * ```js
135 * {
136 * // Flattened list of node metadata (for nodes that generated metada ta)
137 * nodeInfoList: [
138 * {
139 * // `id` attribute for any nodes with id's for generating `$` ma p
140 * id: {string},
141 * // `on-event="handler"` metadata
142 * events: [
143 * {
144 * name: {string}, // event name
145 * value: {string}, // handler method name
146 * }, ...
147 * ],
148 * // Notes when the template contained a `<slot>` for shady DOM
149 * // optimization purposes
150 * hasInsertionPoint: {boolean},
151 * // For nested `<template>`` nodes, nested template metadata
152 * templateInfo: {object}, // nested template metadata
153 * // Metadata to allow efficient retrieval of instanced node
154 * // corresponding to this metadata
155 * parentInfo: {number}, // reference to parent nodeInfo>
156 * parentIndex: {number}, // index in parent's `childNodes` colle ction
157 * infoIndex: {number}, // index of this `nodeInfo` in `templat eInfo.nodeInfoList`
158 * },
159 * ...
160 * ],
161 * // When true, the template had the `strip-whitespace` attribute
162 * // or was nested in a template with that setting
163 * stripWhitespace: {boolean},
164 * // For nested templates, nested template content is moved into
165 * // a document fragment stored here; this is an optimization to
166 * // avoid the cost of nested template cloning
167 * content: {DocumentFragment}
168 * }
169 * ```
170 *
171 * This method kicks off a recursive treewalk as follows:
172 *
173 * ```
174 * _parseTemplate <---------------------+
175 * _parseTemplateContent |
176 * _parseTemplateNode <------------|--+
177 * _parseTemplateNestedTemplate --+ |
178 * _parseTemplateChildNodes ---------+
179 * _parseTemplateNodeAttributes
180 * _parseTemplateNodeAttribute
181 *
182 * ```
183 *
184 * These methods may be overridden to add custom metadata about templates
185 * to either `templateInfo` or `nodeInfo`.
186 *
187 * Note that this method may be destructive to the template, in that
188 * e.g. event annotations may be removed after being noted in the
189 * template metadata.
190 *
191 * @param {HTMLTemplateElement} template Template to parse
192 * @param {Object=} outerTemplateInfo Template metadata from the outer
193 * template, for parsing nested templates
194 * @return {Object} Parsed template metadata
195 */
196 static _parseTemplate(template, outerTemplateInfo) {
197 // since a template may be re-used, memo-ize metadata
198 if (!template._templateInfo) {
199 let templateInfo = template._templateInfo = {};
200 templateInfo.nodeInfoList = [];
201 templateInfo.stripWhiteSpace =
202 (outerTemplateInfo && outerTemplateInfo.stripWhiteSpace) ||
203 template.hasAttribute('strip-whitespace');
204 this._parseTemplateContent(template, templateInfo, {parent: null});
205 }
206 return template._templateInfo;
207 }
208
209 static _parseTemplateContent(template, templateInfo, nodeInfo) {
210 return this._parseTemplateNode(template.content, templateInfo, nodeInfo) ;
211 }
212
213 /**
214 * Parses template node and adds template and node metadata based on
215 * the current node, and its `childNodes` and `attributes`.
216 *
217 * This method may be overridden to add custom node or template specific
218 * metadata based on this node.
219 *
220 * @param {Node} node Node to parse
221 * @param {Object} templateInfo Template metadata for current template
222 * @param {Object} nodeInfo Node metadata for current template.
223 * @return {boolean} `true` if the visited node added node-specific
224 * metadata to `nodeInfo`
225 */
226 static _parseTemplateNode(node, templateInfo, nodeInfo) {
227 let noted;
228 if (node.localName == 'template' && !node.hasAttribute('preserve-content ')) {
229 noted = this._parseTemplateNestedTemplate(node, templateInfo, nodeInfo ) || noted;
230 } else if (node.localName === 'slot') {
231 // For ShadyDom optimization, indicating there is an insertion point
232 templateInfo.hasInsertionPoint = true;
233 }
234 if (node.firstChild) {
235 noted = this._parseTemplateChildNodes(node, templateInfo, nodeInfo) || noted;
236 }
237 if (node.hasAttributes && node.hasAttributes()) {
238 noted = this._parseTemplateNodeAttributes(node, templateInfo, nodeInfo ) || noted;
239 }
240 return noted;
241 }
242
243 /**
244 * Parses template child nodes for the given root node.
245 *
246 * This method also wraps whitelisted legacy template extensions
247 * (`is="dom-if"` and `is="dom-repeat"`) with their equivalent element
248 * wrappers, collapses text nodes, and strips whitespace from the template
249 * if the `templateInfo.stripWhitespace` setting was provided.
250 *
251 * @param {Node} root Root node whose `childNodes` will be parsed
252 * @param {Object} templateInfo Template metadata for current template
253 * @param {Object} nodeInfo Node metadata for current template.
254 */
255 static _parseTemplateChildNodes(root, templateInfo, nodeInfo) {
256 for (let node=root.firstChild, parentIndex=0, next; node; node=next) {
257 // Wrap templates
258 if (node.localName == 'template') {
259 node = wrapTemplateExtension(node);
260 }
261 // collapse adjacent textNodes: fixes an IE issue that can cause
262 // text nodes to be inexplicably split =(
263 // note that root.normalize() should work but does not so we do this
264 // manually.
265 next = node.nextSibling;
266 if (node.nodeType === Node.TEXT_NODE) {
267 let n = next;
268 while (n && (n.nodeType === Node.TEXT_NODE)) {
269 node.textContent += n.textContent;
270 next = n.nextSibling;
271 root.removeChild(n);
272 n = next;
273 }
274 // optionally strip whitespace
275 if (templateInfo.stripWhiteSpace && !node.textContent.trim()) {
276 root.removeChild(node);
277 continue;
278 }
279 }
280 let childInfo = { parentIndex, parentInfo: nodeInfo };
281 if (this._parseTemplateNode(node, templateInfo, childInfo)) {
282 childInfo.infoIndex = templateInfo.nodeInfoList.push(childInfo) - 1;
283 }
284 // Increment if not removed
285 if (node.parentNode) {
286 parentIndex++;
287 }
288 }
289 }
290
291 /**
292 * Parses template content for the given nested `<template>`.
293 *
294 * Nested template info is stored as `templateInfo` in the current node's
295 * `nodeInfo`. `template.content` is removed and stored in `templateInfo`.
296 * It will then be the responsibility of the host to set it back to the
297 * template and for users stamping nested templates to use the
298 * `_contentForTemplate` method to retrieve the content for this template
299 * (an optimization to avoid the cost of cloning nested template content).
300 *
301 * @param {HTMLTemplateElement} node Node to parse (a <template>)
302 * @param {Object} outerTemplateInfo Template metadata for current templat e
303 * that includes the template `node`
304 * @param {Object} nodeInfo Node metadata for current template.
305 * @return {boolean} `true` if the visited node added node-specific
306 * metadata to `nodeInfo`
307 */
308 static _parseTemplateNestedTemplate(node, outerTemplateInfo, nodeInfo) {
309 let templateInfo = this._parseTemplate(node, outerTemplateInfo);
310 let content = templateInfo.content =
311 node.content.ownerDocument.createDocumentFragment();
312 content.appendChild(node.content);
313 nodeInfo.templateInfo = templateInfo;
314 return true;
315 }
316
317 /**
318 * Parses template node attributes and adds node metadata to `nodeInfo`
319 * for nodes of interest.
320 *
321 * @param {Node} node Node to parse
322 * @param {Object} templateInfo Template metadata for current template
323 * @param {Object} nodeInfo Node metadata for current template.
324 * @return {boolean} `true` if the visited node added node-specific
325 * metadata to `nodeInfo`
326 */
327 static _parseTemplateNodeAttributes(node, templateInfo, nodeInfo) {
328 // Make copy of original attribute list, since the order may change
329 // as attributes are added and removed
330 let noted;
331 let attrs = Array.from(node.attributes);
332 for (let i=attrs.length-1, a; (a=attrs[i]); i--) {
333 noted = this._parseTemplateNodeAttribute(node, templateInfo, nodeInfo, a.name, a.value) || noted;
334 }
335 return noted;
336 }
337
338 /**
339 * Parses a single template node attribute and adds node metadata to
340 * `nodeInfo` for attributes of interest.
341 *
342 * This implementation adds metadata for `on-event="handler"` attributes
343 * and `id` attributes.
344 *
345 * @param {Node} node Node to parse
346 * @param {Object} templateInfo Template metadata for current template
347 * @param {Object} nodeInfo Node metadata for current template.
348 * @param {string} name Attribute name
349 * @param {*} value Attribute value
350 * @return {boolean} `true` if the visited node added node-specific
351 * metadata to `nodeInfo`
352 */
353 static _parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, val ue) {
354 // events (on-*)
355 if (name.slice(0, 3) === 'on-') {
356 node.removeAttribute(name);
357 nodeInfo.events = nodeInfo.events || [];
358 nodeInfo.events.push({
359 name: name.slice(3),
360 value
361 });
362 return true;
363 }
364 // static id
365 else if (name === 'id') {
366 nodeInfo.id = value;
367 return true;
368 }
369 }
370
371 /**
372 * Returns the `content` document fragment for a given template.
373 *
374 * For nested templates, Polymer performs an optimization to cache nested
375 * template content to avoid the cost of cloning deeply nested templates.
376 * This method retrieves the cached content for a given template.
377 *
378 * @param {HTMLTemplateElement} template Template to retrieve `content` fo r
379 * @return {DocumentFragment} Content fragment
380 */
381 static _contentForTemplate(template) {
382 let templateInfo = template.__templateInfo;
383 return (templateInfo && templateInfo.content) || template.content;
384 }
385
386 /**
387 * Clones the provided template content and returns a document fragment
388 * containing the cloned dom.
389 *
390 * The template is parsed (once and memoized) using this library's
391 * template parsing features, and provides the following value-added
392 * features:
393 * * Adds declarative event listeners for `on-event="handler"` attributes
394 * * Generates an "id map" for all nodes with id's under `$` on returned
395 * document fragment
396 * * Passes template info including `content` back to templates as
397 * `_templateInfo` (a performance optimization to avoid deep template
398 * cloning)
399 *
400 * Note that the memoized template parsing process is destructive to the
401 * template: attributes for bindings and declarative event listeners are
402 * removed after being noted in notes, and any nested `<template>.content`
403 * is removed and stored in notes as well.
404 *
405 * @param {HTMLTemplateElement} template Template to stamp
406 * @return {DocumentFragment} Cloned template content
407 */
408 _stampTemplate(template) {
409 // Polyfill support: bootstrap the template if it has not already been
410 if (template && !template.content &&
411 window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
412 HTMLTemplateElement.decorate(template);
413 }
414 let templateInfo = this.constructor._parseTemplate(template);
415 let nodeInfo = templateInfo.nodeInfoList;
416 let content = templateInfo.content || template.content;
417 let dom = document.importNode(content, true);
418 // NOTE: ShadyDom optimization indicating there is an insertion point
419 dom.__noInsertionPoint = !templateInfo.hasInsertionPoint;
420 let nodes = dom.nodeList = new Array(nodeInfo.length);
421 dom.$ = {};
422 for (let i=0, l=nodeInfo.length, info; (i<l) && (info=nodeInfo[i]); i++) {
423 let node = nodes[i] = findTemplateNode(dom, info);
424 applyIdToMap(this, dom.$, node, info);
425 applyTemplateContent(this, node, info);
426 applyEventListener(this, node, info);
427 }
428 return dom;
429 }
430
431 /**
432 * Adds an event listener by method name for the event provided.
433 *
434 * This method generates a handler function that looks up the method
435 * name at handling time.
436 *
437 * @param {Node} node Node to add listener on
438 * @param {string} eventName Name of event
439 * @param {string} methodName Name of method
440 * @param {*=} context Context the method will be called on (defaults
441 * to `node`)
442 * @return {Function} Generated handler function
443 */
444 _addMethodEventListenerToNode(node, eventName, methodName, context) {
445 context = context || node;
446 let handler = createNodeEventHandler(context, eventName, methodName);
447 this._addEventListenerToNode(node, eventName, handler);
448 return handler;
449 }
450
451 /**
452 * Override point for adding custom or simulated event handling.
453 *
454 * @param {Node} node Node to add event listener to
455 * @param {string} eventName Name of event
456 * @param {Function} handler Listener function to add
457 */
458 _addEventListenerToNode(node, eventName, handler) {
459 node.addEventListener(eventName, handler);
460 }
461
462 /**
463 * Override point for adding custom or simulated event handling.
464 *
465 * @param {Node} node Node to remove event listener from
466 * @param {string} eventName Name of event
467 * @param {Function} handler Listener function to remove
468 */
469 _removeEventListenerFromNode(node, eventName, handler) {
470 node.removeEventListener(eventName, handler);
471 }
472
473 }
474
475 return TemplateStamp;
476
477 });
478
479 })();
480 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698