1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-ComponentQuery'>/**
2 </span> * @class Ext.ComponentQuery
5 * Provides searching of Components within Ext.ComponentManager (globally) or a specific
6 * Ext.container.Container on the document with a similar syntax to a CSS selector.
8 * Components can be retrieved by using their {@link Ext.Component xtype} with an optional . prefix
10 <li>component or .component</li>
11 <li>gridpanel or .gridpanel</li>
14 * An itemId or id must be prefixed with a #
16 <li>#myContainer</li>
20 * Attributes must be wrapped in brackets
22 <li>component[autoScroll]</li>
23 <li>panel[title="Test"]</li>
26 * Member expressions from candidate Components may be tested. If the expression returns a <i>truthy</i> value,
27 * the candidate Component will be included in the query:<pre><code>
28 var disabledFields = myFormPanel.query("{isDisabled()}");
29 </code></pre>
31 * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}:<code><pre>
32 // Function receives array and returns a filtered array.
33 Ext.ComponentQuery.pseudos.invalid = function(items) {
34 var i = 0, l = items.length, c, result = [];
35 for (; i < l; i++) {
36 if (!(c = items[i]).isValid()) {
43 var invalidFields = myFormPanel.query('field:invalid');
44 if (invalidFields.length) {
45 invalidFields[0].getEl().scrollIntoView(myFormPanel.body);
46 for (var i = 0, l = invalidFields.length; i < l; i++) {
47 invalidFields[i].getEl().frame("red");
50 </pre></code>
52 * Default pseudos include:<br />
56 * Queries return an array of components.
57 * Here are some example queries.
58 <pre><code>
59 // retrieve all Ext.Panels in the document by xtype
60 var panelsArray = Ext.ComponentQuery.query('panel');
62 // retrieve all Ext.Panels within the container with an id myCt
63 var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel');
65 // retrieve all direct children which are Ext.Panels within myCt
66 var directChildPanel = Ext.ComponentQuery.query('#myCt > panel');
68 // retrieve all gridpanels and listviews
69 var gridsAndLists = Ext.ComponentQuery.query('gridpanel, listview');
70 </code></pre>
72 For easy access to queries based from a particular Container see the {@link Ext.container.Container#query},
73 {@link Ext.container.Container#down} and {@link Ext.container.Container#child} methods. Also see
74 {@link Ext.Component#up}.
77 Ext.define('Ext.ComponentQuery', {
79 uses: ['Ext.ComponentManager']
84 // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied
85 // as a member on each item in the passed array.
92 'for (; i < l; i++) {',
101 filterItems = function(items, operation) {
102 // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...]
103 // The operation's method loops over each item in the candidate array and
104 // returns an array of items which match its criteria
105 return operation.method.apply(this, [ items ].concat(operation.args));
108 getItems = function(items, mode) {
111 length = items.length,
113 deep = mode !== '>';
115 for (; i < length; i++) {
116 candidate = items[i];
117 if (candidate.getRefItems) {
118 result = result.concat(candidate.getRefItems(deep));
124 getAncestors = function(items) {
127 length = items.length,
129 for (; i < length; i++) {
130 candidate = items[i];
131 while (!!(candidate = (candidate.ownerCt || candidate.floatParent))) {
132 result.push(candidate);
138 // Filters the passed candidate array and returns only items which match the passed xtype
139 filterByXType = function(items, xtype, shallow) {
141 return items.slice();
146 length = items.length,
148 for (; i < length; i++) {
149 candidate = items[i];
150 if (candidate.isXType(xtype, shallow)) {
151 result.push(candidate);
158 // Filters the passed candidate array and returns only items which have the passed className
159 filterByClassName = function(items, className) {
163 length = items.length,
165 for (; i < length; i++) {
166 candidate = items[i];
167 if (candidate.el ? candidate.el.hasCls(className) : EA.contains(candidate.initCls(), className)) {
168 result.push(candidate);
174 // Filters the passed candidate array and returns only items which have the specified property match
175 filterByAttribute = function(items, property, operator, value) {
178 length = items.length,
180 for (; i < length; i++) {
181 candidate = items[i];
182 if (!value ? !!candidate[property] : (String(candidate[property]) === value)) {
183 result.push(candidate);
189 // Filters the passed candidate array and returns only items which have the specified itemId or id
190 filterById = function(items, id) {
193 length = items.length,
195 for (; i < length; i++) {
196 candidate = items[i];
197 if (candidate.getItemId() === id) {
198 result.push(candidate);
204 // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in
205 filterByPseudo = function(items, name, value) {
206 return cq.pseudos[name](items, value);
209 // Determines leading mode
210 // > for direct child, and ^ to switch to ownerCt axis
211 modeRe = /^(\s?([>\^])\s?|\s|$)/,
213 // Matches a token with possibly (true|false) appended for the "shallow" parameter
214 tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,
217 // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter
218 re: /^\.([\w\-]+)(?:\((true|false)\))?/,
219 method: filterByXType
221 // checks for [attribute=value]
222 re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/,
223 method: filterByAttribute
225 // checks for #cmpItemId
229 // checks for :<pseudo_class>(<selector>)
230 re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
231 method: filterByPseudo
233 // checks for {<member_expression>}
234 re: /^(?:\{([^\}]+)\})/,
235 method: filterFnPattern
238 <span id='Ext-ComponentQuery.Query'> /**
239 </span> * @class Ext.ComponentQuery.Query
243 cq.Query = Ext.extend(Object, {
244 constructor: function(cfg) {
246 Ext.apply(this, cfg);
249 <span id='Ext-ComponentQuery.Query-method-execute'> /**
251 * Executes this Query upon the selected root.
252 * The root provides the initial source of candidate Component matches which are progressively
253 * filtered by iterating through this Query's operations cache.
254 * If no root is provided, all registered Components are searched via the ComponentManager.
255 * root may be a Container who's descendant Components are filtered
256 * root may be a Component with an implementation of getRefItems which provides some nested Components such as the
257 * docked items within a Panel.
258 * root may be an array of candidate Components to filter using this Query.
260 execute : function(root) {
261 var operations = this.operations,
263 length = operations.length,
267 // no root, use all Components in the document
269 workingItems = Ext.ComponentManager.all.getArray();
271 // Root is a candidate Array
272 else if (Ext.isArray(root)) {
276 // We are going to loop over our operations and take care of them
278 for (; i < length; i++) {
279 operation = operations[i];
281 // The mode operation requires some custom handling.
282 // All other operations essentially filter down our current
283 // working items, while mode replaces our current working
284 // items by getting children from each one of our current
285 // working items. The type of mode determines the type of
286 // children we get. (e.g. > only gets direct children)
287 if (operation.mode === '^') {
288 workingItems = getAncestors(workingItems || [root]);
290 else if (operation.mode) {
291 workingItems = getItems(workingItems || [root], operation.mode);
294 workingItems = filterItems(workingItems || getItems([root]), operation);
297 // If this is the last operation, it means our current working
298 // items are the final matched items. Thus return them!
299 if (i === length -1) {
306 is: function(component) {
307 var operations = this.operations,
308 components = Ext.isArray(component) ? component : [component],
309 originalLength = components.length,
310 lastOperation = operations[operations.length-1],
313 components = filterItems(components, lastOperation);
314 if (components.length === originalLength) {
315 if (operations.length > 1) {
316 for (i = 0, ln = components.length; i < ln; i++) {
317 if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
330 // private cache of selectors and matching ComponentQuery.Query objects
333 // private cache of pseudo class filter functions
335 not: function(components, selector){
336 var CQ = Ext.ComponentQuery,
338 length = components.length,
343 for(; i < length; ++i) {
344 component = components[i];
345 if (!CQ.is(component, selector)) {
346 results[++index] = component;
353 <span id='Ext-ComponentQuery-method-query'> /**
354 </span> * <p>Returns an array of matched Components from within the passed root object.</p>
355 * <p>This method filters returned Components in a similar way to how CSS selector based DOM
356 * queries work using a textual selector string.</p>
357 * <p>See class summary for details.</p>
358 * @param selector The selector string to filter returned Components
359 * @param root <p>The Container within which to perform the query. If omitted, all Components
360 * within the document are included in the search.</p>
361 * <p>This parameter may also be an array of Components to filter according to the selector.</p>
362 * @returns {Array} The matched Components.
363 * @member Ext.ComponentQuery
366 query: function(selector, root) {
367 var selectors = selector.split(','),
368 length = selectors.length,
373 query, resultsLn, cmp;
375 for (; i < length; i++) {
376 selector = Ext.String.trim(selectors[i]);
377 query = this.cache[selector];
379 this.cache[selector] = query = this.parse(selector);
381 results = results.concat(query.execute(root));
384 // multiple selectors, potential to find duplicates
385 // lets filter them out.
387 resultsLn = results.length;
388 for (i = 0; i < resultsLn; i++) {
390 if (!dupMatcher[cmp.id]) {
391 noDupResults.push(cmp);
392 dupMatcher[cmp.id] = true;
395 results = noDupResults;
400 <span id='Ext-ComponentQuery-method-query'> /**
401 </span> * Tests whether the passed Component matches the selector string.
402 * @param component The Component to test
403 * @param selector The selector string to test against.
404 * @return {Boolean} True if the Component matches the selector.
405 * @member Ext.ComponentQuery
408 is: function(component, selector) {
412 var query = this.cache[selector];
414 this.cache[selector] = query = this.parse(selector);
416 return query.is(component);
419 parse: function(selector) {
421 length = matchers.length,
429 // We are going to parse the beginning of the selector over and
430 // over again, slicing off the selector any portions we converted into an
431 // operation, until it is an empty string.
432 while (selector && lastSelector !== selector) {
433 lastSelector = selector;
435 // First we check if we are dealing with a token like #, * or an xtype
436 tokenMatch = selector.match(tokenRe);
439 matchedChar = tokenMatch[1];
441 // If the token is prefixed with a # we push a filterById operation to our stack
442 if (matchedChar === '#') {
445 args: [Ext.String.trim(tokenMatch[2])]
448 // If the token is prefixed with a . we push a filterByClassName operation to our stack
449 // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix
450 else if (matchedChar === '.') {
452 method: filterByClassName,
453 args: [Ext.String.trim(tokenMatch[2])]
456 // If the token is a * or an xtype string, we push a filterByXType
457 // operation to the stack.
460 method: filterByXType,
461 args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
465 // Now we slice of the part we just converted into an operation
466 selector = selector.replace(tokenMatch[0], '');
469 // If the next part of the query is not a space or > or ^, it means we
470 // are going to check for more things that our current selection
472 while (!(modeMatch = selector.match(modeRe))) {
473 // Lets loop over each type of matcher and execute it
474 // on our current selector.
475 for (i = 0; selector && i < length; i++) {
476 matcher = matchers[i];
477 selectorMatch = selector.match(matcher.re);
478 method = matcher.method;
480 // If we have a match, add an operation with the method
481 // associated with this matcher, and pass the regular
482 // expression matches are arguments to the operation.
485 method: Ext.isString(matcher.method)
486 // Turn a string method into a function by formatting the string with our selector matche expression
487 // A new method is created for different match expressions, eg {id=='textfield-1024'}
488 // Every expression may be different in different selectors.
489 ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
491 args: selectorMatch.slice(1)
493 selector = selector.replace(selectorMatch[0], '');
494 break; // Break on match
497 // Exhausted all matches: It's an error
498 if (i === (length - 1)) {
499 Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
505 // Now we are going to check for a mode change. This means a space
506 // or a > to determine if we are going to select all the children
507 // of the currently matched items, or a ^ if we are going to use the
508 // ownerCt axis as the candidate source.
509 if (modeMatch[1]) { // Assignment, and test for truthiness!
511 mode: modeMatch[2]||modeMatch[1]
513 selector = selector.replace(modeMatch[0], '');
517 // Now that we have all our operations in an array, we are going
518 // to create a new Query using these operations.
519 return new cq.Query({
520 operations: operations
524 });</pre></pre></body></html>