186 lines
4.8 KiB
JavaScript
186 lines
4.8 KiB
JavaScript
|
if(!dojo._hasResource["dojo.behavior"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
||
|
dojo._hasResource["dojo.behavior"] = true;
|
||
|
dojo.provide("dojo.behavior");
|
||
|
|
||
|
dojo.behavior = new function(){
|
||
|
function arrIn(obj, name){
|
||
|
if(!obj[name]){ obj[name] = []; }
|
||
|
return obj[name];
|
||
|
}
|
||
|
|
||
|
var _inc = 0;
|
||
|
|
||
|
function forIn(obj, scope, func){
|
||
|
var tmpObj = {};
|
||
|
for(var x in obj){
|
||
|
if(typeof tmpObj[x] == "undefined"){
|
||
|
if(!func){
|
||
|
scope(obj[x], x);
|
||
|
}else{
|
||
|
func.call(scope, obj[x], x);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// FIXME: need a better test so we don't exclude nightly Safari's!
|
||
|
this._behaviors = {};
|
||
|
this.add = function(behaviorObj){
|
||
|
// summary:
|
||
|
// add the specified behavior to the list of behaviors which will
|
||
|
// be applied the next time apply() is called. Calls to add() for
|
||
|
// an already existing behavior do not replace the previous rules,
|
||
|
// but are instead additive. New nodes which match the rule will
|
||
|
// have all add()-ed behaviors applied to them when matched.
|
||
|
//
|
||
|
// description:
|
||
|
// behavior objects are specified in the following format(s):
|
||
|
//
|
||
|
// {
|
||
|
// "#id": {
|
||
|
// "found": function(element){
|
||
|
// // ...
|
||
|
// },
|
||
|
//
|
||
|
// "onblah": {targetObj: foo, targetFunc: "bar"},
|
||
|
//
|
||
|
// "onblarg": "/foo/bar/baz/blarg",
|
||
|
//
|
||
|
// "onevent": function(evt){
|
||
|
// },
|
||
|
//
|
||
|
// "onotherevent: function(evt){
|
||
|
// // ...
|
||
|
// }
|
||
|
// },
|
||
|
//
|
||
|
// "#id2": {
|
||
|
// // ...
|
||
|
// },
|
||
|
//
|
||
|
// "#id3": function(element){
|
||
|
// // ...
|
||
|
// },
|
||
|
//
|
||
|
// // publish the match on a topic
|
||
|
// "#id4": "/found/topic/name",
|
||
|
//
|
||
|
// // match all direct descendants
|
||
|
// "#id4 > *": function(element){
|
||
|
// // ...
|
||
|
// },
|
||
|
//
|
||
|
// // match the first child node that's an element
|
||
|
// "#id4 > :first-child": { ... },
|
||
|
//
|
||
|
// // match the last child node that's an element
|
||
|
// "#id4 > :last-child": { ... },
|
||
|
//
|
||
|
// // all elements of type tagname
|
||
|
// "tagname": {
|
||
|
// // ...
|
||
|
// },
|
||
|
//
|
||
|
// "tagname1 tagname2 tagname3": {
|
||
|
// // ...
|
||
|
// },
|
||
|
//
|
||
|
// ".classname": {
|
||
|
// // ...
|
||
|
// },
|
||
|
//
|
||
|
// "tagname.classname": {
|
||
|
// // ...
|
||
|
// },
|
||
|
// }
|
||
|
//
|
||
|
// The "found" method is a generalized handler that's called as soon
|
||
|
// as the node matches the selector. Rules for values that follow also
|
||
|
// apply to the "found" key.
|
||
|
//
|
||
|
// The "on*" handlers are attached with dojo.connect().
|
||
|
//
|
||
|
// If the value corresponding to the ID key is a function and not a
|
||
|
// list, it's treated as though it was the value of "found".
|
||
|
|
||
|
var tmpObj = {};
|
||
|
forIn(behaviorObj, this, function(behavior, name){
|
||
|
var tBehavior = arrIn(this._behaviors, name);
|
||
|
if(typeof tBehavior["id"] != "number"){
|
||
|
tBehavior.id = _inc++;
|
||
|
}
|
||
|
var cversion = [];
|
||
|
tBehavior.push(cversion);
|
||
|
if((dojo.isString(behavior))||(dojo.isFunction(behavior))){
|
||
|
behavior = { found: behavior };
|
||
|
}
|
||
|
forIn(behavior, function(rule, ruleName){
|
||
|
arrIn(cversion, ruleName).push(rule);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var _applyToNode = function(node, action, ruleSetName){
|
||
|
if(dojo.isString(action)){
|
||
|
if(ruleSetName == "found"){
|
||
|
dojo.publish(action, [ node ]);
|
||
|
}else{
|
||
|
dojo.connect(node, ruleSetName, function(){
|
||
|
dojo.publish(action, arguments);
|
||
|
});
|
||
|
}
|
||
|
}else if(dojo.isFunction(action)){
|
||
|
if(ruleSetName == "found"){
|
||
|
action(node);
|
||
|
}else{
|
||
|
dojo.connect(node, ruleSetName, action);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.apply = function(){
|
||
|
// summary:
|
||
|
// applies all currently registered behaviors to the document,
|
||
|
// taking care to ensure that only incremental updates are made
|
||
|
// since the last time add() or apply() were called. If new
|
||
|
// matching nodes have been added, all rules in a behavior will be
|
||
|
// applied to that node. For previously matched nodes, only
|
||
|
// behaviors which have been added since the last call to apply()
|
||
|
// will be added to the nodes.
|
||
|
forIn(this._behaviors, function(tBehavior, id){
|
||
|
dojo.query(id).forEach(
|
||
|
function(elem){
|
||
|
var runFrom = 0;
|
||
|
var bid = "_dj_behavior_"+tBehavior.id;
|
||
|
if(typeof elem[bid] == "number"){
|
||
|
runFrom = elem[bid];
|
||
|
// console.debug(bid, runFrom);
|
||
|
if(runFrom == (tBehavior.length)){
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
// run through the versions, applying newer rules at each step
|
||
|
|
||
|
for(var x=runFrom, tver; tver = tBehavior[x]; x++){
|
||
|
// console.debug(tver);
|
||
|
forIn(tver, function(ruleSet, ruleSetName){
|
||
|
if(dojo.isArray(ruleSet)){
|
||
|
dojo.forEach(ruleSet, function(action){
|
||
|
_applyToNode(elem, action, ruleSetName);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// ensure that re-application only adds new rules to the node
|
||
|
elem[bid] = tBehavior.length;
|
||
|
}
|
||
|
);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dojo.addOnLoad(dojo.behavior, "apply");
|
||
|
|
||
|
}
|