/**
 * 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 AbstractComponent's {@link Ext.AbstractComponent#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
 *
 * ## 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[autoScroll]` - matches any Component that has `autoScroll` 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 operators in {@link Ext.dom.Query DomQuery}'s
 * {@link Ext.dom.Query#operators operators} to compare values.
 *
 * 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 `#id` form; it will
 * do the same but is easier to read.
 *
 * ## 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 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.
 *
 * ## 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.dom.Query'
    ]
}, function() {

    var cq = this,
        domQueryOperators = Ext.dom.Query.operators,
        nthRe = /(\d*)n\+?(\d*)/,
        nthRe2 = /\D/,

        // 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 passed className
        filterByClassName = function(items, className) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.hasCls(className)) {
                    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;

            // 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];

                // Check candidate hasOwnProperty is propName prefixed with a bang.
                if (!mustBeOwnProperty || candidate.hasOwnProperty(property)) {

                    // pull out property value to test
                    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 (domQueryOperators[operator](Ext.coerce(propValue[j], compareTo), compareTo)) {
                                    result.push(candidate);
                                    break;
                                }
                            }
                        }
                    } else if (!compareTo ? !!candidate[property] : domQueryOperators[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
        }, {
            // checks for [attribute=value], [attribute^=value], [attribute$=value], [attribute*=value], [attribute~=value], [attribute%=value], [attribute!=value]
            // Allow [@attribute] to check truthy ownProperty
            // Allow [?attribute] to check for presence of ownProperty
            re: /^(?:\[((?:@|\?)?[\w\-\$]*[^\^\$\*~%!])\s?(?:(=|.=)\s?['"]?(.*?)["']?)?\])/,
            method: filterByAttribute
        }, {
            // checks for #cmpItemId
            re: /^#([\w\-]+)/,
            method: filterById
        }, {
            // checks for :<pseudo_class>(<selector>)
            re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
            method: filterByPseudo
        }, {
            // 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,
                i = 0,
                length = operations.length,
                operation,
                workingItems;

            // no root, use all Components in the document
            if (!root) {
                workingItems = Ext.ComponentManager.all.getArray();
            }
            // 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,
                components = Ext.isArray(component) ? component : [component],
                originalLength = components.length,
                lastOperation = operations[operations.length-1],
                ln, i;

            components = filterItems(components, lastOperation);
            if (components.length === originalLength) {
                if (operations.length > 1) {
                    for (i = 0, ln = components.length; i < ln; i++) {
                        if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
                            return false;
                        }
                    }
                }
                return true;
            }
            return false;
        }
    });

    Ext.apply(this, {

        // private cache of selectors and matching ComponentQuery.Query objects
        cache: {},

        // private cache of pseudo class filter functions
        pseudos: {
            not: function(components, selector){
                var CQ = Ext.ComponentQuery,
                    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 this is a generally focusable Component (has a focusEl, is rendered, enabled and visible)
                    // then it is currently focusable if focus management is enabled or if it is an input field, a button or a menu item
                    if (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, l = m[2] - 0,
                    i, n, nodeIndex;
                for (i = 0; n = c[i]; i++) {
                    nodeIndex = i + 1;
                    if (f == 1) {
                        if (l == 0 || nodeIndex == l) {
                            result.push(n);
                        }
                    } else if ((nodeIndex + l) % 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.
         * @returns {Ext.Component[]} The matched Components.
         * 
         * @member Ext.ComponentQuery
         */
        query: function(selector, root) {
            var selectors = selector.split(','),
                length = selectors.length,
                i = 0,
                results = [],
                noDupResults = [], 
                dupMatcher = {}, 
                query, resultsLn, cmp;

            for (; i < length; i++) {
                selector = Ext.String.trim(selectors[i]);
                query = this.cache[selector] || (this.cache[selector] = this.parse(selector));
                results = results.concat(query.execute(root));
            }

            // multiple selectors, potential to find duplicates
            // lets filter them out.
            if (length > 1) {
                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;
        },

        /**
         * Tests whether the passed Component matches the selector string.
         * @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 selectors = selector.split(','),
                length = selectors.length,
                i = 0,
                query;

            for (; i < length; i++) {
                selector = Ext.String.trim(selectors[i]);
                query = this.cache[selector] || (this.cache[selector] = this.parse(selector));
                if (query.is(component)) {
                    return true;
                }
            }
            return false;
        },

        parse: function(selector) {
            var operations = [],
                length = matchers.length,
                lastSelector,
                tokenMatch,
                matchedChar,
                modeMatch,
                selectorMatch,
                i, matcher, method;

            // 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];

                    // If the token is prefixed with a # we push a filterById operation to our stack
                    if (matchedChar === '#') {
                        operations.push({
                            method: filterById,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    // If the token is prefixed with a . we push a filterByClassName operation to our stack
                    // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix
                    else if (matchedChar === '.') {
                        operations.push({
                            method: filterByClassName,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    // If the token is a * or an xtype string, we push a filterByXType
                    // operation to the stack.
                    else {
                        operations.push({
                            method: filterByXType,
                            args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
                        });
                    }

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

                // 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;

                        // 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) {
                            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: selectorMatch.slice(1)
                            });
                            selector = selector.replace(selectorMatch[0], '');
                            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]
                    });
                    selector = selector.replace(modeMatch[0], '');
                }
            }

            //  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
            });
        }
    });
});