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

Side by Side Diff: third_party/polymer/v0_8/components/polymer/src/lib/annotations/annotations.html

Issue 1082403004: Import Polymer 0.8 and several key elements. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rerun reproduce.sh Created 5 years, 8 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) 2014 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 <link rel="import" href="../module.html">
11 <link rel="import" href="../case-map.html">
12
13 <script>
14 /**
15 * Scans a template to produce an annotation list that that associates
16 * metadata culled from markup with tree locations
17 * metadata and information to associate the metadata with nodes in an instance.
18 *
19 * Supported expressions include:
20 *
21 * Double-mustache annotations in text content. The annotation must be the only
22 * content in the tag, compound expressions are not supported.
23 *
24 * <[tag]>{{annotation}}<[tag]>
25 *
26 * Double-escaped annotations in an attribute, either {{}} or [[]].
27 *
28 * <[tag] someAttribute="{{annotation}}" another="[[annotation]]"><[tag]>
29 *
30 * `on-` style event declarations.
31 *
32 * <[tag] on-<event-name>="annotation"><[tag]>
33 *
34 * Note that the `annotations` feature does not implement any behaviors
35 * associated with these expressions, it only captures the data.
36 *
37 * Generated data-structure:
38 *
39 * [
40 * {
41 * id: '<id>',
42 * events: [
43 * {
44 * name: '<name>'
45 * value: '<annotation>'
46 * }, ...
47 * ],
48 * bindings: [
49 * {
50 * kind: ['text'|'attribute'],
51 * mode: ['{'|'['],
52 * name: '<name>'
53 * value: '<annotation>'
54 * }, ...
55 * ],
56 * // TODO(sjmiles): this is annotation-parent, not node-parent
57 * parent: <reference to parent annotation object>,
58 * index: <integer index in parent's childNodes collection>
59 * },
60 * ...
61 * ]
62 *
63 * @class Template feature
64 */
65
66 // null-array (shared empty array to avoid null-checks)
67 Polymer.nar = [];
68
69 Polymer.Annotations = {
70
71 // preprocess-time
72
73 // construct and return a list of annotation records
74 // by scanning `template`'s content
75 //
76 parseAnnotations: function(template) {
77 var list = [];
78 var content = template._content || template.content;
79 this._parseNodeAnnotations(content, list);
80 return list;
81 },
82
83 // add annotations gleaned from subtree at `node` to `list`
84 _parseNodeAnnotations: function(node, list) {
85 return node.nodeType === Node.TEXT_NODE ?
86 this._parseTextNodeAnnotation(node, list) :
87 // TODO(sjmiles): are there other nodes we may encounter
88 // that are not TEXT_NODE but also not ELEMENT?
89 this._parseElementAnnotations(node, list);
90 },
91
92 // add annotations gleaned from TextNode `node` to `list`
93 _parseTextNodeAnnotation: function(node, list) {
94 var v = node.textContent, escape = v.slice(0, 2);
95 if (escape === '{{' || escape === '[[') {
96 // NOTE: use a space here so the textNode remains; some browsers
97 // (IE) evacipate an empty textNode.
98 node.textContent = ' ';
99 var annote = {
100 bindings: [{
101 kind: 'text',
102 mode: escape[0],
103 value: v.slice(2, -2)
104 }]
105 };
106 list.push(annote);
107 return annote;
108 }
109 },
110
111 // add annotations gleaned from Element `node` to `list`
112 _parseElementAnnotations: function(element, list) {
113 var annote = {
114 bindings: [],
115 events: []
116 };
117 this._parseChildNodesAnnotations(element, annote, list);
118 // TODO(sjmiles): is this for non-ELEMENT nodes? If so, we should
119 // change the contract of this method, or filter these out above.
120 if (element.attributes) {
121 this._parseNodeAttributeAnnotations(element, annote, list);
122 // TODO(sorvell): ad hoc callback for doing work on elements while
123 // leveraging annotator's tree walk.
124 // Consider adding an node callback registry and moving specific
125 // processing out of this module.
126 if (this.prepElement) {
127 this.prepElement(element);
128 }
129 }
130 if (annote.bindings.length || annote.events.length || annote.id) {
131 list.push(annote);
132 }
133 return annote;
134 },
135
136 // add annotations gleaned from children of `root` to `list`, `root`'s
137 // `annote` is supplied as it is the annote.parent of added annotations
138 _parseChildNodesAnnotations: function(root, annote, list, callback) {
139 if (root.firstChild) {
140 for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++){
141 if (node.localName === 'template' &&
142 !node.hasAttribute('preserve-content')) {
143 this._parseTemplate(node, i, list, annote);
144 }
145 //
146 var childAnnotation = this._parseNodeAnnotations(node, list, callback) ;
147 if (childAnnotation) {
148 childAnnotation.parent = annote;
149 childAnnotation.index = i;
150 }
151 }
152 }
153 },
154
155 // 1. Parse annotations from the template and memoize them on
156 // content._notes (recurses into nested templates)
157 // 2. Parse template bindings for parent.* properties and memoize them on
158 // content._parentProps
159 // 3. Create bindings in current scope's annotation list to template for
160 // parent props found in template
161 // 4. Remove template.content and store it in annotation list, where it
162 // will be the responsibility of the host to set it back to the template
163 // (this is both an optimization to avoid re-stamping nested template
164 // children and avoids a bug in Chrome where nested template children
165 // upgrade)
166 _parseTemplate: function(node, index, list, parent) {
167 // TODO(sjmiles): simply altering the .content reference didn't
168 // work (there was some confusion, might need verification)
169 var content = document.createDocumentFragment();
170 content._notes = this.parseAnnotations(node);
171 content.appendChild(node.content);
172 // Special-case treatment of 'parent.*' props for nested templates
173 // Automatically bind `prop` on host to `_parent_prop` on template
174 // for any `parent.prop`'s encountered in template binding; it is
175 // responsibility of the template implementation to forward
176 // these properties as appropriate
177 var bindings = [];
178 this._discoverTemplateParentProps(content);
179 for (var prop in content._parentProps) {
180 bindings.push({
181 index: index,
182 kind: 'property',
183 mode: '{',
184 name: '_parent_' + prop,
185 value: prop
186 });
187 }
188 // TODO(sjmiles): using `nar` to avoid unnecessary allocation;
189 // in general the handling of these arrays needs some cleanup
190 // in this module
191 list.push({
192 bindings: bindings,
193 events: Polymer.nar,
194 templateContent: content,
195 parent: parent,
196 index: index
197 });
198 },
199
200 // Finds all parent.* properties in template content and stores
201 // the path members in content._parentPropChain, which is an array
202 // of maps listing the properties of parent templates required at
203 // each level. Each outer template merges inner _parentPropChains to
204 // propagate inner parent property needs to outer templates.
205 // The top-level parent props from the chain (corresponding to this
206 // template) are stored in content._parentProps.
207 _discoverTemplateParentProps: function(content) {
208 var chain = content._parentPropChain = [];
209 content._notes.forEach(function(n) {
210 // Find all bindings to parent.* and spread them into _parentPropChain
211 n.bindings.forEach(function(b) {
212 var m;
213 if (m = b.value.match(/parent\.((parent\.)*[^.]*)/)) {
214 var parts = m[1].split('.');
215 for (var i=0; i<parts.length; i++) {
216 var pp = chain[i] || (chain[i] = {});
217 pp[parts[i]] = true;
218 }
219 }
220 });
221 // Merge child _parentPropChain[n+1] into this _parentPropChain[n]
222 if (n.templateContent) {
223 var tpp = n.templateContent._parentPropChain;
224 for (var i=1; i<tpp.length; i++) {
225 if (tpp[i]) {
226 var pp = chain[i-1] || (chain[i-1] = {});
227 Polymer.Base.simpleMixin(pp, tpp[i]);
228 }
229 }
230 }
231 });
232 // Store this template's parentProps map
233 content._parentProps = chain[0];
234 },
235
236 // add annotation data from attributes to the `annotation` for node `node`
237 // TODO(sjmiles): the distinction between an `annotation` and
238 // `annotation data` is not as clear as it could be
239 // Walk attributes backwards, since removeAttribute can be vetoed by
240 // IE in certain cases (e.g. <input value="foo">), resulting in the
241 // attribute staying in the attributes list
242 _parseNodeAttributeAnnotations: function(node, annotation) {
243 for (var i=node.attributes.length-1, a; (a=node.attributes[i]); i--) {
244 var n = a.name, v = a.value;
245 // id
246 if (n === 'id') {
247 annotation.id = v;
248 }
249 // events (on-*)
250 else if (n.slice(0, 3) === 'on-') {
251 node.removeAttribute(n);
252 annotation.events.push({
253 name: n.slice(3),
254 value: v
255 });
256 }
257 // bindings (other attributes)
258 else {
259 var b = this._parseNodeAttributeAnnotation(node, n, v);
260 if (b) {
261 annotation.bindings.push(b);
262 }
263 }
264 }
265 },
266
267 // construct annotation data from a generic attribute, or undefined
268 _parseNodeAttributeAnnotation: function(node, n, v) {
269 var mode = '', escape = v.slice(0, 2), name = n;
270 if (escape === '{{' || escape === '[[') {
271 // Mode (one-way or two)
272 mode = escape[0];
273 v = v.slice(2, -2);
274 // Negate
275 var not = false;
276 if (v[0] == '!') {
277 v = v.substring(1);
278 not = true;
279 }
280 // Attribute or property
281 var kind = 'property';
282 if (n[n.length-1] == '$') {
283 name = n.slice(0, -1);
284 kind = 'attribute';
285 }
286 // Custom notification event
287 var notifyEvent, colon;
288 if (mode == '{' && (colon = v.indexOf('::')) > 0) {
289 notifyEvent = v.substring(colon + 2);
290 v = v.substring(0, colon);
291 }
292 // Remove annotation
293 node.removeAttribute(n);
294 // Case hackery: attributes are lower-case, but bind targets
295 // (properties) are case sensitive. Gambit is to map dash-case to
296 // camel-case: `foo-bar` becomes `fooBar`.
297 // Attribute bindings are excepted.
298 if (kind === 'property') {
299 name = Polymer.CaseMap.dashToCamelCase(name);
300 }
301 return {
302 kind: kind,
303 mode: mode,
304 name: name,
305 value: v,
306 negate: not,
307 event: notifyEvent
308 };
309 }
310 },
311
312 // instance-time
313
314 _localSubTree: function(node, host) {
315 return (node === host) ? node.childNodes :
316 (node.lightChildren || node.childNodes);
317 },
318
319 findAnnotatedNode: function(root, annote) {
320 // recursively ascend tree until we hit root
321 var parent = annote.parent &&
322 Polymer.Annotations.findAnnotatedNode(root, annote.parent);
323 // unwind the stack, returning the indexed node at each level
324 return !parent ? root :
325 Polymer.Annotations._localSubTree(parent, root)[annote.index];
326 }
327
328 };
329
330
331 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698