| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-ComponentQuery'>/**</span> * 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. * * 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 (See {@link Ext.AbstractComponent#isXType AbstractComponent's isXType method}) * *     prevTextField = myField.previousNode('textfield(true)'); * * An itemId or id must be prefixed with a # * * - `#myContainer` * * Attributes must be wrapped in brackets * * - `component[autoScroll]` * - `panel[title="Test"]` * * 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()}"); * * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}: * *     // 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"); *         } *     } * * Default pseudos include: * * - not * - first * - last * * 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 and trees *     var gridsAndTrees = Ext.ComponentQuery.query('gridpanel, treepanel'); * * 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,    uses: ['Ext.ComponentManager']}, function() {    var cq = this,        // 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.ownerCt || candidate.floatParent))) {                    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 EA = Ext.Array,                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, value) {            var result = [],                i = 0,                length = items.length,                candidate;            for (; i < length; i++) {                candidate = items[i];                if (!value ? !!candidate[property] : (String(candidate[property]) === value)) {                    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]            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 a candidate Array            else if (Ext.isArray(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;            }        },<span id='Ext-ComponentQuery-method-query'>        /**</span>         * 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.</p>         * @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;        },<span id='Ext-ComponentQuery-method-is'>        /**</span>         * 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            });        }    });});</pre></body></html>
 |