/**
 * Provides searching of Components within Ext.ComponentManager (globally) or a specific
 * Ext.container.Container on the document with a similar syntax to a CSS selector.
 * Returns Array of matching Components, or empty Array.
 *
 * ## Basic Component lookup
 *
 * Components can be retrieved by using their {@link Ext.Component xtype}:
 *
 * - `component`
 * - `gridpanel`
 *
 * Matching by `xtype` matches inherited types, so in the following code, the previous field
 * *of any type which inherits from `TextField`* will be found:
 *
 *     prevField = myField.previousNode('textfield');
 *
 * To match only the exact type, pass the "shallow" flag by adding `(true)` to xtype
 * (See Component's {@link Ext.Component#isXType isXType} method):
 *
 *     prevTextField = myField.previousNode('textfield(true)');
 *
 * You can search Components by their `id` or `itemId` property, prefixed with a #:
 *
 *     #myContainer
 *
 * Component `xtype` and `id` or `itemId` can be used together to avoid possible
 * id collisions between Components of different types:
 *
 *     panel#myPanel
 *
 * When Component's `id` or `xtype` contains dots, you can escape them in your selector:
 *
 *     my\.panel#myPanel
 *
 * Keep in mind that JavaScript treats the backslash character in a special way, so you
 * need to escape it, too, in the actual code:
 *
 *     var myPanel = Ext.ComponentQuery.query('my\\.panel#myPanel');
 *
 * ## Traversing Component tree
 *
 * Components can be found by their relation to other Components. There are several
 * relationship operators, mostly taken from CSS selectors:
 *
 * - **`E F`** All descendant Components of E that match F
 * - **`E > F`** All direct children Components of E that match F
 * - **`E ^ F`** All parent Components of E that match F
 *
 * Expressions between relationship operators are matched left to right, i.e. leftmost
 * selector is applied first, then if one or more matches are found, relationship operator
 * itself is applied, then next selector expression, etc. It is possible to combine
 * relationship operators in complex selectors:
 *
 *     window[title="Input form"] textfield[name=login] ^ form > button[action=submit]
 *
 * That selector can be read this way: Find a window with title "Input form", in that
 * window find a TextField with name "login" at any depth (including subpanels and/or
 * FieldSets), then find an `Ext.form.Panel` that is a parent of the TextField, and in
 * that form find a direct child that is a button with custom property `action` set to
 * value "submit".
 *
 * Whitespace on both sides of `^` and `>` operators is non-significant, i.e. can be
 * omitted, but usually is used for clarity.
 *
 * ## Searching by Component attributes
 *
 * Components can be searched by their object property values (attributes). To do that,
 * use attribute matching expression in square brackets:
 *
 * - `component[disabled]` - matches any Component that has `disabled` property with
 * any truthy (non-empty, not `false`) value.
 * - `panel[title="Test"]` - matches any Component that has `title` property set to
 * "Test". Note that if the value does not contain spaces, the quotes are optional.
 *
 * Attributes can use any of the following operators to compare values:
 * `=`, `!=`, `^=`, `$=`, `*=`, `%=`, `|=` and `~=`.
 *
 * Prefixing the attribute name with an at sign `@` means that the property must be
 * the object's `ownProperty`, not a property from the prototype chain.
 *
 * Specifications like `[propName]` check that the property is a truthy value. To check
 * that the object has an `ownProperty` of a certain name, regardless of the value use
 * the form `[?propName]`.
 *
 * The specified value is coerced to match the type of the property found in the
 * candidate Component using {@link Ext#coerce}.
 *
 * If you need to find Components by their `itemId` property, use the `#id` form; it will
 * do the same as `[itemId=id]` but is easier to read.
 *
 * If you need to include a metacharacter like (, ), [, ], etc., in the query, escape it
 * by prefixing it with a backslash:
 *
 *      var component = Ext.ComponentQuery.query('[myProperty=\\[foo\\]]');
 *
 * ## Attribute matching operators
 *
 * The '=' operator will return the results that **exactly** match the
 * specified object property (attribute):
 *
 *     Ext.ComponentQuery.query('panel[cls=my-cls]');
 *
 * Will match the following Component:
 *
 *     Ext.create('Ext.window.Window', {
 *         cls: 'my-cls'
 *     });
 *
 * But will not match the following Component, because 'my-cls' is one value
 * among others:
 *
 *      Ext.create('Ext.panel.Panel', {
 *          cls: 'foo-cls my-cls bar-cls'
 *      });
 *
 * You can use the '~=' operator instead, it will return Components with
 * the property that **exactly** matches one of the whitespace-separated
 * values. This is also true for properties that only have *one* value:
 *
 *     Ext.ComponentQuery.query('panel[cls~=my-cls]');
 *
 * Will match both Components:
 *
 *     Ext.create('Ext.panel.Panel', {
 *         cls: 'foo-cls my-cls bar-cls'
 *     });
 *     
 *     Ext.create('Ext.window.Window', {
 *         cls: 'my-cls'
 *     });
 *
 * Generally, '=' operator is more suited for object properties other than
 * CSS classes, while '~=' operator will work best with properties that
 * hold lists of whitespace-separated CSS classes.
 *
 * The '^=' operator will return Components with specified attribute that
 * start with the passed value:
 *
 *     Ext.ComponentQuery.query('panel[title^=Sales]');
 *
 * Will match the following Component:
 *
 *     Ext.create('Ext.panel.Panel', {
 *         title: 'Sales estimate for Q4'
 *     });
 *
 * The '$=' operator will return Components with specified properties that
 * end with the passed value:
 *
 *     Ext.ComponentQuery.query('field[fieldLabel$=name]');
 *
 * Will match the following Component:
 *
 *     Ext.create('Ext.form.field.Text', {
 *         fieldLabel: 'Enter your name'
 *     });
 *
 * The '/=' operator will return Components with specified properties that
 * match the passed regular expression:
 *
 *     Ext.ComponentQuery.query('button[action/="edit|save"]');
 *
 * Will match the following Components with a custom `action` property:
 *
 *     Ext.create('Ext.button.Button', {
 *          action: 'edit'
 *     });
 *
 *     Ext.create('Ext.button.Button', {
 *          action: 'save'
 *     });
 *
 * When you need to use meta characters like [], (), etc. in your query, make sure
 * to escape them with back slashes:
 *
 *     Ext.ComponentQuery.query('panel[title="^Sales for Q\\[1-4\\]"]');
 *
 * The following test will find panels with their `ownProperty` collapsed being equal to
 * `false`. It will **not** match a collapsed property from the prototype chain.
 *
 *     Ext.ComponentQuery.query('panel[@collapsed=false]');
 *
 * Member expressions from candidate Components may be tested. If the expression returns
 * a *truthy* value, the candidate Component will be included in the query:
 *
 *     var disabledFields = myFormPanel.query("{isDisabled()}");
 *
 * Such expressions are executed in Component's context, and the above expression is
 * similar to running this snippet for every Component in your application:
 *
 *      if (component.isDisabled()) {
 *          matches.push(component);
 *      }
 *
 * It is important to use only methods that are available in **every** Component instance
 * to avoid run time exceptions. If you need to match your Components with a custom
 * condition formula, you can augment `Ext.Component` to provide custom matcher that
 * will return `false` by default, and override it in your custom classes:
 * 
 *      Ext.define('My.Component', {
 *          override: 'Ext.Component',
 *          myMatcher: function() { return false; }
 *      });
 *
 *      Ext.define('My.Panel', {
 *          extend: 'Ext.panel.Panel',
 *          requires: ['My.Component'],     // Ensure that Component override is applied
 *          myMatcher: function(selector) {
 *              return selector === 'myPanel';
 *          }
 *      });
 *
 * After that you can use a selector with your custom matcher to find all instances
 * of `My.Panel`:
 *
 *      Ext.ComponentQuery.query("{myMatcher('myPanel')}");
 *
 * However if you really need to use a custom matcher, you may find it easier to implement
 * a custom Pseudo class instead (see below).
 *
 * ## Conditional matching
 *
 * Attribute matchers can be combined to select only Components that match **all**
 * conditions (logical AND operator):
 *
 *     Ext.ComponentQuery.query('panel[cls~=my-cls][floating=true][title$="sales data"]');
 *
 * E.g., the query above will match only a Panel-descended Component that has 'my-cls'
 * CSS class *and* is floating *and* with a title that ends with "sales data".
 *
 * Expressions separated with commas will match any Component that satisfies
 * *either* expression (logical OR operator):
 *
 *     Ext.ComponentQuery.query('field[fieldLabel^=User], field[fieldLabel*=password]');
 *
 * E.g., the query above will match any field with field label starting with "User",
 * *or* any field that has "password" in its label.
 *
 * If you need to include a comma in an attribute matching expression, escape it with a
 * backslash:
 *
 *     Ext.ComponentQuery.query('field[fieldLabel^="User\\, foo"], field[fieldLabel*=password]');
 *
 * ## Pseudo classes
 *
 * Pseudo classes may be used to filter results in the same way as in
 * {@link Ext.dom.Query}. There are five default pseudo classes:
 *
 * * `not` Negates a selector.
 * * `first` Filters out all except the first matching item for a selector.
 * * `last` Filters out all except the last matching item for a selector.
 * * `focusable` Filters out all except Components which are currently able to recieve
 * focus.
 * * `nth-child` Filters Components by ordinal position in the selection.
 *
 * These pseudo classes can be used with other matchers or without them:
 *
 *      // Select first direct child button in any panel
 *      Ext.ComponentQuery.query('panel > button:first');
 *
 *      // Select last field in Profile form
 *      Ext.ComponentQuery.query('form[title=Profile] field:last');
 * 
 *      // Find first focusable Component in a panel and focus it
 *      panel.down(':focusable').focus();
 * 
 *      // Select any field that is not hidden in a form
 *      form.query('field:not(hiddenfield)');
 *
 * Pseudo class `nth-child` can be used to find any child Component by its
 * position relative to its siblings. This class' handler takes one argument
 * that specifies the selection formula as `Xn` or `Xn+Y`:
 *
 *      // Find every odd field in a form
 *      form.query('field:nth-child(2n+1)'); // or use shortcut: :nth-child(odd)
 *
 *      // Find every even field in a form
 *      form.query('field:nth-child(2n)');   // or use shortcut: :nth-child(even)
 *
 *      // Find every 3rd field in a form
 *      form.query('field:nth-child(3n)');
 *
 * Pseudo classes can be combined to further filter the results, e.g., in the
 * form example above we can modify the query to exclude hidden fields:
 *
 *      // Find every 3rd non-hidden field in a form
 *      form.query('field:not(hiddenfield):nth-child(3n)');
 *
 * Note that when combining pseudo classes, whitespace is significant, i.e.
 * there should be no spaces between pseudo classes. This is a common mistake;
 * if you accidentally type a space between `field` and `:not`, the query
 * will not return any result because it will mean "find *field's children
 * Components* that are not hidden fields...".
 *
 * ## Custom pseudo classes
 *
 * It is possible to define your own custom pseudo classes. In fact, a
 * pseudo class is just a property in `Ext.ComponentQuery.pseudos` object
 * that defines pseudo class name (property name) and pseudo class handler
 * (property value):
 *
 *     // Function receives array and returns a filtered array.
 *     Ext.ComponentQuery.pseudos.invalid = function(items) {
 *         var i = 0, l = items.length, c, result = [];
 *         for (; i < l; i++) {
 *             if (!(c = items[i]).isValid()) {
 *                 result.push(c);
 *             }
 *         }
 *         return result;
 *     };
 * 
 *     var invalidFields = myFormPanel.query('field:invalid');
 *     if (invalidFields.length) {
 *         invalidFields[0].getEl().scrollIntoView(myFormPanel.body);
 *         for (var i = 0, l = invalidFields.length; i < l; i++) {
 *             invalidFields[i].getEl().frame("red");
 *         }
 *     }
 *
 * Pseudo class handlers can be even more flexible, with a selector
 * argument used to define the logic:
 *
 *      // Handler receives array of itmes and selector in parentheses
 *      Ext.ComponentQuery.pseudos.titleRegex = function(components, selector) {
 *          var i = 0, l = components.length, c, result = [], regex = new RegExp(selector);
 *          for (; i < l; i++) {
 *              c = components[i];
 *              if (c.title && regex.test(c.title)) {
 *                  result.push(c);
 *              }
 *          }
 *          return result;
 *      }
 *
 *      var salesTabs = tabPanel.query('panel:titleRegex("sales\\s+for\\s+201[123]")');
 *
 * Be careful when using custom pseudo classes with MVC Controllers: when
 * you use a pseudo class in Controller's `control` or `listen` component
 * selectors, the pseudo class' handler function will be called very often
 * and may slow down your application significantly. A good rule of thumb
 * is to always specify Component xtype with the pseudo class so that the
 * handlers are only called on Components that you need, and try to make
 * the condition checks as cheap in terms of execution time as possible.
 * Note how in the example above, handler function checks that Component
 * *has* a title first, before running regex test on it.
 *
 * ## Query examples
 *
 * Queries return an array of Components. Here are some example queries:
 *
 *     // retrieve all Ext.Panels in the document by xtype
 *     var panelsArray = Ext.ComponentQuery.query('panel');
 *
 *     // retrieve all Ext.Panels within the container with an id myCt
 *     var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel');
 *
 *     // retrieve all direct children which are Ext.Panels within myCt
 *     var directChildPanel = Ext.ComponentQuery.query('#myCt > panel');
 *
 *     // retrieve all grids or trees
 *     var gridsAndTrees = Ext.ComponentQuery.query('gridpanel, treepanel');
 *     
 *     // Focus first Component
 *     myFormPanel.child(':focusable').focus();
 *
 *     // Retrieve every odd text field in a form
 *     myFormPanel.query('textfield:nth-child(odd)');
 *
 *     // Retrieve every even field in a form, excluding hidden fields
 *     myFormPanel.query('field:not(hiddenfield):nth-child(even)');
 *
 * For easy access to queries based from a particular Container see the
 * {@link Ext.container.Container#query}, {@link Ext.container.Container#down} and
 * {@link Ext.container.Container#child} methods. Also see
 * {@link Ext.Component#up}.
 */
