Browse files

* Replace .reset() method with .change(). It bears more semantic valu…

…e and frees up `reset` input type role and lets .reset() method be define for forms.

* Make microdata propagate up
* Allow role subclassing by an `id` attribute
* Make application of role ignore objects by unknown keys
* Make getConstructior() cache camelcased property lookups. Allows to tell unknown properties from known
* Make LSD.Roles lookup by a composite key
  • Loading branch information...
1 parent 7f7874f commit aff5b5cd959e1b71ae6f6745c7bbbe4c6d68d301 Yaroslaff Fedin committed Mar 24, 2012
View
16 Source/DOM/ChildNodes.js
@@ -34,25 +34,25 @@ LSD.ChildNodes.prototype.onSet = function(value, index, state, old, memo) {
var previous = this[index - 1] || null;
var next = this[index + 1] || null;
if (previous !== value && memo !== 'collapse') {
- if (previous && (memo !== 'splice' || (!state && !next))) previous.reset('nextSibling', state ? value : next, memo);
- if ((state || old === false)) value.reset('previousSibling', previous, memo);
+ if (previous && (memo !== 'splice' || (!state && !next))) previous.change('nextSibling', state ? value : next, memo);
+ if ((state || old === false)) value.change('previousSibling', previous, memo);
else if (value.previousSibling == previous) value.unset('previousSibling', previous, memo);
}
if (next !== value && memo !== 'collapse') {
- if (next && (memo !== 'splice' || (!state && !previous))) next.reset('previousSibling', state ? value : previous, memo);
- if ((state || old === false)) value.reset('nextSibling', next, memo);
+ if (next && (memo !== 'splice' || (!state && !previous))) next.change('previousSibling', state ? value : previous, memo);
+ if ((state || old === false)) value.change('nextSibling', next, memo);
else if (value.nextSibling == next) value.unset('nextSibling', next, memo);
}
if (this._elements !== false && value.nodeType === 1) {
for (var i = index, node; node = this[--i];) {
if (node === value) continue;
- if (state || old === false) node.reset('nextElementSibling', value, memo);
+ if (state || old === false) node.change('nextElementSibling', value, memo);
else if (node.nextElementSibling === value) node.unset('nextElementSibling', value, memo);
if (node.nodeType === 1) break;
}
for (var i = index, node; node = this[++i];) {
if (node === value) continue;
- if (state || old === false) node.reset('previousElementSibling', value, memo);
+ if (state || old === false) node.change('previousElementSibling', value, memo);
else if (node.previousElementSibling === value) node.unset('previousElementSibling', value, memo);
if (node.nodeType === 1) break;
}
@@ -63,12 +63,12 @@ LSD.ChildNodes.prototype.onSet = function(value, index, state, old, memo) {
}
if (this._parent) {
if (index === 0) {
- if (state) this._parent.reset('firstChild', value);
+ if (state) this._parent.change('firstChild', value);
else this._parent.unset('firstChild', this._parent.firstChild);
}
var last = this.length - +state;
if (index === last && memo !== 'collapse') {
- if (state || last) this._parent.reset('lastChild', state ? value : this[last - 1]);
+ if (state || last) this._parent.change('lastChild', state ? value : this[last - 1]);
else this._parent.unset('lastChild', this._parent.lastChild);
}
if (this._parent.onChildSet) this._parent.onChildSet.apply(this._parent, arguments);
View
55 Source/DOM/Element.js
@@ -73,10 +73,10 @@ LSD.Element.prototype.onChange = function(key, value, state, old, memo) {
if (!methods) {
compiled[key] = methods = {};
methods[definition[0]] = function(memo) {
- return this.reset(key, true, memo);
+ return this.change(key, true, memo);
};
methods[definition[1]] = function(memo) {
- return this.reset(key, false, memo);
+ return this.change(key, false, memo);
};
}
for (var method in methods) this._set(method, methods[method]);
@@ -109,23 +109,39 @@ LSD.Element.prototype.__properties = {
Role may be a string, to be used as "search terms" for role lookup.
LSD provides a handy mechanism of finding the right class for
- an element. It takes tag name, `type` and `kind` attributes into
+ an element. It takes tag name, `type`, `kind` and `id` attributes into
account. It tries to find the role by tag name, then in looks
- for a sub-role based on `type` attribute, and finally checks
+ for a sub-role based on `type` attribute, then it checks
if there's a customized sub-role by the name that goes in `kind`
- attribute. If it doesn't find the sub-role, it uses the generic
- role. It powers up form controls widget, where datepicker tries
+ attribute, and finally tries to find a subclass by `id` attribute.
+ If it doesn't find the sub-role, it uses matching role it could find.
+
+ It powers up form controls widget, where datepicker tries
to find `input.date` role. And if it's not there, it tries `input`
- which in terms of html is a text input. `input.datetime-local`
+ which in terms of html is a text input. `input.datetime-local`.
*/
role: function(value, old, memo) {
var roles = (this.document || LSD.Document.prototype).roles
if (!roles) return;
if (typeof value == 'string')
value = typeof roles[value] == 'undefined' ? roles._get(value) : roles[value];
- if (value) this.mix(value, null, memo, true, true, true);
+ if (value) {
+ var skip = value._skip;
+ for (var property in value) {
+ if (!value.hasOwnProperty(property) || (skip && skip[property])
+ || (typeof value[property] == 'object' && !this._properties[property])) continue;
+ this.set(property, value[property], memo, true);
+ }
+ }
if (typeof old == 'string') old = roles[old];
- if (old) this.mix(old, null, memo, false, true, true);
+ if (old) {
+ var skip = old._skip;
+ for (var property in old) {
+ if (!old.hasOwnProperty(property) || (skip && skip[property])
+ || (typeof old[property] == 'object' && !this._properties[property])) continue;
+ this.unset(property, old[property], memo, true);
+ }
+ }
},
type: function(value, old, memo) {
@@ -135,6 +151,10 @@ LSD.Element.prototype.__properties = {
kind: function(value, old, memo) {
this.setRoleBit(2, value);
},
+
+ id: function(value, old, memo) {
+ this.setRoleBit(3, value);
+ },
/*
Tag name is an element role category. Most of the categories have only
one role, but some have a family of sub-roles like `input` or `menu`.
@@ -445,7 +465,7 @@ LSD.Element.prototype.__properties = {
focused: function(value, old, memo) {
if (memo === this) return;
if (value) this.mix('parentNode.focused', value, memo || this, null, null, null, null, true);
- if (value && !memo && this.ownerDocument) this.ownerDocument.reset('activeElement', this, false);
+ if (value && !memo && this.ownerDocument) this.ownerDocument.change('activeElement', this, false);
if (old) this.mix('parentNode.focused', old, memo || this, false, null, null, null, true);
},
rendered: function(value, old) {
@@ -470,7 +490,7 @@ LSD.Element.prototype.__properties = {
if (memo !== false) for (var node = this, next, nodes, i = 0; node; node = next) {
for (next = node.firstChild || node.nextSibling; !next && (node = node.parentNode); next = node.nextSibling)
if (value) node.sourceLastIndex = index + i;
- if (next) next.reset('sourceIndex', index + ++i, false)
+ if (next) next.change('sourceIndex', index + ++i, false)
}
},
/*
@@ -486,10 +506,10 @@ LSD.Element.prototype.__properties = {
collections resorts.
*/
firstChild: function(value, old) {
- if (value) value.reset('sourceIndex', (this.sourceIndex || 0) + 1);
+ if (value) value.change('sourceIndex', (this.sourceIndex || 0) + 1);
},
previousSibling: function(value, old, memo) {
- if (value) this.reset('sourceIndex', (value.sourceLastIndex || value.sourceIndex || 0) + 1, memo);
+ if (value) this.change('sourceIndex', (value.sourceLastIndex || value.sourceIndex || 0) + 1, memo);
},
previousElementSibling: function(value, old) {
for (var i = 0, node, method; i < 2; i++) {
@@ -552,7 +572,7 @@ LSD.Element.prototype.__properties = {
date: Date,
value: function(value, old, memo) {
if (this.checked === true || typeof this.checked == 'undefined')
- this.reset('nodeValue', value);
+ this.change('nodeValue', value);
},
textContent: function(value, old, memo) {
for (var node = this; node = node.parentNode;) {
@@ -588,15 +608,18 @@ LSD.Element.prototype.__properties = {
microdata._shared = true;
this.set('nodeValue', microdata);
this.set('microdata', microdata);
+ this.mix('parentNode.microdata', microdata, 'itemscope', true, null, true, null, true);
if (this.itemprop && memo !== 'itemprop')
this.mix('parentNode.microdata.' + this.itemprop, value, 'itemscope', true, null, null, null, true);
}
if (old) {
this.unset('nodeValue', this.microdata);
this.unset('microdata', this.microdata);
+ this.mix('parentNode.microdata', value, 'itemscope', false, null, true, null, true);
if (this.itemprop && memo !== 'itemprop')
this.mix('parentNode.microdata.' + this.itemprop, old, 'itemscope', false, null, null, null, true);
}
+ return microdata;
},
itemprop: function(value, old, memo) {
if (value) {
@@ -667,7 +690,7 @@ LSD.Element.prototype.__initialize = function(/* options, element, selector, doc
LSD.Element.prototype.setRoleBit = function(key, value) {
var bits = (this.roleBits || (this.roleBits = {})), autorole = this.autorole;
bits[key] = value;
- for (var i = 0, role; i < 3; i++)
+ for (var i = 0, role; i < 4; i++)
if (bits[i]) role = (role ? role + '-' : '') + bits[i];
this.set('role', role)
if (autorole != null) this.unset('role', autorole)
@@ -698,7 +721,7 @@ LSD.Element.prototype.getAttributeNode = function(name) {
}
};
LSD.Element.prototype.setAttribute = function(name, value) {
- this.attributes.reset(name, value);
+ this.attributes.change(name, value);
return this;
};
LSD.Element.prototype.removeAttribute = function(name) {
View
8 Source/DOM/Range.js
@@ -37,13 +37,13 @@ LSD.Range = new LSD.Struct.Array({
});
// Sets the start position of a Range
LSD.Range.prototype.setStart = function(startContainer, startOffset) {
- this.reset('startContainer', startContainer);
- this.reset('startOffset', startOffset || 0);
+ this.change('startContainer', startContainer);
+ this.change('startOffset', startOffset || 0);
};
// Sets the end position of a Range
LSD.Range.prototype.setEnd = function(endContainer, endOffset) {
- this.reset('endContainer', endContainer);
- this.reset('endOffset', endOffset || 0);
+ this.change('endContainer', endContainer);
+ this.change('endOffset', endOffset || 0);
};
// Sets the start position of a Range relative to another Node
LSD.Range.prototype.setStartBefore = function(node) {
View
8 Source/Object/Object.Stack.js
@@ -132,16 +132,16 @@ LSD.Object.Stack.prototype = {
} else if (this[key] != null) this.unset(key, this[key], memo);
},
/*
- Reset method first sets the new value, and triggers all callbacks,
- and then removes old value from the stack without callbacks.
+ Change method first sets the new value, and triggers all callbacks,
+ and then removes old value from the stack without calling callbacks.
The method is useful to alter the state of the object in an
stack-based object and not pollute the stacks with changed
- values. When objects use .reset() to mutate the state of an object,
+ values. When objects use .change() to mutate the state of an object,
even in the case of the conflicting change, no values will be lost
in the stack, but only the top value on the stack of them will be used.
*/
- reset: function(key, value, memo) {
+ change: function(key, value, memo) {
var old = this[key];
this.set(key, value, memo);
if (typeof old != 'undefined') this.unset(key, old, memo)
View
4 Source/Object/Object.js
@@ -375,7 +375,7 @@ LSD.Object.prototype = {
on a respective property.
*/
switch (memo) {
- case 'set': case '_set': case 'unset': case '_unset': case 'reset': case 'mix':
+ case 'set': case '_set': case 'unset': case '_unset': case 'change': case 'mix':
this[memo](key, value, memo, prepend);
break;
default:
@@ -693,4 +693,4 @@ LSD.toObject = LSD.Object.toObject = LSD.Object.prototype.toObject;
['set', 'unset', 'watch', 'unwatch', 'get'].each(function(method) {
LSD.Object.prototype['_' + method] = LSD.Object.prototype[method];
});
-LSD.Object.prototype.reset = LSD.Object.prototype.set;
+LSD.Object.prototype.change = LSD.Object.prototype.set;
View
4 Source/Object/Struct.js
@@ -193,9 +193,9 @@ LSD.Struct.prototype = {
_getConstructor: function(key) {
if (this._properties) {
var prop = this._properties[key];
- if (prop == null) {
+ if (typeof prop == 'undefined') {
var Key = key.charAt(0).toUpperCase() + key.substring(1);
- var prop = this._properties[Key];
+ if (typeof (prop = this._properties[Key]) != 'undefined') this._properties[key] = prop;
}
}
if (prop == null && this.__properties) prop = this.__properties[key];
View
2 Source/Properties/Attributes.js
@@ -49,7 +49,7 @@ LSD.Properties.Attributes.prototype.onChange = function(key, value, state, old,
if (vdef) this._parent.variables[state ? 'set' : 'unset'](property, value);
if (odef) this._parent.variables.unset(property, old);
}
- if (this._parent._properties[key]) {
+ if (this._parent.__properties[key]) {
if (vdef) this._parent.set(key, value);
if (odef) this._parent.unset(key, old);
}
View
2 Source/Properties/Elements.js
@@ -101,7 +101,7 @@ LSD.Properties.Elements.prototype._identify = function(call, key, value, old) {
if (old) old.unwatch('nodeValue', object);
};
LSD.Properties.Elements.prototype._observe = function(call, key, value, old) {
- call.callback.values.reset(this.attributes.name, value);
+ call.callback.values.change(this.attributes.name, value);
};
LSD.Properties.Elements.prototype._get = LSD.Properties.Fields.prototype.get;
LSD.Properties.Elements.prototype._set = LSD.Properties.Fields.prototype.set;
View
14 Source/Properties/Resource.js
@@ -43,18 +43,18 @@ LSD.Resource = new LSD.Struct.Array({
urls: Object,
name: '_name',
_name: function(value, old) {
- this.reset('path', this.prefix ? value ? this.prefix + '/' + value : this.prefix : value || '');
- this.reset('singular', value ? value.singularize() : value);
- this.reset('foreign_key', this.singular + '_id')
+ this.change('path', this.prefix ? value ? this.prefix + '/' + value : this.prefix : value || '');
+ this.change('singular', value ? value.singularize() : value);
+ this.change('foreign_key', this.singular + '_id')
},
prefix: function(value, old) {
- this.reset('path', this._name ? value ? value + '/' + this._name : this._name : value || '');
+ this.change('path', this._name ? value ? value + '/' + this._name : this._name : value || '');
},
path: function(value, old) {
- this.reset('url', this.domain ? value ? this.domain + '/' + value : this.domain : value || '');
+ this.change('url', this.domain ? value ? this.domain + '/' + value : this.domain : value || '');
},
domain: function(value, old) {
- this.reset('url', value + (this.path ? '/' + this.path : ''))
+ this.change('url', value + (this.path ? '/' + this.path : ''))
}
});
LSD.Properties.Resource = LSD.Resource;
@@ -322,7 +322,7 @@ LSD.Resource.prototype.validate = function(params) {
};
LSD.Resource.attributes = {
_id: function(value) {
- this.reset('url', this.constructor.url + '/' + value);
+ this.change('url', this.constructor.url + '/' + value);
},
'id': '_id'
};
View
73 Source/Properties/Roles.js
@@ -1,10 +1,42 @@
-LSD.Properties.Roles = LSD.Struct.Stack({
- get: function(name) {
+/*
+---
+
+script: Roles.js
+
+description: A library of widget presets
+
+license: Public domain (http://unlicense.org).
+
+requires:
+ - LSD.Document
+ - LSD.Properties
+
+provides:
+ - LSD.Roles
+
+...
+*/
+
+/*
+ Roles object allows finding specific roles by a composite
+ string key where key bits are separated with dashes.
+*/
+LSD.Properties.Roles = LSD.Roles = LSD.Struct.Stack({
+ get: function(key) {
+ for (var i, previous = 0, role = this, obj; i !== -1;) {
+ i = key.indexOf('-', i != null ? i + 1 : i);
+ bit = key.substring(previous, i > -1 ? i : undefined);
+ obj = role[bit];
+ if (i == null || obj == null || typeof obj !== 'object') break;
+ else role = obj;
+ previous = i + 1;
+ }
+ if (role === this) role = null;
+ this[key] = role;
+ return role;
}
})
-LSD.Roles = LSD.Properties.Roles;
-
-LSD.Document.prototype.mix('roles', {
+LSD.Document.prototype.set('roles', new LSD.Roles({
input: {
localName: 'input',
checkbox: {
@@ -28,7 +60,7 @@ LSD.Document.prototype.mix('roles', {
},
date: {
- 'button.opener[onclick=open]': 'Open date picker'
+ 'button.opener[onclick=open]': 'Open date picker',
'if &[open]': {
'::dialog': {
'button[onclick=increment]': 'Previous month',
@@ -41,7 +73,7 @@ LSD.Document.prototype.mix('roles', {
},
datetime: {
local: {
-
+ timezone: 'local'
}
},
file: {
@@ -56,26 +88,26 @@ LSD.Document.prototype.mix('roles', {
'if &[multiple]': "Select files to upload",
'else': "Select file to upload"
}
- }
+ },
list: {
- extends: 'menu-list'
+ base: 'menu-list'
},
item: {
- extends: 'li'
+ role: 'li'
},
message: {
- extends: 'li'
+ role: 'li'
}
},
submit: {
custom: {
- extends: 'button'
+ role: 'button'
},
onclick: 'submit'
},
reset: {
custom: {
- extends: 'button'
+ role: 'button'
},
onclick: 'reset'
},
@@ -86,8 +118,8 @@ LSD.Document.prototype.mix('roles', {
},
thumb: {
- extends: 'button'
- }
+ archrole: 'button'
+ },
slider: true
}
},
@@ -98,10 +130,10 @@ LSD.Document.prototype.mix('roles', {
select: {
collection: 'options',
option: {
- extends: 'option'
+ role: 'option'
},
menu: {
- extends: 'menu-context'
+ role: 'menu-context'
}
},
@@ -129,7 +161,8 @@ LSD.Document.prototype.mix('roles', {
},
a: {
- request: 'href'
+ request: 'href',
+ nodeValueAttribute: 'href'
},
link: {
@@ -152,7 +185,7 @@ LSD.Document.prototype.mix('roles', {
},
item: {
- extends: 'li'
+ role: 'li'
}
},
@@ -182,4 +215,4 @@ LSD.Document.prototype.mix('roles', {
submit: 'input[type=submit]',
calendar: 'table[type=calendar]',
clock: 'table[type=clock]',
-});
+}));
View
18 Source/Script/Script.js
@@ -160,7 +160,7 @@ LSD.Script.Struct = new LSD.Struct({
if (value) this.set('attached', true);
},
placeholder: function(value, old) {
- if (this.placeheld) this.reset('value', value);
+ if (this.placeheld) this.change('value', value);
},
value: function(value, old, memo) {
if (this.frozen) return;
@@ -215,7 +215,7 @@ LSD.Script.Struct = new LSD.Struct({
attached: function(value, old) {
if (!value && typeof this.value != 'undefined') this.unset('value', this.value);
if (this.yielded || this.type == 'function') {
- this.reset('executed', !!value);
+ this.change('executed', !!value);
} else if (this.yields) {
for (var property in this.yields) {
var yield = this.yields[property];
@@ -228,11 +228,11 @@ LSD.Script.Struct = new LSD.Struct({
if (typeof this.scope != 'function') {
if (!this.setter) var self = this, setter = this.setter = function(value, old) {
if (typeof value == 'undefined') return self.unset('value', old);
- else return self.reset('value', value)
+ else return self.change('value', value)
};
(this.scope.variables || this.scope)[value ? 'watch' : 'unwatch'](this.name || this.input, this.setter);
} else this.scope(this.input, this, this.scope, !!value);
- if (typeof this.value == 'undefined' && !this.input) this.reset('executed', value);
+ if (typeof this.value == 'undefined' && !this.input) this.change('executed', value);
}
},
/*
@@ -362,15 +362,15 @@ LSD.Script.Struct = new LSD.Struct({
}
}
if (args == null || !args.push) {
- this.reset('value', args)
+ this.change('value', args)
return args;
}
if (name) {
var method = this.lookup(name, args[0]);
if (method === true) val = args[0][name].apply(args[0], Array.prototype.slice.call(args, 1));
else if (method) val = method.apply(this, args);
} else val = args[0];
- this.reset('value', val, memo);
+ this.change('value', val, memo);
},
/*
@@ -552,7 +552,7 @@ LSD.Script.prototype.callback = function(value, old) {
switch (typeof object) {
case 'string':
if (typeof value == 'undefined' && typeof old != 'undefined') this.scope.unset(object, old)
- else this.scope[typeof old != 'undefined' ? 'reset' : 'set'](object, value);
+ else this.scope[typeof old != 'undefined' ? 'change' : 'set'](object, value);
break;
case 'function':
object(value);
@@ -607,12 +607,12 @@ LSD.Script.prototype._regexp = /^[a-zA-Z0-9-_.]+$/;
LSD.Script.compile = LSD.Script.prototype.compile;
LSD.Script.toJS = LSD.Script.prototype.toJS;
LSD.Script.prototype.onSuccess = function(value) {
- this.reset('value', value);
+ this.change('value', value);
};
LSD.Script.prototype.onFailure = function(value) {
var object = new Boolean(false);
object.failure = value;
- this.reset('value', object);
+ this.change('value', object);
};
LSD.Script.prototype.yield = function(keyword, args, callback, index, old, memo) {
if (args == null) args = [];
View
1 package.yml
@@ -34,6 +34,7 @@ sources:
- Source/Properties/Events.js
- Source/Properties/Resource.js
- Source/Properties/Proxies.js
+ - Source/Properties/Roles.js
- Source/Script/Script.js
- Source/Script/Parser.js
- Source/Script/Helpers.js

0 comments on commit aff5b5c

Please sign in to comment.