NodeList-dom.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. define("dojo/NodeList-dom", ["./_base/kernel", "./query", "./_base/array", "./_base/lang", "./dom-class", "./dom-construct", "./dom-geometry", "./dom-attr", "./dom-style"], function(dojo, query, array, lang, domCls, domCtr, domGeom, domAttr, domStyle){
  2. /*===== var NodeList = dojo.NodeList; =====*/
  3. var magicGuard = function(a){
  4. // summary:
  5. // the guard function for dojo.attr() and dojo.style()
  6. return a.length == 1 && (typeof a[0] == "string"); // inline'd type check
  7. };
  8. var orphan = function(node){
  9. // summary:
  10. // function to orphan nodes
  11. var p = node.parentNode;
  12. if(p){
  13. p.removeChild(node);
  14. }
  15. };
  16. // FIXME: should we move orphan() to dojo.html?
  17. var NodeList = query.NodeList,
  18. awc = NodeList._adaptWithCondition,
  19. aafe = NodeList._adaptAsForEach,
  20. aam = NodeList._adaptAsMap;
  21. function getSet(module){
  22. return function(node, name, value){
  23. if(arguments.length == 2){
  24. return module[typeof name == "string" ? "get" : "set"](node, name);
  25. }
  26. // setter
  27. return module.set(node, name, value);
  28. };
  29. }
  30. lang.extend(NodeList, {
  31. _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){
  32. // summary:
  33. // normalizes data to an array of items to insert.
  34. // description:
  35. // If content is an object, it can have special properties "template" and
  36. // "parse". If "template" is defined, then the template value is run through
  37. // dojo.string.substitute (if dojo.string.substitute has been dojo.required elsewhere),
  38. // or if templateFunc is a function on the content, that function will be used to
  39. // transform the template into a final string to be used for for passing to dojo._toDom.
  40. // If content.parse is true, then it is remembered for later, for when the content
  41. // nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets
  42. // (if dojo.parser has been dojo.required elsewhere).
  43. //Wanted to just use a DocumentFragment, but for the array/NodeList
  44. //case that meant using cloneNode, but we may not want that.
  45. //Cloning should only happen if the node operations span
  46. //multiple refNodes. Also, need a real array, not a NodeList from the
  47. //DOM since the node movements could change those NodeLists.
  48. var parse = content.parse === true;
  49. //Do we have an object that needs to be run through a template?
  50. if(typeof content.template == "string"){
  51. var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute);
  52. content = templateFunc ? templateFunc(content.template, content) : content;
  53. }
  54. var type = (typeof content);
  55. if(type == "string" || type == "number"){
  56. content = domCtr.toDom(content, (refNode && refNode.ownerDocument));
  57. if(content.nodeType == 11){
  58. //DocumentFragment. It cannot handle cloneNode calls, so pull out the children.
  59. content = lang._toArray(content.childNodes);
  60. }else{
  61. content = [content];
  62. }
  63. }else if(!lang.isArrayLike(content)){
  64. content = [content];
  65. }else if(!lang.isArray(content)){
  66. //To get to this point, content is array-like, but
  67. //not an array, which likely means a DOM NodeList. Convert it now.
  68. content = lang._toArray(content);
  69. }
  70. //Pass around the parse info
  71. if(parse){
  72. content._runParse = true;
  73. }
  74. return content; //Array
  75. },
  76. _cloneNode: function(/*DOMNode*/ node){
  77. // summary:
  78. // private utility to clone a node. Not very interesting in the vanilla
  79. // dojo.NodeList case, but delegates could do interesting things like
  80. // clone event handlers if that is derivable from the node.
  81. return node.cloneNode(true);
  82. },
  83. _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){
  84. // summary:
  85. // private utility to handle placing an array of nodes relative to another node.
  86. // description:
  87. // Allows for cloning the nodes in the array, and for
  88. // optionally parsing widgets, if ary._runParse is true.
  89. //Avoid a disallowed operation if trying to do an innerHTML on a non-element node.
  90. if(refNode.nodeType != 1 && position == "only"){
  91. return;
  92. }
  93. var rNode = refNode, tempNode;
  94. //Always cycle backwards in case the array is really a
  95. //DOM NodeList and the DOM operations take it out of the live collection.
  96. var length = ary.length;
  97. for(var i = length - 1; i >= 0; i--){
  98. var node = (useClone ? this._cloneNode(ary[i]) : ary[i]);
  99. //If need widget parsing, use a temp node, instead of waiting after inserting into
  100. //real DOM because we need to start widget parsing at one node up from current node,
  101. //which could cause some already parsed widgets to be parsed again.
  102. if(ary._runParse && dojo.parser && dojo.parser.parse){
  103. if(!tempNode){
  104. tempNode = rNode.ownerDocument.createElement("div");
  105. }
  106. tempNode.appendChild(node);
  107. dojo.parser.parse(tempNode);
  108. node = tempNode.firstChild;
  109. while(tempNode.firstChild){
  110. tempNode.removeChild(tempNode.firstChild);
  111. }
  112. }
  113. if(i == length - 1){
  114. domCtr.place(node, rNode, position);
  115. }else{
  116. rNode.parentNode.insertBefore(node, rNode);
  117. }
  118. rNode = node;
  119. }
  120. },
  121. /*=====
  122. position: function(){
  123. // summary:
  124. // Returns border-box objects (x/y/w/h) of all elements in a node list
  125. // as an Array (*not* a NodeList). Acts like `dojo.position`, though
  126. // assumes the node passed is each node in this list.
  127. return dojo.map(this, dojo.position); // Array
  128. },
  129. attr: function(property, value){
  130. // summary:
  131. // gets or sets the DOM attribute for every element in the
  132. // NodeList. See also `dojo.attr`
  133. // property: String
  134. // the attribute to get/set
  135. // value: String?
  136. // optional. The value to set the property to
  137. // returns:
  138. // if no value is passed, the result is an array of attribute values
  139. // If a value is passed, the return is this NodeList
  140. // example:
  141. // Make all nodes with a particular class focusable:
  142. // | dojo.query(".focusable").attr("tabIndex", -1);
  143. // example:
  144. // Disable a group of buttons:
  145. // | dojo.query("button.group").attr("disabled", true);
  146. // example:
  147. // innerHTML can be assigned or retrieved as well:
  148. // | // get the innerHTML (as an array) for each list item
  149. // | var ih = dojo.query("li.replaceable").attr("innerHTML");
  150. return; // dojo.NodeList
  151. return; // Array
  152. },
  153. style: function(property, value){
  154. // summary:
  155. // gets or sets the CSS property for every element in the NodeList
  156. // property: String
  157. // the CSS property to get/set, in JavaScript notation
  158. // ("lineHieght" instead of "line-height")
  159. // value: String?
  160. // optional. The value to set the property to
  161. // returns:
  162. // if no value is passed, the result is an array of strings.
  163. // If a value is passed, the return is this NodeList
  164. return; // dojo.NodeList
  165. return; // Array
  166. },
  167. addClass: function(className){
  168. // summary:
  169. // adds the specified class to every node in the list
  170. // className: String|Array
  171. // A String class name to add, or several space-separated class names,
  172. // or an array of class names.
  173. return; // dojo.NodeList
  174. },
  175. removeClass: function(className){
  176. // summary:
  177. // removes the specified class from every node in the list
  178. // className: String|Array?
  179. // An optional String class name to remove, or several space-separated
  180. // class names, or an array of class names. If omitted, all class names
  181. // will be deleted.
  182. // returns:
  183. // dojo.NodeList, this list
  184. return; // dojo.NodeList
  185. },
  186. toggleClass: function(className, condition){
  187. // summary:
  188. // Adds a class to node if not present, or removes if present.
  189. // Pass a boolean condition if you want to explicitly add or remove.
  190. // condition: Boolean?
  191. // If passed, true means to add the class, false means to remove.
  192. // className: String
  193. // the CSS class to add
  194. return; // dojo.NodeList
  195. },
  196. empty: function(){
  197. // summary:
  198. // clears all content from each node in the list. Effectively
  199. // equivalent to removing all child nodes from every item in
  200. // the list.
  201. return this.forEach("item.innerHTML='';"); // dojo.NodeList
  202. // FIXME: should we be checking for and/or disposing of widgets below these nodes?
  203. },
  204. =====*/
  205. // useful html methods
  206. attr: awc(getSet(domAttr), magicGuard),
  207. style: awc(getSet(domStyle), magicGuard),
  208. addClass: aafe(domCls.add),
  209. removeClass: aafe(domCls.remove),
  210. replaceClass: aafe(domCls.replace),
  211. toggleClass: aafe(domCls.toggle),
  212. empty: aafe(domCtr.empty),
  213. removeAttr: aafe(domAttr.remove),
  214. position: aam(domGeom.position),
  215. marginBox: aam(domGeom.getMarginBox),
  216. // FIXME: connectPublisher()? connectRunOnce()?
  217. /*
  218. destroy: function(){
  219. // summary:
  220. // destroys every item in the list.
  221. this.forEach(d.destroy);
  222. // FIXME: should we be checking for and/or disposing of widgets below these nodes?
  223. },
  224. */
  225. place: function(/*String||Node*/ queryOrNode, /*String*/ position){
  226. // summary:
  227. // places elements of this node list relative to the first element matched
  228. // by queryOrNode. Returns the original NodeList. See: `dojo.place`
  229. // queryOrNode:
  230. // may be a string representing any valid CSS3 selector or a DOM node.
  231. // In the selector case, only the first matching element will be used
  232. // for relative positioning.
  233. // position:
  234. // can be one of:
  235. // | "last" (default)
  236. // | "first"
  237. // | "before"
  238. // | "after"
  239. // | "only"
  240. // | "replace"
  241. // or an offset in the childNodes property
  242. var item = query(queryOrNode)[0];
  243. return this.forEach(function(node){ domCtr.place(node, item, position); }); // dojo.NodeList
  244. },
  245. orphan: function(/*String?*/ filter){
  246. // summary:
  247. // removes elements in this list that match the filter
  248. // from their parents and returns them as a new NodeList.
  249. // filter:
  250. // CSS selector like ".foo" or "div > span"
  251. // returns:
  252. // `dojo.NodeList` containing the orphaned elements
  253. return (filter ? query._filterResult(this, filter) : this).forEach(orphan); // dojo.NodeList
  254. },
  255. adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
  256. // summary:
  257. // places any/all elements in queryOrListOrNode at a
  258. // position relative to the first element in this list.
  259. // Returns a dojo.NodeList of the adopted elements.
  260. // queryOrListOrNode:
  261. // a DOM node or a query string or a query result.
  262. // Represents the nodes to be adopted relative to the
  263. // first element of this NodeList.
  264. // position:
  265. // can be one of:
  266. // | "last" (default)
  267. // | "first"
  268. // | "before"
  269. // | "after"
  270. // | "only"
  271. // | "replace"
  272. // or an offset in the childNodes property
  273. return query(queryOrListOrNode).place(this[0], position)._stash(this); // dojo.NodeList
  274. },
  275. // FIXME: do we need this?
  276. query: function(/*String*/ queryStr){
  277. // summary:
  278. // Returns a new list whose members match the passed query,
  279. // assuming elements of the current NodeList as the root for
  280. // each search.
  281. // example:
  282. // assume a DOM created by this markup:
  283. // | <div id="foo">
  284. // | <p>
  285. // | bacon is tasty, <span>dontcha think?</span>
  286. // | </p>
  287. // | </div>
  288. // | <div id="bar">
  289. // | <p>great comedians may not be funny <span>in person</span></p>
  290. // | </div>
  291. // If we are presented with the following definition for a NodeList:
  292. // | var l = new dojo.NodeList(dojo.byId("foo"), dojo.byId("bar"));
  293. // it's possible to find all span elements under paragraphs
  294. // contained by these elements with this sub-query:
  295. // | var spans = l.query("p span");
  296. // FIXME: probably slow
  297. if(!queryStr){ return this; }
  298. var ret = new NodeList;
  299. this.map(function(node){
  300. // FIXME: why would we ever get undefined here?
  301. query(queryStr, node).forEach(function(subNode){
  302. if(subNode !== undefined){
  303. ret.push(subNode);
  304. }
  305. });
  306. });
  307. return ret._stash(this); // dojo.NodeList
  308. },
  309. filter: function(/*String|Function*/ filter){
  310. // summary:
  311. // "masks" the built-in javascript filter() method (supported
  312. // in Dojo via `dojo.filter`) to support passing a simple
  313. // string filter in addition to supporting filtering function
  314. // objects.
  315. // filter:
  316. // If a string, a CSS rule like ".thinger" or "div > span".
  317. // example:
  318. // "regular" JS filter syntax as exposed in dojo.filter:
  319. // | dojo.query("*").filter(function(item){
  320. // | // highlight every paragraph
  321. // | return (item.nodeName == "p");
  322. // | }).style("backgroundColor", "yellow");
  323. // example:
  324. // the same filtering using a CSS selector
  325. // | dojo.query("*").filter("p").styles("backgroundColor", "yellow");
  326. var a = arguments, items = this, start = 0;
  327. if(typeof filter == "string"){ // inline'd type check
  328. items = query._filterResult(this, a[0]);
  329. if(a.length == 1){
  330. // if we only got a string query, pass back the filtered results
  331. return items._stash(this); // dojo.NodeList
  332. }
  333. // if we got a callback, run it over the filtered items
  334. start = 1;
  335. }
  336. return this._wrap(array.filter(items, a[start], a[start + 1]), this); // dojo.NodeList
  337. },
  338. /*
  339. // FIXME: should this be "copyTo" and include parenting info?
  340. clone: function(){
  341. // summary:
  342. // creates node clones of each element of this list
  343. // and returns a new list containing the clones
  344. },
  345. */
  346. addContent: function(/*String||DomNode||Object||dojo.NodeList*/ content, /*String||Integer?*/ position){
  347. // summary:
  348. // add a node, NodeList or some HTML as a string to every item in the
  349. // list. Returns the original list.
  350. // description:
  351. // a copy of the HTML content is added to each item in the
  352. // list, with an optional position argument. If no position
  353. // argument is provided, the content is appended to the end of
  354. // each item.
  355. // content:
  356. // DOM node, HTML in string format, a NodeList or an Object. If a DOM node or
  357. // NodeList, the content will be cloned if the current NodeList has more than one
  358. // element. Only the DOM nodes are cloned, no event handlers. If it is an Object,
  359. // it should be an object with at "template" String property that has the HTML string
  360. // to insert. If dojo.string has already been dojo.required, then dojo.string.substitute
  361. // will be used on the "template" to generate the final HTML string. Other allowed
  362. // properties on the object are: "parse" if the HTML
  363. // string should be parsed for widgets (dojo.require("dojo.parser") to get that
  364. // option to work), and "templateFunc" if a template function besides dojo.string.substitute
  365. // should be used to transform the "template".
  366. // position:
  367. // can be one of:
  368. // | "last"||"end" (default)
  369. // | "first||"start"
  370. // | "before"
  371. // | "after"
  372. // | "replace" (replaces nodes in this NodeList with new content)
  373. // | "only" (removes other children of the nodes so new content is the only child)
  374. // or an offset in the childNodes property
  375. // example:
  376. // appends content to the end if the position is omitted
  377. // | dojo.query("h3 > p").addContent("hey there!");
  378. // example:
  379. // add something to the front of each element that has a
  380. // "thinger" property:
  381. // | dojo.query("[thinger]").addContent("...", "first");
  382. // example:
  383. // adds a header before each element of the list
  384. // | dojo.query(".note").addContent("<h4>NOTE:</h4>", "before");
  385. // example:
  386. // add a clone of a DOM node to the end of every element in
  387. // the list, removing it from its existing parent.
  388. // | dojo.query(".note").addContent(dojo.byId("foo"));
  389. // example:
  390. // Append nodes from a templatized string.
  391. // dojo.require("dojo.string");
  392. // dojo.query(".note").addContent({
  393. // template: '<b>${id}: </b><span>${name}</span>',
  394. // id: "user332",
  395. // name: "Mr. Anderson"
  396. // });
  397. // example:
  398. // Append nodes from a templatized string that also has widgets parsed.
  399. // dojo.require("dojo.string");
  400. // dojo.require("dojo.parser");
  401. // var notes = dojo.query(".note").addContent({
  402. // template: '<button dojoType="dijit.form.Button">${text}</button>',
  403. // parse: true,
  404. // text: "Send"
  405. // });
  406. content = this._normalize(content, this[0]);
  407. for(var i = 0, node; (node = this[i]); i++){
  408. this._place(content, node, position, i > 0);
  409. }
  410. return this; //dojo.NodeList
  411. }
  412. });
  413. /*===== return dojo.NodeList; =====*/
  414. return NodeList;
  415. });