| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 | "use strict";// Copyright 2012 United States Government, as represented by the Secretary of Defense, Under// Secretary of Defense (Personnel & Readiness).// // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except// in compliance with the License. You may obtain a copy of the License at// //   http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software distributed under the License// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express// or implied. See the License for the specific language governing permissions and limitations under// the License./// XPath resolution functions./// /// @module vwf/utility/xpathdefine( [ "module" ], function( module ) {    var exports = {        // -- resolve --------------------------------------------------------------------------        /// Resolve an XPath expression, using a callback function to interpret each step.        ///         /// @param {String|String[]|Object[]} xpath        /// @param {ID} rootID        /// @param {ID|ID[]} contextIDs        /// @param {Function} callback        /// @param {Object} [thisArg]        ///         /// @returns {ID[]|undefined}        resolve: function( xpath, rootID, contextIDs, callback /* ( step, id, resolveAttributes ) */, thisArg ) {            // Accept contextIDs as either a single id or an array of ids.            if ( ! ( contextIDs instanceof Array ) ) {                contextIDs = [ contextIDs ];            }            // Parse the expression.            var steps = this.parse( xpath );            if ( steps ) {                // Reset the context if it's an absolute path.                if ( steps.absolute ) {                    contextIDs = rootID ? [ rootID ] : [];                }                // Resolve each step.                steps.forEach( function( step ) {                    contextIDs = Array.prototype.concat.apply( [], contextIDs.map( function( id ) {                        var stepIDs = callback.call( thisArg, step, id );                        step.predicates && step.predicates.forEach( function( predicate ) {                            stepIDs = stepIDs.filter( function( stepID ) {                                return this.resolve( predicate, rootID, stepID, function( step, id ) {                                    return callback.call( this, step, id, true );                                }, thisArg ).length;                            }, this );                        }, this  );                        return stepIDs;                    }, this ) );                }, this );                return contextIDs;            }        },        // -- parse ----------------------------------------------------------------------------        /// Parse an XPath expression into a series of steps.        ///         /// @param {String|String[]|Object[]} xpath        ///         /// @returns {Object[]|undefined}        parse: function( xpath ) {            var steps = [], step;            if ( typeof xpath == "string" || xpath instanceof String ) {                if ( xpath[0] == "/" ) {                    steps.absolute = true;                    xpath = { string: xpath, index: 1 };                } else {                    xpath = { string: xpath, index: 0 };                }                while ( xpath.index < xpath.string.length &&                        ( step = /* assignment! */ this.parseStep( xpath ) ) ) {                    steps.push( step );                }                if ( xpath.index < xpath.string.length ) {                    return undefined;                }            } else if ( typeof xpath[0] == "string" || xpath[0] instanceof String ) {                var valid = true;                steps = xpath.map( function( step ) {                    step = this.parseStep( step );                    valid = valid && step;                    return step;                }, this );                if ( ! valid ) {                    return undefined;                }            } else {                steps = xpath;            }            return steps;        },        // -- parseStep ------------------------------------------------------------------------        /// Parse an XPath step expression.        ///         /// @param {String|Object} xpath        ///         /// @returns {Object|undefined}        parseStep: function( xpath ) {            if ( typeof xpath == "string" || xpath instanceof String ) {                xpath = { string: xpath, index: 0 };            }            if ( xpath.index < xpath.string.length && ! this.regex.separator.test( xpath.string.slice( xpath.index ) ) ) {                var step_match = this.regex.step.exec( xpath.string.slice( xpath.index ) );            } else {                var step_match = [].concat( "", new Array( 11 ), "" /* abbreviated_step */ ); // special case for "//"            }            if ( step_match ) {                xpath.index += step_match[0].length;                var axis_name = step_match[1],                    abbreviated_axis_specifier = step_match[2],                    node_kind = step_match[3],                    node_name = step_match[4],                    node_name_quoted = step_match[5],                    node_name_wildcard = step_match[6],                    type_name = step_match[7],                    type_name_quoted = step_match[8],                    name_test = step_match[9],                    name_test_quoted = step_match[10],                    name_test_wildcard = step_match[11],                    abbreviated_step = step_match[12];                if ( name_test || name_test_quoted || name_test_wildcard ) {                  node_name = name_test;                  node_name_quoted = name_test_quoted;                  node_name_wildcard = name_test_wildcard;                }                if ( node_name_quoted ) {                  node_name = this.unquoteName( node_name_quoted );                }                if ( type_name_quoted ) {                  type_name = this.unquoteName( type_name_quoted );                }                switch ( abbreviated_step ) {                    case "": // "" == "descendant-or-self:node()"                        axis_name = "descendant-or-self";                        node_kind = "node";                        break;                    case ".": // "." == "self::node()"                        axis_name = "self";                        node_kind = "node";                        break;                    case "..": // ".." == "parent::node()"                        axis_name = "parent";                        node_kind = "node";                        break;                }                switch ( abbreviated_axis_specifier ) {                    case "": // "name" == "child::name"                        axis_name = "child";                        break;                    case "@": // "@name" == "attribute::name"                        axis_name = "attribute";                        break;                }                // // * == element()                // "preceding::"                // "preceding-sibling::"                // "ancestor-or-self::"                // "ancestor::"                // "parent::"                // "self::"                // "child::"                // "descendant::"                // "descendant-or-self::"                // "following-sibling::"                // "following::"                // // * == attribute()                // "attribute::"                // // * == namespace()                // "namespace::"                if ( node_name_wildcard && ! node_kind ) {                    switch ( axis_name ) {                        default:                            node_kind = "element";                            break;                        case "attribute":                            node_kind = "attribute";                            break;                        case "namespace":                            node_kind = "namespace";                            break;                    }                }                // Parse the predicates.                var predicates = [], predicate;                while ( predicate = /* assignment! */ this.parsePredicate( xpath ) ) {                    predicates.push( predicate );                }                // Absorb the separator.                this.parseSeparator( xpath );                // Now have: axis_name and name_test | node_kind(node_name,type_name)                var step = {                    axis: axis_name,                    kind: node_kind,                    name: node_name,                    type: type_name,                };                if ( predicates.length ) {                    step.predicates = predicates;                }                return step;            }        },        // -- parsePredicate -------------------------------------------------------------------        /// Parse an XPath step predicate.        ///         /// @param {String|Object} xpath        ///         /// @returns {Object[]|undefined}        parsePredicate: function( xpath ) {            if ( typeof xpath == "string" || xpath instanceof String ) {                xpath = { string: xpath, index: 0 };            }            var predicate_match = this.regex.predicate.exec( xpath.string.slice( xpath.index ) );            if ( predicate_match ) {                xpath.index += predicate_match[0].length;                return this.parse( predicate_match[1] );            }        },        // -- parseSeparator -------------------------------------------------------------------        /// Parse an XPath step separator.        ///         /// @param {String|Object} xpath        ///         /// @returns {Boolean|undefined}        parseSeparator: function( xpath ) {            if ( typeof xpath == "string" || xpath instanceof String ) {                xpath = { string: xpath, index: 0 };            }            var separator_match = this.regex.separator.exec( xpath.string.slice( xpath.index ) );            if ( separator_match ) {                xpath.index += separator_match[0].length;                return true;            }        },        // -- quoteName --------------------------------------------------------------------------        /// Apply quotation marks around a name and escape internal quotation marks and escape        /// characters.        ///         /// @param {String} unquoted_name        ///         /// @returns {String}        quoteName: function( unquoted_name ) {            return '"' + unquoted_name.replace( /(["\\])/g, "\\$1" ) + '"';        },        // -- unquoteName ------------------------------------------------------------------------        /// Remove the enclosing quotation marks and unescape internal quotation marks and escape        /// characters of a quoted name.        ///         /// @param {String} quoted_name        ///         /// @returns {String}        unquoteName: function( quoted_name ) {            if ( quoted_name[0] == "'" ) {              return quoted_name.slice( 1, -1 ).replace( /\\(['\\])/g, "$1" );            } else if ( quoted_name[0] == '"' ) {              return quoted_name.slice( 1, -1 ).replace( /\\(["\\])/g, "$1" );            }        },        // -- regex ----------------------------------------------------------------------------        /// Regexes to crack the XPath string.        regex: ( function() {            var name = "[A-Za-z_][A-Za-z_0-9.-]*",              // XPath QName: http://www.w3.org/TR/xpath20/#prod-xpath-QName                singleQuotedName = "'(?:[^'\\\\]|\\'|\\\\)+'",  // Single-quoted QName (VWF extension)                doubleQuotedName = '"(?:[^"\\\\]|\\"|\\\\)+"',  // Double-quoted QName (VWF extension)                wildcard = "\\*";                               // XPath Wildcard: http://www.w3.org/TR/xpath20/#prod-xpath-Wildcard            /// @field            var step =                                          // XPath StepExpr: http://www.w3.org/TR/xpath20/#prod-xpath-StepExpr                "(?:" +                  "(?:" +                    "(?:" +                      "(?:(" + name + ")::)" +                  // "axis", as in "axis::"" (axis_name)                    "|" +                      "(@|)" +                                  // "@", "" (abbreviated_axis_specifier)                    ")" +                    "(?:" +                      "(?:" +                        "(" + name + ")" +                      // "kind" (node_kind)                        "\\(" +                                 // "("                          "(?:" +                            "(?:" +                              "(" + name + ")" +                // "node" (node_name)                            "|" +                              "(" +                                "(?:" + singleQuotedName + ")" + // "'node'" (node_name_quoted, quoted and with internal escapes)                              "|" +                                "(?:" + doubleQuotedName + ")" + // "\"node\"" (node_name_quoted, quoted and with internal escapes)                              ")" +                            "|" +                              "(" + wildcard + ")" +            // "*" (node_name_wildcard)                            ")" +                            "(?:" +                              "," +                             // ","                              "(?:" +                                "(" + name + ")" +              // "type" (type_name)                              "|" +                                "(" +                                  "(?:" + singleQuotedName + ")" + // "'type'" (type_name_quoted, quoted and with internal escapes)                                "|" +                                  "(?:" + doubleQuotedName + ")" + // '"type"' (type_name_quoted, quoted and with internal escapes)                                ")" +                              ")" +                            ")?" +                          ")?" +                        "\\)" +                                 // ")"                      ")" +                    "|" +                      "(?:" +                        "(" + name + ")" +                      // "name" (name_test)                      "|" +                        "(" +                          "(?:" + singleQuotedName + ")" +      // "'name'" (name_test_quoted, quoted and with internal escapes)                        "|" +                          "(?:" + doubleQuotedName + ")" +      // '"name"' (name_test_quoted, quoted and with internal escapes)                        ")" +                      "|" +                        "(" + wildcard + ")" +                  // "*" (name_test_wildcard)                      ")" +                    ")" +                  ")" +                "|" +                  "(\\.\\.|\\.)" +                             // "..", "." (abbreviated_step)                ")";            /// @field            var predicate =                                     // XPath Predicate: http://www.w3.org/TR/xpath20/#prod-xpath-Predicate                "\\[" +                    "(" +                        step + // "[^\\]]*" +                    ")" +                "\\]";            /// @field            var separator = "/";            var regexes = {                /// @field                step: new RegExp( "^" + step ),                /// @field                predicate: new RegExp( "^" + predicate ),                /// @field                separator: new RegExp( "^" + separator ),            };            return regexes;        } )(),    };    return exports;} );
 |