248 lines
6.4 KiB
JavaScript
248 lines
6.4 KiB
JavaScript
/*
|
|
Copyright (c) 2004-2006, The Dojo Foundation
|
|
All Rights Reserved.
|
|
|
|
Licensed under the Academic Free License version 2.1 or above OR the
|
|
modified BSD license. For more information on Dojo licensing, see:
|
|
|
|
http://dojotoolkit.org/community/licensing.shtml
|
|
*/
|
|
|
|
dojo.provide("dojo.behavior");
|
|
dojo.require("dojo.event.*");
|
|
|
|
dojo.require("dojo.experimental");
|
|
dojo.experimental("dojo.behavior");
|
|
|
|
dojo.behavior = new function(){
|
|
function arrIn(obj, name){
|
|
if(!obj[name]){ obj[name] = []; }
|
|
return obj[name];
|
|
}
|
|
|
|
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){
|
|
/* behavior objects are specified in the following format:
|
|
*
|
|
* {
|
|
* "#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 > @firstElement": { ... },
|
|
*
|
|
* // match the last child node that's an element
|
|
* "#id4 > @lastElement": { ... },
|
|
*
|
|
* // all elements of type tagname
|
|
* "tagname": {
|
|
* // ...
|
|
* },
|
|
*
|
|
* // maps to roughly:
|
|
* // dojo.lang.forEach(body.getElementsByTagName("tagname1"), function(node){
|
|
* // dojo.lang.forEach(node.getElementsByTagName("tagname2"), function(node2){
|
|
* // dojo.lang.forEach(node2.getElementsByTagName("tagname3", function(node3){
|
|
* // // apply rules
|
|
* // });
|
|
* // });
|
|
* // });
|
|
* "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.event.connect(). If the
|
|
* value is not a function but is rather an object, it's assumed to be
|
|
* the "other half" of a dojo.event.kwConnect() argument object. It
|
|
* may contain any/all properties of such a connection modifier save
|
|
* for the sourceObj and sourceFunc properties which are filled in by
|
|
* the system automatically. If a string is instead encountered, the
|
|
* node publishes the specified event on the topic contained in the
|
|
* string value.
|
|
*
|
|
* 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((dojo.lang.isString(behavior))||(dojo.lang.isFunction(behavior))){
|
|
behavior = { found: behavior };
|
|
}
|
|
forIn(behavior, function(rule, ruleName){
|
|
arrIn(tBehavior, ruleName).push(rule);
|
|
});
|
|
});
|
|
}
|
|
|
|
this.apply = function(){
|
|
dojo.profile.start("dojo.behavior.apply");
|
|
var r = dojo.render.html;
|
|
// note, we apply one way for fast queries and one way for slow
|
|
// iteration. So be it.
|
|
var safariGoodEnough = (!r.safari);
|
|
if(r.safari){
|
|
// Anything over release #420 should work the fast way
|
|
var uas = r.UA.split("AppleWebKit/")[1];
|
|
if(parseInt(uas.match(/[0-9.]{3,}/)) >= 420){
|
|
safariGoodEnough = true;
|
|
}
|
|
}
|
|
if((dj_undef("behaviorFastParse", djConfig) ? (safariGoodEnough) : djConfig["behaviorFastParse"])){
|
|
this.applyFast();
|
|
}else{
|
|
this.applySlow();
|
|
}
|
|
dojo.profile.end("dojo.behavior.apply");
|
|
}
|
|
|
|
this.matchCache = {};
|
|
|
|
this.elementsById = function(id, handleRemoved){
|
|
var removed = [];
|
|
var added = [];
|
|
arrIn(this.matchCache, id);
|
|
if(handleRemoved){
|
|
var nodes = this.matchCache[id];
|
|
for(var x=0; x<nodes.length; x++){
|
|
if(nodes[x].id != ""){
|
|
removed.push(nodes[x]);
|
|
nodes.splice(x, 1);
|
|
x--;
|
|
}
|
|
}
|
|
}
|
|
var tElem = dojo.byId(id);
|
|
while(tElem){
|
|
if(!tElem["idcached"]){
|
|
added.push(tElem);
|
|
}
|
|
tElem.id = "";
|
|
tElem = dojo.byId(id);
|
|
}
|
|
this.matchCache[id] = this.matchCache[id].concat(added);
|
|
dojo.lang.forEach(this.matchCache[id], function(node){
|
|
node.id = id;
|
|
node.idcached = true;
|
|
});
|
|
return { "removed": removed, "added": added, "match": this.matchCache[id] };
|
|
}
|
|
|
|
this.applyToNode = function(node, action, ruleSetName){
|
|
if(typeof action == "string"){
|
|
dojo.event.topic.registerPublisher(action, node, ruleSetName);
|
|
}else if(typeof action == "function"){
|
|
if(ruleSetName == "found"){
|
|
action(node);
|
|
}else{
|
|
dojo.event.connect(node, ruleSetName, action);
|
|
}
|
|
}else{
|
|
action.srcObj = node;
|
|
action.srcFunc = ruleSetName;
|
|
dojo.event.kwConnect(action);
|
|
}
|
|
}
|
|
|
|
this.applyFast = function(){
|
|
dojo.profile.start("dojo.behavior.applyFast");
|
|
// fast DOM queries...wheeee!
|
|
forIn(this.behaviors, function(tBehavior, id){
|
|
var elems = dojo.behavior.elementsById(id);
|
|
dojo.lang.forEach(elems.added,
|
|
function(elem){
|
|
forIn(tBehavior, function(ruleSet, ruleSetName){
|
|
if(dojo.lang.isArray(ruleSet)){
|
|
dojo.lang.forEach(ruleSet, function(action){
|
|
dojo.behavior.applyToNode(elem, action, ruleSetName);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
);
|
|
});
|
|
dojo.profile.end("dojo.behavior.applyFast");
|
|
}
|
|
|
|
this.applySlow = function(){
|
|
// iterate. Ugg.
|
|
dojo.profile.start("dojo.behavior.applySlow");
|
|
var all = document.getElementsByTagName("*");
|
|
var allLen = all.length;
|
|
for(var x=0; x<allLen; x++){
|
|
var elem = all[x];
|
|
if((elem.id)&&(!elem["behaviorAdded"])&&(this.behaviors[elem.id])){
|
|
elem["behaviorAdded"] = true;
|
|
forIn(this.behaviors[elem.id], function(ruleSet, ruleSetName){
|
|
if(dojo.lang.isArray(ruleSet)){
|
|
dojo.lang.forEach(ruleSet, function(action){
|
|
dojo.behavior.applyToNode(elem, action, ruleSetName);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
dojo.profile.end("dojo.behavior.applySlow");
|
|
}
|
|
}
|
|
|
|
dojo.addOnLoad(dojo.behavior, "apply");
|