Skip to content

Commit

Permalink
Store binding parts in notes.
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinpschaaf committed Oct 10, 2015
1 parent d09c597 commit 1026498
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 84 deletions.
121 changes: 74 additions & 47 deletions src/lib/annotations/annotations.html
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -203,58 +252,39 @@
// `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),
value: v
});
}
// 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 `$`
Expand All @@ -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
};
}
},
Expand Down
10 changes: 5 additions & 5 deletions src/lib/bind/effects.html
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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'));
Expand Down
35 changes: 22 additions & 13 deletions src/standard/annotations.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,14 @@
// Parse bindings for methods & path roots (models)
for (var j=0; j<note.bindings.length; j++) {
var b = note.bindings[j];
b.signature = this._parseMethod(b.value);
if (!b.signature) {
b.model = this._modelForPath(b.value);
for (var k=0; k<b.parts.length; k++) {
var p = b.parts[k];
if (!p.literal) {
p.signature = this._parseMethod(p.value);
if (!p.signature) {
p.model = this._modelForPath(p.value);
}
}
}
}
// Recurse into nested templates & bind parent props
Expand All @@ -154,10 +159,12 @@
bindings.push({
index: note.index,
kind: 'property',
mode: '{',
name: '_parent_' + prop,
model: prop,
value: prop
parts: [{
mode: '{',
model: prop,
value: prop
}]
});
}
note.bindings = note.bindings.concat(bindings);
Expand All @@ -174,14 +181,16 @@
notes.forEach(function(n) {
// Find all bindings to parent.* and spread them into _parentPropChain
n.bindings.forEach(function(b) {
if (b.signature) {
var args = b.signature.args;
for (var k=0; k<args.length; k++) {
pp[args[k].model] = true;
b.parts.forEach(function(p) {
if (p.signature) {
var args = p.signature.args;
for (var k=0; k<args.length; k++) {
pp[args[k].model] = true;
}
} else {
pp[p.model] = true;
}
} else {
pp[b.model] = true;
}
});
});
// Merge child _parentProps into this _parentProps
if (n.templateContent) {
Expand Down
51 changes: 32 additions & 19 deletions src/standard/effectBuilder.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
method: sig.method,
args: sig.args,
trigger: arg,
property: name
name: name
});
}, this);
},
Expand Down Expand Up @@ -129,39 +129,52 @@
if (Polymer.Bind._shouldAddListener(note)) {
// <node>.on.<dash-case-property>-changed: <path> = 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<note.parts.length; i++) {
var part = note.parts[i];
if (part.signature) {
this._addAnnotatedComputationEffect(note, part, index);
} else if (!part.literal) {
// add 'annotation' binding effect for property 'model'
this._addPropertyEffect(part.model, 'annotation', {
kind: note.kind,
index: index,
name: note.name,
value: part.value,
isCompound: note.isCompound,
compoundIndex: part.compoundIndex,
event: part.event,
customEvent: part.customEvent,
negate: part.negate
});
}
}
},

_addAnnotatedComputationEffect: function(note, index) {
var sig = note.signature;
_addAnnotatedComputationEffect: function(note, part, index) {
var sig = part.signature;
if (sig.static) {
this.__addAnnotatedComputationEffect('__static__', index, note, sig, null);
this.__addAnnotatedComputationEffect('__static__', index, note, part, null);
} else {
sig.args.forEach(function(arg) {
if (!arg.literal) {
this.__addAnnotatedComputationEffect(arg.model, index, note, sig, arg);
this.__addAnnotatedComputationEffect(arg.model, index, note, part, arg);
}
}, this);
}
},

__addAnnotatedComputationEffect: function(property, index, note, sig, trigger) {
__addAnnotatedComputationEffect: function(property, index, note, part, trigger) {
this._addPropertyEffect(property, 'annotatedComputation', {
index: index,
isCompound: note.isCompound,
compoundIndex: part.compoundIndex,
kind: note.kind,
property: note.name,
negate: note.negate,
method: sig.method,
args: sig.args,
name: note.name,
negate: part.negate,
method: part.signature.method,
args: part.signature.args,
trigger: trigger
});
},
Expand Down Expand Up @@ -253,7 +266,7 @@
var node = this._nodes[info.index];
// TODO(sorvell): ideally, the info object is normalized for easy
// lookup here.
var property = info.property || info.name || 'textContent';
var property = info.name || 'textContent';
// special processing for 'class' and 'className'; 'class' handled
// when attr is serialized.
if (info.kind == 'attribute') {
Expand Down

0 comments on commit 1026498

Please sign in to comment.