/ext-4.0.7/docs/source/ComponentQuery.html
HTML | 535 lines | 495 code | 40 blank | 0 comment | 0 complexity | eb12dfb7152b116026fe3e67204537ea MD5 | raw file
1<!DOCTYPE html>
2<html>
3<head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
10 </style>
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14 }
15 </script>
16</head>
17<body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-ComponentQuery'>/**
19</span> * Provides searching of Components within Ext.ComponentManager (globally) or a specific
20 * Ext.container.Container on the document with a similar syntax to a CSS selector.
21 *
22 * Components can be retrieved by using their {@link Ext.Component xtype} with an optional . prefix
23 *
24 * - `component` or `.component`
25 * - `gridpanel` or `.gridpanel`
26 *
27 * An itemId or id must be prefixed with a #
28 *
29 * - `#myContainer`
30 *
31 * Attributes must be wrapped in brackets
32 *
33 * - `component[autoScroll]`
34 * - `panel[title="Test"]`
35 *
36 * Member expressions from candidate Components may be tested. If the expression returns a *truthy* value,
37 * the candidate Component will be included in the query:
38 *
39 * var disabledFields = myFormPanel.query("{isDisabled()}");
40 *
41 * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}:
42 *
43 * // Function receives array and returns a filtered array.
44 * Ext.ComponentQuery.pseudos.invalid = function(items) {
45 * var i = 0, l = items.length, c, result = [];
46 * for (; i < l; i++) {
47 * if (!(c = items[i]).isValid()) {
48 * result.push(c);
49 * }
50 * }
51 * return result;
52 * };
53 *
54 * var invalidFields = myFormPanel.query('field:invalid');
55 * if (invalidFields.length) {
56 * invalidFields[0].getEl().scrollIntoView(myFormPanel.body);
57 * for (var i = 0, l = invalidFields.length; i < l; i++) {
58 * invalidFields[i].getEl().frame("red");
59 * }
60 * }
61 *
62 * Default pseudos include:
63 *
64 * - not
65 * - last
66 *
67 * Queries return an array of components.
68 * Here are some example queries.
69 *
70 * // retrieve all Ext.Panels in the document by xtype
71 * var panelsArray = Ext.ComponentQuery.query('panel');
72 *
73 * // retrieve all Ext.Panels within the container with an id myCt
74 * var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel');
75 *
76 * // retrieve all direct children which are Ext.Panels within myCt
77 * var directChildPanel = Ext.ComponentQuery.query('#myCt > panel');
78 *
79 * // retrieve all grids and trees
80 * var gridsAndTrees = Ext.ComponentQuery.query('gridpanel, treepanel');
81 *
82 * For easy access to queries based from a particular Container see the {@link Ext.container.Container#query},
83 * {@link Ext.container.Container#down} and {@link Ext.container.Container#child} methods. Also see
84 * {@link Ext.Component#up}.
85 */
86Ext.define('Ext.ComponentQuery', {
87 singleton: true,
88 uses: ['Ext.ComponentManager']
89}, function() {
90
91 var cq = this,
92
93 // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied
94 // as a member on each item in the passed array.
95 filterFnPattern = [
96 'var r = [],',
97 'i = 0,',
98 'it = items,',
99 'l = it.length,',
100 'c;',
101 'for (; i < l; i++) {',
102 'c = it[i];',
103 'if (c.{0}) {',
104 'r.push(c);',
105 '}',
106 '}',
107 'return r;'
108 ].join(''),
109
110 filterItems = function(items, operation) {
111 // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...]
112 // The operation's method loops over each item in the candidate array and
113 // returns an array of items which match its criteria
114 return operation.method.apply(this, [ items ].concat(operation.args));
115 },
116
117 getItems = function(items, mode) {
118 var result = [],
119 i = 0,
120 length = items.length,
121 candidate,
122 deep = mode !== '>';
123
124 for (; i < length; i++) {
125 candidate = items[i];
126 if (candidate.getRefItems) {
127 result = result.concat(candidate.getRefItems(deep));
128 }
129 }
130 return result;
131 },
132
133 getAncestors = function(items) {
134 var result = [],
135 i = 0,
136 length = items.length,
137 candidate;
138 for (; i < length; i++) {
139 candidate = items[i];
140 while (!!(candidate = (candidate.ownerCt || candidate.floatParent))) {
141 result.push(candidate);
142 }
143 }
144 return result;
145 },
146
147 // Filters the passed candidate array and returns only items which match the passed xtype
148 filterByXType = function(items, xtype, shallow) {
149 if (xtype === '*') {
150 return items.slice();
151 }
152 else {
153 var result = [],
154 i = 0,
155 length = items.length,
156 candidate;
157 for (; i < length; i++) {
158 candidate = items[i];
159 if (candidate.isXType(xtype, shallow)) {
160 result.push(candidate);
161 }
162 }
163 return result;
164 }
165 },
166
167 // Filters the passed candidate array and returns only items which have the passed className
168 filterByClassName = function(items, className) {
169 var EA = Ext.Array,
170 result = [],
171 i = 0,
172 length = items.length,
173 candidate;
174 for (; i < length; i++) {
175 candidate = items[i];
176 if (candidate.el ? candidate.el.hasCls(className) : EA.contains(candidate.initCls(), className)) {
177 result.push(candidate);
178 }
179 }
180 return result;
181 },
182
183 // Filters the passed candidate array and returns only items which have the specified property match
184 filterByAttribute = function(items, property, operator, value) {
185 var result = [],
186 i = 0,
187 length = items.length,
188 candidate;
189 for (; i < length; i++) {
190 candidate = items[i];
191 if (!value ? !!candidate[property] : (String(candidate[property]) === value)) {
192 result.push(candidate);
193 }
194 }
195 return result;
196 },
197
198 // Filters the passed candidate array and returns only items which have the specified itemId or id
199 filterById = function(items, id) {
200 var result = [],
201 i = 0,
202 length = items.length,
203 candidate;
204 for (; i < length; i++) {
205 candidate = items[i];
206 if (candidate.getItemId() === id) {
207 result.push(candidate);
208 }
209 }
210 return result;
211 },
212
213 // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in
214 filterByPseudo = function(items, name, value) {
215 return cq.pseudos[name](items, value);
216 },
217
218 // Determines leading mode
219 // > for direct child, and ^ to switch to ownerCt axis
220 modeRe = /^(\s?([>\^])\s?|\s|$)/,
221
222 // Matches a token with possibly (true|false) appended for the "shallow" parameter
223 tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,
224
225 matchers = [{
226 // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter
227 re: /^\.([\w\-]+)(?:\((true|false)\))?/,
228 method: filterByXType
229 },{
230 // checks for [attribute=value]
231 re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/,
232 method: filterByAttribute
233 }, {
234 // checks for #cmpItemId
235 re: /^#([\w\-]+)/,
236 method: filterById
237 }, {
238 // checks for :<pseudo_class>(<selector>)
239 re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
240 method: filterByPseudo
241 }, {
242 // checks for {<member_expression>}
243 re: /^(?:\{([^\}]+)\})/,
244 method: filterFnPattern
245 }];
246
247 // @class Ext.ComponentQuery.Query
248 // This internal class is completely hidden in documentation.
249 cq.Query = Ext.extend(Object, {
250 constructor: function(cfg) {
251 cfg = cfg || {};
252 Ext.apply(this, cfg);
253 },
254
255 // Executes this Query upon the selected root.
256 // The root provides the initial source of candidate Component matches which are progressively
257 // filtered by iterating through this Query's operations cache.
258 // If no root is provided, all registered Components are searched via the ComponentManager.
259 // root may be a Container who's descendant Components are filtered
260 // root may be a Component with an implementation of getRefItems which provides some nested Components such as the
261 // docked items within a Panel.
262 // root may be an array of candidate Components to filter using this Query.
263 execute : function(root) {
264 var operations = this.operations,
265 i = 0,
266 length = operations.length,
267 operation,
268 workingItems;
269
270 // no root, use all Components in the document
271 if (!root) {
272 workingItems = Ext.ComponentManager.all.getArray();
273 }
274 // Root is a candidate Array
275 else if (Ext.isArray(root)) {
276 workingItems = root;
277 }
278
279 // We are going to loop over our operations and take care of them
280 // one by one.
281 for (; i < length; i++) {
282 operation = operations[i];
283
284 // The mode operation requires some custom handling.
285 // All other operations essentially filter down our current
286 // working items, while mode replaces our current working
287 // items by getting children from each one of our current
288 // working items. The type of mode determines the type of
289 // children we get. (e.g. > only gets direct children)
290 if (operation.mode === '^') {
291 workingItems = getAncestors(workingItems || [root]);
292 }
293 else if (operation.mode) {
294 workingItems = getItems(workingItems || [root], operation.mode);
295 }
296 else {
297 workingItems = filterItems(workingItems || getItems([root]), operation);
298 }
299
300 // If this is the last operation, it means our current working
301 // items are the final matched items. Thus return them!
302 if (i === length -1) {
303 return workingItems;
304 }
305 }
306 return [];
307 },
308
309 is: function(component) {
310 var operations = this.operations,
311 components = Ext.isArray(component) ? component : [component],
312 originalLength = components.length,
313 lastOperation = operations[operations.length-1],
314 ln, i;
315
316 components = filterItems(components, lastOperation);
317 if (components.length === originalLength) {
318 if (operations.length > 1) {
319 for (i = 0, ln = components.length; i < ln; i++) {
320 if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
321 return false;
322 }
323 }
324 }
325 return true;
326 }
327 return false;
328 }
329 });
330
331 Ext.apply(this, {
332
333 // private cache of selectors and matching ComponentQuery.Query objects
334 cache: {},
335
336 // private cache of pseudo class filter functions
337 pseudos: {
338 not: function(components, selector){
339 var CQ = Ext.ComponentQuery,
340 i = 0,
341 length = components.length,
342 results = [],
343 index = -1,
344 component;
345
346 for(; i < length; ++i) {
347 component = components[i];
348 if (!CQ.is(component, selector)) {
349 results[++index] = component;
350 }
351 }
352 return results;
353 },
354 last: function(components) {
355 return components[components.length - 1];
356 }
357 },
358
359<span id='Ext-ComponentQuery-method-query'> /**
360</span> * Returns an array of matched Components from within the passed root object.
361 *
362 * This method filters returned Components in a similar way to how CSS selector based DOM
363 * queries work using a textual selector string.
364 *
365 * See class summary for details.
366 *
367 * @param {String} selector The selector string to filter returned Components
368 * @param {Ext.container.Container} root The Container within which to perform the query.
369 * If omitted, all Components within the document are included in the search.
370 *
371 * This parameter may also be an array of Components to filter according to the selector.</p>
372 * @returns {Ext.Component[]} The matched Components.
373 *
374 * @member Ext.ComponentQuery
375 */
376 query: function(selector, root) {
377 var selectors = selector.split(','),
378 length = selectors.length,
379 i = 0,
380 results = [],
381 noDupResults = [],
382 dupMatcher = {},
383 query, resultsLn, cmp;
384
385 for (; i < length; i++) {
386 selector = Ext.String.trim(selectors[i]);
387 query = this.cache[selector];
388 if (!query) {
389 this.cache[selector] = query = this.parse(selector);
390 }
391 results = results.concat(query.execute(root));
392 }
393
394 // multiple selectors, potential to find duplicates
395 // lets filter them out.
396 if (length > 1) {
397 resultsLn = results.length;
398 for (i = 0; i < resultsLn; i++) {
399 cmp = results[i];
400 if (!dupMatcher[cmp.id]) {
401 noDupResults.push(cmp);
402 dupMatcher[cmp.id] = true;
403 }
404 }
405 results = noDupResults;
406 }
407 return results;
408 },
409
410<span id='Ext-ComponentQuery-method-is'> /**
411</span> * Tests whether the passed Component matches the selector string.
412 * @param {Ext.Component} component The Component to test
413 * @param {String} selector The selector string to test against.
414 * @return {Boolean} True if the Component matches the selector.
415 * @member Ext.ComponentQuery
416 */
417 is: function(component, selector) {
418 if (!selector) {
419 return true;
420 }
421 var query = this.cache[selector];
422 if (!query) {
423 this.cache[selector] = query = this.parse(selector);
424 }
425 return query.is(component);
426 },
427
428 parse: function(selector) {
429 var operations = [],
430 length = matchers.length,
431 lastSelector,
432 tokenMatch,
433 matchedChar,
434 modeMatch,
435 selectorMatch,
436 i, matcher, method;
437
438 // We are going to parse the beginning of the selector over and
439 // over again, slicing off the selector any portions we converted into an
440 // operation, until it is an empty string.
441 while (selector && lastSelector !== selector) {
442 lastSelector = selector;
443
444 // First we check if we are dealing with a token like #, * or an xtype
445 tokenMatch = selector.match(tokenRe);
446
447 if (tokenMatch) {
448 matchedChar = tokenMatch[1];
449
450 // If the token is prefixed with a # we push a filterById operation to our stack
451 if (matchedChar === '#') {
452 operations.push({
453 method: filterById,
454 args: [Ext.String.trim(tokenMatch[2])]
455 });
456 }
457 // If the token is prefixed with a . we push a filterByClassName operation to our stack
458 // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix
459 else if (matchedChar === '.') {
460 operations.push({
461 method: filterByClassName,
462 args: [Ext.String.trim(tokenMatch[2])]
463 });
464 }
465 // If the token is a * or an xtype string, we push a filterByXType
466 // operation to the stack.
467 else {
468 operations.push({
469 method: filterByXType,
470 args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
471 });
472 }
473
474 // Now we slice of the part we just converted into an operation
475 selector = selector.replace(tokenMatch[0], '');
476 }
477
478 // If the next part of the query is not a space or > or ^, it means we
479 // are going to check for more things that our current selection
480 // has to comply to.
481 while (!(modeMatch = selector.match(modeRe))) {
482 // Lets loop over each type of matcher and execute it
483 // on our current selector.
484 for (i = 0; selector && i < length; i++) {
485 matcher = matchers[i];
486 selectorMatch = selector.match(matcher.re);
487 method = matcher.method;
488
489 // If we have a match, add an operation with the method
490 // associated with this matcher, and pass the regular
491 // expression matches are arguments to the operation.
492 if (selectorMatch) {
493 operations.push({
494 method: Ext.isString(matcher.method)
495 // Turn a string method into a function by formatting the string with our selector matche expression
496 // A new method is created for different match expressions, eg {id=='textfield-1024'}
497 // Every expression may be different in different selectors.
498 ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
499 : matcher.method,
500 args: selectorMatch.slice(1)
501 });
502 selector = selector.replace(selectorMatch[0], '');
503 break; // Break on match
504 }
505 //<debug>
506 // Exhausted all matches: It's an error
507 if (i === (length - 1)) {
508 Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
509 }
510 //</debug>
511 }
512 }
513
514 // Now we are going to check for a mode change. This means a space
515 // or a > to determine if we are going to select all the children
516 // of the currently matched items, or a ^ if we are going to use the
517 // ownerCt axis as the candidate source.
518 if (modeMatch[1]) { // Assignment, and test for truthiness!
519 operations.push({
520 mode: modeMatch[2]||modeMatch[1]
521 });
522 selector = selector.replace(modeMatch[0], '');
523 }
524 }
525
526 // Now that we have all our operations in an array, we are going
527 // to create a new Query using these operations.
528 return new cq.Query({
529 operations: operations
530 });
531 }
532 });
533});</pre>
534</body>
535</html>