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

Side by Side Diff: appengine/config_service/ui/bower_components/polymer/lib/utils/templatize.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="boot.html">
12 <link rel="import" href="../mixins/property-effects.html">
13 <link rel="import" href="../mixins/mutable-data.html">
14
15 <script>
16 (function() {
17 'use strict';
18
19 // Base class for HTMLTemplateElement extension that has property effects
20 // machinery for propagating host properties to children. This is an ES5
21 // class only because Babel (incorrectly) requires super() in the class
22 // constructor even though no `this` is used and it returns an instance.
23 let newInstance = null;
24 function HTMLTemplateElementExtension() { return newInstance; }
25 HTMLTemplateElementExtension.prototype = Object.create(HTMLTemplateElement.p rototype, {
26 constructor: {
27 value: HTMLTemplateElementExtension,
28 writable: true
29 }
30 });
31 const DataTemplate = Polymer.PropertyEffects(HTMLTemplateElementExtension);
32 const MutableDataTemplate = Polymer.MutableData(DataTemplate);
33
34 // Applies a DataTemplate subclass to a <template> instance
35 function upgradeTemplate(template, constructor) {
36 newInstance = template;
37 Object.setPrototypeOf(template, constructor.prototype);
38 new constructor();
39 newInstance = null;
40 }
41
42 // Base class for TemplateInstance's
43 /**
44 * @constructor
45 * @implements {Polymer_PropertyEffects}
46 */
47 const base = Polymer.PropertyEffects(class {});
48 class TemplateInstanceBase extends base {
49 constructor(props) {
50 super();
51 this._configureProperties(props);
52 this.root = this._stampTemplate(this.__dataHost);
53 // Save list of stamped children
54 let children = this.children = [];
55 for (let n = this.root.firstChild; n; n=n.nextSibling) {
56 children.push(n);
57 n.__templatizeInstance = this;
58 }
59 if (this.__templatizeOwner.__hideTemplateChildren__) {
60 this._showHideChildren(true);
61 }
62 // Flush props only when props are passed if instance props exist
63 // or when there isn't instance props.
64 let options = this.__templatizeOptions;
65 if ((props && options.instanceProps) || !options.instanceProps) {
66 this._enableProperties();
67 }
68 }
69 /**
70 * Configure the given `props` by calling `_setPendingProperty`. Also
71 * sets any properties stored in `__hostProps`.
72 * @private
73 * @param {Object} props Object of property name-value pairs to set.
74 */
75 _configureProperties(props) {
76 let options = this.__templatizeOptions;
77 if (props) {
78 for (let iprop in options.instanceProps) {
79 if (iprop in props) {
80 this._setPendingProperty(iprop, props[iprop]);
81 }
82 }
83 }
84 for (let hprop in this.__hostProps) {
85 this._setPendingProperty(hprop, this.__dataHost['_host_' + hprop]);
86 }
87 }
88 /**
89 * Forwards a host property to this instance. This method should be
90 * called on instances from the `options.forwardHostProp` callback
91 * to propagate changes of host properties to each instance.
92 *
93 * Note this method enqueues the change, which are flushed as a batch.
94 *
95 * @param {string} prop Property or path name
96 * @param {*} value Value of the property to forward
97 */
98 forwardHostProp(prop, value) {
99 if (this._setPendingPropertyOrPath(prop, value, false, true)) {
100 this.__dataHost._enqueueClient(this);
101 }
102 }
103 /**
104 * @override
105 */
106 _addEventListenerToNode(node, eventName, handler) {
107 if (this._methodHost && this.__templatizeOptions.parentModel) {
108 // If this instance should be considered a parent model, decorate
109 // events this template instance as `model`
110 this._methodHost._addEventListenerToNode(node, eventName, (e) => {
111 e.model = this;
112 handler(e);
113 });
114 } else {
115 // Otherwise delegate to the template's host (which could be)
116 // another template instance
117 let templateHost = this.__dataHost.__dataHost;
118 if (templateHost) {
119 templateHost._addEventListenerToNode(node, eventName, handler);
120 }
121 }
122 }
123 /**
124 * Shows or hides the template instance top level child elements. For
125 * text nodes, `textContent` is removed while "hidden" and replaced when
126 * "shown."
127 * @param {boolean} hide Set to true to hide the children;
128 * set to false to show them.
129 * @protected
130 */
131 _showHideChildren(hide) {
132 let c = this.children;
133 for (let i=0; i<c.length; i++) {
134 let n = c[i];
135 // Ignore non-changes
136 if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) {
137 if (n.nodeType === Node.TEXT_NODE) {
138 if (hide) {
139 n.__polymerTextContent__ = n.textContent;
140 n.textContent = '';
141 } else {
142 n.textContent = n.__polymerTextContent__;
143 }
144 } else if (n.style) {
145 if (hide) {
146 n.__polymerDisplay__ = n.style.display;
147 n.style.display = 'none';
148 } else {
149 n.style.display = n.__polymerDisplay__;
150 }
151 }
152 }
153 n.__hideTemplateChildren__ = hide;
154 if (n._showHideChildren) {
155 n._showHideChildren(hide);
156 }
157 }
158 }
159 /**
160 * Overrides default property-effects implementation to intercept
161 * textContent bindings while children are "hidden" and cache in
162 * private storage for later retrieval.
163 *
164 * @override
165 */
166 _setUnmanagedPropertyToNode(node, prop, value) {
167 if (node.__hideTemplateChildren__ &&
168 node.nodeType == Node.TEXT_NODE && prop == 'textContent') {
169 node.__polymerTextContent__ = value;
170 } else {
171 super._setUnmanagedPropertyToNode(node, prop, value);
172 }
173 }
174 /**
175 * Find the parent model of this template instance. The parent model
176 * is either another templatize instance that had option `parentModel: tru e`,
177 * or else the host element.
178 *
179 * @return {Polymer.PropertyEffectsInterface} The parent model of this ins tance
180 */
181 get parentModel() {
182 let model = this.__parentModel;
183 if (!model) {
184 let options;
185 model = this
186 do {
187 // A template instance's `__dataHost` is a <template>
188 // `model.__dataHost.__dataHost` is the template's host
189 model = model.__dataHost.__dataHost;
190 } while ((options = model.__templatizeOptions) && !options.parentModel )
191 this.__parentModel = model;
192 }
193 return model;
194 }
195 }
196
197 const MutableTemplateInstanceBase = Polymer.MutableData(TemplateInstanceBase );
198
199 function findMethodHost(template) {
200 // Technically this should be the owner of the outermost template.
201 // In shadow dom, this is always getRootNode().host, but we can
202 // approximate this via cooperation with our dataHost always setting
203 // `_methodHost` as long as there were bindings (or id's) on this
204 // instance causing it to get a dataHost.
205 let templateHost = template.__dataHost;
206 return templateHost && templateHost._methodHost || templateHost;
207 }
208
209 function createTemplatizerClass(template, templateInfo, options) {
210 // Anonymous class created by the templatize
211 /**
212 * @unrestricted
213 */
214 let base = options.mutableData ?
215 MutableTemplateInstanceBase : TemplateInstanceBase;
216 let klass = class extends base { }
217 klass.prototype.__templatizeOptions = options;
218 klass.prototype._bindTemplate(template);
219 addNotifyEffects(klass, template, templateInfo, options);
220 return klass;
221 }
222
223 function addPropagateEffects(template, templateInfo, options) {
224 let userForwardHostProp = options.forwardHostProp;
225 if (userForwardHostProp) {
226 // Provide data API and property effects on memoized template class
227 let klass = templateInfo.templatizeTemplateClass;
228 if (!klass) {
229 let base = options.mutableData ? MutableDataTemplate : DataTemplate;
230 klass = templateInfo.templatizeTemplateClass =
231 class TemplatizedTemplate extends base {}
232 // Add template - >instances effects
233 // and host <- template effects
234 let hostProps = templateInfo.hostProps;
235 for (let prop in hostProps) {
236 klass.prototype._addPropertyEffect('_host_' + prop,
237 klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,
238 {fn: createForwardHostPropEffect(prop, userForwardHostProp)});
239 klass.prototype._createNotifyingProperty('_host_' + prop);
240 }
241 }
242 upgradeTemplate(template, klass);
243 // Mix any pre-bound data into __data; no need to flush this to
244 // instances since they pull from the template at instance-time
245 if (template.__dataProto) {
246 // Note, generally `__dataProto` could be chained, but it's guaranteed
247 // to not be since this is a vanilla template we just added effects to
248 Object.assign(template.__data, template.__dataProto);
249 }
250 // Clear any pending data for performance
251 template.__dataTemp = {};
252 template.__dataPending = null;
253 template.__dataOld = null;
254 template._enableProperties();
255 }
256 }
257
258 function createForwardHostPropEffect(hostProp, userForwardHostProp) {
259 return function forwardHostProp(template, prop, props) {
260 userForwardHostProp.call(template.__templatizeOwner,
261 prop.substring('_host_'.length), props[prop]);
262 }
263 }
264
265 function addNotifyEffects(klass, template, templateInfo, options) {
266 let hostProps = templateInfo.hostProps || {};
267 for (let iprop in options.instanceProps) {
268 delete hostProps[iprop];
269 let userNotifyInstanceProp = options.notifyInstanceProp;
270 if (userNotifyInstanceProp) {
271 klass.prototype._addPropertyEffect(iprop,
272 klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,
273 {fn: createNotifyInstancePropEffect(iprop, userNotifyInstanceProp)}) ;
274 }
275 }
276 if (options.forwardHostProp && template.__dataHost) {
277 for (let hprop in hostProps) {
278 klass.prototype._addPropertyEffect(hprop,
279 klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,
280 {fn: createNotifyHostPropEffect()})
281 }
282 }
283 }
284
285 function createNotifyInstancePropEffect(instProp, userNotifyInstanceProp) {
286 return function notifyInstanceProp(inst, prop, props) {
287 userNotifyInstanceProp.call(inst.__templatizeOwner,
288 inst, prop, props[prop]);
289 }
290 }
291
292 function createNotifyHostPropEffect() {
293 return function notifyHostProp(inst, prop, props) {
294 inst.__dataHost._setPendingPropertyOrPath('_host_' + prop, props[prop], true, true);
295 }
296 }
297
298 /**
299 * Module for preparing and stamping instances of templates that utilize
300 * Polymer's data-binding and declarative event listener features.
301 *
302 * Example:
303 *
304 * // Get a template from somewhere, e.g. light DOM
305 * let template = this.querySelector('template');
306 * // Prepare the template
307 * let TemplateClass = Polymer.Templatize.templatize(template);
308 * // Instance the template with an initial data model
309 * let instance = new TemplateClass({myProp: 'initial'});
310 * // Insert the instance's DOM somewhere, e.g. element's shadow DOM
311 * this.shadowRoot.appendChild(instance.root);
312 * // Changing a property on the instance will propagate to bindings
313 * // in the template
314 * instance.myProp = 'new value';
315 *
316 * The `options` dictionary passed to `templatize` allows for customizing
317 * features of the generated template class, including how outer-scope host
318 * properties should be forwarded into template instances, how any instance
319 * properties added into the template's scope should be notified out to
320 * the host, and whether the instance should be decorated as a "parent model "
321 * of any event handlers.
322 *
323 * // Customze property forwarding and event model decoration
324 * let TemplateClass = Polymer.Tempaltize.templatize(template, this, {
325 * parentModel: true,
326 * instanceProps: {...},
327 * forwardHostProp(property, value) {...},
328 * notifyInstanceProp(instance, property, value) {...},
329 * });
330 *
331 *
332 * @namespace
333 * @memberof Polymer
334 * @summary Module for preparing and stamping instances of templates
335 * utilizing Polymer templating features.
336 */
337 const Templatize = {
338
339 /**
340 * Returns an anonymous `Polymer.PropertyEffects` class bound to the
341 * `<template>` provided. Instancing the class will result in the
342 * template being stamped into document fragment stored as the instance's
343 * `root` property, after which it can be appended to the DOM.
344 *
345 * Templates may utilize all Polymer data-binding features as well as
346 * declarative event listeners. Event listeners and inline computing
347 * functions in the template will be called on the host of the template.
348 *
349 * The constructor returned takes a single argument dictionary of initial
350 * property values to propagate into template bindings. Additionally
351 * host properties can be forwarded in, and instance properties can be
352 * notified out by providing optional callbacks in the `options` dictionar y.
353 *
354 * Valid configuration in `options` are as follows:
355 *
356 * - `forwardHostProp(property, value)`: Called when a property referenced
357 * in the template changed on the template's host. As this library does
358 * not retain references to templates instanced by the user, it is the
359 * templatize owner's responsibility to forward host property changes in to
360 * user-stamped instances. The `instance.forwardHostProp(property, valu e)`
361 * method on the generated class should be called to forward host
362 * properties into the template to prevent unnecessary property-changed
363 * notifications. Any properties referenced in the template that are not
364 * defined in `instanceProps` will be notified up to the template's host
365 * automatically.
366 * - `instanceProps`: Dictionary of property names that will be added
367 * to the instance by the templatize owner. These properties shadow any
368 * host properties, and changes within the template to these properties
369 * will result in `notifyInstanceProp` being called.
370 * - `mutableData`: When `true`, the generated class will skip strict
371 * dirty-checking for objects and arrays (always consider them to be
372 * "dirty").
373 * - `notifyInstanceProp(instance, property, value)`: Called when
374 * an instance property changes. Users may choose to call `notifyPath`
375 * on e.g. the owner to notify the change.
376 * - `parentModel`: When `true`, events handled by declarative event liste ners
377 * (`on-event="handler"`) will be decorated with a `model` property poin ting
378 * to the template instance that stamped it. It will also be returned
379 * from `instance.parentModel` in cases where template instance nesting
380 * causes an inner model to shadow an outer model.
381 *
382 * Note that the class returned from `templatize` is generated only once
383 * for a given `<template>` using `options` from the first call for that
384 * template, and the cached class is returned for all subsequent calls to
385 * `templatize` for that template. As such, `options` callbacks should no t
386 * close over owner-specific properties since only the first `options` is
387 * used; rather, callbacks are called bound to the `owner`, and so context
388 * needed from the callbacks (such as references to `instances` stamped)
389 * should be stored on the `owner` such that they can be retrieved via `th is`.
390 *
391 * @memberof Polymer.Templatize
392 * @param {HTMLTemplateElement} template Template to templatize
393 * @param {*} owner Owner of the template instances; any optional callback s
394 * will be bound to this owner.
395 * @param {*=} options Options dictionary (see summary for details)
396 * @return {TemplateInstanceBase} Generated class bound to the template
397 * provided
398 */
399 templatize(template, owner, options) {
400 options = options || {};
401 if (template.__templatizeOwner) {
402 throw new Error('A <template> can only be templatized once');
403 }
404 template.__templatizeOwner = owner;
405 let templateInfo = owner.constructor._parseTemplate(template);
406 // Get memoized base class for the prototypical template, which
407 // includes property effects for binding template & forwarding
408 let baseClass = templateInfo.templatizeInstanceClass;
409 if (!baseClass) {
410 baseClass = createTemplatizerClass(template, templateInfo, options);
411 templateInfo.templatizeInstanceClass = baseClass;
412 }
413 // Host property forwarding must be installed onto template instance
414 addPropagateEffects(template, templateInfo, options);
415 // Subclass base class and add reference for this specific template
416 let klass = class TemplateInstance extends baseClass {};
417 klass.prototype._methodHost = findMethodHost(template);
418 klass.prototype.__dataHost = template;
419 klass.prototype.__templatizeOwner = owner;
420 klass.prototype.__hostProps = templateInfo.hostProps;
421 return klass;
422 },
423
424 /**
425 * Returns the template "model" associated with a given element, which
426 * serves as the binding scope for the template instance the element is
427 * contained in. A template model is an instance of
428 * `TemplateInstanceBase`, and should be used to manipulate data
429 * associated with this template instance.
430 *
431 * Example:
432 *
433 * let model = modelForElement(el);
434 * if (model.index < 10) {
435 * model.set('item.checked', true);
436 * }
437 *
438 * @memberof Polymer.Templatize
439 * @param {HTMLTemplateElement} template The model will be returned for
440 * elements stamped from this template
441 * @param {HTMLElement} el Element for which to return a template model.
442 * @return {TemplateInstanceBase} Template instance representing the
443 * binding scope for the element
444 */
445 modelForElement(template, el) {
446 let model;
447 while (el) {
448 // An element with a __templatizeInstance marks the top boundary
449 // of a scope; walk up until we find one, and then ensure that
450 // its __dataHost matches `this`, meaning this dom-repeat stamped it
451 if ((model = el.__templatizeInstance)) {
452 // Found an element stamped by another template; keep walking up
453 // from its __dataHost
454 if (model.__dataHost != template) {
455 el = model.__dataHost;
456 } else {
457 return model;
458 }
459 } else {
460 // Still in a template scope, keep going up until
461 // a __templatizeInstance is found
462 el = el.parentNode;
463 }
464 }
465 return null;
466 }
467 }
468
469 Polymer.Templatize = Templatize;
470
471 })();
472
473 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698