diff options
author | Andrew Dolgov <[email protected]> | 2011-03-04 19:02:28 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2011-03-04 19:02:59 +0300 |
commit | a089699c8915636ba4f158d77dba9b012bc93208 (patch) | |
tree | b2d7d051f1f55d44a6be07d3ee137e5a7ccfcefb /lib/dojo/behavior.js | |
parent | cfad9259a6feacfa8194b1312770ae6db1ecce50 (diff) |
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
Diffstat (limited to 'lib/dojo/behavior.js')
-rw-r--r-- | lib/dojo/behavior.js | 324 |
1 files changed, 240 insertions, 84 deletions
diff --git a/lib/dojo/behavior.js b/lib/dojo/behavior.js index 3420fec20..15f1f23a3 100644 --- a/lib/dojo/behavior.js +++ b/lib/dojo/behavior.js @@ -5,90 +5,246 @@ */ -if(!dojo._hasResource["dojo.behavior"]){ -dojo._hasResource["dojo.behavior"]=true; +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 _1(_2,_3){ -if(!_2[_3]){ -_2[_3]=[]; -} -return _2[_3]; -}; -var _4=0; -function _5(_6,_7,_8){ -var _9={}; -for(var x in _6){ -if(typeof _9[x]=="undefined"){ -if(!_8){ -_7(_6[x],x); -}else{ -_8.call(_7,_6[x],x); -} -} -} -}; -this._behaviors={}; -this.add=function(_a){ -var _b={}; -_5(_a,this,function(_c,_d){ -var _e=_1(this._behaviors,_d); -if(typeof _e["id"]!="number"){ -_e.id=_4++; -} -var _f=[]; -_e.push(_f); -if((dojo.isString(_c))||(dojo.isFunction(_c))){ -_c={found:_c}; -} -_5(_c,function(_10,_11){ -_1(_f,_11).push(_10); -}); -}); -}; -var _12=function(_13,_14,_15){ -if(dojo.isString(_14)){ -if(_15=="found"){ -dojo.publish(_14,[_13]); -}else{ -dojo.connect(_13,_15,function(){ -dojo.publish(_14,arguments); -}); -} -}else{ -if(dojo.isFunction(_14)){ -if(_15=="found"){ -_14(_13); -}else{ -dojo.connect(_13,_15,_14); -} -} -} -}; -this.apply=function(){ -_5(this._behaviors,function(_16,id){ -dojo.query(id).forEach(function(_17){ -var _18=0; -var bid="_dj_behavior_"+_16.id; -if(typeof _17[bid]=="number"){ -_18=_17[bid]; -if(_18==(_16.length)){ -return; -} -} -for(var x=_18,_19;_19=_16[x];x++){ -_5(_19,function(_1a,_1b){ -if(dojo.isArray(_1a)){ -dojo.forEach(_1a,function(_1c){ -_12(_17,_1c,_1b); -}); -} -}); + +dojo.behavior = new function(){ + // summary: + // Utility for unobtrusive/progressive event binding, DOM traversal, + // and manipulation. + // + // description: + // + // A very simple, lightweight mechanism for applying code to + // existing documents, based around `dojo.query` (CSS3 selectors) for node selection, + // and a simple two-command API: `dojo.behavior.add()` and `dojo.behavior.apply()`; + // + // Behaviors apply to a given page, and are registered following the syntax + // options described by `dojo.behavior.add` to match nodes to actions, or "behaviors". + // + // Added behaviors are applied to the current DOM when .apply() is called, + // matching only new nodes found since .apply() was last called. + // + 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(/* Object */behaviorObj){ + // summary: + // Add the specified behavior to the list of behaviors, ignoring existing + // matches. + // + // description: + // 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. + // + // 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()`, using the + // matching node + // + // 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". + // + // dojo.behavior.add() can be called any number of times before + // the DOM is ready. `dojo.behavior.apply()` is called automatically + // by `dojo.addOnLoad`, though can be called to re-apply previously added + // behaviors anytime the DOM changes. + // + // There are a variety of formats permitted in the behaviorObject + // + // example: + // Simple list of properties. "found" is special. "Found" is assumed if + // no property object for a given selector, and property is a function. + // + // | dojo.behavior.add({ + // | "#id": { + // | "found": function(element){ + // | // node match found + // | }, + // | "onclick": function(evt){ + // | // register onclick handler for found node + // | } + // | }, + // | "#otherid": function(element){ + // | // assumes "found" with this syntax + // | } + // | }); + // + // example: + // If property is a string, a dojo.publish will be issued on the channel: + // + // | dojo.behavior.add({ + // | // dojo.publish() whenever class="noclick" found on anchors + // | "a.noclick": "/got/newAnchor", + // | "div.wrapper": { + // | "onclick": "/node/wasClicked" + // | } + // | }); + // | dojo.subscribe("/got/newAnchor", function(node){ + // | // handle node finding when dojo.behavior.apply() is called, + // | // provided a newly matched node is found. + // | }); + // + // example: + // Scoping can be accomplished by passing an object as a property to + // a connection handle (on*): + // + // | dojo.behavior.add({ + // | "#id": { + // | // like calling dojo.hitch(foo,"bar"). execute foo.bar() in scope of foo + // | "onmouseenter": { targetObj: foo, targetFunc: "bar" }, + // | "onmouseleave": { targetObj: foo, targetFunc: "baz" } + // | } + // | }); + // + // example: + // Bahaviors match on CSS3 Selectors, powered by dojo.query. Example selectors: + // + // | dojo.behavior.add({ + // | // 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": { + // | // ... + // | } + // | }); + // + + 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. + // + // description: + // 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. + // + // apply() is called once automatically by `dojo.addOnLoad`, so + // registering behaviors with `dojo.behavior.add` before the DOM is + // ready is acceptable, provided the dojo.behavior module is ready. + // + // Calling appy() manually after manipulating the DOM is required + // to rescan the DOM and apply newly .add()ed behaviors, or to match + // nodes that match existing behaviors when those nodes are added to + // the DOM. + // + 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]; + if(runFrom == (tBehavior.length)){ + return; + } + } + // run through the versions, applying newer rules at each step + + for(var x=runFrom, tver; tver = tBehavior[x]; x++){ + 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; + } + ); + }); + } } -_17[bid]=_16.length; -}); -}); -}; -}; -dojo.addOnLoad(dojo.behavior,"apply"); + +dojo.addOnLoad(dojo.behavior, "apply"); + } |