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/_base/NodeList.js | |
parent | cfad9259a6feacfa8194b1312770ae6db1ecce50 (diff) |
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
Diffstat (limited to 'lib/dojo/_base/NodeList.js')
-rw-r--r-- | lib/dojo/_base/NodeList.js | 1222 |
1 files changed, 1003 insertions, 219 deletions
diff --git a/lib/dojo/_base/NodeList.js b/lib/dojo/_base/NodeList.js index 6df6db976..12b631a4b 100644 --- a/lib/dojo/_base/NodeList.js +++ b/lib/dojo/_base/NodeList.js @@ -5,228 +5,1012 @@ */ -if(!dojo._hasResource["dojo._base.NodeList"]){ -dojo._hasResource["dojo._base.NodeList"]=true; +if(!dojo._hasResource["dojo._base.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.NodeList"] = true; dojo.provide("dojo._base.NodeList"); dojo.require("dojo._base.lang"); dojo.require("dojo._base.array"); + (function(){ -var d=dojo; -var ap=Array.prototype,_1=ap.slice,_2=ap.concat; -var _3=function(a,_4,_5){ -if(!a.sort){ -a=_1.call(a,0); -} -var _6=_5||this._NodeListCtor||d._NodeListCtor; -a.constructor=_6; -dojo._mixin(a,_6.prototype); -a._NodeListCtor=_6; -return _4?a._stash(_4):a; -}; -var _7=function(f,a,o){ -a=[0].concat(_1.call(a,0)); -o=o||d.global; -return function(_8){ -a[0]=_8; -return f.apply(o,a); -}; -}; -var _9=function(f,o){ -return function(){ -this.forEach(_7(f,arguments,o)); -return this; -}; -}; -var _a=function(f,o){ -return function(){ -return this.map(_7(f,arguments,o)); -}; -}; -var _b=function(f,o){ -return function(){ -return this.filter(_7(f,arguments,o)); -}; -}; -var _c=function(f,g,o){ -return function(){ -var a=arguments,_d=_7(f,a,o); -if(g.call(o||d.global,a)){ -return this.map(_d); -} -this.forEach(_d); -return this; -}; -}; -var _e=function(a){ -return a.length==1&&(typeof a[0]=="string"); -}; -var _f=function(_10){ -var p=_10.parentNode; -if(p){ -p.removeChild(_10); -} -}; -dojo.NodeList=function(){ -return _3(Array.apply(null,arguments)); -}; -d._NodeListCtor=d.NodeList; -var nl=d.NodeList,nlp=nl.prototype; -nl._wrap=nlp._wrap=_3; -nl._adaptAsMap=_a; -nl._adaptAsForEach=_9; -nl._adaptAsFilter=_b; -nl._adaptWithCondition=_c; -d.forEach(["slice","splice"],function(_11){ -var f=ap[_11]; -nlp[_11]=function(){ -return this._wrap(f.apply(this,arguments),_11=="slice"?this:null); -}; -}); -d.forEach(["indexOf","lastIndexOf","every","some"],function(_12){ -var f=d[_12]; -nlp[_12]=function(){ -return f.apply(d,[this].concat(_1.call(arguments,0))); -}; -}); -d.forEach(["attr","style"],function(_13){ -nlp[_13]=_c(d[_13],_e); -}); -d.forEach(["connect","addClass","removeClass","toggleClass","empty","removeAttr"],function(_14){ -nlp[_14]=_9(d[_14]); -}); -dojo.extend(dojo.NodeList,{_normalize:function(_15,_16){ -var _17=_15.parse===true?true:false; -if(typeof _15.template=="string"){ -var _18=_15.templateFunc||(dojo.string&&dojo.string.substitute); -_15=_18?_18(_15.template,_15):_15; -} -var _19=(typeof _15); -if(_19=="string"||_19=="number"){ -_15=dojo._toDom(_15,(_16&&_16.ownerDocument)); -if(_15.nodeType==11){ -_15=dojo._toArray(_15.childNodes); -}else{ -_15=[_15]; -} -}else{ -if(!dojo.isArrayLike(_15)){ -_15=[_15]; -}else{ -if(!dojo.isArray(_15)){ -_15=dojo._toArray(_15); -} -} -} -if(_17){ -_15._runParse=true; -} -return _15; -},_cloneNode:function(_1a){ -return _1a.cloneNode(true); -},_place:function(ary,_1b,_1c,_1d){ -if(_1b.nodeType!=1&&_1c=="only"){ -return; -} -var _1e=_1b,_1f; -var _20=ary.length; -for(var i=_20-1;i>=0;i--){ -var _21=(_1d?this._cloneNode(ary[i]):ary[i]); -if(ary._runParse&&dojo.parser&&dojo.parser.parse){ -if(!_1f){ -_1f=_1e.ownerDocument.createElement("div"); -} -_1f.appendChild(_21); -dojo.parser.parse(_1f); -_21=_1f.firstChild; -while(_1f.firstChild){ -_1f.removeChild(_1f.firstChild); -} -} -if(i==_20-1){ -dojo.place(_21,_1e,_1c); -}else{ -_1e.parentNode.insertBefore(_21,_1e); -} -_1e=_21; -} -},_stash:function(_22){ -this._parent=_22; -return this; -},end:function(){ -if(this._parent){ -return this._parent; -}else{ -return new this._NodeListCtor(); -} -},concat:function(_23){ -var t=d.isArray(this)?this:_1.call(this,0),m=d.map(arguments,function(a){ -return a&&!d.isArray(a)&&(typeof NodeList!="undefined"&&a.constructor===NodeList||a.constructor===this._NodeListCtor)?_1.call(a,0):a; -}); -return this._wrap(_2.apply(t,m),this); -},map:function(_24,obj){ -return this._wrap(d.map(this,_24,obj),this); -},forEach:function(_25,_26){ -d.forEach(this,_25,_26); -return this; -},coords:_a(d.coords),position:_a(d.position),place:function(_27,_28){ -var _29=d.query(_27)[0]; -return this.forEach(function(_2a){ -d.place(_2a,_29,_28); -}); -},orphan:function(_2b){ -return (_2b?d._filterQueryResult(this,_2b):this).forEach(_f); -},adopt:function(_2c,_2d){ -return d.query(_2c).place(this[0],_2d)._stash(this); -},query:function(_2e){ -if(!_2e){ -return this; -} -var ret=this.map(function(_2f){ -return d.query(_2e,_2f).filter(function(_30){ -return _30!==undefined; -}); -}); -return this._wrap(_2.apply([],ret),this); -},filter:function(_31){ -var a=arguments,_32=this,_33=0; -if(typeof _31=="string"){ -_32=d._filterQueryResult(this,a[0]); -if(a.length==1){ -return _32._stash(this); -} -_33=1; -} -return this._wrap(d.filter(_32,a[_33],a[_33+1]),this); -},addContent:function(_34,_35){ -_34=this._normalize(_34,this[0]); -for(var i=0,_36;_36=this[i];i++){ -this._place(_34,_36,_35,i>0); -} -return this; -},instantiate:function(_37,_38){ -var c=d.isFunction(_37)?_37:d.getObject(_37); -_38=_38||{}; -return this.forEach(function(_39){ -new c(_38,_39); -}); -},at:function(){ -var t=new this._NodeListCtor(); -d.forEach(arguments,function(i){ -if(i<0){ -i=this.length+i; -} -if(this[i]){ -t.push(this[i]); -} -},this); -return t._stash(this); -}}); -nl.events=["blur","focus","change","click","error","keydown","keypress","keyup","load","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup","submit"]; -d.forEach(nl.events,function(evt){ -var _3a="on"+evt; -nlp[_3a]=function(a,b){ -return this.connect(_3a,a,b); -}; -}); + + var d = dojo; + + var ap = Array.prototype, aps = ap.slice, apc = ap.concat; + + var tnl = function(/*Array*/ a, /*dojo.NodeList?*/ parent, /*Function?*/ NodeListCtor){ + // summary: + // decorate an array to make it look like a `dojo.NodeList`. + // a: + // Array of nodes to decorate. + // parent: + // An optional parent NodeList that generated the current + // list of nodes. Used to call _stash() so the parent NodeList + // can be accessed via end() later. + // NodeListCtor: + // An optional constructor function to use for any + // new NodeList calls. This allows a certain chain of + // NodeList calls to use a different object than dojo.NodeList. + if(!a.sort){ + // make sure it's a real array before we pass it on to be wrapped + a = aps.call(a, 0); + } + var ctor = NodeListCtor || this._NodeListCtor || d._NodeListCtor; + a.constructor = ctor; + dojo._mixin(a, ctor.prototype); + a._NodeListCtor = ctor; + return parent ? a._stash(parent) : a; + }; + + var loopBody = function(f, a, o){ + a = [0].concat(aps.call(a, 0)); + o = o || d.global; + return function(node){ + a[0] = node; + return f.apply(o, a); + }; + }; + + // adapters + + var adaptAsForEach = function(f, o){ + // summary: + // adapts a single node function to be used in the forEach-type + // actions. The initial object is returned from the specialized + // function. + // f: Function + // a function to adapt + // o: Object? + // an optional context for f + return function(){ + this.forEach(loopBody(f, arguments, o)); + return this; // Object + }; + }; + + var adaptAsMap = function(f, o){ + // summary: + // adapts a single node function to be used in the map-type + // actions. The return is a new array of values, as via `dojo.map` + // f: Function + // a function to adapt + // o: Object? + // an optional context for f + return function(){ + return this.map(loopBody(f, arguments, o)); + }; + }; + + var adaptAsFilter = function(f, o){ + // summary: + // adapts a single node function to be used in the filter-type actions + // f: Function + // a function to adapt + // o: Object? + // an optional context for f + return function(){ + return this.filter(loopBody(f, arguments, o)); + }; + }; + + var adaptWithCondition = function(f, g, o){ + // summary: + // adapts a single node function to be used in the map-type + // actions, behaves like forEach() or map() depending on arguments + // f: Function + // a function to adapt + // g: Function + // a condition function, if true runs as map(), otherwise runs as forEach() + // o: Object? + // an optional context for f and g + return function(){ + var a = arguments, body = loopBody(f, a, o); + if(g.call(o || d.global, a)){ + return this.map(body); // self + } + this.forEach(body); + return this; // self + }; + }; + + var magicGuard = function(a){ + // summary: + // the guard function for dojo.attr() and dojo.style() + return a.length == 1 && (typeof a[0] == "string"); // inline'd type check + }; + + var orphan = function(node){ + // summary: + // function to orphan nodes + var p = node.parentNode; + if(p){ + p.removeChild(node); + } + }; + // FIXME: should we move orphan() to dojo.html? + + dojo.NodeList = function(){ + // summary: + // dojo.NodeList is an of Array subclass which adds syntactic + // sugar for chaining, common iteration operations, animation, and + // node manipulation. NodeLists are most often returned as the + // result of dojo.query() calls. + // description: + // dojo.NodeList instances provide many utilities that reflect + // core Dojo APIs for Array iteration and manipulation, DOM + // manipulation, and event handling. Instead of needing to dig up + // functions in the dojo.* namespace, NodeLists generally make the + // full power of Dojo available for DOM manipulation tasks in a + // simple, chainable way. + // example: + // create a node list from a node + // | new dojo.NodeList(dojo.byId("foo")); + // example: + // get a NodeList from a CSS query and iterate on it + // | var l = dojo.query(".thinger"); + // | l.forEach(function(node, index, nodeList){ + // | console.log(index, node.innerHTML); + // | }); + // example: + // use native and Dojo-provided array methods to manipulate a + // NodeList without needing to use dojo.* functions explicitly: + // | var l = dojo.query(".thinger"); + // | // since NodeLists are real arrays, they have a length + // | // property that is both readable and writable and + // | // push/pop/shift/unshift methods + // | console.log(l.length); + // | l.push(dojo.create("span")); + // | + // | // dojo's normalized array methods work too: + // | console.log( l.indexOf(dojo.byId("foo")) ); + // | // ...including the special "function as string" shorthand + // | console.log( l.every("item.nodeType == 1") ); + // | + // | // NodeLists can be [..] indexed, or you can use the at() + // | // function to get specific items wrapped in a new NodeList: + // | var node = l[3]; // the 4th element + // | var newList = l.at(1, 3); // the 2nd and 4th elements + // example: + // the style functions you expect are all there too: + // | // style() as a getter... + // | var borders = dojo.query(".thinger").style("border"); + // | // ...and as a setter: + // | dojo.query(".thinger").style("border", "1px solid black"); + // | // class manipulation + // | dojo.query("li:nth-child(even)").addClass("even"); + // | // even getting the coordinates of all the items + // | var coords = dojo.query(".thinger").coords(); + // example: + // DOM manipulation functions from the dojo.* namespace area also + // available: + // | // remove all of the elements in the list from their + // | // parents (akin to "deleting" them from the document) + // | dojo.query(".thinger").orphan(); + // | // place all elements in the list at the front of #foo + // | dojo.query(".thinger").place("foo", "first"); + // example: + // Event handling couldn't be easier. `dojo.connect` is mapped in, + // and shortcut handlers are provided for most DOM events: + // | // like dojo.connect(), but with implicit scope + // | dojo.query("li").connect("onclick", console, "log"); + // | + // | // many common event handlers are already available directly: + // | dojo.query("li").onclick(console, "log"); + // | var toggleHovered = dojo.hitch(dojo, "toggleClass", "hovered"); + // | dojo.query("p") + // | .onmouseenter(toggleHovered) + // | .onmouseleave(toggleHovered); + // example: + // chainability is a key advantage of NodeLists: + // | dojo.query(".thinger") + // | .onclick(function(e){ /* ... */ }) + // | .at(1, 3, 8) // get a subset + // | .style("padding", "5px") + // | .forEach(console.log); + + return tnl(Array.apply(null, arguments)); + }; + + //Allow things that new up a NodeList to use a delegated or alternate NodeList implementation. + d._NodeListCtor = d.NodeList; + + var nl = d.NodeList, nlp = nl.prototype; + + // expose adapters and the wrapper as private functions + + nl._wrap = nlp._wrap = tnl; + nl._adaptAsMap = adaptAsMap; + nl._adaptAsForEach = adaptAsForEach; + nl._adaptAsFilter = adaptAsFilter; + nl._adaptWithCondition = adaptWithCondition; + + // mass assignment + + // add array redirectors + d.forEach(["slice", "splice"], function(name){ + var f = ap[name]; + //Use a copy of the this array via this.slice() to allow .end() to work right in the splice case. + // CANNOT apply ._stash()/end() to splice since it currently modifies + // the existing this array -- it would break backward compatibility if we copy the array before + // the splice so that we can use .end(). So only doing the stash option to this._wrap for slice. + nlp[name] = function(){ return this._wrap(f.apply(this, arguments), name == "slice" ? this : null); }; + }); + // concat should be here but some browsers with native NodeList have problems with it + + // add array.js redirectors + d.forEach(["indexOf", "lastIndexOf", "every", "some"], function(name){ + var f = d[name]; + nlp[name] = function(){ return f.apply(d, [this].concat(aps.call(arguments, 0))); }; + }); + + // add conditional methods + d.forEach(["attr", "style"], function(name){ + nlp[name] = adaptWithCondition(d[name], magicGuard); + }); + + // add forEach actions + d.forEach(["connect", "addClass", "removeClass", "toggleClass", "empty", "removeAttr"], function(name){ + nlp[name] = adaptAsForEach(d[name]); + }); + + dojo.extend(dojo.NodeList, { + _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){ + // summary: + // normalizes data to an array of items to insert. + // description: + // If content is an object, it can have special properties "template" and + // "parse". If "template" is defined, then the template value is run through + // dojo.string.substitute (if dojo.string.substitute has been dojo.required elsewhere), + // or if templateFunc is a function on the content, that function will be used to + // transform the template into a final string to be used for for passing to dojo._toDom. + // If content.parse is true, then it is remembered for later, for when the content + // nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets + // (if dojo.parser has been dojo.required elsewhere). + + //Wanted to just use a DocumentFragment, but for the array/NodeList + //case that meant using cloneNode, but we may not want that. + //Cloning should only happen if the node operations span + //multiple refNodes. Also, need a real array, not a NodeList from the + //DOM since the node movements could change those NodeLists. + + var parse = content.parse === true ? true : false; + + //Do we have an object that needs to be run through a template? + if(typeof content.template == "string"){ + var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute); + content = templateFunc ? templateFunc(content.template, content) : content; + } + + var type = (typeof content); + if(type == "string" || type == "number"){ + content = dojo._toDom(content, (refNode && refNode.ownerDocument)); + if(content.nodeType == 11){ + //DocumentFragment. It cannot handle cloneNode calls, so pull out the children. + content = dojo._toArray(content.childNodes); + }else{ + content = [content]; + } + }else if(!dojo.isArrayLike(content)){ + content = [content]; + }else if(!dojo.isArray(content)){ + //To get to this point, content is array-like, but + //not an array, which likely means a DOM NodeList. Convert it now. + content = dojo._toArray(content); + } + + //Pass around the parse info + if(parse){ + content._runParse = true; + } + return content; //Array + }, + + _cloneNode: function(/*DOMNode*/ node){ + // summary: + // private utiltity to clone a node. Not very interesting in the vanilla + // dojo.NodeList case, but delegates could do interesting things like + // clone event handlers if that is derivable from the node. + return node.cloneNode(true); + }, + + _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){ + // summary: + // private utility to handle placing an array of nodes relative to another node. + // description: + // Allows for cloning the nodes in the array, and for + // optionally parsing widgets, if ary._runParse is true. + + //Avoid a disallowed operation if trying to do an innerHTML on a non-element node. + if(refNode.nodeType != 1 && position == "only"){ + return; + } + var rNode = refNode, tempNode; + + //Always cycle backwards in case the array is really a + //DOM NodeList and the DOM operations take it out of the live collection. + var length = ary.length; + for(var i = length - 1; i >= 0; i--){ + var node = (useClone ? this._cloneNode(ary[i]) : ary[i]); + + //If need widget parsing, use a temp node, instead of waiting after inserting into + //real DOM because we need to start widget parsing at one node up from current node, + //which could cause some already parsed widgets to be parsed again. + if(ary._runParse && dojo.parser && dojo.parser.parse){ + if(!tempNode){ + tempNode = rNode.ownerDocument.createElement("div"); + } + tempNode.appendChild(node); + dojo.parser.parse(tempNode); + node = tempNode.firstChild; + while(tempNode.firstChild){ + tempNode.removeChild(tempNode.firstChild); + } + } + + if(i == length - 1){ + dojo.place(node, rNode, position); + }else{ + rNode.parentNode.insertBefore(node, rNode); + } + rNode = node; + } + }, + + _stash: function(parent){ + // summary: + // private function to hold to a parent NodeList. end() to return the parent NodeList. + // + // example: + // How to make a `dojo.NodeList` method that only returns the third node in + // the dojo.NodeList but allows access to the original NodeList by using this._stash: + // | dojo.extend(dojo.NodeList, { + // | third: function(){ + // | var newNodeList = dojo.NodeList(this[2]); + // | return newNodeList._stash(this); + // | } + // | }); + // | // then see how _stash applies a sub-list, to be .end()'ed out of + // | dojo.query(".foo") + // | .third() + // | .addClass("thirdFoo") + // | .end() + // | // access to the orig .foo list + // | .removeClass("foo") + // | + // + this._parent = parent; + return this; //dojo.NodeList + }, + + end: function(){ + // summary: + // Ends use of the current `dojo.NodeList` by returning the previous dojo.NodeList + // that generated the current dojo.NodeList. + // description: + // Returns the `dojo.NodeList` that generated the current `dojo.NodeList`. If there + // is no parent dojo.NodeList, an empty dojo.NodeList is returned. + // example: + // | dojo.query("a") + // | .filter(".disabled") + // | // operate on the anchors that only have a disabled class + // | .style("color", "grey") + // | .end() + // | // jump back to the list of anchors + // | .style(...) + // + if(this._parent){ + return this._parent; + }else{ + //Just return empy list. + return new this._NodeListCtor(); + } + }, + + // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods + + // FIXME: handle return values for #3244 + // http://trac.dojotoolkit.org/ticket/3244 + + // FIXME: + // need to wrap or implement: + // join (perhaps w/ innerHTML/outerHTML overload for toString() of items?) + // reduce + // reduceRight + + /*===== + slice: function(begin, end){ + // summary: + // Returns a new NodeList, maintaining this one in place + // description: + // This method behaves exactly like the Array.slice method + // with the caveat that it returns a dojo.NodeList and not a + // raw Array. For more details, see Mozilla's (slice + // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice] + // begin: Integer + // Can be a positive or negative integer, with positive + // integers noting the offset to begin at, and negative + // integers denoting an offset from the end (i.e., to the left + // of the end) + // end: Integer? + // Optional parameter to describe what position relative to + // the NodeList's zero index to end the slice at. Like begin, + // can be positive or negative. + return this._wrap(a.slice.apply(this, arguments)); + }, + + splice: function(index, howmany, item){ + // summary: + // Returns a new NodeList, manipulating this NodeList based on + // the arguments passed, potentially splicing in new elements + // at an offset, optionally deleting elements + // description: + // This method behaves exactly like the Array.splice method + // with the caveat that it returns a dojo.NodeList and not a + // raw Array. For more details, see Mozilla's (splice + // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice] + // For backwards compatibility, calling .end() on the spliced NodeList + // does not return the original NodeList -- splice alters the NodeList in place. + // index: Integer + // begin can be a positive or negative integer, with positive + // integers noting the offset to begin at, and negative + // integers denoting an offset from the end (i.e., to the left + // of the end) + // howmany: Integer? + // Optional parameter to describe what position relative to + // the NodeList's zero index to end the slice at. Like begin, + // can be positive or negative. + // item: Object...? + // Any number of optional parameters may be passed in to be + // spliced into the NodeList + // returns: + // dojo.NodeList + return this._wrap(a.splice.apply(this, arguments)); + }, + + indexOf: function(value, fromIndex){ + // summary: + // see dojo.indexOf(). The primary difference is that the acted-on + // array is implicitly this NodeList + // value: Object: + // The value to search for. + // fromIndex: Integer?: + // The loction to start searching from. Optional. Defaults to 0. + // description: + // For more details on the behavior of indexOf, see Mozilla's + // (indexOf + // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf] + // returns: + // Positive Integer or 0 for a match, -1 of not found. + return d.indexOf(this, value, fromIndex); // Integer + }, + + lastIndexOf: function(value, fromIndex){ + // summary: + // see dojo.lastIndexOf(). The primary difference is that the + // acted-on array is implicitly this NodeList + // description: + // For more details on the behavior of lastIndexOf, see + // Mozilla's (lastIndexOf + // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf] + // value: Object + // The value to search for. + // fromIndex: Integer? + // The loction to start searching from. Optional. Defaults to 0. + // returns: + // Positive Integer or 0 for a match, -1 of not found. + return d.lastIndexOf(this, value, fromIndex); // Integer + }, + + every: function(callback, thisObject){ + // summary: + // see `dojo.every()` and the (Array.every + // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every]. + // Takes the same structure of arguments and returns as + // dojo.every() with the caveat that the passed array is + // implicitly this NodeList + // callback: Function: the callback + // thisObject: Object?: the context + return d.every(this, callback, thisObject); // Boolean + }, + + some: function(callback, thisObject){ + // summary: + // Takes the same structure of arguments and returns as + // `dojo.some()` with the caveat that the passed array is + // implicitly this NodeList. See `dojo.some()` and Mozilla's + // (Array.some + // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some]. + // callback: Function: the callback + // thisObject: Object?: the context + return d.some(this, callback, thisObject); // Boolean + }, + =====*/ + + concat: function(item){ + // summary: + // Returns a new NodeList comprised of items in this NodeList + // as well as items passed in as parameters + // description: + // This method behaves exactly like the Array.concat method + // with the caveat that it returns a `dojo.NodeList` and not a + // raw Array. For more details, see the (Array.concat + // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat] + // item: Object? + // Any number of optional parameters may be passed in to be + // spliced into the NodeList + // returns: + // dojo.NodeList + + //return this._wrap(apc.apply(this, arguments)); + // the line above won't work for the native NodeList :-( + + // implementation notes: + // 1) Native NodeList is not an array, and cannot be used directly + // in concat() --- the latter doesn't recognize it as an array, and + // does not inline it, but append as a single entity. + // 2) On some browsers (e.g., Safari) the "constructor" property is + // read-only and cannot be changed. So we have to test for both + // native NodeList and dojo.NodeList in this property to recognize + // the node list. + + var t = d.isArray(this) ? this : aps.call(this, 0), + m = d.map(arguments, function(a){ + return a && !d.isArray(a) && + (typeof NodeList != "undefined" && a.constructor === NodeList || a.constructor === this._NodeListCtor) ? + aps.call(a, 0) : a; + }); + return this._wrap(apc.apply(t, m), this); // dojo.NodeList + }, + + map: function(/*Function*/ func, /*Function?*/ obj){ + // summary: + // see dojo.map(). The primary difference is that the acted-on + // array is implicitly this NodeList and the return is a + // dojo.NodeList (a subclass of Array) + ///return d.map(this, func, obj, d.NodeList); // dojo.NodeList + return this._wrap(d.map(this, func, obj), this); // dojo.NodeList + }, + + forEach: function(callback, thisObj){ + // summary: + // see `dojo.forEach()`. The primary difference is that the acted-on + // array is implicitly this NodeList. If you want the option to break out + // of the forEach loop, use every() or some() instead. + d.forEach(this, callback, thisObj); + // non-standard return to allow easier chaining + return this; // dojo.NodeList + }, + + /*===== + coords: function(){ + // summary: + // Returns the box objects of all elements in a node list as + // an Array (*not* a NodeList). Acts like `dojo.coords`, though assumes + // the node passed is each node in this list. + + return d.map(this, d.coords); // Array + }, + + position: function(){ + // summary: + // Returns border-box objects (x/y/w/h) of all elements in a node list + // as an Array (*not* a NodeList). Acts like `dojo.position`, though + // assumes the node passed is each node in this list. + + return d.map(this, d.position); // Array + }, + + attr: function(property, value){ + // summary: + // gets or sets the DOM attribute for every element in the + // NodeList. See also `dojo.attr` + // property: String + // the attribute to get/set + // value: String? + // optional. The value to set the property to + // returns: + // if no value is passed, the result is an array of attribute values + // If a value is passed, the return is this NodeList + // example: + // Make all nodes with a particular class focusable: + // | dojo.query(".focusable").attr("tabIndex", -1); + // example: + // Disable a group of buttons: + // | dojo.query("button.group").attr("disabled", true); + // example: + // innerHTML can be assigned or retreived as well: + // | // get the innerHTML (as an array) for each list item + // | var ih = dojo.query("li.replaceable").attr("innerHTML"); + return; // dojo.NodeList + return; // Array + }, + + style: function(property, value){ + // summary: + // gets or sets the CSS property for every element in the NodeList + // property: String + // the CSS property to get/set, in JavaScript notation + // ("lineHieght" instead of "line-height") + // value: String? + // optional. The value to set the property to + // returns: + // if no value is passed, the result is an array of strings. + // If a value is passed, the return is this NodeList + return; // dojo.NodeList + return; // Array + }, + + addClass: function(className){ + // summary: + // adds the specified class to every node in the list + // className: String|Array + // A String class name to add, or several space-separated class names, + // or an array of class names. + return; // dojo.NodeList + }, + + removeClass: function(className){ + // summary: + // removes the specified class from every node in the list + // className: String|Array? + // An optional String class name to remove, or several space-separated + // class names, or an array of class names. If omitted, all class names + // will be deleted. + // returns: + // dojo.NodeList, this list + return; // dojo.NodeList + }, + + toggleClass: function(className, condition){ + // summary: + // Adds a class to node if not present, or removes if present. + // Pass a boolean condition if you want to explicitly add or remove. + // condition: Boolean? + // If passed, true means to add the class, false means to remove. + // className: String + // the CSS class to add + return; // dojo.NodeList + }, + + connect: function(methodName, objOrFunc, funcName){ + // summary: + // attach event handlers to every item of the NodeList. Uses dojo.connect() + // so event properties are normalized + // methodName: String + // the name of the method to attach to. For DOM events, this should be + // the lower-case name of the event + // objOrFunc: Object|Function|String + // if 2 arguments are passed (methodName, objOrFunc), objOrFunc should + // reference a function or be the name of the function in the global + // namespace to attach. If 3 arguments are provided + // (methodName, objOrFunc, funcName), objOrFunc must be the scope to + // locate the bound function in + // funcName: String? + // optional. A string naming the function in objOrFunc to bind to the + // event. May also be a function reference. + // example: + // add an onclick handler to every button on the page + // | dojo.query("div:nth-child(odd)").connect("onclick", function(e){ + // | console.log("clicked!"); + // | }); + // example: + // attach foo.bar() to every odd div's onmouseover + // | dojo.query("div:nth-child(odd)").connect("onmouseover", foo, "bar"); + }, + + empty: function(){ + // summary: + // clears all content from each node in the list. Effectively + // equivalent to removing all child nodes from every item in + // the list. + return this.forEach("item.innerHTML='';"); // dojo.NodeList + // FIXME: should we be checking for and/or disposing of widgets below these nodes? + }, + =====*/ + + // useful html methods + coords: adaptAsMap(d.coords), + position: adaptAsMap(d.position), + + // FIXME: connectPublisher()? connectRunOnce()? + + /* + destroy: function(){ + // summary: + // destroys every item in the list. + this.forEach(d.destroy); + // FIXME: should we be checking for and/or disposing of widgets below these nodes? + }, + */ + + place: function(/*String||Node*/ queryOrNode, /*String*/ position){ + // summary: + // places elements of this node list relative to the first element matched + // by queryOrNode. Returns the original NodeList. See: `dojo.place` + // queryOrNode: + // may be a string representing any valid CSS3 selector or a DOM node. + // In the selector case, only the first matching element will be used + // for relative positioning. + // position: + // can be one of: + // | "last" (default) + // | "first" + // | "before" + // | "after" + // | "only" + // | "replace" + // or an offset in the childNodes property + var item = d.query(queryOrNode)[0]; + return this.forEach(function(node){ d.place(node, item, position); }); // dojo.NodeList + }, + + orphan: function(/*String?*/ simpleFilter){ + // summary: + // removes elements in this list that match the simple filter + // from their parents and returns them as a new NodeList. + // simpleFilter: + // single-expression CSS rule. For example, ".thinger" or + // "#someId[attrName='value']" but not "div > span". In short, + // anything which does not invoke a descent to evaluate but + // can instead be used to test a single node is acceptable. + // returns: + // `dojo.NodeList` containing the orpahned elements + return (simpleFilter ? d._filterQueryResult(this, simpleFilter) : this).forEach(orphan); // dojo.NodeList + }, + + adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){ + // summary: + // places any/all elements in queryOrListOrNode at a + // position relative to the first element in this list. + // Returns a dojo.NodeList of the adopted elements. + // queryOrListOrNode: + // a DOM node or a query string or a query result. + // Represents the nodes to be adopted relative to the + // first element of this NodeList. + // position: + // can be one of: + // | "last" (default) + // | "first" + // | "before" + // | "after" + // | "only" + // | "replace" + // or an offset in the childNodes property + return d.query(queryOrListOrNode).place(this[0], position)._stash(this); // dojo.NodeList + }, + + // FIXME: do we need this? + query: function(/*String*/ queryStr){ + // summary: + // Returns a new list whose memebers match the passed query, + // assuming elements of the current NodeList as the root for + // each search. + // example: + // assume a DOM created by this markup: + // | <div id="foo"> + // | <p> + // | bacon is tasty, <span>dontcha think?</span> + // | </p> + // | </div> + // | <div id="bar"> + // | <p>great commedians may not be funny <span>in person</span></p> + // | </div> + // If we are presented with the following defintion for a NodeList: + // | var l = new dojo.NodeList(dojo.byId("foo"), dojo.byId("bar")); + // it's possible to find all span elements under paragraphs + // contained by these elements with this sub-query: + // | var spans = l.query("p span"); + + // FIXME: probably slow + if(!queryStr){ return this; } + var ret = this.map(function(node){ + // FIXME: why would we ever get undefined here? + return d.query(queryStr, node).filter(function(subNode){ return subNode !== undefined; }); + }); + return this._wrap(apc.apply([], ret), this); // dojo.NodeList + }, + + filter: function(/*String|Function*/ simpleFilter){ + // summary: + // "masks" the built-in javascript filter() method (supported + // in Dojo via `dojo.filter`) to support passing a simple + // string filter in addition to supporting filtering function + // objects. + // simpleFilter: + // If a string, a single-expression CSS rule. For example, + // ".thinger" or "#someId[attrName='value']" but not "div > + // span". In short, anything which does not invoke a descent + // to evaluate but can instead be used to test a single node + // is acceptable. + // example: + // "regular" JS filter syntax as exposed in dojo.filter: + // | dojo.query("*").filter(function(item){ + // | // highlight every paragraph + // | return (item.nodeName == "p"); + // | }).style("backgroundColor", "yellow"); + // example: + // the same filtering using a CSS selector + // | dojo.query("*").filter("p").styles("backgroundColor", "yellow"); + + var a = arguments, items = this, start = 0; + if(typeof simpleFilter == "string"){ // inline'd type check + items = d._filterQueryResult(this, a[0]); + if(a.length == 1){ + // if we only got a string query, pass back the filtered results + return items._stash(this); // dojo.NodeList + } + // if we got a callback, run it over the filtered items + start = 1; + } + return this._wrap(d.filter(items, a[start], a[start + 1]), this); // dojo.NodeList + }, + + /* + // FIXME: should this be "copyTo" and include parenting info? + clone: function(){ + // summary: + // creates node clones of each element of this list + // and returns a new list containing the clones + }, + */ + + addContent: function(/*String||DomNode||Object||dojo.NodeList*/ content, /*String||Integer?*/ position){ + // summary: + // add a node, NodeList or some HTML as a string to every item in the + // list. Returns the original list. + // description: + // a copy of the HTML content is added to each item in the + // list, with an optional position argument. If no position + // argument is provided, the content is appended to the end of + // each item. + // content: + // DOM node, HTML in string format, a NodeList or an Object. If a DOM node or + // NodeList, the content will be cloned if the current NodeList has more than one + // element. Only the DOM nodes are cloned, no event handlers. If it is an Object, + // it should be an object with at "template" String property that has the HTML string + // to insert. If dojo.string has already been dojo.required, then dojo.string.substitute + // will be used on the "template" to generate the final HTML string. Other allowed + // properties on the object are: "parse" if the HTML + // string should be parsed for widgets (dojo.require("dojo.parser") to get that + // option to work), and "templateFunc" if a template function besides dojo.string.substitute + // should be used to transform the "template". + // position: + // can be one of: + // | "last"||"end" (default) + // | "first||"start" + // | "before" + // | "after" + // | "replace" (replaces nodes in this NodeList with new content) + // | "only" (removes other children of the nodes so new content is hte only child) + // or an offset in the childNodes property + // example: + // appends content to the end if the position is ommitted + // | dojo.query("h3 > p").addContent("hey there!"); + // example: + // add something to the front of each element that has a + // "thinger" property: + // | dojo.query("[thinger]").addContent("...", "first"); + // example: + // adds a header before each element of the list + // | dojo.query(".note").addContent("<h4>NOTE:</h4>", "before"); + // example: + // add a clone of a DOM node to the end of every element in + // the list, removing it from its existing parent. + // | dojo.query(".note").addContent(dojo.byId("foo")); + // example: + // Append nodes from a templatized string. + // dojo.require("dojo.string"); + // dojo.query(".note").addContent({ + // template: '<b>${id}: </b><span>${name}</span>', + // id: "user332", + // name: "Mr. Anderson" + // }); + // example: + // Append nodes from a templatized string that also has widgets parsed. + // dojo.require("dojo.string"); + // dojo.require("dojo.parser"); + // var notes = dojo.query(".note").addContent({ + // template: '<button dojoType="dijit.form.Button">${text}</button>', + // parse: true, + // text: "Send" + // }); + content = this._normalize(content, this[0]); + for(var i = 0, node; node = this[i]; i++){ + this._place(content, node, position, i > 0); + } + return this; //dojo.NodeList + }, + + instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){ + // summary: + // Create a new instance of a specified class, using the + // specified properties and each node in the nodeList as a + // srcNodeRef. + // example: + // Grabs all buttons in the page and converts them to diji.form.Buttons. + // | var buttons = dojo.query("button").instantiate("dijit.form.Button", {showLabel: true}); + var c = d.isFunction(declaredClass) ? declaredClass : d.getObject(declaredClass); + properties = properties || {}; + return this.forEach(function(node){ + new c(properties, node); + }); // dojo.NodeList + }, + + at: function(/*===== index =====*/){ + // summary: + // Returns a new NodeList comprised of items in this NodeList + // at the given index or indices. + // + // index: Integer... + // One or more 0-based indices of items in the current + // NodeList. A negative index will start at the end of the + // list and go backwards. + // + // example: + // Shorten the list to the first, second, and third elements + // | dojo.query("a").at(0, 1, 2).forEach(fn); + // + // example: + // Retrieve the first and last elements of a unordered list: + // | dojo.query("ul > li").at(0, -1).forEach(cb); + // + // example: + // Do something for the first element only, but end() out back to + // the original list and continue chaining: + // | dojo.query("a").at(0).onclick(fn).end().forEach(function(n){ + // | console.log(n); // all anchors on the page. + // | }) + // + // returns: + // dojo.NodeList + var t = new this._NodeListCtor(); + d.forEach(arguments, function(i){ + if(i < 0){ i = this.length + i } + if(this[i]){ t.push(this[i]); } + }, this); + return t._stash(this); // dojo.NodeList + } + + }); + + nl.events = [ + // summary: list of all DOM events used in NodeList + "blur", "focus", "change", "click", "error", "keydown", "keypress", + "keyup", "load", "mousedown", "mouseenter", "mouseleave", "mousemove", + "mouseout", "mouseover", "mouseup", "submit" + ]; + + // FIXME: pseudo-doc the above automatically generated on-event functions + + // syntactic sugar for DOM events + d.forEach(nl.events, function(evt){ + var _oe = "on" + evt; + nlp[_oe] = function(a, b){ + return this.connect(_oe, a, b); + } + // FIXME: should these events trigger publishes? + /* + return (a ? this.connect(_oe, a, b) : + this.forEach(function(n){ + // FIXME: + // listeners get buried by + // addEventListener and can't be dug back + // out to be triggered externally. + // see: + // http://developer.mozilla.org/en/docs/DOM:element + + console.log(n, evt, _oe); + + // FIXME: need synthetic event support! + var _e = { target: n, faux: true, type: evt }; + // dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt }); + try{ n[evt](_e); }catch(e){ console.log(e); } + try{ n[_oe](_e); }catch(e){ console.log(e); } + }) + ); + */ + } + ); + })(); + } |