]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dojo/query", ["./_base/kernel", "./has", "./dom", "./on", "./_base/array", "./_base/lang", "./selector/_loader", "./selector/_loader!default"], |
2 | function(dojo, has, dom, on, array, lang, loader, defaultEngine){ | |
3 | ||
4 | "use strict"; | |
5 | ||
6 | has.add("array-extensible", function(){ | |
7 | // test to see if we can extend an array (not supported in old IE) | |
8 | return lang.delegate([], {length: 1}).length == 1 && !has("bug-for-in-skips-shadowed"); | |
9 | }); | |
10 | ||
11 | var ap = Array.prototype, aps = ap.slice, apc = ap.concat, forEach = array.forEach; | |
12 | ||
13 | var tnl = function(/*Array*/ a, /*dojo/NodeList?*/ parent, /*Function?*/ NodeListCtor){ | |
14 | // summary: | |
15 | // decorate an array to make it look like a `dojo/NodeList`. | |
16 | // a: | |
17 | // Array of nodes to decorate. | |
18 | // parent: | |
19 | // An optional parent NodeList that generated the current | |
20 | // list of nodes. Used to call _stash() so the parent NodeList | |
21 | // can be accessed via end() later. | |
22 | // NodeListCtor: | |
23 | // An optional constructor function to use for any | |
24 | // new NodeList calls. This allows a certain chain of | |
25 | // NodeList calls to use a different object than dojo/NodeList. | |
26 | var nodeList = new (NodeListCtor || this._NodeListCtor || nl)(a); | |
27 | return parent ? nodeList._stash(parent) : nodeList; | |
28 | }; | |
29 | ||
30 | var loopBody = function(f, a, o){ | |
31 | a = [0].concat(aps.call(a, 0)); | |
32 | o = o || dojo.global; | |
33 | return function(node){ | |
34 | a[0] = node; | |
35 | return f.apply(o, a); | |
36 | }; | |
37 | }; | |
38 | ||
39 | // adapters | |
40 | ||
41 | var adaptAsForEach = function(f, o){ | |
42 | // summary: | |
43 | // adapts a single node function to be used in the forEach-type | |
44 | // actions. The initial object is returned from the specialized | |
45 | // function. | |
46 | // f: Function | |
47 | // a function to adapt | |
48 | // o: Object? | |
49 | // an optional context for f | |
50 | return function(){ | |
51 | this.forEach(loopBody(f, arguments, o)); | |
52 | return this; // Object | |
53 | }; | |
54 | }; | |
55 | ||
56 | var adaptAsMap = function(f, o){ | |
57 | // summary: | |
58 | // adapts a single node function to be used in the map-type | |
59 | // actions. The return is a new array of values, as via `dojo.map` | |
60 | // f: Function | |
61 | // a function to adapt | |
62 | // o: Object? | |
63 | // an optional context for f | |
64 | return function(){ | |
65 | return this.map(loopBody(f, arguments, o)); | |
66 | }; | |
67 | }; | |
68 | ||
69 | var adaptAsFilter = function(f, o){ | |
70 | // summary: | |
71 | // adapts a single node function to be used in the filter-type actions | |
72 | // f: Function | |
73 | // a function to adapt | |
74 | // o: Object? | |
75 | // an optional context for f | |
76 | return function(){ | |
77 | return this.filter(loopBody(f, arguments, o)); | |
78 | }; | |
79 | }; | |
80 | ||
81 | var adaptWithCondition = function(f, g, o){ | |
82 | // summary: | |
83 | // adapts a single node function to be used in the map-type | |
84 | // actions, behaves like forEach() or map() depending on arguments | |
85 | // f: Function | |
86 | // a function to adapt | |
87 | // g: Function | |
88 | // a condition function, if true runs as map(), otherwise runs as forEach() | |
89 | // o: Object? | |
90 | // an optional context for f and g | |
91 | return function(){ | |
92 | var a = arguments, body = loopBody(f, a, o); | |
93 | if(g.call(o || dojo.global, a)){ | |
94 | return this.map(body); // self | |
95 | } | |
96 | this.forEach(body); | |
97 | return this; // self | |
98 | }; | |
99 | }; | |
100 | ||
101 | var NodeList = function(array){ | |
102 | // summary: | |
103 | // Array-like object which adds syntactic | |
104 | // sugar for chaining, common iteration operations, animation, and | |
105 | // node manipulation. NodeLists are most often returned as the | |
106 | // result of dojo.query() calls. | |
107 | // description: | |
108 | // NodeList instances provide many utilities that reflect | |
109 | // core Dojo APIs for Array iteration and manipulation, DOM | |
110 | // manipulation, and event handling. Instead of needing to dig up | |
111 | // functions in the dojo.* namespace, NodeLists generally make the | |
112 | // full power of Dojo available for DOM manipulation tasks in a | |
113 | // simple, chainable way. | |
114 | // example: | |
115 | // create a node list from a node | |
116 | // | new query.NodeList(dojo.byId("foo")); | |
117 | // example: | |
118 | // get a NodeList from a CSS query and iterate on it | |
119 | // | var l = dojo.query(".thinger"); | |
120 | // | l.forEach(function(node, index, nodeList){ | |
121 | // | console.log(index, node.innerHTML); | |
122 | // | }); | |
123 | // example: | |
124 | // use native and Dojo-provided array methods to manipulate a | |
125 | // NodeList without needing to use dojo.* functions explicitly: | |
126 | // | var l = dojo.query(".thinger"); | |
127 | // | // since NodeLists are real arrays, they have a length | |
128 | // | // property that is both readable and writable and | |
129 | // | // push/pop/shift/unshift methods | |
130 | // | console.log(l.length); | |
131 | // | l.push(dojo.create("span")); | |
132 | // | | |
133 | // | // dojo's normalized array methods work too: | |
134 | // | console.log( l.indexOf(dojo.byId("foo")) ); | |
135 | // | // ...including the special "function as string" shorthand | |
136 | // | console.log( l.every("item.nodeType == 1") ); | |
137 | // | | |
138 | // | // NodeLists can be [..] indexed, or you can use the at() | |
139 | // | // function to get specific items wrapped in a new NodeList: | |
140 | // | var node = l[3]; // the 4th element | |
141 | // | var newList = l.at(1, 3); // the 2nd and 4th elements | |
142 | // example: | |
143 | // the style functions you expect are all there too: | |
144 | // | // style() as a getter... | |
145 | // | var borders = dojo.query(".thinger").style("border"); | |
146 | // | // ...and as a setter: | |
147 | // | dojo.query(".thinger").style("border", "1px solid black"); | |
148 | // | // class manipulation | |
149 | // | dojo.query("li:nth-child(even)").addClass("even"); | |
150 | // | // even getting the coordinates of all the items | |
151 | // | var coords = dojo.query(".thinger").coords(); | |
152 | // example: | |
153 | // DOM manipulation functions from the dojo.* namespace area also available: | |
154 | // | // remove all of the elements in the list from their | |
155 | // | // parents (akin to "deleting" them from the document) | |
156 | // | dojo.query(".thinger").orphan(); | |
157 | // | // place all elements in the list at the front of #foo | |
158 | // | dojo.query(".thinger").place("foo", "first"); | |
159 | // example: | |
160 | // Event handling couldn't be easier. `dojo.connect` is mapped in, | |
161 | // and shortcut handlers are provided for most DOM events: | |
162 | // | // like dojo.connect(), but with implicit scope | |
163 | // | dojo.query("li").connect("onclick", console, "log"); | |
164 | // | | |
165 | // | // many common event handlers are already available directly: | |
166 | // | dojo.query("li").onclick(console, "log"); | |
167 | // | var toggleHovered = dojo.hitch(dojo, "toggleClass", "hovered"); | |
168 | // | dojo.query("p") | |
169 | // | .onmouseenter(toggleHovered) | |
170 | // | .onmouseleave(toggleHovered); | |
171 | // example: | |
172 | // chainability is a key advantage of NodeLists: | |
173 | // | dojo.query(".thinger") | |
174 | // | .onclick(function(e){ /* ... */ }) | |
175 | // | .at(1, 3, 8) // get a subset | |
176 | // | .style("padding", "5px") | |
177 | // | .forEach(console.log); | |
178 | var isNew = this instanceof nl && has("array-extensible"); | |
179 | if(typeof array == "number"){ | |
180 | array = Array(array); | |
181 | } | |
182 | var nodeArray = (array && "length" in array) ? array : arguments; | |
183 | if(isNew || !nodeArray.sort){ | |
184 | // make sure it's a real array before we pass it on to be wrapped | |
185 | var target = isNew ? this : [], | |
186 | l = target.length = nodeArray.length; | |
187 | for(var i = 0; i < l; i++){ | |
188 | target[i] = nodeArray[i]; | |
189 | } | |
190 | if(isNew){ | |
191 | // called with new operator, this means we are going to use this instance and push | |
192 | // the nodes on to it. This is usually much faster since the NodeList properties | |
193 | // don't need to be copied (unless the list of nodes is extremely large). | |
194 | return target; | |
195 | } | |
196 | nodeArray = target; | |
197 | } | |
198 | // called without new operator, use a real array and copy prototype properties, | |
199 | // this is slower and exists for back-compat. Should be removed in 2.0. | |
200 | lang._mixin(nodeArray, nlp); | |
201 | nodeArray._NodeListCtor = function(array){ | |
202 | // call without new operator to preserve back-compat behavior | |
203 | return nl(array); | |
204 | }; | |
205 | return nodeArray; | |
206 | }; | |
207 | ||
208 | var nl = NodeList, nlp = nl.prototype = | |
209 | has("array-extensible") ? [] : {};// extend an array if it is extensible | |
210 | ||
211 | // expose adapters and the wrapper as private functions | |
212 | ||
213 | nl._wrap = nlp._wrap = tnl; | |
214 | nl._adaptAsMap = adaptAsMap; | |
215 | nl._adaptAsForEach = adaptAsForEach; | |
216 | nl._adaptAsFilter = adaptAsFilter; | |
217 | nl._adaptWithCondition = adaptWithCondition; | |
218 | ||
219 | // mass assignment | |
220 | ||
221 | // add array redirectors | |
222 | forEach(["slice", "splice"], function(name){ | |
223 | var f = ap[name]; | |
224 | //Use a copy of the this array via this.slice() to allow .end() to work right in the splice case. | |
225 | // CANNOT apply ._stash()/end() to splice since it currently modifies | |
226 | // the existing this array -- it would break backward compatibility if we copy the array before | |
227 | // the splice so that we can use .end(). So only doing the stash option to this._wrap for slice. | |
228 | nlp[name] = function(){ return this._wrap(f.apply(this, arguments), name == "slice" ? this : null); }; | |
229 | }); | |
230 | // concat should be here but some browsers with native NodeList have problems with it | |
231 | ||
232 | // add array.js redirectors | |
233 | forEach(["indexOf", "lastIndexOf", "every", "some"], function(name){ | |
234 | var f = array[name]; | |
235 | nlp[name] = function(){ return f.apply(dojo, [this].concat(aps.call(arguments, 0))); }; | |
236 | }); | |
237 | ||
238 | lang.extend(NodeList, { | |
239 | // copy the constructors | |
240 | constructor: nl, | |
241 | _NodeListCtor: nl, | |
242 | toString: function(){ | |
243 | // Array.prototype.toString can't be applied to objects, so we use join | |
244 | return this.join(","); | |
245 | }, | |
246 | _stash: function(parent){ | |
247 | // summary: | |
248 | // private function to hold to a parent NodeList. end() to return the parent NodeList. | |
249 | // | |
250 | // example: | |
251 | // How to make a `dojo/NodeList` method that only returns the third node in | |
252 | // the dojo/NodeList but allows access to the original NodeList by using this._stash: | |
253 | // | dojo.extend(NodeList, { | |
254 | // | third: function(){ | |
255 | // | var newNodeList = NodeList(this[2]); | |
256 | // | return newNodeList._stash(this); | |
257 | // | } | |
258 | // | }); | |
259 | // | // then see how _stash applies a sub-list, to be .end()'ed out of | |
260 | // | dojo.query(".foo") | |
261 | // | .third() | |
262 | // | .addClass("thirdFoo") | |
263 | // | .end() | |
264 | // | // access to the orig .foo list | |
265 | // | .removeClass("foo") | |
266 | // | | |
267 | // | |
268 | this._parent = parent; | |
269 | return this; // dojo/NodeList | |
270 | }, | |
271 | ||
272 | on: function(eventName, listener){ | |
273 | // summary: | |
274 | // Listen for events on the nodes in the NodeList. Basic usage is: | |
275 | // | query(".my-class").on("click", listener); | |
276 | // This supports event delegation by using selectors as the first argument with the event names as | |
277 | // pseudo selectors. For example: | |
278 | // | dojo.query("#my-list").on("li:click", listener); | |
279 | // This will listen for click events within `<li>` elements that are inside the `#my-list` element. | |
280 | // Because on supports CSS selector syntax, we can use comma-delimited events as well: | |
281 | // | dojo.query("#my-list").on("li button:mouseover, li:click", listener); | |
282 | var handles = this.map(function(node){ | |
283 | return on(node, eventName, listener); // TODO: apply to the NodeList so the same selector engine is used for matches | |
284 | }); | |
285 | handles.remove = function(){ | |
286 | for(var i = 0; i < handles.length; i++){ | |
287 | handles[i].remove(); | |
288 | } | |
289 | }; | |
290 | return handles; | |
291 | }, | |
292 | ||
293 | end: function(){ | |
294 | // summary: | |
295 | // Ends use of the current `NodeList` by returning the previous NodeList | |
296 | // that generated the current NodeList. | |
297 | // description: | |
298 | // Returns the `NodeList` that generated the current `NodeList`. If there | |
299 | // is no parent NodeList, an empty NodeList is returned. | |
300 | // example: | |
301 | // | dojo.query("a") | |
302 | // | .filter(".disabled") | |
303 | // | // operate on the anchors that only have a disabled class | |
304 | // | .style("color", "grey") | |
305 | // | .end() | |
306 | // | // jump back to the list of anchors | |
307 | // | .style(...) | |
308 | // | |
309 | if(this._parent){ | |
310 | return this._parent; | |
311 | }else{ | |
312 | //Just return empty list. | |
313 | return new this._NodeListCtor(0); | |
314 | } | |
315 | }, | |
316 | ||
317 | // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods | |
318 | ||
319 | // FIXME: handle return values for #3244 | |
320 | // http://trac.dojotoolkit.org/ticket/3244 | |
321 | ||
322 | // FIXME: | |
323 | // need to wrap or implement: | |
324 | // join (perhaps w/ innerHTML/outerHTML overload for toString() of items?) | |
325 | // reduce | |
326 | // reduceRight | |
327 | ||
328 | /*===== | |
329 | slice: function(begin, end){ | |
330 | // summary: | |
331 | // Returns a new NodeList, maintaining this one in place | |
332 | // description: | |
333 | // This method behaves exactly like the Array.slice method | |
334 | // with the caveat that it returns a dojo/NodeList and not a | |
335 | // raw Array. For more details, see Mozilla's [slice | |
336 | // documentation](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/slice) | |
337 | // begin: Integer | |
338 | // Can be a positive or negative integer, with positive | |
339 | // integers noting the offset to begin at, and negative | |
340 | // integers denoting an offset from the end (i.e., to the left | |
341 | // of the end) | |
342 | // end: Integer? | |
343 | // Optional parameter to describe what position relative to | |
344 | // the NodeList's zero index to end the slice at. Like begin, | |
345 | // can be positive or negative. | |
346 | return this._wrap(a.slice.apply(this, arguments)); | |
347 | }, | |
348 | ||
349 | splice: function(index, howmany, item){ | |
350 | // summary: | |
351 | // Returns a new NodeList, manipulating this NodeList based on | |
352 | // the arguments passed, potentially splicing in new elements | |
353 | // at an offset, optionally deleting elements | |
354 | // description: | |
355 | // This method behaves exactly like the Array.splice method | |
356 | // with the caveat that it returns a dojo/NodeList and not a | |
357 | // raw Array. For more details, see Mozilla's [splice | |
358 | // documentation](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) | |
359 | // For backwards compatibility, calling .end() on the spliced NodeList | |
360 | // does not return the original NodeList -- splice alters the NodeList in place. | |
361 | // index: Integer | |
362 | // begin can be a positive or negative integer, with positive | |
363 | // integers noting the offset to begin at, and negative | |
364 | // integers denoting an offset from the end (i.e., to the left | |
365 | // of the end) | |
366 | // howmany: Integer? | |
367 | // Optional parameter to describe what position relative to | |
368 | // the NodeList's zero index to end the slice at. Like begin, | |
369 | // can be positive or negative. | |
370 | // item: Object...? | |
371 | // Any number of optional parameters may be passed in to be | |
372 | // spliced into the NodeList | |
373 | return this._wrap(a.splice.apply(this, arguments)); // dojo/NodeList | |
374 | }, | |
375 | ||
376 | indexOf: function(value, fromIndex){ | |
377 | // summary: | |
378 | // see dojo.indexOf(). The primary difference is that the acted-on | |
379 | // array is implicitly this NodeList | |
380 | // value: Object | |
381 | // The value to search for. | |
382 | // fromIndex: Integer? | |
383 | // The location to start searching from. Optional. Defaults to 0. | |
384 | // description: | |
385 | // For more details on the behavior of indexOf, see Mozilla's | |
386 | // [indexOf | |
387 | // docs](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf) | |
388 | // returns: | |
389 | // Positive Integer or 0 for a match, -1 of not found. | |
390 | return d.indexOf(this, value, fromIndex); // Integer | |
391 | }, | |
392 | ||
393 | lastIndexOf: function(value, fromIndex){ | |
394 | // summary: | |
395 | // see dojo.lastIndexOf(). The primary difference is that the | |
396 | // acted-on array is implicitly this NodeList | |
397 | // description: | |
398 | // For more details on the behavior of lastIndexOf, see | |
399 | // Mozilla's [lastIndexOf | |
400 | // docs](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf) | |
401 | // value: Object | |
402 | // The value to search for. | |
403 | // fromIndex: Integer? | |
404 | // The location to start searching from. Optional. Defaults to 0. | |
405 | // returns: | |
406 | // Positive Integer or 0 for a match, -1 of not found. | |
407 | return d.lastIndexOf(this, value, fromIndex); // Integer | |
408 | }, | |
409 | ||
410 | every: function(callback, thisObject){ | |
411 | // summary: | |
412 | // see `dojo.every()` and the [Array.every | |
413 | // docs](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every). | |
414 | // Takes the same structure of arguments and returns as | |
415 | // dojo.every() with the caveat that the passed array is | |
416 | // implicitly this NodeList | |
417 | // callback: Function | |
418 | // the callback | |
419 | // thisObject: Object? | |
420 | // the context | |
421 | return d.every(this, callback, thisObject); // Boolean | |
422 | }, | |
423 | ||
424 | some: function(callback, thisObject){ | |
425 | // summary: | |
426 | // Takes the same structure of arguments and returns as | |
427 | // `dojo.some()` with the caveat that the passed array is | |
428 | // implicitly this NodeList. See `dojo.some()` and Mozilla's | |
429 | // [Array.some | |
430 | // documentation](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some). | |
431 | // callback: Function | |
432 | // the callback | |
433 | // thisObject: Object? | |
434 | // the context | |
435 | return d.some(this, callback, thisObject); // Boolean | |
436 | }, | |
437 | =====*/ | |
438 | ||
439 | concat: function(item){ | |
440 | // summary: | |
441 | // Returns a new NodeList comprised of items in this NodeList | |
442 | // as well as items passed in as parameters | |
443 | // description: | |
444 | // This method behaves exactly like the Array.concat method | |
445 | // with the caveat that it returns a `NodeList` and not a | |
446 | // raw Array. For more details, see the [Array.concat | |
447 | // docs](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/concat) | |
448 | // item: Object? | |
449 | // Any number of optional parameters may be passed in to be | |
450 | // spliced into the NodeList | |
451 | ||
452 | //return this._wrap(apc.apply(this, arguments)); | |
453 | // the line above won't work for the native NodeList, or for Dojo NodeLists either :-( | |
454 | ||
455 | // implementation notes: | |
456 | // Array.concat() doesn't recognize native NodeLists or Dojo NodeLists | |
457 | // as arrays, and so does not inline them into a unioned array, but | |
458 | // appends them as single entities. Both the original NodeList and the | |
459 | // items passed in as parameters must be converted to raw Arrays | |
460 | // and then the concatenation result may be re-_wrap()ed as a Dojo NodeList. | |
461 | ||
462 | var t = aps.call(this, 0), | |
463 | m = array.map(arguments, function(a){ | |
464 | return aps.call(a, 0); | |
465 | }); | |
466 | return this._wrap(apc.apply(t, m), this); // dojo/NodeList | |
467 | }, | |
468 | ||
469 | map: function(/*Function*/ func, /*Function?*/ obj){ | |
470 | // summary: | |
471 | // see dojo.map(). The primary difference is that the acted-on | |
472 | // array is implicitly this NodeList and the return is a | |
473 | // NodeList (a subclass of Array) | |
474 | return this._wrap(array.map(this, func, obj), this); // dojo/NodeList | |
475 | }, | |
476 | ||
477 | forEach: function(callback, thisObj){ | |
478 | // summary: | |
479 | // see `dojo.forEach()`. The primary difference is that the acted-on | |
480 | // array is implicitly this NodeList. If you want the option to break out | |
481 | // of the forEach loop, use every() or some() instead. | |
482 | forEach(this, callback, thisObj); | |
483 | // non-standard return to allow easier chaining | |
484 | return this; // dojo/NodeList | |
485 | }, | |
486 | filter: function(/*String|Function*/ filter){ | |
487 | // summary: | |
488 | // "masks" the built-in javascript filter() method (supported | |
489 | // in Dojo via `dojo.filter`) to support passing a simple | |
490 | // string filter in addition to supporting filtering function | |
491 | // objects. | |
492 | // filter: | |
493 | // If a string, a CSS rule like ".thinger" or "div > span". | |
494 | // example: | |
495 | // "regular" JS filter syntax as exposed in dojo.filter: | |
496 | // | dojo.query("*").filter(function(item){ | |
497 | // | // highlight every paragraph | |
498 | // | return (item.nodeName == "p"); | |
499 | // | }).style("backgroundColor", "yellow"); | |
500 | // example: | |
501 | // the same filtering using a CSS selector | |
502 | // | dojo.query("*").filter("p").styles("backgroundColor", "yellow"); | |
503 | ||
504 | var a = arguments, items = this, start = 0; | |
505 | if(typeof filter == "string"){ // inline'd type check | |
506 | items = query._filterResult(this, a[0]); | |
507 | if(a.length == 1){ | |
508 | // if we only got a string query, pass back the filtered results | |
509 | return items._stash(this); // dojo/NodeList | |
510 | } | |
511 | // if we got a callback, run it over the filtered items | |
512 | start = 1; | |
513 | } | |
514 | return this._wrap(array.filter(items, a[start], a[start + 1]), this); // dojo/NodeList | |
515 | }, | |
516 | instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){ | |
517 | // summary: | |
518 | // Create a new instance of a specified class, using the | |
519 | // specified properties and each node in the NodeList as a | |
520 | // srcNodeRef. | |
521 | // example: | |
522 | // Grabs all buttons in the page and converts them to dijit/form/Button's. | |
523 | // | var buttons = query("button").instantiate(Button, {showLabel: true}); | |
524 | var c = lang.isFunction(declaredClass) ? declaredClass : lang.getObject(declaredClass); | |
525 | properties = properties || {}; | |
526 | return this.forEach(function(node){ | |
527 | new c(properties, node); | |
528 | }); // dojo/NodeList | |
529 | }, | |
530 | at: function(/*===== index =====*/){ | |
531 | // summary: | |
532 | // Returns a new NodeList comprised of items in this NodeList | |
533 | // at the given index or indices. | |
534 | // | |
535 | // index: Integer... | |
536 | // One or more 0-based indices of items in the current | |
537 | // NodeList. A negative index will start at the end of the | |
538 | // list and go backwards. | |
539 | // | |
540 | // example: | |
541 | // Shorten the list to the first, second, and third elements | |
542 | // | query("a").at(0, 1, 2).forEach(fn); | |
543 | // | |
544 | // example: | |
545 | // Retrieve the first and last elements of a unordered list: | |
546 | // | query("ul > li").at(0, -1).forEach(cb); | |
547 | // | |
548 | // example: | |
549 | // Do something for the first element only, but end() out back to | |
550 | // the original list and continue chaining: | |
551 | // | query("a").at(0).onclick(fn).end().forEach(function(n){ | |
552 | // | console.log(n); // all anchors on the page. | |
553 | // | }) | |
554 | ||
555 | var t = new this._NodeListCtor(0); | |
556 | forEach(arguments, function(i){ | |
557 | if(i < 0){ i = this.length + i; } | |
558 | if(this[i]){ t.push(this[i]); } | |
559 | }, this); | |
560 | return t._stash(this); // dojo/NodeList | |
561 | } | |
562 | }); | |
563 | ||
564 | function queryForEngine(engine, NodeList){ | |
565 | var query = function(/*String*/ query, /*String|DOMNode?*/ root){ | |
566 | // summary: | |
567 | // Returns nodes which match the given CSS selector, searching the | |
568 | // entire document by default but optionally taking a node to scope | |
569 | // the search by. Returns an instance of NodeList. | |
570 | if(typeof root == "string"){ | |
571 | root = dom.byId(root); | |
572 | if(!root){ | |
573 | return new NodeList([]); | |
574 | } | |
575 | } | |
576 | var results = typeof query == "string" ? engine(query, root) : query ? query.orphan ? query : [query] : []; | |
577 | if(results.orphan){ | |
578 | // already wrapped | |
579 | return results; | |
580 | } | |
581 | return new NodeList(results); | |
582 | }; | |
583 | query.matches = engine.match || function(node, selector, root){ | |
584 | // summary: | |
585 | // Test to see if a node matches a selector | |
586 | return query.filter([node], selector, root).length > 0; | |
587 | }; | |
588 | // the engine provides a filtering function, use it to for matching | |
589 | query.filter = engine.filter || function(nodes, selector, root){ | |
590 | // summary: | |
591 | // Filters an array of nodes. Note that this does not guarantee to return a NodeList, just an array. | |
592 | return query(selector, root).filter(function(node){ | |
593 | return array.indexOf(nodes, node) > -1; | |
594 | }); | |
595 | }; | |
596 | if(typeof engine != "function"){ | |
597 | var search = engine.search; | |
598 | engine = function(selector, root){ | |
599 | // Slick does it backwards (or everyone else does it backwards, probably the latter) | |
600 | return search(root || document, selector); | |
601 | }; | |
602 | } | |
603 | return query; | |
604 | } | |
605 | var query = queryForEngine(defaultEngine, NodeList); | |
606 | /*===== | |
607 | query = function(selector, context){ | |
608 | // summary: | |
609 | // This modules provides DOM querying functionality. The module export is a function | |
610 | // that can be used to query for DOM nodes by CSS selector and returns a NodeList | |
611 | // representing the matching nodes. | |
612 | // selector: String | |
613 | // A CSS selector to search for. | |
614 | // context: String|DomNode? | |
615 | // An optional context to limit the searching scope. Only nodes under `context` will be | |
616 | // scanned. | |
617 | // example: | |
618 | // add an onclick handler to every submit button in the document | |
619 | // which causes the form to be sent via Ajax instead: | |
620 | // | require(["dojo/query"], function(query){ | |
621 | // | query("input[type='submit']").on("click", function(e){ | |
622 | // | dojo.stopEvent(e); // prevent sending the form | |
623 | // | var btn = e.target; | |
624 | // | dojo.xhrPost({ | |
625 | // | form: btn.form, | |
626 | // | load: function(data){ | |
627 | // | // replace the form with the response | |
628 | // | var div = dojo.doc.createElement("div"); | |
629 | // | dojo.place(div, btn.form, "after"); | |
630 | // | div.innerHTML = data; | |
631 | // | dojo.style(btn.form, "display", "none"); | |
632 | // | } | |
633 | // | }); | |
634 | // | }); | |
635 | // | }); | |
636 | // | |
637 | // description: | |
638 | // dojo/query is responsible for loading the appropriate query engine and wrapping | |
639 | // its results with a `NodeList`. You can use dojo/query with a specific selector engine | |
640 | // by using it as a plugin. For example, if you installed the sizzle package, you could | |
641 | // use it as the selector engine with: | |
642 | // | require(["dojo/query!sizzle"], function(query){ | |
643 | // | query("div")... | |
644 | // | |
645 | // The id after the ! can be a module id of the selector engine or one of the following values: | |
646 | // | |
647 | // - acme: This is the default engine used by Dojo base, and will ensure that the full | |
648 | // Acme engine is always loaded. | |
649 | // | |
650 | // - css2: If the browser has a native selector engine, this will be used, otherwise a | |
651 | // very minimal lightweight selector engine will be loaded that can do simple CSS2 selectors | |
652 | // (by #id, .class, tag, and [name=value] attributes, with standard child or descendant (>) | |
653 | // operators) and nothing more. | |
654 | // | |
655 | // - css2.1: If the browser has a native selector engine, this will be used, otherwise the | |
656 | // full Acme engine will be loaded. | |
657 | // | |
658 | // - css3: If the browser has a native selector engine with support for CSS3 pseudo | |
659 | // selectors (most modern browsers except IE8), this will be used, otherwise the | |
660 | // full Acme engine will be loaded. | |
661 | // | |
662 | // - Or the module id of a selector engine can be used to explicitly choose the selector engine | |
663 | // | |
664 | // For example, if you are using CSS3 pseudo selectors in module, you can specify that | |
665 | // you will need support them with: | |
666 | // | require(["dojo/query!css3"], function(query){ | |
667 | // | query('#t > h3:nth-child(odd)')... | |
668 | // | |
669 | // You can also choose the selector engine/load configuration by setting the query-selector: | |
670 | // For example: | |
671 | // | <script data-dojo-config="query-selector:'css3'" src="dojo.js"></script> | |
672 | // | |
673 | return new NodeList(); // dojo/NodeList | |
674 | }; | |
675 | =====*/ | |
676 | ||
677 | // the query that is returned from this module is slightly different than dojo.query, | |
678 | // because dojo.query has to maintain backwards compatibility with returning a | |
679 | // true array which has performance problems. The query returned from the module | |
680 | // does not use true arrays, but rather inherits from Array, making it much faster to | |
681 | // instantiate. | |
682 | dojo.query = queryForEngine(defaultEngine, function(array){ | |
683 | // call it without the new operator to invoke the back-compat behavior that returns a true array | |
684 | return NodeList(array); // dojo/NodeList | |
685 | }); | |
686 | ||
687 | query.load = function(id, parentRequire, loaded){ | |
688 | // summary: | |
689 | // can be used as AMD plugin to conditionally load new query engine | |
690 | // example: | |
691 | // | require(["dojo/query!custom"], function(qsa){ | |
692 | // | // loaded selector/custom.js as engine | |
693 | // | qsa("#foobar").forEach(...); | |
694 | // | }); | |
695 | loader.load(id, parentRequire, function(engine){ | |
696 | loaded(queryForEngine(engine, NodeList)); | |
697 | }); | |
698 | }; | |
699 | ||
700 | dojo._filterQueryResult = query._filterResult = function(nodes, selector, root){ | |
701 | return new NodeList(query.filter(nodes, selector, root)); | |
702 | }; | |
703 | dojo.NodeList = query.NodeList = NodeList; | |
704 | return query; | |
705 | }); |