NodeList.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojo._base.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojo._base.NodeList"] = true;
  8. dojo.provide("dojo._base.NodeList");
  9. dojo.require("dojo._base.lang");
  10. dojo.require("dojo._base.array");
  11. dojo.require("dojo._base.connect");
  12. dojo.require("dojo._base.html");
  13. (function(){
  14. var d = dojo;
  15. var ap = Array.prototype, aps = ap.slice, apc = ap.concat;
  16. var tnl = function(/*Array*/ a, /*dojo.NodeList?*/ parent, /*Function?*/ NodeListCtor){
  17. // summary:
  18. // decorate an array to make it look like a `dojo.NodeList`.
  19. // a:
  20. // Array of nodes to decorate.
  21. // parent:
  22. // An optional parent NodeList that generated the current
  23. // list of nodes. Used to call _stash() so the parent NodeList
  24. // can be accessed via end() later.
  25. // NodeListCtor:
  26. // An optional constructor function to use for any
  27. // new NodeList calls. This allows a certain chain of
  28. // NodeList calls to use a different object than dojo.NodeList.
  29. if(!a.sort){
  30. // make sure it's a real array before we pass it on to be wrapped
  31. a = aps.call(a, 0);
  32. }
  33. var ctor = NodeListCtor || this._NodeListCtor || d._NodeListCtor;
  34. a.constructor = ctor;
  35. dojo._mixin(a, ctor.prototype);
  36. a._NodeListCtor = ctor;
  37. return parent ? a._stash(parent) : a;
  38. };
  39. var loopBody = function(f, a, o){
  40. a = [0].concat(aps.call(a, 0));
  41. o = o || d.global;
  42. return function(node){
  43. a[0] = node;
  44. return f.apply(o, a);
  45. };
  46. };
  47. // adapters
  48. var adaptAsForEach = function(f, o){
  49. // summary:
  50. // adapts a single node function to be used in the forEach-type
  51. // actions. The initial object is returned from the specialized
  52. // function.
  53. // f: Function
  54. // a function to adapt
  55. // o: Object?
  56. // an optional context for f
  57. return function(){
  58. this.forEach(loopBody(f, arguments, o));
  59. return this; // Object
  60. };
  61. };
  62. var adaptAsMap = function(f, o){
  63. // summary:
  64. // adapts a single node function to be used in the map-type
  65. // actions. The return is a new array of values, as via `dojo.map`
  66. // f: Function
  67. // a function to adapt
  68. // o: Object?
  69. // an optional context for f
  70. return function(){
  71. return this.map(loopBody(f, arguments, o));
  72. };
  73. };
  74. var adaptAsFilter = function(f, o){
  75. // summary:
  76. // adapts a single node function to be used in the filter-type actions
  77. // f: Function
  78. // a function to adapt
  79. // o: Object?
  80. // an optional context for f
  81. return function(){
  82. return this.filter(loopBody(f, arguments, o));
  83. };
  84. };
  85. var adaptWithCondition = function(f, g, o){
  86. // summary:
  87. // adapts a single node function to be used in the map-type
  88. // actions, behaves like forEach() or map() depending on arguments
  89. // f: Function
  90. // a function to adapt
  91. // g: Function
  92. // a condition function, if true runs as map(), otherwise runs as forEach()
  93. // o: Object?
  94. // an optional context for f and g
  95. return function(){
  96. var a = arguments, body = loopBody(f, a, o);
  97. if(g.call(o || d.global, a)){
  98. return this.map(body); // self
  99. }
  100. this.forEach(body);
  101. return this; // self
  102. };
  103. };
  104. var magicGuard = function(a){
  105. // summary:
  106. // the guard function for dojo.attr() and dojo.style()
  107. return a.length == 1 && (typeof a[0] == "string"); // inline'd type check
  108. };
  109. var orphan = function(node){
  110. // summary:
  111. // function to orphan nodes
  112. var p = node.parentNode;
  113. if(p){
  114. p.removeChild(node);
  115. }
  116. };
  117. // FIXME: should we move orphan() to dojo.html?
  118. dojo.NodeList = function(){
  119. // summary:
  120. // dojo.NodeList is an of Array subclass which adds syntactic
  121. // sugar for chaining, common iteration operations, animation, and
  122. // node manipulation. NodeLists are most often returned as the
  123. // result of dojo.query() calls.
  124. // description:
  125. // dojo.NodeList instances provide many utilities that reflect
  126. // core Dojo APIs for Array iteration and manipulation, DOM
  127. // manipulation, and event handling. Instead of needing to dig up
  128. // functions in the dojo.* namespace, NodeLists generally make the
  129. // full power of Dojo available for DOM manipulation tasks in a
  130. // simple, chainable way.
  131. // example:
  132. // create a node list from a node
  133. // | new dojo.NodeList(dojo.byId("foo"));
  134. // example:
  135. // get a NodeList from a CSS query and iterate on it
  136. // | var l = dojo.query(".thinger");
  137. // | l.forEach(function(node, index, nodeList){
  138. // | console.log(index, node.innerHTML);
  139. // | });
  140. // example:
  141. // use native and Dojo-provided array methods to manipulate a
  142. // NodeList without needing to use dojo.* functions explicitly:
  143. // | var l = dojo.query(".thinger");
  144. // | // since NodeLists are real arrays, they have a length
  145. // | // property that is both readable and writable and
  146. // | // push/pop/shift/unshift methods
  147. // | console.log(l.length);
  148. // | l.push(dojo.create("span"));
  149. // |
  150. // | // dojo's normalized array methods work too:
  151. // | console.log( l.indexOf(dojo.byId("foo")) );
  152. // | // ...including the special "function as string" shorthand
  153. // | console.log( l.every("item.nodeType == 1") );
  154. // |
  155. // | // NodeLists can be [..] indexed, or you can use the at()
  156. // | // function to get specific items wrapped in a new NodeList:
  157. // | var node = l[3]; // the 4th element
  158. // | var newList = l.at(1, 3); // the 2nd and 4th elements
  159. // example:
  160. // the style functions you expect are all there too:
  161. // | // style() as a getter...
  162. // | var borders = dojo.query(".thinger").style("border");
  163. // | // ...and as a setter:
  164. // | dojo.query(".thinger").style("border", "1px solid black");
  165. // | // class manipulation
  166. // | dojo.query("li:nth-child(even)").addClass("even");
  167. // | // even getting the coordinates of all the items
  168. // | var coords = dojo.query(".thinger").coords();
  169. // example:
  170. // DOM manipulation functions from the dojo.* namespace area also
  171. // available:
  172. // | // remove all of the elements in the list from their
  173. // | // parents (akin to "deleting" them from the document)
  174. // | dojo.query(".thinger").orphan();
  175. // | // place all elements in the list at the front of #foo
  176. // | dojo.query(".thinger").place("foo", "first");
  177. // example:
  178. // Event handling couldn't be easier. `dojo.connect` is mapped in,
  179. // and shortcut handlers are provided for most DOM events:
  180. // | // like dojo.connect(), but with implicit scope
  181. // | dojo.query("li").connect("onclick", console, "log");
  182. // |
  183. // | // many common event handlers are already available directly:
  184. // | dojo.query("li").onclick(console, "log");
  185. // | var toggleHovered = dojo.hitch(dojo, "toggleClass", "hovered");
  186. // | dojo.query("p")
  187. // | .onmouseenter(toggleHovered)
  188. // | .onmouseleave(toggleHovered);
  189. // example:
  190. // chainability is a key advantage of NodeLists:
  191. // | dojo.query(".thinger")
  192. // | .onclick(function(e){ /* ... */ })
  193. // | .at(1, 3, 8) // get a subset
  194. // | .style("padding", "5px")
  195. // | .forEach(console.log);
  196. return tnl(Array.apply(null, arguments));
  197. };
  198. //Allow things that new up a NodeList to use a delegated or alternate NodeList implementation.
  199. d._NodeListCtor = d.NodeList;
  200. var nl = d.NodeList, nlp = nl.prototype;
  201. // expose adapters and the wrapper as private functions
  202. nl._wrap = nlp._wrap = tnl;
  203. nl._adaptAsMap = adaptAsMap;
  204. nl._adaptAsForEach = adaptAsForEach;
  205. nl._adaptAsFilter = adaptAsFilter;
  206. nl._adaptWithCondition = adaptWithCondition;
  207. // mass assignment
  208. // add array redirectors
  209. d.forEach(["slice", "splice"], function(name){
  210. var f = ap[name];
  211. //Use a copy of the this array via this.slice() to allow .end() to work right in the splice case.
  212. // CANNOT apply ._stash()/end() to splice since it currently modifies
  213. // the existing this array -- it would break backward compatibility if we copy the array before
  214. // the splice so that we can use .end(). So only doing the stash option to this._wrap for slice.
  215. nlp[name] = function(){ return this._wrap(f.apply(this, arguments), name == "slice" ? this : null); };
  216. });
  217. // concat should be here but some browsers with native NodeList have problems with it
  218. // add array.js redirectors
  219. d.forEach(["indexOf", "lastIndexOf", "every", "some"], function(name){
  220. var f = d[name];
  221. nlp[name] = function(){ return f.apply(d, [this].concat(aps.call(arguments, 0))); };
  222. });
  223. // add conditional methods
  224. d.forEach(["attr", "style"], function(name){
  225. nlp[name] = adaptWithCondition(d[name], magicGuard);
  226. });
  227. // add forEach actions
  228. d.forEach(["connect", "addClass", "removeClass", "replaceClass", "toggleClass", "empty", "removeAttr"], function(name){
  229. nlp[name] = adaptAsForEach(d[name]);
  230. });
  231. dojo.extend(dojo.NodeList, {
  232. _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){
  233. // summary:
  234. // normalizes data to an array of items to insert.
  235. // description:
  236. // If content is an object, it can have special properties "template" and
  237. // "parse". If "template" is defined, then the template value is run through
  238. // dojo.string.substitute (if dojo.string.substitute has been dojo.required elsewhere),
  239. // or if templateFunc is a function on the content, that function will be used to
  240. // transform the template into a final string to be used for for passing to dojo._toDom.
  241. // If content.parse is true, then it is remembered for later, for when the content
  242. // nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets
  243. // (if dojo.parser has been dojo.required elsewhere).
  244. //Wanted to just use a DocumentFragment, but for the array/NodeList
  245. //case that meant using cloneNode, but we may not want that.
  246. //Cloning should only happen if the node operations span
  247. //multiple refNodes. Also, need a real array, not a NodeList from the
  248. //DOM since the node movements could change those NodeLists.
  249. var parse = content.parse === true ? true : false;
  250. //Do we have an object that needs to be run through a template?
  251. if(typeof content.template == "string"){
  252. var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute);
  253. content = templateFunc ? templateFunc(content.template, content) : content;
  254. }
  255. var type = (typeof content);
  256. if(type == "string" || type == "number"){
  257. content = dojo._toDom(content, (refNode && refNode.ownerDocument));
  258. if(content.nodeType == 11){
  259. //DocumentFragment. It cannot handle cloneNode calls, so pull out the children.
  260. content = dojo._toArray(content.childNodes);
  261. }else{
  262. content = [content];
  263. }
  264. }else if(!dojo.isArrayLike(content)){
  265. content = [content];
  266. }else if(!dojo.isArray(content)){
  267. //To get to this point, content is array-like, but
  268. //not an array, which likely means a DOM NodeList. Convert it now.
  269. content = dojo._toArray(content);
  270. }
  271. //Pass around the parse info
  272. if(parse){
  273. content._runParse = true;
  274. }
  275. return content; //Array
  276. },
  277. _cloneNode: function(/*DOMNode*/ node){
  278. // summary:
  279. // private utility to clone a node. Not very interesting in the vanilla
  280. // dojo.NodeList case, but delegates could do interesting things like
  281. // clone event handlers if that is derivable from the node.
  282. return node.cloneNode(true);
  283. },
  284. _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){
  285. // summary:
  286. // private utility to handle placing an array of nodes relative to another node.
  287. // description:
  288. // Allows for cloning the nodes in the array, and for
  289. // optionally parsing widgets, if ary._runParse is true.
  290. //Avoid a disallowed operation if trying to do an innerHTML on a non-element node.
  291. if(refNode.nodeType != 1 && position == "only"){
  292. return;
  293. }
  294. var rNode = refNode, tempNode;
  295. //Always cycle backwards in case the array is really a
  296. //DOM NodeList and the DOM operations take it out of the live collection.
  297. var length = ary.length;
  298. for(var i = length - 1; i >= 0; i--){
  299. var node = (useClone ? this._cloneNode(ary[i]) : ary[i]);
  300. //If need widget parsing, use a temp node, instead of waiting after inserting into
  301. //real DOM because we need to start widget parsing at one node up from current node,
  302. //which could cause some already parsed widgets to be parsed again.
  303. if(ary._runParse && dojo.parser && dojo.parser.parse){
  304. if(!tempNode){
  305. tempNode = rNode.ownerDocument.createElement("div");
  306. }
  307. tempNode.appendChild(node);
  308. dojo.parser.parse(tempNode);
  309. node = tempNode.firstChild;
  310. while(tempNode.firstChild){
  311. tempNode.removeChild(tempNode.firstChild);
  312. }
  313. }
  314. if(i == length - 1){
  315. dojo.place(node, rNode, position);
  316. }else{
  317. rNode.parentNode.insertBefore(node, rNode);
  318. }
  319. rNode = node;
  320. }
  321. },
  322. _stash: function(parent){
  323. // summary:
  324. // private function to hold to a parent NodeList. end() to return the parent NodeList.
  325. //
  326. // example:
  327. // How to make a `dojo.NodeList` method that only returns the third node in
  328. // the dojo.NodeList but allows access to the original NodeList by using this._stash:
  329. // | dojo.extend(dojo.NodeList, {
  330. // | third: function(){
  331. // | var newNodeList = dojo.NodeList(this[2]);
  332. // | return newNodeList._stash(this);
  333. // | }
  334. // | });
  335. // | // then see how _stash applies a sub-list, to be .end()'ed out of
  336. // | dojo.query(".foo")
  337. // | .third()
  338. // | .addClass("thirdFoo")
  339. // | .end()
  340. // | // access to the orig .foo list
  341. // | .removeClass("foo")
  342. // |
  343. //
  344. this._parent = parent;
  345. return this; //dojo.NodeList
  346. },
  347. end: function(){
  348. // summary:
  349. // Ends use of the current `dojo.NodeList` by returning the previous dojo.NodeList
  350. // that generated the current dojo.NodeList.
  351. // description:
  352. // Returns the `dojo.NodeList` that generated the current `dojo.NodeList`. If there
  353. // is no parent dojo.NodeList, an empty dojo.NodeList is returned.
  354. // example:
  355. // | dojo.query("a")
  356. // | .filter(".disabled")
  357. // | // operate on the anchors that only have a disabled class
  358. // | .style("color", "grey")
  359. // | .end()
  360. // | // jump back to the list of anchors
  361. // | .style(...)
  362. //
  363. if(this._parent){
  364. return this._parent;
  365. }else{
  366. //Just return empty list.
  367. return new this._NodeListCtor();
  368. }
  369. },
  370. // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods
  371. // FIXME: handle return values for #3244
  372. // http://trac.dojotoolkit.org/ticket/3244
  373. // FIXME:
  374. // need to wrap or implement:
  375. // join (perhaps w/ innerHTML/outerHTML overload for toString() of items?)
  376. // reduce
  377. // reduceRight
  378. /*=====
  379. slice: function(begin, end){
  380. // summary:
  381. // Returns a new NodeList, maintaining this one in place
  382. // description:
  383. // This method behaves exactly like the Array.slice method
  384. // with the caveat that it returns a dojo.NodeList and not a
  385. // raw Array. For more details, see Mozilla's (slice
  386. // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice]
  387. // begin: Integer
  388. // Can be a positive or negative integer, with positive
  389. // integers noting the offset to begin at, and negative
  390. // integers denoting an offset from the end (i.e., to the left
  391. // of the end)
  392. // end: Integer?
  393. // Optional parameter to describe what position relative to
  394. // the NodeList's zero index to end the slice at. Like begin,
  395. // can be positive or negative.
  396. return this._wrap(a.slice.apply(this, arguments));
  397. },
  398. splice: function(index, howmany, item){
  399. // summary:
  400. // Returns a new NodeList, manipulating this NodeList based on
  401. // the arguments passed, potentially splicing in new elements
  402. // at an offset, optionally deleting elements
  403. // description:
  404. // This method behaves exactly like the Array.splice method
  405. // with the caveat that it returns a dojo.NodeList and not a
  406. // raw Array. For more details, see Mozilla's (splice
  407. // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice]
  408. // For backwards compatibility, calling .end() on the spliced NodeList
  409. // does not return the original NodeList -- splice alters the NodeList in place.
  410. // index: Integer
  411. // begin can be a positive or negative integer, with positive
  412. // integers noting the offset to begin at, and negative
  413. // integers denoting an offset from the end (i.e., to the left
  414. // of the end)
  415. // howmany: Integer?
  416. // Optional parameter to describe what position relative to
  417. // the NodeList's zero index to end the slice at. Like begin,
  418. // can be positive or negative.
  419. // item: Object...?
  420. // Any number of optional parameters may be passed in to be
  421. // spliced into the NodeList
  422. // returns:
  423. // dojo.NodeList
  424. return this._wrap(a.splice.apply(this, arguments));
  425. },
  426. indexOf: function(value, fromIndex){
  427. // summary:
  428. // see dojo.indexOf(). The primary difference is that the acted-on
  429. // array is implicitly this NodeList
  430. // value: Object:
  431. // The value to search for.
  432. // fromIndex: Integer?:
  433. // The location to start searching from. Optional. Defaults to 0.
  434. // description:
  435. // For more details on the behavior of indexOf, see Mozilla's
  436. // (indexOf
  437. // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf]
  438. // returns:
  439. // Positive Integer or 0 for a match, -1 of not found.
  440. return d.indexOf(this, value, fromIndex); // Integer
  441. },
  442. lastIndexOf: function(value, fromIndex){
  443. // summary:
  444. // see dojo.lastIndexOf(). The primary difference is that the
  445. // acted-on array is implicitly this NodeList
  446. // description:
  447. // For more details on the behavior of lastIndexOf, see
  448. // Mozilla's (lastIndexOf
  449. // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf]
  450. // value: Object
  451. // The value to search for.
  452. // fromIndex: Integer?
  453. // The location to start searching from. Optional. Defaults to 0.
  454. // returns:
  455. // Positive Integer or 0 for a match, -1 of not found.
  456. return d.lastIndexOf(this, value, fromIndex); // Integer
  457. },
  458. every: function(callback, thisObject){
  459. // summary:
  460. // see `dojo.every()` and the (Array.every
  461. // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every].
  462. // Takes the same structure of arguments and returns as
  463. // dojo.every() with the caveat that the passed array is
  464. // implicitly this NodeList
  465. // callback: Function: the callback
  466. // thisObject: Object?: the context
  467. return d.every(this, callback, thisObject); // Boolean
  468. },
  469. some: function(callback, thisObject){
  470. // summary:
  471. // Takes the same structure of arguments and returns as
  472. // `dojo.some()` with the caveat that the passed array is
  473. // implicitly this NodeList. See `dojo.some()` and Mozilla's
  474. // (Array.some
  475. // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some].
  476. // callback: Function: the callback
  477. // thisObject: Object?: the context
  478. return d.some(this, callback, thisObject); // Boolean
  479. },
  480. =====*/
  481. concat: function(item){
  482. // summary:
  483. // Returns a new NodeList comprised of items in this NodeList
  484. // as well as items passed in as parameters
  485. // description:
  486. // This method behaves exactly like the Array.concat method
  487. // with the caveat that it returns a `dojo.NodeList` and not a
  488. // raw Array. For more details, see the (Array.concat
  489. // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat]
  490. // item: Object?
  491. // Any number of optional parameters may be passed in to be
  492. // spliced into the NodeList
  493. // returns:
  494. // dojo.NodeList
  495. //return this._wrap(apc.apply(this, arguments));
  496. // the line above won't work for the native NodeList :-(
  497. // implementation notes:
  498. // 1) Native NodeList is not an array, and cannot be used directly
  499. // in concat() --- the latter doesn't recognize it as an array, and
  500. // does not inline it, but append as a single entity.
  501. // 2) On some browsers (e.g., Safari) the "constructor" property is
  502. // read-only and cannot be changed. So we have to test for both
  503. // native NodeList and dojo.NodeList in this property to recognize
  504. // the node list.
  505. var t = d.isArray(this) ? this : aps.call(this, 0),
  506. m = d.map(arguments, function(a){
  507. return a && !d.isArray(a) &&
  508. (typeof NodeList != "undefined" && a.constructor === NodeList || a.constructor === this._NodeListCtor) ?
  509. aps.call(a, 0) : a;
  510. });
  511. return this._wrap(apc.apply(t, m), this); // dojo.NodeList
  512. },
  513. map: function(/*Function*/ func, /*Function?*/ obj){
  514. // summary:
  515. // see dojo.map(). The primary difference is that the acted-on
  516. // array is implicitly this NodeList and the return is a
  517. // dojo.NodeList (a subclass of Array)
  518. ///return d.map(this, func, obj, d.NodeList); // dojo.NodeList
  519. return this._wrap(d.map(this, func, obj), this); // dojo.NodeList
  520. },
  521. forEach: function(callback, thisObj){
  522. // summary:
  523. // see `dojo.forEach()`. The primary difference is that the acted-on
  524. // array is implicitly this NodeList. If you want the option to break out
  525. // of the forEach loop, use every() or some() instead.
  526. d.forEach(this, callback, thisObj);
  527. // non-standard return to allow easier chaining
  528. return this; // dojo.NodeList
  529. },
  530. /*=====
  531. coords: function(){
  532. // summary:
  533. // Returns the box objects of all elements in a node list as
  534. // an Array (*not* a NodeList). Acts like `dojo.coords`, though assumes
  535. // the node passed is each node in this list.
  536. return d.map(this, d.coords); // Array
  537. },
  538. position: function(){
  539. // summary:
  540. // Returns border-box objects (x/y/w/h) of all elements in a node list
  541. // as an Array (*not* a NodeList). Acts like `dojo.position`, though
  542. // assumes the node passed is each node in this list.
  543. return d.map(this, d.position); // Array
  544. },
  545. attr: function(property, value){
  546. // summary:
  547. // gets or sets the DOM attribute for every element in the
  548. // NodeList. See also `dojo.attr`
  549. // property: String
  550. // the attribute to get/set
  551. // value: String?
  552. // optional. The value to set the property to
  553. // returns:
  554. // if no value is passed, the result is an array of attribute values
  555. // If a value is passed, the return is this NodeList
  556. // example:
  557. // Make all nodes with a particular class focusable:
  558. // | dojo.query(".focusable").attr("tabIndex", -1);
  559. // example:
  560. // Disable a group of buttons:
  561. // | dojo.query("button.group").attr("disabled", true);
  562. // example:
  563. // innerHTML can be assigned or retrieved as well:
  564. // | // get the innerHTML (as an array) for each list item
  565. // | var ih = dojo.query("li.replaceable").attr("innerHTML");
  566. return; // dojo.NodeList
  567. return; // Array
  568. },
  569. style: function(property, value){
  570. // summary:
  571. // gets or sets the CSS property for every element in the NodeList
  572. // property: String
  573. // the CSS property to get/set, in JavaScript notation
  574. // ("lineHieght" instead of "line-height")
  575. // value: String?
  576. // optional. The value to set the property to
  577. // returns:
  578. // if no value is passed, the result is an array of strings.
  579. // If a value is passed, the return is this NodeList
  580. return; // dojo.NodeList
  581. return; // Array
  582. },
  583. addClass: function(className){
  584. // summary:
  585. // adds the specified class to every node in the list
  586. // className: String|Array
  587. // A String class name to add, or several space-separated class names,
  588. // or an array of class names.
  589. return; // dojo.NodeList
  590. },
  591. removeClass: function(className){
  592. // summary:
  593. // removes the specified class from every node in the list
  594. // className: String|Array?
  595. // An optional String class name to remove, or several space-separated
  596. // class names, or an array of class names. If omitted, all class names
  597. // will be deleted.
  598. // returns:
  599. // dojo.NodeList, this list
  600. return; // dojo.NodeList
  601. },
  602. toggleClass: function(className, condition){
  603. // summary:
  604. // Adds a class to node if not present, or removes if present.
  605. // Pass a boolean condition if you want to explicitly add or remove.
  606. // condition: Boolean?
  607. // If passed, true means to add the class, false means to remove.
  608. // className: String
  609. // the CSS class to add
  610. return; // dojo.NodeList
  611. },
  612. connect: function(methodName, objOrFunc, funcName){
  613. // summary:
  614. // attach event handlers to every item of the NodeList. Uses dojo.connect()
  615. // so event properties are normalized
  616. // methodName: String
  617. // the name of the method to attach to. For DOM events, this should be
  618. // the lower-case name of the event
  619. // objOrFunc: Object|Function|String
  620. // if 2 arguments are passed (methodName, objOrFunc), objOrFunc should
  621. // reference a function or be the name of the function in the global
  622. // namespace to attach. If 3 arguments are provided
  623. // (methodName, objOrFunc, funcName), objOrFunc must be the scope to
  624. // locate the bound function in
  625. // funcName: String?
  626. // optional. A string naming the function in objOrFunc to bind to the
  627. // event. May also be a function reference.
  628. // example:
  629. // add an onclick handler to every button on the page
  630. // | dojo.query("div:nth-child(odd)").connect("onclick", function(e){
  631. // | console.log("clicked!");
  632. // | });
  633. // example:
  634. // attach foo.bar() to every odd div's onmouseover
  635. // | dojo.query("div:nth-child(odd)").connect("onmouseover", foo, "bar");
  636. },
  637. empty: function(){
  638. // summary:
  639. // clears all content from each node in the list. Effectively
  640. // equivalent to removing all child nodes from every item in
  641. // the list.
  642. return this.forEach("item.innerHTML='';"); // dojo.NodeList
  643. // FIXME: should we be checking for and/or disposing of widgets below these nodes?
  644. },
  645. =====*/
  646. // useful html methods
  647. coords: adaptAsMap(d.coords),
  648. position: adaptAsMap(d.position),
  649. // FIXME: connectPublisher()? connectRunOnce()?
  650. /*
  651. destroy: function(){
  652. // summary:
  653. // destroys every item in the list.
  654. this.forEach(d.destroy);
  655. // FIXME: should we be checking for and/or disposing of widgets below these nodes?
  656. },
  657. */
  658. place: function(/*String||Node*/ queryOrNode, /*String*/ position){
  659. // summary:
  660. // places elements of this node list relative to the first element matched
  661. // by queryOrNode. Returns the original NodeList. See: `dojo.place`
  662. // queryOrNode:
  663. // may be a string representing any valid CSS3 selector or a DOM node.
  664. // In the selector case, only the first matching element will be used
  665. // for relative positioning.
  666. // position:
  667. // can be one of:
  668. // | "last" (default)
  669. // | "first"
  670. // | "before"
  671. // | "after"
  672. // | "only"
  673. // | "replace"
  674. // or an offset in the childNodes property
  675. var item = d.query(queryOrNode)[0];
  676. return this.forEach(function(node){ d.place(node, item, position); }); // dojo.NodeList
  677. },
  678. orphan: function(/*String?*/ filter){
  679. // summary:
  680. // removes elements in this list that match the filter
  681. // from their parents and returns them as a new NodeList.
  682. // filter:
  683. // CSS selector like ".foo" or "div > span"
  684. // returns:
  685. // `dojo.NodeList` containing the orphaned elements
  686. return (filter ? d._filterQueryResult(this, filter) : this).forEach(orphan); // dojo.NodeList
  687. },
  688. adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
  689. // summary:
  690. // places any/all elements in queryOrListOrNode at a
  691. // position relative to the first element in this list.
  692. // Returns a dojo.NodeList of the adopted elements.
  693. // queryOrListOrNode:
  694. // a DOM node or a query string or a query result.
  695. // Represents the nodes to be adopted relative to the
  696. // first element of this NodeList.
  697. // position:
  698. // can be one of:
  699. // | "last" (default)
  700. // | "first"
  701. // | "before"
  702. // | "after"
  703. // | "only"
  704. // | "replace"
  705. // or an offset in the childNodes property
  706. return d.query(queryOrListOrNode).place(this[0], position)._stash(this); // dojo.NodeList
  707. },
  708. // FIXME: do we need this?
  709. query: function(/*String*/ queryStr){
  710. // summary:
  711. // Returns a new list whose members match the passed query,
  712. // assuming elements of the current NodeList as the root for
  713. // each search.
  714. // example:
  715. // assume a DOM created by this markup:
  716. // | <div id="foo">
  717. // | <p>
  718. // | bacon is tasty, <span>dontcha think?</span>
  719. // | </p>
  720. // | </div>
  721. // | <div id="bar">
  722. // | <p>great comedians may not be funny <span>in person</span></p>
  723. // | </div>
  724. // If we are presented with the following definition for a NodeList:
  725. // | var l = new dojo.NodeList(dojo.byId("foo"), dojo.byId("bar"));
  726. // it's possible to find all span elements under paragraphs
  727. // contained by these elements with this sub-query:
  728. // | var spans = l.query("p span");
  729. // FIXME: probably slow
  730. if(!queryStr){ return this; }
  731. var ret = this.map(function(node){
  732. // FIXME: why would we ever get undefined here?
  733. return d.query(queryStr, node).filter(function(subNode){ return subNode !== undefined; });
  734. });
  735. return this._wrap(apc.apply([], ret), this); // dojo.NodeList
  736. },
  737. filter: function(/*String|Function*/ filter){
  738. // summary:
  739. // "masks" the built-in javascript filter() method (supported
  740. // in Dojo via `dojo.filter`) to support passing a simple
  741. // string filter in addition to supporting filtering function
  742. // objects.
  743. // filter:
  744. // If a string, a CSS rule like ".thinger" or "div > span".
  745. // example:
  746. // "regular" JS filter syntax as exposed in dojo.filter:
  747. // | dojo.query("*").filter(function(item){
  748. // | // highlight every paragraph
  749. // | return (item.nodeName == "p");
  750. // | }).style("backgroundColor", "yellow");
  751. // example:
  752. // the same filtering using a CSS selector
  753. // | dojo.query("*").filter("p").styles("backgroundColor", "yellow");
  754. var a = arguments, items = this, start = 0;
  755. if(typeof filter == "string"){ // inline'd type check
  756. items = d._filterQueryResult(this, a[0]);
  757. if(a.length == 1){
  758. // if we only got a string query, pass back the filtered results
  759. return items._stash(this); // dojo.NodeList
  760. }
  761. // if we got a callback, run it over the filtered items
  762. start = 1;
  763. }
  764. return this._wrap(d.filter(items, a[start], a[start + 1]), this); // dojo.NodeList
  765. },
  766. /*
  767. // FIXME: should this be "copyTo" and include parenting info?
  768. clone: function(){
  769. // summary:
  770. // creates node clones of each element of this list
  771. // and returns a new list containing the clones
  772. },
  773. */
  774. addContent: function(/*String||DomNode||Object||dojo.NodeList*/ content, /*String||Integer?*/ position){
  775. // summary:
  776. // add a node, NodeList or some HTML as a string to every item in the
  777. // list. Returns the original list.
  778. // description:
  779. // a copy of the HTML content is added to each item in the
  780. // list, with an optional position argument. If no position
  781. // argument is provided, the content is appended to the end of
  782. // each item.
  783. // content:
  784. // DOM node, HTML in string format, a NodeList or an Object. If a DOM node or
  785. // NodeList, the content will be cloned if the current NodeList has more than one
  786. // element. Only the DOM nodes are cloned, no event handlers. If it is an Object,
  787. // it should be an object with at "template" String property that has the HTML string
  788. // to insert. If dojo.string has already been dojo.required, then dojo.string.substitute
  789. // will be used on the "template" to generate the final HTML string. Other allowed
  790. // properties on the object are: "parse" if the HTML
  791. // string should be parsed for widgets (dojo.require("dojo.parser") to get that
  792. // option to work), and "templateFunc" if a template function besides dojo.string.substitute
  793. // should be used to transform the "template".
  794. // position:
  795. // can be one of:
  796. // | "last"||"end" (default)
  797. // | "first||"start"
  798. // | "before"
  799. // | "after"
  800. // | "replace" (replaces nodes in this NodeList with new content)
  801. // | "only" (removes other children of the nodes so new content is the only child)
  802. // or an offset in the childNodes property
  803. // example:
  804. // appends content to the end if the position is omitted
  805. // | dojo.query("h3 > p").addContent("hey there!");
  806. // example:
  807. // add something to the front of each element that has a
  808. // "thinger" property:
  809. // | dojo.query("[thinger]").addContent("...", "first");
  810. // example:
  811. // adds a header before each element of the list
  812. // | dojo.query(".note").addContent("<h4>NOTE:</h4>", "before");
  813. // example:
  814. // add a clone of a DOM node to the end of every element in
  815. // the list, removing it from its existing parent.
  816. // | dojo.query(".note").addContent(dojo.byId("foo"));
  817. // example:
  818. // Append nodes from a templatized string.
  819. // dojo.require("dojo.string");
  820. // dojo.query(".note").addContent({
  821. // template: '<b>${id}: </b><span>${name}</span>',
  822. // id: "user332",
  823. // name: "Mr. Anderson"
  824. // });
  825. // example:
  826. // Append nodes from a templatized string that also has widgets parsed.
  827. // dojo.require("dojo.string");
  828. // dojo.require("dojo.parser");
  829. // var notes = dojo.query(".note").addContent({
  830. // template: '<button dojoType="dijit.form.Button">${text}</button>',
  831. // parse: true,
  832. // text: "Send"
  833. // });
  834. content = this._normalize(content, this[0]);
  835. for(var i = 0, node; (node = this[i]); i++){
  836. this._place(content, node, position, i > 0);
  837. }
  838. return this; //dojo.NodeList
  839. },
  840. instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){
  841. // summary:
  842. // Create a new instance of a specified class, using the
  843. // specified properties and each node in the nodeList as a
  844. // srcNodeRef.
  845. // example:
  846. // Grabs all buttons in the page and converts them to diji.form.Buttons.
  847. // | var buttons = dojo.query("button").instantiate("dijit.form.Button", {showLabel: true});
  848. var c = d.isFunction(declaredClass) ? declaredClass : d.getObject(declaredClass);
  849. properties = properties || {};
  850. return this.forEach(function(node){
  851. new c(properties, node);
  852. }); // dojo.NodeList
  853. },
  854. at: function(/*===== index =====*/){
  855. // summary:
  856. // Returns a new NodeList comprised of items in this NodeList
  857. // at the given index or indices.
  858. //
  859. // index: Integer...
  860. // One or more 0-based indices of items in the current
  861. // NodeList. A negative index will start at the end of the
  862. // list and go backwards.
  863. //
  864. // example:
  865. // Shorten the list to the first, second, and third elements
  866. // | dojo.query("a").at(0, 1, 2).forEach(fn);
  867. //
  868. // example:
  869. // Retrieve the first and last elements of a unordered list:
  870. // | dojo.query("ul > li").at(0, -1).forEach(cb);
  871. //
  872. // example:
  873. // Do something for the first element only, but end() out back to
  874. // the original list and continue chaining:
  875. // | dojo.query("a").at(0).onclick(fn).end().forEach(function(n){
  876. // | console.log(n); // all anchors on the page.
  877. // | })
  878. //
  879. // returns:
  880. // dojo.NodeList
  881. var t = new this._NodeListCtor();
  882. d.forEach(arguments, function(i){
  883. if(i < 0){ i = this.length + i }
  884. if(this[i]){ t.push(this[i]); }
  885. }, this);
  886. return t._stash(this); // dojo.NodeList
  887. }
  888. });
  889. nl.events = [
  890. // summary:
  891. // list of all DOM events used in NodeList
  892. "blur", "focus", "change", "click", "error", "keydown", "keypress",
  893. "keyup", "load", "mousedown", "mouseenter", "mouseleave", "mousemove",
  894. "mouseout", "mouseover", "mouseup", "submit"
  895. ];
  896. // FIXME: pseudo-doc the above automatically generated on-event functions
  897. // syntactic sugar for DOM events
  898. d.forEach(nl.events, function(evt){
  899. var _oe = "on" + evt;
  900. nlp[_oe] = function(a, b){
  901. return this.connect(_oe, a, b);
  902. };
  903. // FIXME: should these events trigger publishes?
  904. /*
  905. return (a ? this.connect(_oe, a, b) :
  906. this.forEach(function(n){
  907. // FIXME:
  908. // listeners get buried by
  909. // addEventListener and can't be dug back
  910. // out to be triggered externally.
  911. // see:
  912. // http://developer.mozilla.org/en/docs/DOM:element
  913. console.log(n, evt, _oe);
  914. // FIXME: need synthetic event support!
  915. var _e = { target: n, faux: true, type: evt };
  916. // dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt });
  917. try{ n[evt](_e); }catch(e){ console.log(e); }
  918. try{ n[_oe](_e); }catch(e){ console.log(e); }
  919. })
  920. );
  921. */
  922. }
  923. );
  924. })();
  925. }