From 1026498e9e870cc11d52ac968f77e38ef3a49d76 Mon Sep 17 00:00:00 2001 From: Kevin Schaaf Date: Fri, 31 Jul 2015 21:44:24 -0700 Subject: [PATCH] Store binding parts in notes. --- src/lib/annotations/annotations.html | 121 ++++++++++++++++----------- src/lib/bind/effects.html | 10 +-- src/standard/annotations.html | 35 +++++--- src/standard/effectBuilder.html | 51 ++++++----- 4 files changed, 133 insertions(+), 84 deletions(-) diff --git a/src/lib/annotations/annotations.html b/src/lib/annotations/annotations.html index c5db794906..790faf218b 100644 --- a/src/lib/annotations/annotations.html +++ b/src/lib/annotations/annotations.html @@ -88,26 +88,75 @@ this._parseElementAnnotations(node, list); }, - _testEscape: function(value) { - var escape = value.slice(0, 2); - if (escape === '{{' || escape === '[[') { - return escape; + _bindingRegex: /([^{[]*)({{|\[\[)([^}\]]*)(?:]]|}})/g, + + _parseBindings: function(text) { + var re = this._bindingRegex; + var parts = []; + var m, lastIndex; + // Example: "literal1{{binding1}}literal2[[binding2]]final" + // Regex matches: + // Iteration 1: Iteration 2: + // m[1]: 'literal1' 'literal2' + // m[2]: '{{' '[[' + // m[3]: 'binding1' 'binding2' + // 'final' is manually substring'ed from end + while ((m = re.exec(text)) !== null) { + // Add literal part + if (m[1]) { + parts.push({literal: m[1]}); + } + // Add binding part + // Mode (one-way or two) + var mode = m[2][0]; + var value = m[3].trim(); + // Negate + var negate = false; + if (value[0] == '!') { + negate = true; + value = value.substring(1); + } + var customEvent, notifyEvent, colon; + if (mode == '{' && (colon = value.indexOf('::')) > 0) { + notifyEvent = value.substring(colon + 2); + value = value.substring(0, colon); + customEvent = true; + } + parts.push({ + compoundIndex: parts.length, + value: value, + mode: mode, + negate: negate, + event: notifyEvent, + customEvent: customEvent + }); + lastIndex = re.lastIndex; + } + // Add a final literal part + if (lastIndex && lastIndex < text.length) { + var literal = text.substring(lastIndex); + if (literal) { + parts.push({ + literal: literal + }); + } + } + if (parts.length) { + return parts; } }, // add annotations gleaned from TextNode `node` to `list` _parseTextNodeAnnotation: function(node, list) { - var v = node.textContent; - var escape = this._testEscape(v); - if (escape) { + var parts = this._parseBindings(node.textContent); + if (parts) { // 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).trim() + parts: parts }] }; list.push(annote); @@ -152,7 +201,7 @@ !node.hasAttribute('preserve-content')) { this._parseTemplate(node, i, list, annote); } - // collapse adjacent textNodes: fixes an IE issue that can cause + // collapse adjacent textNodes: fixes an IE issue that can cause // text nodes to be inexplicably split =( // note that root.normalize() should work but does not so we do this // manually. @@ -203,13 +252,11 @@ // `annotation data` is not as clear as it could be _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 (unless actually an escaped binding annotation) - if (n === 'id' && !this._testEscape(v)) { - annotation.id = v; - } + var n = a.name; + var v = a.value; + var b; // events (on-*) - else if (n.slice(0, 3) === 'on-') { + if (n.slice(0, 3) === 'on-') { node.removeAttribute(n); annotation.events.push({ name: n.slice(3), @@ -217,44 +264,27 @@ }); } // bindings (other attributes) - else { - var b = this._parseNodeAttributeAnnotation(node, n, v); - if (b) { - annotation.bindings.push(b); - } + else if (b = this._parseNodeAttributeAnnotation(node, n, v)) { + annotation.bindings.push(b); + } + // static id + else if (n === 'id') { + annotation.id = v; } } }, // construct annotation data from a generic attribute, or undefined _parseNodeAttributeAnnotation: function(node, n, v) { - var escape = this._testEscape(v); - if (escape) { - var customEvent; - // Cache name (`n` will be mangled) - var name = n; - // Mode (one-way or two) - var mode = escape[0]; - v = v.slice(2, -2).trim(); - // Negate - var not = false; - if (v[0] == '!') { - v = v.substring(1); - not = true; - } + var parts = this._parseBindings(v); + if (parts) { // Attribute or property + var name = n; 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); - customEvent = true; - } // Clear attribute before removing, since IE won't allow removing // `value` attribute if it previously had a value (can't // unconditionally set '' before removing since attributes with `$` @@ -273,12 +303,9 @@ } return { kind: kind, - mode: mode, name: name, - value: v, - negate: not, - event: notifyEvent, - customEvent: customEvent + parts: parts, + isCompound: parts.length !== 1 }; } }, diff --git a/src/lib/bind/effects.html b/src/lib/bind/effects.html index eb6a80ac4a..b605664395 100644 --- a/src/lib/bind/effects.html +++ b/src/lib/bind/effects.html @@ -14,10 +14,10 @@ _shouldAddListener: function(effect) { return effect.name && - effect.mode === '{' && - !effect.negate && - effect.kind != 'attribute' - ; + effect.kind != 'attribute' && + !effect.isCompound && + effect.parts[0].mode === '{' && + !effect.parts[0].negate; }, _annotationEffect: function(source, value, effect) { @@ -77,7 +77,7 @@ if (args) { var fn = this[effect.method]; if (fn) { - this.__setProperty(effect.property, fn.apply(this, args)); + this.__setProperty(effect.name, fn.apply(this, args)); } else { this._warn(this._logf('_computeEffect', 'compute method `' + effect.method + '` not defined')); diff --git a/src/standard/annotations.html b/src/standard/annotations.html index 06f81bd0a6..4842f71346 100644 --- a/src/standard/annotations.html +++ b/src/standard/annotations.html @@ -139,9 +139,14 @@ // Parse bindings for methods & path roots (models) for (var j=0; j.on.-changed: = e.detail.value Polymer.Bind._addAnnotatedListener(this, index, - note.name, note.value, note.event); + note.name, note.parts[0].value, note.parts[0].event); } - if (note.signature) { - this._addAnnotatedComputationEffect(note, index); - } else { - // capture the node index - note.index = index; - // add 'annotation' binding effect for property 'model' - this._addPropertyEffect(note.model, 'annotation', note); + for (var i=0; i