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