Add attribute binding. attribute$="{{property}}"

This commit is contained in:
Kevin Schaaf
2015-01-28 13:02:43 -08:00
parent dccfa8643e
commit 3f95fe3ef2
8 changed files with 269 additions and 40 deletions

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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
};

View File

@@ -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 + ');';
}
}
});

View File

@@ -1,5 +1,5 @@
<template>
<div id="boundChild" value="{{value}}" negvalue="{{!bool}}" computedvalue="{{computedvalue}}"></div>
<div id="boundChild" value="{{value}}" negvalue="{{!bool}}" computedvalue="{{computedvalue}}" attrvalue$="{{attrvalue}}"></div>
</template>
<script>
Polymer({

View File

@@ -375,6 +375,76 @@ suite('reflection to attribute', function() {
});
suite('binding to attribute', function() {
var el;
beforeEach(function() {
el = document.createElement('x-basic');
document.body.appendChild(el);
});
afterEach(function() {
document.body.removeChild(el);
});
test('bind object to attribute', function() {
el.attrvalue = {foo: 'bar', array: [1, '2', {3:3}]};
assert.equal(el.$.boundChild.getAttribute('attrvalue'),
'{"foo":"bar","array":[1,"2",{"3":3}]}');
el.attrvalue = null;
assert(!el.$.boundChild.hasAttribute('attrvalue'));
});
test('bind array to attribute', function() {
el.attrvalue = [1, '2', {3:3}, {'four': 'four'}];
assert.equal(el.$.boundChild.getAttribute('attrvalue'),
'[1,"2",{"3":3},{"four":"four"}]');
el.attrvalue = null;
assert(!el.$.boundChild.hasAttribute('attrvalue'));
});
test('bind string to attribute', function() {
el.attrvalue = '"polymer is grrrrreat, ain\'t it?"';
assert.equal(el.$.boundChild.getAttribute('attrvalue'),
'"polymer is grrrrreat, ain\'t it?"');
el.attrvalue = '';
assert.equal(el.$.boundChild.getAttribute('attrvalue'), '');
el.attrvalue = null;
assert(!el.$.boundChild.hasAttribute('attrvalue'));
});
test('bind number to attribute', function() {
el.attrvalue = 765;
assert.equal(el.$.boundChild.getAttribute('attrvalue'), '765');
el.attrvalue = 765.4321;
assert.equal(el.$.boundChild.getAttribute('attrvalue'), '765.4321');
el.attrvalue = null;
assert(!el.$.boundChild.hasAttribute('attrvalue'));
});
test('bind boolean to attribute', function() {
el.attrvalue = true;
assert(el.$.boundChild.hasAttribute('attrvalue'));
assert.equal(el.$.boundChild.getAttribute('attrvalue'), '');
el.attrvalue = false;
assert(!el.$.boundChild.hasAttribute('attrvalue'));
el.attrvalue = true;
el.attrvalue = null;
assert(!el.$.boundChild.hasAttribute('attrvalue'));
});
test('bind date to attribute', function() {
el.attrvalue = new Date('Fri Jan 23 2015 17:40:29 GMT-0800 (PST)');
assert(el.$.boundChild.hasAttribute('attrvalue'));
assert.equal(Date.parse(el.$.boundChild.getAttribute('attrvalue')),
el.attrvalue.getTime());
el.attrvalue = null;
assert(!el.$.boundChild.hasAttribute('attrvalue'));
});
});
</script>

View File

@@ -11,9 +11,9 @@
</script>
<template>
<x-basic id="basic1" notifyingvalue="{{obj.value}}" othervalue="{{obj.value2}}"></x-basic>
<x-basic id="basic2" notifyingvalue="{{obj.value}}"></x-basic>
<x-basic id="basic3" notifyingvalue="{{!obj.value}}"></x-basic>
<x-basic id="basic1" notifyingvalue="{{obj.value}}" attrvalue$="{{obj.value}}" othervalue="{{obj.value2}}"></x-basic>
<x-basic id="basic2" notifyingvalue="{{obj.value}}" attrvalue$="{{obj.value}}"></x-basic>
<x-basic id="basic3" notifyingvalue="{{!obj.value}}" attrvalue$="{{!obj.value}}"></x-basic>
</template>
<script>
Polymer({
@@ -55,7 +55,7 @@
</script>
<template>
<x-basic id="basic" notifyingvalue="{{nested.obj.value}}"></x-basic>
<x-basic id="basic" notifyingvalue="{{nested.obj.value}}" attrvalue$="{{nested.obj.value}}"></x-basic>
<x-compose id="compose" obj="{{nested.obj}}"></x-compose>
<x-forward id="forward" obj="{{nested.obj}}"></x-forward>
</template>

View File

@@ -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);
});
});
</script>