uiProperty

This commit is contained in:
Tony Garnock-Jones 2016-05-15 14:58:13 -04:00
parent b92c439f07
commit 152a76af5e
1 changed files with 77 additions and 39 deletions

View File

@ -63,6 +63,9 @@ var uiFragmentExists = Struct.makeConstructor('ui-fragment-exists', ['fragmentId
// already present. (See the implementation for details.) // already present. (See the implementation for details.)
var uiAttribute = Struct.makeConstructor('ui-attribute', ['selector', 'attribute', 'value']); var uiAttribute = Struct.makeConstructor('ui-attribute', ['selector', 'attribute', 'value']);
// Assertion. Similar to uiAttribute, but for properties of DOM nodes.
var uiProperty = Struct.makeConstructor('ui-property', ['selector', 'property', 'value']);
// Messages. // Messages.
// NOTE: These do not treat "class" specially! // NOTE: These do not treat "class" specially!
var setAttribute = Struct.makeConstructor('set-ui-attribute', ['selector', 'attribute', 'value']); var setAttribute = Struct.makeConstructor('set-ui-attribute', ['selector', 'attribute', 'value']);
@ -120,7 +123,16 @@ function spawnUIDriver(options) {
new DemandMatcher([uiAttribute(_$('selector'), _$('attribute'), _$('value'))], new DemandMatcher([uiAttribute(_$('selector'), _$('attribute'), _$('value'))],
[Patch.advertise(uiAttribute(_$('selector'), _$('attribute'), _$('value')))], [Patch.advertise(uiAttribute(_$('selector'), _$('attribute'), _$('value')))],
function (c) { function (c) {
Dataspace.spawn(new UIAttribute(c.selector, c.attribute, c.value)); Dataspace.spawn(new UIAttribute(
c.selector, c.attribute, c.value, 'attribute'));
}));
Dataspace.spawn(
new DemandMatcher([uiProperty(_$('selector'), _$('property'), _$('value'))],
[Patch.advertise(uiProperty(_$('selector'), _$('property'), _$('value')))],
function (c) {
Dataspace.spawn(new UIAttribute(
c.selector, c.property, c.value, 'property'));
})); }));
Dataspace.spawn(new AttributeUpdater()); Dataspace.spawn(new AttributeUpdater());
@ -430,24 +442,31 @@ UIFragment.prototype.handleDomEvent = function (c, e) {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
function UIAttribute(selector, attribute, value) { function UIAttribute(selector, key, value, kind) {
if (['attribute', 'property'].indexOf(kind) === -1) {
throw new Error("UIAttribute: kind must be 'attribute' or 'property'; got " + kind);
}
this.selector = selector; this.selector = selector;
this.attribute = attribute; this.key = key;
this.value = value; this.value = value;
this.kind = kind;
this.savedValues = []; this.savedValues = [];
// ^ Array of {node: DOMNode, value: (U Null String)}, when attribute !== 'class'. // ^ Array of {node: DOMNode, value: (U Null String)},
// ^ Array of {node: DOMNode}, when attribute === 'class'. // when attribute !== 'class' or kind !== 'attribute'.
// ^ Array of {node: DOMNode},
// when attribute === 'class' and kind === 'attribute'.
} }
UIAttribute.prototype.boot = function () { UIAttribute.prototype.boot = function () {
var a = uiAttribute(this.selector, this.attribute, this.value); var a = ((this.kind === 'attribute') ? uiAttribute : uiProperty)(this.selector, this.key, this.value);
this.install(); this.install();
return Patch.sub(a).andThen(Patch.pub(a)); return Patch.sub(a).andThen(Patch.pub(a));
}; };
UIAttribute.prototype.trapexit = function () { UIAttribute.prototype.trapexit = function () {
console.log('UIAttribute trapexit running', this.selector, this.attribute, this.value); console.log('UIAttribute trapexit running', this.selector, this.key, this.value, this.kind);
this.restoreSavedValues(); this.restoreSavedValues();
}; };
@ -458,20 +477,27 @@ function splitClassValue(v) {
UIAttribute.prototype.install = function () { UIAttribute.prototype.install = function () {
var self = this; var self = this;
var nodes = Array.prototype.slice.call(document.querySelectorAll(self.selector)); selectorMatch(document, self.selector).forEach(function (node) {
switch (self.kind) {
nodes.forEach(function (node) { case 'attribute':
if (self.attribute === 'class') { if (self.key === 'class') {
// Deliberately maintains duplicates, so we don't interfere with // Deliberately maintains duplicates, so we don't interfere
// potential other UIAttribute instances on the same objects for // with potential other UIAttribute instances on the same
// the same attribute. See also restoreSavedValues. // objects for the same attribute. See also
var existing = splitClassValue(node.getAttribute('class')); // restoreSavedValues.
var toAdd = splitClassValue(self.value); var existing = splitClassValue(node.getAttribute('class'));
self.savedValues.push({node: node}); var toAdd = splitClassValue(self.value);
node.setAttribute('class', existing.concat(toAdd).join(' ')); self.savedValues.push({node: node});
} else { node.setAttribute('class', existing.concat(toAdd).join(' '));
self.savedValues.push({node: node, value: node.getAttribute(self.attribute)}); } else {
node.setAttribute(self.attribute, self.value); self.savedValues.push({node: node, value: node.getAttribute(self.key)});
node.setAttribute(self.key, self.value);
}
break;
case 'property':
self.savedValues.push({node: node, value: node[self.key]});
node[self.key] = self.value;
break;
} }
}); });
}; };
@ -479,24 +505,35 @@ UIAttribute.prototype.install = function () {
UIAttribute.prototype.restoreSavedValues = function () { UIAttribute.prototype.restoreSavedValues = function () {
var self = this; var self = this;
self.savedValues.forEach(function (entry) { self.savedValues.forEach(function (entry) {
if (self.attribute === 'class') { switch (self.kind) {
var existing = splitClassValue(entry.node.getAttribute('class')); case 'attribute':
var toRemove = splitClassValue(self.value); if (self.key === 'class') {
toRemove.forEach(function (v) { var existing = splitClassValue(entry.node.getAttribute('class'));
var i = existing.indexOf(v); var toRemove = splitClassValue(self.value);
if (i !== -1) { existing.splice(i, 1); } toRemove.forEach(function (v) {
}); var i = existing.indexOf(v);
if (existing.length === 0) { if (i !== -1) { existing.splice(i, 1); }
entry.node.removeAttribute('class'); });
} else { if (existing.length === 0) {
entry.node.setAttribute('class', existing.join(' ')); entry.node.removeAttribute('class');
} } else {
} else { entry.node.setAttribute('class', existing.join(' '));
if (entry.value === null) { }
entry.node.removeAttribute(self.attribute); } else {
} else { if (entry.value === null) {
entry.node.setAttribute(self.attribute, entry.value); entry.node.removeAttribute(self.key);
} } else {
entry.node.setAttribute(self.key, entry.value);
}
}
break;
case 'property':
if (typeof entry.value === 'undefined') {
delete entry.node[self.key];
} else {
entry.node[self.key] = entry.value;
}
break;
} }
}); });
self.savedValues = []; self.savedValues = [];
@ -683,6 +720,7 @@ module.exports.uiEvent = uiEvent;
module.exports.uiFragment = uiFragment; module.exports.uiFragment = uiFragment;
module.exports.uiFragmentExists = uiFragmentExists; module.exports.uiFragmentExists = uiFragmentExists;
module.exports.uiAttribute = uiAttribute; module.exports.uiAttribute = uiAttribute;
module.exports.uiProperty = uiProperty;
module.exports.setAttribute = setAttribute; module.exports.setAttribute = setAttribute;
module.exports.removeAttribute = removeAttribute; module.exports.removeAttribute = removeAttribute;
module.exports.setProperty = setProperty; module.exports.setProperty = setProperty;