| Index: third_party/polymer/v0_8/components-chromium/polymer/src/lib/annotations/annotations-extracted.js
|
| diff --git a/third_party/polymer/v0_8/components-chromium/polymer/src/lib/annotations/annotations-extracted.js b/third_party/polymer/v0_8/components-chromium/polymer/src/lib/annotations/annotations-extracted.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f69af1855e5f7e4a317c54865f9bc0d41933340b
|
| --- /dev/null
|
| +++ b/third_party/polymer/v0_8/components-chromium/polymer/src/lib/annotations/annotations-extracted.js
|
| @@ -0,0 +1,318 @@
|
| +
|
| +/**
|
| + * Scans a template to produce an annotation list that that associates
|
| + * metadata culled from markup with tree locations
|
| + * metadata and information to associate the metadata with nodes in an instance.
|
| + *
|
| + * Supported expressions include:
|
| + *
|
| + * Double-mustache annotations in text content. The annotation must be the only
|
| + * content in the tag, compound expressions are not supported.
|
| + *
|
| + * <[tag]>{{annotation}}<[tag]>
|
| + *
|
| + * Double-escaped annotations in an attribute, either {{}} or [[]].
|
| + *
|
| + * <[tag] someAttribute="{{annotation}}" another="[[annotation]]"><[tag]>
|
| + *
|
| + * `on-` style event declarations.
|
| + *
|
| + * <[tag] on-<event-name>="annotation"><[tag]>
|
| + *
|
| + * Note that the `annotations` feature does not implement any behaviors
|
| + * associated with these expressions, it only captures the data.
|
| + *
|
| + * Generated data-structure:
|
| + *
|
| + * [
|
| + * {
|
| + * id: '<id>',
|
| + * events: [
|
| + * {
|
| + * name: '<name>'
|
| + * value: '<annotation>'
|
| + * }, ...
|
| + * ],
|
| + * bindings: [
|
| + * {
|
| + * kind: ['text'|'attribute'],
|
| + * mode: ['{'|'['],
|
| + * name: '<name>'
|
| + * value: '<annotation>'
|
| + * }, ...
|
| + * ],
|
| + * // TODO(sjmiles): this is annotation-parent, not node-parent
|
| + * parent: <reference to parent annotation object>,
|
| + * index: <integer index in parent's childNodes collection>
|
| + * },
|
| + * ...
|
| + * ]
|
| + *
|
| + * @class Template feature
|
| + */
|
| +
|
| + // null-array (shared empty array to avoid null-checks)
|
| + Polymer.nar = [];
|
| +
|
| + Polymer.Annotations = {
|
| +
|
| + // preprocess-time
|
| +
|
| + // construct and return a list of annotation records
|
| + // by scanning `template`'s content
|
| + //
|
| + parseAnnotations: function(template) {
|
| + var list = [];
|
| + var content = template._content || template.content;
|
| + this._parseNodeAnnotations(content, list);
|
| + return list;
|
| + },
|
| +
|
| + // add annotations gleaned from subtree at `node` to `list`
|
| + _parseNodeAnnotations: function(node, list) {
|
| + return node.nodeType === Node.TEXT_NODE ?
|
| + this._parseTextNodeAnnotation(node, list) :
|
| + // TODO(sjmiles): are there other nodes we may encounter
|
| + // that are not TEXT_NODE but also not ELEMENT?
|
| + this._parseElementAnnotations(node, list);
|
| + },
|
| +
|
| + // add annotations gleaned from TextNode `node` to `list`
|
| + _parseTextNodeAnnotation: function(node, list) {
|
| + var v = node.textContent, escape = v.slice(0, 2);
|
| + if (escape === '{{' || escape === '[[') {
|
| + // NOTE: use a space here so the textNode remains; some browsers
|
| + // (IE) evacipate an empty textNode.
|
| + node.textContent = ' ';
|
| + var annote = {
|
| + bindings: [{
|
| + kind: 'text',
|
| + mode: escape[0],
|
| + value: v.slice(2, -2)
|
| + }]
|
| + };
|
| + list.push(annote);
|
| + return annote;
|
| + }
|
| + },
|
| +
|
| + // add annotations gleaned from Element `node` to `list`
|
| + _parseElementAnnotations: function(element, list) {
|
| + var annote = {
|
| + bindings: [],
|
| + events: []
|
| + };
|
| + this._parseChildNodesAnnotations(element, annote, list);
|
| + // TODO(sjmiles): is this for non-ELEMENT nodes? If so, we should
|
| + // change the contract of this method, or filter these out above.
|
| + if (element.attributes) {
|
| + this._parseNodeAttributeAnnotations(element, annote, list);
|
| + // TODO(sorvell): ad hoc callback for doing work on elements while
|
| + // leveraging annotator's tree walk.
|
| + // Consider adding an node callback registry and moving specific
|
| + // processing out of this module.
|
| + if (this.prepElement) {
|
| + this.prepElement(element);
|
| + }
|
| + }
|
| + if (annote.bindings.length || annote.events.length || annote.id) {
|
| + list.push(annote);
|
| + }
|
| + return annote;
|
| + },
|
| +
|
| + // add annotations gleaned from children of `root` to `list`, `root`'s
|
| + // `annote` is supplied as it is the annote.parent of added annotations
|
| + _parseChildNodesAnnotations: function(root, annote, list, callback) {
|
| + if (root.firstChild) {
|
| + for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++){
|
| + if (node.localName === 'template' &&
|
| + !node.hasAttribute('preserve-content')) {
|
| + this._parseTemplate(node, i, list, annote);
|
| + }
|
| + //
|
| + var childAnnotation = this._parseNodeAnnotations(node, list, callback);
|
| + if (childAnnotation) {
|
| + childAnnotation.parent = annote;
|
| + childAnnotation.index = i;
|
| + }
|
| + }
|
| + }
|
| + },
|
| +
|
| + // 1. Parse annotations from the template and memoize them on
|
| + // content._notes (recurses into nested templates)
|
| + // 2. Parse template bindings for parent.* properties and memoize them on
|
| + // content._parentProps
|
| + // 3. Create bindings in current scope's annotation list to template for
|
| + // parent props found in template
|
| + // 4. Remove template.content and store it in annotation list, where it
|
| + // will be the responsibility of the host to set it back to the template
|
| + // (this is both an optimization to avoid re-stamping nested template
|
| + // children and avoids a bug in Chrome where nested template children
|
| + // upgrade)
|
| + _parseTemplate: function(node, index, list, parent) {
|
| + // TODO(sjmiles): simply altering the .content reference didn't
|
| + // work (there was some confusion, might need verification)
|
| + var content = document.createDocumentFragment();
|
| + content._notes = this.parseAnnotations(node);
|
| + content.appendChild(node.content);
|
| + // Special-case treatment of 'parent.*' props for nested templates
|
| + // Automatically bind `prop` on host to `_parent_prop` on template
|
| + // for any `parent.prop`'s encountered in template binding; it is
|
| + // responsibility of the template implementation to forward
|
| + // these properties as appropriate
|
| + var bindings = [];
|
| + this._discoverTemplateParentProps(content);
|
| + for (var prop in content._parentProps) {
|
| + bindings.push({
|
| + index: index,
|
| + kind: 'property',
|
| + mode: '{',
|
| + name: '_parent_' + prop,
|
| + value: prop
|
| + });
|
| + }
|
| + // TODO(sjmiles): using `nar` to avoid unnecessary allocation;
|
| + // in general the handling of these arrays needs some cleanup
|
| + // in this module
|
| + list.push({
|
| + bindings: bindings,
|
| + events: Polymer.nar,
|
| + templateContent: content,
|
| + parent: parent,
|
| + index: index
|
| + });
|
| + },
|
| +
|
| + // Finds all parent.* properties in template content and stores
|
| + // the path members in content._parentPropChain, which is an array
|
| + // of maps listing the properties of parent templates required at
|
| + // each level. Each outer template merges inner _parentPropChains to
|
| + // propagate inner parent property needs to outer templates.
|
| + // The top-level parent props from the chain (corresponding to this
|
| + // template) are stored in content._parentProps.
|
| + _discoverTemplateParentProps: function(content) {
|
| + var chain = content._parentPropChain = [];
|
| + content._notes.forEach(function(n) {
|
| + // Find all bindings to parent.* and spread them into _parentPropChain
|
| + n.bindings.forEach(function(b) {
|
| + var m;
|
| + if (m = b.value.match(/parent\.((parent\.)*[^.]*)/)) {
|
| + var parts = m[1].split('.');
|
| + for (var i=0; i<parts.length; i++) {
|
| + var pp = chain[i] || (chain[i] = {});
|
| + pp[parts[i]] = true;
|
| + }
|
| + }
|
| + });
|
| + // Merge child _parentPropChain[n+1] into this _parentPropChain[n]
|
| + if (n.templateContent) {
|
| + var tpp = n.templateContent._parentPropChain;
|
| + for (var i=1; i<tpp.length; i++) {
|
| + if (tpp[i]) {
|
| + var pp = chain[i-1] || (chain[i-1] = {});
|
| + Polymer.Base.simpleMixin(pp, tpp[i]);
|
| + }
|
| + }
|
| + }
|
| + });
|
| + // Store this template's parentProps map
|
| + content._parentProps = chain[0];
|
| + },
|
| +
|
| + // add annotation data from attributes to the `annotation` for node `node`
|
| + // TODO(sjmiles): the distinction between an `annotation` and
|
| + // `annotation data` is not as clear as it could be
|
| + // Walk attributes backwards, since removeAttribute can be vetoed by
|
| + // IE in certain cases (e.g. <input value="foo">), resulting in the
|
| + // attribute staying in the attributes list
|
| + _parseNodeAttributeAnnotations: function(node, annotation) {
|
| + for (var i=node.attributes.length-1, a; (a=node.attributes[i]); i--) {
|
| + var n = a.name, v = a.value;
|
| + // id
|
| + if (n === 'id') {
|
| + annotation.id = v;
|
| + }
|
| + // events (on-*)
|
| + else if (n.slice(0, 3) === 'on-') {
|
| + node.removeAttribute(n);
|
| + annotation.events.push({
|
| + name: n.slice(3),
|
| + value: v
|
| + });
|
| + }
|
| + // bindings (other attributes)
|
| + else {
|
| + var b = this._parseNodeAttributeAnnotation(node, n, v);
|
| + if (b) {
|
| + annotation.bindings.push(b);
|
| + }
|
| + }
|
| + }
|
| + },
|
| +
|
| + // construct annotation data from a generic attribute, or undefined
|
| + _parseNodeAttributeAnnotation: function(node, n, v) {
|
| + var mode = '', escape = v.slice(0, 2), name = n;
|
| + if (escape === '{{' || escape === '[[') {
|
| + // Mode (one-way or two)
|
| + mode = escape[0];
|
| + v = v.slice(2, -2);
|
| + // Negate
|
| + var not = false;
|
| + if (v[0] == '!') {
|
| + v = v.substring(1);
|
| + not = true;
|
| + }
|
| + // Attribute or property
|
| + var kind = 'property';
|
| + if (n[n.length-1] == '$') {
|
| + name = n.slice(0, -1);
|
| + kind = 'attribute';
|
| + }
|
| + // Custom notification event
|
| + var notifyEvent, colon;
|
| + if (mode == '{' && (colon = v.indexOf('::')) > 0) {
|
| + notifyEvent = v.substring(colon + 2);
|
| + v = v.substring(0, colon);
|
| + }
|
| + // Remove annotation
|
| + node.removeAttribute(n);
|
| + // Case hackery: attributes are lower-case, but bind targets
|
| + // (properties) are case sensitive. Gambit is to map dash-case to
|
| + // camel-case: `foo-bar` becomes `fooBar`.
|
| + // Attribute bindings are excepted.
|
| + if (kind === 'property') {
|
| + name = Polymer.CaseMap.dashToCamelCase(name);
|
| + }
|
| + return {
|
| + kind: kind,
|
| + mode: mode,
|
| + name: name,
|
| + value: v,
|
| + negate: not,
|
| + event: notifyEvent
|
| + };
|
| + }
|
| + },
|
| +
|
| + // instance-time
|
| +
|
| + _localSubTree: function(node, host) {
|
| + return (node === host) ? node.childNodes :
|
| + (node.lightChildren || node.childNodes);
|
| + },
|
| +
|
| + findAnnotatedNode: function(root, annote) {
|
| + // recursively ascend tree until we hit root
|
| + var parent = annote.parent &&
|
| + Polymer.Annotations.findAnnotatedNode(root, annote.parent);
|
| + // unwind the stack, returning the indexed node at each level
|
| + return !parent ? root :
|
| + Polymer.Annotations._localSubTree(parent, root)[annote.index];
|
| + }
|
| +
|
| + };
|
| +
|
| +
|
|
|