diff --git a/src/features/micro/attributes.html b/src/features/micro/attributes.html index c8206454..98550331 100644 --- a/src/features/micro/attributes.html +++ b/src/features/micro/attributes.html @@ -127,29 +127,32 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }, - // Convert value to string based on type - // Return undefined to indicate attribute should be removed - serialize: function(name, value, type) { - switch (type) { - case Object: - case Array: - return value ? JSON.stringify(value) : undefined; - - case Boolean: + serialize: function(value) { + switch (typeof value) { + case 'boolean': return value ? '' : undefined; - + case 'object': + if (value instanceof Date) { + return value; + } else { + return value ? JSON.stringify(value) : undefined; + } default: return value != null ? value : undefined; } }, reflectPropertyToAttribute: function(name) { - var type = this.getPublishedPropertyType(name); - var value = this.serialize(name, this[name], type); + this.serializeValueToAttribute(this[name], name); + }, + + serializeValueToAttribute: function(value, attribute, node) { + node = node || this; + value = this.serialize(value); if (value !== undefined) { - this.setAttribute(name, value); + node.setAttribute(attribute, value); } else { - this.removeAttribute(name); + node.removeAttribute(attribute); } } diff --git a/src/features/standard/notify-path.html b/src/features/standard/notify-path.html index a302048c..087fa3bd 100644 --- a/src/features/standard/notify-path.html +++ b/src/features/standard/notify-path.html @@ -97,9 +97,8 @@ using('Base', function(Base) { if (parts.length > 1) { var last = parts.pop(); var prop = this; - while (parts.length && (prop=prop[parts.shift()])) { - // TODO(sjmiles): can't ever get here if !prop because of the - // while conditional + while (parts.length) { + prop = prop[parts.shift()]; if (!prop) { return; } @@ -118,7 +117,8 @@ using('Base', function(Base) { var parts = path.split('.'); var last = parts.pop(); var prop = this; - while (parts.length && (prop=prop[parts.shift()])) { + while (parts.length) { + prop = prop[parts.shift()]; if (!prop) { return; } @@ -146,10 +146,14 @@ using('Base', function(Base) { var effect = x.effect; var n = this._nodeForBinding(effect); if (n) { - if (effect.value === path) { - n[effect.name || 'textContent'] = effect.negate ? !value : value; - } else if (effect.value.indexOf(path + '.') == 0) { - n[effect.name || 'textContent'] = this.getPath(effect.value); + if (effect.value === path || effect.value.indexOf(path + '.') === 0) { + var v = (effect.value === path) ? value : this.getPath(effect.value); + v = effect.negate ? !v : v; + if (effect.kind == 'property') { + n[effect.name || 'textContent'] = v; + } else { // attribute + this.serializeValueToAttribute(v, effect.name, n); + } // path == item.stuff.count // value == item.stuff // name == zizz diff --git a/src/lib/annotations/annotations.html b/src/lib/annotations/annotations.html index 56ebc89e..5b38f6c7 100644 --- a/src/lib/annotations/annotations.html +++ b/src/lib/annotations/annotations.html @@ -174,22 +174,29 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }, _parseNodeAttributeAnnotation: function(node, n, v) { - var mode = '', escape = v.slice(0, 2); + 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); - } - var not = false; - if (v[0] == '!') { - v = v.substring(1); - not = true; - } - if (mode) { + // Negate + var not = false; + if (v[0] == '!') { + v = v.substring(1); + not = true; + } + // Attribute or property + var attr = false; + if (n[n.length-1] == '$') { + name = n.slice(0, -1); + attr = true; + } + // Remove annotation node.removeAttribute(n); return { - kind: 'property', + kind: attr ? 'attribute' : 'property', mode: mode, - name: n, + name: name, value: v, negate: not }; diff --git a/src/lib/bind/bind-effects.html b/src/lib/bind/bind-effects.html index d84e2e4a..6656f9d8 100644 --- a/src/lib/bind/bind-effects.html +++ b/src/lib/bind/bind-effects.html @@ -27,7 +27,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }; Bind._shouldAddListener = function(info) { - return info.name && info.mode === '{' && !info.negate; + return info.name && + info.mode === '{' && + !info.negate && + info.kind != 'attribute'; }; Bind.addBuilders({ @@ -125,8 +128,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ; value = 'val'; } - return setData + 'this._nodes[' + info.index + '].' + property + ' = ' - + (info.negate ? '!' : '') + value + ';'; + value = (info.negate ? '!' : '') + value; + var node = 'this._nodes[' + info.index + ']'; + if (info.kind == 'property') { + return setData + node + '.' + property + ' = ' + value + ';'; + } else { + return setData + 'this.serializeValueToAttribute(' + value + ',' + + '\'' + property + '\',' + node + ');'; + } } }); diff --git a/test/unit/bind-elements.html b/test/unit/bind-elements.html index e2afa607..9fba1023 100644 --- a/test/unit/bind-elements.html +++ b/test/unit/bind-elements.html @@ -1,5 +1,5 @@ diff --git a/test/unit/notify-path-elements.html b/test/unit/notify-path-elements.html index c765ba70..9eb7396b 100644 --- a/test/unit/notify-path-elements.html +++ b/test/unit/notify-path-elements.html @@ -11,9 +11,9 @@ diff --git a/test/unit/notify-path.html b/test/unit/notify-path.html index 476bf785..658e13dd 100644 --- a/test/unit/notify-path.html +++ b/test/unit/notify-path.html @@ -64,6 +64,11 @@ suite('basic path bindings', function() { assert.equal(el.$.forward.$.compose.obj, nested.obj); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from basic element property change', function() { @@ -94,6 +99,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from composed element property change', function() { @@ -124,6 +134,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from forward\'s composed element property change', function() { @@ -154,6 +169,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from setPath in top element', function() { @@ -184,6 +204,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from setPath in composed element', function() { @@ -214,6 +239,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from setPath in forward element', function() { @@ -244,6 +274,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from setPath in forward\'s composed element', function() { @@ -274,6 +309,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from object change in compose element', function() { @@ -308,6 +348,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from object change in forward element', function() { @@ -342,6 +387,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('notification from object change in forward\'s compose element', function() { @@ -376,6 +426,11 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic2.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, 42); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, 42); + assert.equal(el.$.basic.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.compose.$.basic2.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic1.getAttribute('attrvalue'), '42'); + assert.equal(el.$.forward.$.compose.$.basic2.getAttribute('attrvalue'), '42'); }); test('negation', function() { @@ -391,6 +446,14 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic3.notifyingvalue, true); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, false); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, false); + assert.equal(el.$.forward.$.compose.$.basic3.notifyingvalue, true); + assert.equal(el.$.basic.hasAttribute('attrvalue'), false); + assert.equal(el.$.compose.$.basic1.hasAttribute('attrvalue'), false); + assert.equal(el.$.compose.$.basic2.hasAttribute('attrvalue'), false); + assert.equal(el.$.compose.$.basic3.hasAttribute('attrvalue'), true); + assert.equal(el.$.forward.$.compose.$.basic1.hasAttribute('attrvalue'), false); + assert.equal(el.$.forward.$.compose.$.basic2.hasAttribute('attrvalue'), false); + assert.equal(el.$.forward.$.compose.$.basic3.hasAttribute('attrvalue'), true); el.$.basic.notifyingvalue = true; assert.equal(el.$.basic.notifyingvalue, true); @@ -399,6 +462,14 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic3.notifyingvalue, false); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, true); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, true); + assert.equal(el.$.forward.$.compose.$.basic3.notifyingvalue, false); + assert.equal(el.$.basic.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic1.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic2.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic3.hasAttribute('attrvalue'), false); + assert.equal(el.$.forward.$.compose.$.basic1.hasAttribute('attrvalue'), true); + assert.equal(el.$.forward.$.compose.$.basic2.hasAttribute('attrvalue'), true); + assert.equal(el.$.forward.$.compose.$.basic3.hasAttribute('attrvalue'), false); el.$.forward.$.compose.$.basic1.notifyingvalue = false; assert.equal(el.$.basic.notifyingvalue, false); @@ -407,6 +478,14 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic3.notifyingvalue, true); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, false); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, false); + assert.equal(el.$.forward.$.compose.$.basic3.notifyingvalue, true); + assert.equal(el.$.basic.hasAttribute('attrvalue'), false); + assert.equal(el.$.compose.$.basic1.hasAttribute('attrvalue'), false); + assert.equal(el.$.compose.$.basic2.hasAttribute('attrvalue'), false); + assert.equal(el.$.compose.$.basic3.hasAttribute('attrvalue'), true); + assert.equal(el.$.forward.$.compose.$.basic1.hasAttribute('attrvalue'), false); + assert.equal(el.$.forward.$.compose.$.basic2.hasAttribute('attrvalue'), false); + assert.equal(el.$.forward.$.compose.$.basic3.hasAttribute('attrvalue'), true); el.setPath('nested.obj.value', true); assert.equal(el.$.basic.notifyingvalue, true); @@ -415,6 +494,14 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic3.notifyingvalue, false); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, true); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, true); + assert.equal(el.$.forward.$.compose.$.basic3.notifyingvalue, false); + assert.equal(el.$.basic.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic1.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic2.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic3.hasAttribute('attrvalue'), false); + assert.equal(el.$.forward.$.compose.$.basic1.hasAttribute('attrvalue'), true); + assert.equal(el.$.forward.$.compose.$.basic2.hasAttribute('attrvalue'), true); + assert.equal(el.$.forward.$.compose.$.basic3.hasAttribute('attrvalue'), false); // no two way binding through negation el.$.compose.$.basic3.notifyingvalue = true; @@ -424,6 +511,14 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic3.notifyingvalue, true); assert.equal(el.$.forward.$.compose.$.basic1.notifyingvalue, true); assert.equal(el.$.forward.$.compose.$.basic2.notifyingvalue, true); + assert.equal(el.$.forward.$.compose.$.basic3.notifyingvalue, false); + assert.equal(el.$.basic.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic1.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic2.hasAttribute('attrvalue'), true); + assert.equal(el.$.compose.$.basic3.hasAttribute('attrvalue'), false); + assert.equal(el.$.forward.$.compose.$.basic1.hasAttribute('attrvalue'), true); + assert.equal(el.$.forward.$.compose.$.basic2.hasAttribute('attrvalue'), true); + assert.equal(el.$.forward.$.compose.$.basic3.hasAttribute('attrvalue'), false); }); @@ -445,6 +540,47 @@ suite('basic path bindings', function() { assert.equal(el.$.compose.$.basic1.othervalue, 98); }); + test('getPath', function() { + el.simple = 11; + el.nested = { + again: { + again: { + wayOverThere: 99 + }, + there: 55 + }, + here: 42 + }; + assert.equal(el.getPath('simple'), 11); + assert.equal(el.getPath('nested'), el.nested); + assert.equal(el.getPath('nested.here'), 42); + assert.equal(el.getPath('nested.again'), el.nested.again); + assert.equal(el.getPath('nested.again.there'), 55); + assert.equal(el.getPath('nested.again.again'), el.nested.again.again); + assert.equal(el.getPath('nested.again.again.wayOverThere'), 99); + }); + + test('setPath', function() { + el.setPath('simple', 11); + el.setPath('nested', {}); + el.setPath('nested.here', 42); + el.setPath('nested.again', {}); + el.setPath('nested.again.there', 55); + el.setPath('nested.again.again', {}); + el.setPath('nested.again.again.wayOverThere', 99); + assert.equal(el.simple, 11); + assert.equal(el.getPath('simple'), 11); + assert.equal(el.getPath('nested'), el.nested); + assert.equal(el.nested.here, 42); + assert.equal(el.getPath('nested.here'), 42); + assert.equal(el.getPath('nested.again'), el.nested.again); + assert.equal(el.nested.again.there, 55); + assert.equal(el.getPath('nested.again.there'), 55); + assert.equal(el.getPath('nested.again.again'), el.nested.again.again); + assert.equal(el.nested.again.again.wayOverThere, 99); + assert.equal(el.getPath('nested.again.again.wayOverThere'), 99); + }); + });