Ext.define('Ext.ComponentQuery', {
    singleton: true,
    requires: [
        'Ext.ComponentManager',
        'Ext.util.Operators',
        'Ext.util.LruCache'
    ]
}, function() {

    var cq = this,
        queryOperators = Ext.util.Operators,
        nthRe = /(\d*)n\+?(\d*)/,
        nthRe2 = /\D/,
        stripLeadingSpaceRe = /^(\s)+/,
        unescapeRe = /\\(.)/g,
        regexCache = new Ext.util.LruCache({
            maxSize: 100
        }),

        // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied
        // as a member on each item in the passed array.
        filterFnPattern = [
            'var r = [],',
                'i = 0,',
                'it = items,',
                'l = it.length,',
                'c;',
            'for (; i < l; i++) {',
                'c = it[i];',
                'if (c.{0}) {',
                   'r.push(c);',
                '}',
            '}',
            'return r;'
        ].join(''),

        filterItems = function(items, operation) {
            // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...]
            // The operation's method loops over each item in the candidate array and
            // returns an array of items which match its criteria
            return operation.method.apply(this, [ items ].concat(operation.args));
        },

        getItems = function(items, mode) {
            var result = [],
                i = 0,
                length = items.length,
                candidate,
                deep = mode !== '>';
                
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getRefItems) {
                    result = result.concat(candidate.getRefItems(deep));
                }
            }
            return result;
        },

        getAncestors = function(items) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                while (!!(candidate = candidate.getRefOwner())) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which match the passed xtype
        filterByXType = function(items, xtype, shallow) {
            if (xtype === '*') {
                return items.slice();
            }
            else {
                var result = [],
                    i = 0,
                    length = items.length,
                    candidate;
                for (; i < length; i++) {
                    candidate = items[i];
                    if (candidate.isXType(xtype, shallow)) {
                        result.push(candidate);
                    }
                }
                return result;
            }
        },

        // Filters the passed candidate array and returns only items which have the specified property match
        filterByAttribute = function(items, property, operator, compareTo) {
            var result = [],
                i = 0,
                length = items.length,
                mustBeOwnProperty,
                presenceOnly,
                candidate, propValue,
                j, propLen,
                config;

            // Prefixing property name with an @ means that the property must be in the candidate, not in its prototype
            if (property.charAt(0) === '@') {
                mustBeOwnProperty = true;
                property = property.substr(1);
            }
            if (property.charAt(0) === '?') {
                mustBeOwnProperty = true;
                presenceOnly = true;
                property = property.substr(1);
            }

            for (; i < length; i++) {
                candidate = items[i];

                config = candidate.self.$config.configs[property];
                if (config) {
                    propValue = candidate[config.names.get]();
                } else if (mustBeOwnProperty && !candidate.hasOwnProperty(property)) {
                    continue;
                } else {
                    propValue = candidate[property];
                }

                if (presenceOnly) {
                    result.push(candidate);
                }
                // implies property is an array, and we must compare value against each element.
                else if (operator === '~=') {
                    if (propValue) {
                        //We need an array
                        if (!Ext.isArray(propValue)) {
                            propValue = propValue.split(' ');
                        }

                        for (j = 0, propLen = propValue.length; j < propLen; j++) {
                            if (queryOperators[operator](Ext.coerce(propValue[j], compareTo), compareTo)) {
                                result.push(candidate);
                                break;
                            }
                        }
                    }
                }
                else if (operator === '/=') {
                    if (propValue != null && compareTo.test(propValue)) {
                        result.push(candidate);
                    }
                } else if (!compareTo ? !!candidate[property] : queryOperators[operator](Ext.coerce(propValue, compareTo), compareTo)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which have the specified itemId or id
        filterById = function(items, id) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getItemId() === id) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in
        filterByPseudo = function(items, name, value) {
            return cq.pseudos[name](items, value);
        },

        // Determines leading mode
        // > for direct child, and ^ to switch to ownerCt axis
        modeRe = /^(\s?([>\^])\s?|\s|$)/,

        // Matches a token with possibly (true|false) appended for the "shallow" parameter
        tokenRe = /^(#)?((?:\\\.|[\w\-])+|\*)(?:\((true|false)\))?/,

        matchers = [{
            // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter
            re: /^\.((?:\\\.|[\w\-])+)(?:\((true|false)\))?/,
            method: filterByXType,
            argTransform: function(args) {
                //<debug>
                var selector = args[0];
                Ext.log.warn('"'+ selector +'" ComponentQuery selector style is deprecated,' +
                             ' use "' + selector.replace(/^\./, '') + '" without the leading dot instead');
                //</debug>
                
                if (args[1] !== undefined) {
                    args[1] = args[1].replace(unescapeRe, '$1');
                }
                
                return args.slice(1);
            }
        }, {
            // Allow [@attribute] to check truthy ownProperty
            // Allow [?attribute] to check for presence of ownProperty
            // Allow [$attribute]
            // Checks for @|?|$ -> word/hyphen chars -> any special attribute selector characters before
            // the '=', etc. It strips out whitespace.
            // For example:
            //     [attribute=value], [attribute^=value], [attribute$=value], [attribute*=value],
            //     [attribute~=value], [attribute%=value], [attribute!=value]
            re: /^(?:\[((?:[@?$])?[\w\-]*)\s*(?:([\^$*~%!\/]?=)\s*(['"])?((?:\\\]|.)*?)\3)?(?!\\)\])/,
            method: filterByAttribute,
            argTransform: function(args) {
                var selector  = args[0],
                    property  = args[1],
                    operator  = args[2],
                    //quote     = args[3],
                    compareTo = args[4],
                    compareRe;
                
                // Unescape the attribute value matcher first
                if (compareTo !== undefined) {
                    compareTo = compareTo.replace(unescapeRe, '$1');

                    //<debug>
                    var format = Ext.String.format,
                        msg = "ComponentQuery selector '{0}' has an unescaped ({1}) character at the {2} " +
                              "of the attribute value pattern. Usually that indicates an error " +
                              "where the opening quote is not followed by the closing quote. " +
                              "If you need to match a ({1}) character at the {2} of the attribute " +
                              "value, escape the quote character in your pattern: (\\{1})",
                        match;
                
                    if (match = /^(['"]).*?[^'"]$/.exec(compareTo)) { // jshint ignore:line
                        Ext.log.warn(format(msg, selector, match[1], 'beginning'));
                    }
                    else if (match = /^[^'"].*?(['"])$/.exec(compareTo)) { // jshint ignore:line
                        Ext.log.warn(format(msg, selector, match[1], 'end'));
                    }
                    //</debug>
                }
                
                if (operator === '/=') {
                    compareRe = regexCache.get(compareTo);
                    if (compareRe) {
                        compareTo = compareRe;
                    } else {
                        compareTo = regexCache.add(compareTo, new RegExp(compareTo));
                    }
                }
                
                return [property, operator, compareTo];
            }
        }, {
            // checks for #cmpItemId
            re: /^#((?:\\\.|[\w\-])+)/,
            method: filterById
        }, {
            // checks for :<pseudo_class>(<selector>)
            re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
            method: filterByPseudo,
            argTransform: function(args) {
                if (args[2] !== undefined) {
                    args[2] = args[2].replace(unescapeRe, '$1');
                }
                
                return args.slice(1);
            }
        }, {
            // checks for {<member_expression>}
            re: /^(?:\{([^\}]+)\})/,
            method: filterFnPattern
        }];

    // Internal class Ext.ComponentQuery.Query
    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },
        
        // Executes this Query upon the selected root.
        // The root provides the initial source of candidate Component matches which are progressively
        // filtered by iterating through this Query's operations cache.
        // If no root is provided, all registered Components are searched via the ComponentManager.
        // root may be a Container who's descendant Components are filtered
        // root may be a Component with an implementation of getRefItems which provides some nested Components such as the
        // docked items within a Panel.
        // root may be an array of candidate Components to filter using this Query.
        execute: function(root) {
            var operations = this.operations,
                result = [],
                op, i, len;
            
            for (i = 0, len = operations.length; i < len; i++) {
                op = operations[i];
                
                result = result.concat(this._execute(root, op));
            }
            
            return result;
        },
        
        _execute: function(root, operations) {
            var i = 0,
                length = operations.length,
                operation,
                workingItems;

            // no root, use all Components in the document
            if (!root) {
                workingItems = Ext.ComponentManager.getAll();
            }
            // Root is an iterable object like an Array, or system Collection, eg HtmlCollection
            else if (Ext.isIterable(root)) {
                workingItems = root;
            }
            // Root is a MixedCollection
            else if (root.isMixedCollection) {
                workingItems = root.items;
            }

            // We are going to loop over our operations and take care of them
            // one by one.
            for (; i < length; i++) {
                operation = operations[i];

                // The mode operation requires some custom handling.
                // All other operations essentially filter down our current
                // working items, while mode replaces our current working
                // items by getting children from each one of our current
                // working items. The type of mode determines the type of
                // children we get. (e.g. > only gets direct children)
                if (operation.mode === '^') {
                    workingItems = getAncestors(workingItems || [root]);
                }
                else if (operation.mode) {
                    workingItems = getItems(workingItems || [root], operation.mode);
                }
                else {
                    workingItems = filterItems(workingItems || getItems([root]), operation);
                }

                // If this is the last operation, it means our current working
                // items are the final matched items. Thus return them!
                if (i === length -1) {
                    return workingItems;
                }
            }
            return [];
        },

        is: function(component) {
            var operations = this.operations,
                result = false,
                len = operations.length,
                op, i;
            
            if (len === 0) {
                return true;
            }
            
            for (i = 0; i < len; i++) {
                op = operations[i];
                
                result = this._is(component, op);
                
                if (result) {
                    return result;
                }
            }
            
            return false;
        },
        
        _is: function(component, operations) {
            var len = operations.length,
                active = [component],
                operation, i, j, mode, items, item;
                
            // Loop backwards, since we're going up the hierarchy
            for (i = len - 1; i >= 0; --i) {
                operation = operations[i];
                mode = operation.mode;
                // Traversing hierarchy
                if (mode) {
                    if (mode === '^') {
                        active = getItems(active, ' ');
                    } else if (mode === '>') {
                        items = [];
                        for (j = 0, len = active.length; j < len; ++j) {
                            item = active[j].getRefOwner();
                            if (item) {
                                items.push(item);
                            }
                        }
                        active = items;
                    } else {
                        active = getAncestors(active);
                    }
                    
                    // After traversing the hierarchy, if we have no items, jump out
                    if (active.length === 0) {
                        return false;
                    }
                    
                } else {
                    active = filterItems(active, operation);
                    if (active.length === 0) {
                        return false;
                    }
                }
            }
            return true;
        },
        
        getMatches: function(components, operations) {
            var len = operations.length,
                i;
                
            for (i = 0; i < len; ++i) {
                components = filterItems(components, operations[i]);
                // Performance enhancement, if we have nothing, we can
                // never add anything new, so jump out
                if (components.length === 0) {
                    break;
                }  
            }
            return components;
        },
        
        isMultiMatch: function() {
            return this.operations.length > 1;
        }
    });

    Ext.apply(cq, {

        // private cache of selectors and matching ComponentQuery.Query objects
        cache: new Ext.util.LruCache({
            maxSize: 100
        }),

        // private cache of pseudo class filter functions
        pseudos: {
            not: function(components, selector){
                var i = 0,
                    length = components.length,
                    results = [],
                    index = -1,
                    component;
                
                for(; i < length; ++i) {
                    component = components[i];
                    if (!cq.is(component, selector)) {
                        results[++index] = component;
                    }
                }
                return results;
            },
            first: function(components) {
                var ret = [];
                    
                if (components.length > 0) {
                    ret.push(components[0]);
                }
                return ret;       
            },
            last: function(components) {
                var len = components.length,
                    ret = [];
                    
                if (len > 0) {
                    ret.push(components[len - 1]);
                }
                return ret;
            },
            focusable: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;

                for (; i < len; i++) {
                    c = cmps[i];
                    
                    if (c.isFocusable && c.isFocusable()) {
                        results.push(c);
                    }
                }

                return results;
            },
            "nth-child" : function(c, a) {
                var result = [],
                    m = nthRe.exec(a === "even" && "2n" || a === "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
                    f = (m[1] || 1) - 0, len = m[2] - 0,
                    i, n, nodeIndex;

                for (i = 0; n = c[i]; i++) { // jshint ignore:line
                    nodeIndex = i + 1;
                    if (f === 1) {
                        if (len === 0 || nodeIndex === len) {
                            result.push(n);
                        }
                    } else if ((nodeIndex + len) % f === 0){
                        result.push(n);
                    }
                }

                return result;
            }
        },

        /**
         * Returns an array of matched Components from within the passed root object.
         *
         * This method filters returned Components in a similar way to how CSS selector based DOM
         * queries work using a textual selector string.
         *
         * See class summary for details.
         *
         * @param {String} selector The selector string to filter returned Components
         * @param {Ext.container.Container} [root] The Container within which to perform the query.
         * If omitted, all Components within the document are included in the search.
         * 
         * This parameter may also be an array of Components to filter according to the selector.
         * @return {Ext.Component[]} The matched Components.
         * 
         * @member Ext.ComponentQuery
         */
        query: function(selector, root) {
            // An empty query will match every Component
            if (!selector) {
                return Ext.ComponentManager.all.getArray();
            }
            
            var results = [],
                noDupResults = [], 
                dupMatcher = {}, 
                query = cq.cache.get(selector),
                resultsLn, cmp, i;

            if (!query) {
                query = cq.cache.add(selector, cq.parse(selector));
            }
            
            results = query.execute(root);
            
            // multiple selectors, potential to find duplicates
            // lets filter them out.
            if (query.isMultiMatch()) {
                resultsLn = results.length;
                for (i = 0; i < resultsLn; i++) {
                    cmp = results[i];
                    if (!dupMatcher[cmp.id]) {
                        noDupResults.push(cmp);
                        dupMatcher[cmp.id] = true;
                    }
                }
                results = noDupResults;
            }
            return results;
        },

        /**
         * Traverses the tree rooted at the passed root in pre-order mode, calling the passed function on the nodes at each level.
         * That is the function is called upon each node **before** being called on its children).
         *
         * For an object to be queryable, it must implement the `getRefItems` method which returns all
         * immediate child items.
         *
         * This method is used at each level down the cascade. Currently {@link Ext.Component Component}s
         * and {@link Ext.data.TreeModel TreeModel}s are queryable.
         *
         * If you have tree-structured data, you can make your nodes queryable, and use ComponentQuery on them.
         *
         * @param {Object} selector A ComponentQuery selector used to filter candidate nodes before calling the function.
         * An empty string matches any node.
         * @param {String} root The root queryable object to start from.
         * @param {Function} fn The function to call. Return `false` to abort the traverse.
         * @param {Object} fn.node The node being visited.
         * @param {Object} [scope] The context (`this` reference) in which the function is executed.
         * @param {Array} [extraArgs] A set of arguments to be appended to the function's argument list to pass down extra data known to the caller
         * **after** the node being visited.
         */
        visitPreOrder: function(selector, root, fn, scope, extraArgs) {
            cq._visit(true, selector, root, fn, scope, extraArgs);
        },

        /**
         * Traverses the tree rooted at the passed root in post-order mode, calling the passed function on the nodes at each level.
         * That is the function is called upon each node **after** being called on its children).
         *
         * For an object to be queryable, it must implement the `getRefItems` method which returns all
         * immediate child items.
         *
         * This method is used at each level down the cascade. Currently {@link Ext.Component Component}s
         * and {@link Ext.data.TreeModel TreeModel}s are queryable.
         *
         * If you have tree-structured data, you can make your nodes queryable, and use ComponentQuery on them.
         *
         * @param {Object} selector A ComponentQuery selector used to filter candidate nodes before calling the function.
         * An empty string matches any node.
         * @param {String} root The root queryable object to start from.
         * @param {Function} fn The function to call. Return `false` to abort the traverse.
         * @param {Object} fn.node The node being visited.
         * @param {Object} [scope] The context (`this` reference) in which the function is executed.
         * @param {Array} [extraArgs] A set of arguments to be appended to the function's argument list to pass down extra data known to the caller
         * **after** the node being visited.
         */
        visitPostOrder: function(selector, root, fn, scope, extraArgs) {
            cq._visit(false, selector, root, fn, scope, extraArgs);
        },

        // @private
        // visit implementation which handles both preOrder and postOrder modes.
        _visit: function(preOrder, selector, root, fn, scope, extraArgs) {
            var query = cq.cache.get(selector),
                callArgs = [root],
                children,
                len = 0,
                i, rootMatch;

            if (!query) {
                query = cq.cache.add(selector, cq.parse(selector));
            }

            rootMatch = query.is(root);

            if (root.getRefItems) {
                children = root.getRefItems();
                len = children.length;
            }

            // append optional extraArgs
            if (extraArgs) {
                Ext.Array.push(callArgs, extraArgs);
            }
            if (preOrder) {
                if (rootMatch) {
                    if (fn.apply(scope || root, callArgs) === false) {
                        return false;
                    }
                }
            }
            for (i = 0; i < len; i++) {
                if (cq._visit.call(cq, preOrder, selector, children[i], fn, scope, extraArgs) === false) {
                    return false;
                }
            }
            if (!preOrder) {
                if (rootMatch) {
                    if (fn.apply(scope || root, callArgs) === false) {
                        return false;
                    }
                }
            }
        },

        /**
         * Tests whether the passed Component matches the selector string.
         * An empty selector will always match.
         *
         * @param {Ext.Component} component The Component to test
         * @param {String} selector The selector string to test against.
         * @return {Boolean} True if the Component matches the selector.
         * @member Ext.ComponentQuery
         */
        is: function(component, selector) {
            if (!selector) {
                return true;
            }
            
            var query = cq.cache.get(selector);
            if (!query) {
                query = cq.cache.add(selector, cq.parse(selector));
            }
            
            return query.is(component);
        },

        parse: function(selector) {
            var operations = [],
                selectors, sel, i, len;
            
            selectors = Ext.splitAndUnescape(selector, ',');
            
            for (i = 0, len = selectors.length; i < len; i++) {
                // Trim the whitespace as the parser expects it.
                sel = Ext.String.trim(selectors[i]);
                
                // Usually, a dangling comma at the end of a selector means a typo.
                // In that case, the last sel value will be an empty string; the parser
                // will silently ignore it which is not good, so we throw an error here.
                //<debug>
                if (sel === '') {
                    Ext.Error.raise('Invalid ComponentQuery selector: ""');
                }
                //</debug>
                
                operations.push(cq._parse(sel));
            }

            //  Now that we have all our operations in an array, we are going
            // to create a new Query using these operations.
            return new cq.Query({
                operations: operations
            });
        },
        
        _parse: function(selector) {
            var operations = [],
                trim = Ext.String.trim,
                length = matchers.length,
                lastSelector,
                tokenMatch,
                token,
                matchedChar,
                modeMatch,
                selectorMatch,
                transform,
                i, matcher, method, args;

            // We are going to parse the beginning of the selector over and
            // over again, slicing off the selector any portions we converted into an
            // operation, until it is an empty string.
            while (selector && lastSelector !== selector) {
                lastSelector = selector;

                // First we check if we are dealing with a token like #, * or an xtype
                tokenMatch = selector.match(tokenRe);

                if (tokenMatch) {
                    matchedChar = tokenMatch[1];
                    token = trim(tokenMatch[2]).replace(unescapeRe, '$1');

                    // If the token is prefixed with a # we push a filterById operation to our stack
                    if (matchedChar === '#') {
                        operations.push({
                            method: filterById,
                            args: [token]
                        });
                    }
                    // If the token is a * or an xtype string, we push a filterByXType
                    // operation to the stack.
                    else {
                        operations.push({
                            method: filterByXType,
                            args: [token, Boolean(tokenMatch[3])]
                        });
                    }

                    // Now we slice of the part we just converted into an operation
                    selector = selector.replace(tokenMatch[0], '').replace(stripLeadingSpaceRe, '$1');
                }

                // If the next part of the query is not a space or > or ^, it means we
                // are going to check for more things that our current selection
                // has to comply to.
                while (!(modeMatch = selector.match(modeRe))) {
                    // Lets loop over each type of matcher and execute it
                    // on our current selector.
                    for (i = 0; selector && i < length; i++) {
                        matcher = matchers[i];
                        selectorMatch = selector.match(matcher.re);
                        method = matcher.method;
                        transform = matcher.argTransform;

                        // If we have a match, add an operation with the method
                        // associated with this matcher, and pass the regular
                        // expression matches are arguments to the operation.
                        if (selectorMatch) {
                            // Transform function will do unescaping and additional checks
                            if (transform) {
                                args = transform(selectorMatch);
                            }
                            else {
                                args = selectorMatch.slice(1);
                            }
                            
                            operations.push({
                                method: Ext.isString(matcher.method)
                                    // Turn a string method into a function by formatting the string with our selector matche expression
                                    // A new method is created for different match expressions, eg {id=='textfield-1024'}
                                    // Every expression may be different in different selectors.
                                    ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
                                    : matcher.method,
                                args: args
                            });
                            
                            selector = selector.replace(selectorMatch[0], '').replace(stripLeadingSpaceRe, '$1');
                            break; // Break on match
                        }
                        // Exhausted all matches: It's an error
                        if (i === (length - 1)) {
                            Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
                        }
                    }
                }

                // Now we are going to check for a mode change. This means a space
                // or a > to determine if we are going to select all the children
                // of the currently matched items, or a ^ if we are going to use the
                // ownerCt axis as the candidate source.
                if (modeMatch[1]) { // Assignment, and test for truthiness!
                    operations.push({
                        mode: modeMatch[2]||modeMatch[1]
                    });
                    
                    // When we have consumed the mode character, clean up leading spaces
                    selector = selector.replace(modeMatch[0], '').replace(stripLeadingSpaceRe, '');
                }
            }
            
            return operations;
        }
    });
});
