]> git.wh0rd.org - tt-rss.git/blobdiff - lib/dojo/_base/NodeList.js
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
[tt-rss.git] / lib / dojo / _base / NodeList.js
index 6df6db9764c63fb28dadf6fecb201aa799ee1125..12b631a4b439eef34930b6eeec1b1d4fcbbb7832 100644 (file)
 */
 
 
-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); }
+                                                       })
+                               );
+                               */
+               }
+       );
+
 })();
+
 }