<commit_before>
var defineProperty = Object.defineProperty;

var defineProperties = Object.defineProperties;

var slice = Array.prototype.slice;

var map = Array.prototype.map;

var splice = Array.prototype.splice;

var floor = Math.floor;

var reduce = Array.prototype.reduce;

var svgNS = "http://www.w3.org/2000/svg";

var xlinkNS = "http://www.w3.org/1999/xlink";

var evNS = "http://www.w3.org/2001/xml-events";

var xhtmlNS = "http://www.w3.org/1999/xhtml";

var xmlNS = "http://www.w3.org/2000/xmlns/";

var window = root;

var document = root.document;

var isArray = Array.isArray;

var max = Math.max;

var min = Math.min;

var create = Object.create;

var isNullOrUndefined = function (value) {
    return value === undefined || value === null;
};

var isObject = function (value) {
    return value !== null && (typeof value === "object" || typeof value === "function");
};

var isCallable = function (value) {
    return typeof value === "function";
};

var isPrimitive = function (value) {
    return !isObject(value);
};

var setPrototypeOf = function (obj, prototype) {
    if (obj === undefined || obj === null)
        throw new TypeError("setPrototypeOf called on null or undefined");
    if ( !(prototype === null || isObject(prototype)) )
        throw new TypeError("Object prototype may only be an Object or null: " + String(prototype));

    var protoDesc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
    // If Object.prototype.__proto__ does not exist or it is
    // not an accessor property then just throw errors
    if (protoDesc === undefined || !isCallable(protoDesc.set))
        throw new TypeError("Object.prototype.__proto__ accessor property does not exist");

    protoDesc.set.call(obj, prototype);
    return obj;
};

var isIEOrEdgeBrowser = function () {
    var ua = window.navigator.userAgent;
    return (/MSIE/i.test(ua) || /rv:11\.0/i.test(ua) || /Edge/i.test(ua));
};

var isFirefox = function () {
    var ua = window.navigator.userAgent;
    return (/Firefox/i.test(ua) || /Mozilla/i.test(ua));
};

var isSVGSupported = function () {
    return isCallable(window.SVGElement)
        && isCallable(document.createElementNS)
        && document.createElementNS(svgNS, "svg") instanceof window.SVGElement;
};<commit_msg>fixed isFirefox() method<commit_after>
var defineProperty = Object.defineProperty;

var defineProperties = Object.defineProperties;

var slice = Array.prototype.slice;

var map = Array.prototype.map;

var splice = Array.prototype.splice;

var floor = Math.floor;

var reduce = Array.prototype.reduce;

var svgNS = "http://www.w3.org/2000/svg";

var xlinkNS = "http://www.w3.org/1999/xlink";

var evNS = "http://www.w3.org/2001/xml-events";

var xhtmlNS = "http://www.w3.org/1999/xhtml";

var xmlNS = "http://www.w3.org/2000/xmlns/";

var window = root;

var document = root.document;

var isArray = Array.isArray;

var max = Math.max;

var min = Math.min;

var create = Object.create;

var isNullOrUndefined = function (value) {
    return value === undefined || value === null;
};

var isObject = function (value) {
    return value !== null && (typeof value === "object" || typeof value === "function");
};

var isCallable = function (value) {
    return typeof value === "function";
};

var isPrimitive = function (value) {
    return !isObject(value);
};

var setPrototypeOf = function (obj, prototype) {
    if (obj === undefined || obj === null)
        throw new TypeError("setPrototypeOf called on null or undefined");
    if ( !(prototype === null || isObject(prototype)) )
        throw new TypeError("Object prototype may only be an Object or null: " + String(prototype));

    var protoDesc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
    // If Object.prototype.__proto__ does not exist or it is
    // not an accessor property then just throw errors
    if (protoDesc === undefined || !isCallable(protoDesc.set))
        throw new TypeError("Object.prototype.__proto__ accessor property does not exist");

    protoDesc.set.call(obj, prototype);
    return obj;
};

var isIEOrEdgeBrowser = function () {
    var ua = window.navigator.userAgent;
    return (/MSIE/i.test(ua) || /rv:11\.0/i.test(ua) || /Edge/i.test(ua));
};

var isFirefox = function () {
    var ua = window.navigator.userAgent;
    return (/Firefox/i.test(ua) && /Mozilla/i.test(ua));
};

var isSVGSupported = function () {
    return isCallable(window.SVGElement)
        && isCallable(document.createElementNS)
        && document.createElementNS(svgNS, "svg") instanceof window.SVGElement;
};<|endoftext|>


<commit_before>import Model from "./model";

export default Ember.DataAdapter.extend({

    detect: function(klass) {
        return klass !== Model && Model.detect(klass);
    },

    columnsForType: function( type ) {
        var columns = [],
            type = type._debugContainerKey.replace( 'model:',''),
            record = this.get( 'store' )._cache._getRecords( type ).records[0];

        if( record ){
            Ember.keys( record.content ).forEach( function( key ){
                columns.push( { name: key, desc: key });
            });
        }

        return columns;
    },

    getRecords: function( type ){
        var type = type._debugContainerKey.replace( 'model:','');
        return this.get( 'store' )._cache._getRecords( type ).records;
    },

    getRecordColumnValues: function( record ){
        var values = {};

        if( record ){
            Ember.keys( record.content ).forEach( function( key ){
                values[ key ] = Ember.get( record, key );
            });
        }

        return values;
    },

    observeRecord: function( record, recordUpdated ){
        var releaseMethods = Ember.A(),
            self = this,
            keysToObserve = Ember.keys( record.content );

        keysToObserve.forEach(function(key) {
            var handler = function() {
                recordUpdated(self.wrapRecord(record));
            };
            Ember.addObserver(record, key, handler);
            releaseMethods.push(function() {
                Ember.removeObserver(record, key, handler);
            });
        });

        var release = function() {
            releaseMethods.forEach(function(fn) { fn(); } );
        };

        return release;
    }

});<commit_msg>updated with comments<commit_after>import Model from "./model";

export default Ember.DataAdapter.extend({

    /**
     * Returns the columns for a specific model type
     * @param  {Object} type Model Class
     * @return {Array}      Array of objs describing model columns
     */
    columnsForType: function( type ) {
        var columns = [],
            type = type._debugContainerKey.replace( 'model:',''),
            record = this.get( 'store' )._cache._getRecords( type ).records[0];

        if( record ){
            Ember.keys( record.content ).forEach( function( key ){
                columns.push( { name: key, desc: key });
            });
        }

        return columns;
    },

    /**
     * Returns the array of records for the model type
     * @param {Object} type Model Class
     * @return {Array} array of model records
     */
    getRecords: function( type ){
        var type = type._debugContainerKey.replace( 'model:','');
        return this.get( 'store' )._cache._getRecords( type ).records;
    },

    /**
     * Returns the values for the columns in a record
     * @param  {Object} record
     * @return {Object}        The values for the keys of this record
     */
    getRecordColumnValues: function( record ){
        var values = {};

        if( record ){
            Ember.keys( record.content ).forEach( function( key ){
                values[ key ] = Ember.get( record, key );
            });
        }

        return values;
    },

    /**
     * Sets up observers for records
     * @param  {Object} record
     * @param  {Function} recordUpdated callback when a record is updated
     * @return {Function}               callback when a record is destroyed
     */
    observeRecord: function( record, recordUpdated ){
        var releaseMethods = Ember.A(),
            self = this,
            keysToObserve = Ember.keys( record.content );

        keysToObserve.forEach(function(key) {
            var handler = function() {
                recordUpdated(self.wrapRecord(record));
            };
            Ember.addObserver(record, key, handler);
            releaseMethods.push(function() {
                Ember.removeObserver(record, key, handler);
            });
        });

        var release = function() {
            releaseMethods.forEach(function(fn) { fn(); } );
        };

        return release;
    }

});<|endoftext|>


<commit_before>/*!
 * XRegExp Unicode Base 4.4.1
 * <xregexp.com>
 * Steven Levithan (c) 2008-present MIT License
 */

export default (XRegExp) => {

    /**
     * Adds base support for Unicode matching:
     * - Adds syntax `\p{..}` for matching Unicode tokens. Tokens can be inverted using `\P{..}` or
     *   `\p{^..}`. Token names ignore case, spaces, hyphens, and underscores. You can omit the
     *   braces for token names that are a single letter (e.g. `\pL` or `PL`).
     * - Adds flag A (astral), which enables 21-bit Unicode support.
     * - Adds the `XRegExp.addUnicodeData` method used by other addons to provide character data.
     *
     * Unicode Base relies on externally provided Unicode character data. Official addons are
     * available to provide data for Unicode categories, scripts, blocks, and properties.
     *
     * @requires XRegExp
     */

    // ==--------------------------==
    // Private stuff
    // ==--------------------------==

    // Storage for Unicode data
    const unicode = {};
    const unicodeTypes = {};

    // Reuse utils
    const dec = XRegExp._dec;
    const hex = XRegExp._hex;
    const pad4 = XRegExp._pad4;

    // Generates a token lookup name: lowercase, with hyphens, spaces, and underscores removed
    function normalize(name) {
        return name.replace(/[- _]+/g, '').toLowerCase();
    }

    // Gets the decimal code of a literal code unit, \xHH, \uHHHH, or a backslash-escaped literal
    function charCode(chr) {
        const esc = /^\\[xu](.+)/.exec(chr);
        return esc ?
            dec(esc[1]) :
            chr.charCodeAt(chr[0] === '\\' ? 1 : 0);
    }

    // Inverts a list of ordered BMP characters and ranges
    function invertBmp(range) {
        let output = '';
        let lastEnd = -1;

        XRegExp.forEach(
            range,
            /(\\x..|\\u....|\\?[\s\S])(?:-(\\x..|\\u....|\\?[\s\S]))?/,
            (m) => {
                const start = charCode(m[1]);
                if (start > (lastEnd + 1)) {
                    output += `\\u${pad4(hex(lastEnd + 1))}`;
                    if (start > (lastEnd + 2)) {
                        output += `-\\u${pad4(hex(start - 1))}`;
                    }
                }
                lastEnd = charCode(m[2] || m[1]);
            }
        );

        if (lastEnd < 0xFFFF) {
            output += `\\u${pad4(hex(lastEnd + 1))}`;
            if (lastEnd < 0xFFFE) {
                output += '-\\uFFFF';
            }
        }

        return output;
    }

    // Generates an inverted BMP range on first use
    function cacheInvertedBmp(slug) {
        const prop = 'b!';
        return (
            unicode[slug][prop] ||
            (unicode[slug][prop] = invertBmp(unicode[slug].bmp))
        );
    }

    // Combines and optionally negates BMP and astral data
    function buildAstral(slug, isNegated) {
        const item = unicode[slug];
        let combined = '';

        if (item.bmp && !item.isBmpLast) {
            combined = `[${item.bmp}]${item.astral ? '|' : ''}`;
        }
        if (item.astral) {
            combined += item.astral;
        }
        if (item.isBmpLast && item.bmp) {
            combined += `${item.astral ? '|' : ''}[${item.bmp}]`;
        }

        // Astral Unicode tokens always match a code point, never a code unit
        return isNegated ?
            `(?:(?!${combined})(?:[\uD800-\uDBFF][\uDC00-\uDFFF]|[\0-\uFFFF]))` :
            `(?:${combined})`;
    }

    // Builds a complete astral pattern on first use
    function cacheAstral(slug, isNegated) {
        const prop = isNegated ? 'a!' : 'a=';
        return (
            unicode[slug][prop] ||
            (unicode[slug][prop] = buildAstral(slug, isNegated))
        );
    }

    // ==--------------------------==
    // Core functionality
    // ==--------------------------==

    /*
     * Add astral mode (flag A) and Unicode token syntax: `\p{..}`, `\P{..}`, `\p{^..}`, `\pC`.
     */
    XRegExp.addToken(
        // Use `*` instead of `+` to avoid capturing `^` as the token name in `\p{^}`
        /\\([pP])(?:{(\^?)(?:(Script|sc)=)?([^}]*)}|([A-Za-z]))/,
        (match, scope, flags) => {
            const ERR_DOUBLE_NEG = 'Invalid double negation ';
            const ERR_UNKNOWN_NAME = 'Unknown Unicode token ';
            const ERR_UNKNOWN_REF = 'Unicode token missing data ';
            const ERR_ASTRAL_ONLY = 'Astral mode required for Unicode token ';
            const ERR_ASTRAL_IN_CLASS = 'Astral mode does not support Unicode tokens within character classes';
            const [
                fullToken,
                pPrefix,
                caretNegation,
                typePrefix,
                tokenName,
                tokenSingleCharName
            ] = match;
            // Negated via \P{..} or \p{^..}
            let isNegated = pPrefix === 'P' || !!caretNegation;
            // Switch from BMP (0-FFFF) to astral (0-10FFFF) mode via flag A
            const isAstralMode = flags.includes('A');
            // Token lookup name. Check `tokenSingleCharName` first to avoid passing `undefined`
            // via `\p{}`
            let slug = normalize(tokenSingleCharName || tokenName);
            // Token data object
            let item = unicode[slug];

            if (pPrefix === 'P' && caretNegation) {
                throw new SyntaxError(ERR_DOUBLE_NEG + fullToken);
            }
            if (!unicode.hasOwnProperty(slug)) {
                throw new SyntaxError(ERR_UNKNOWN_NAME + fullToken);
            }

            if (typePrefix) {
                if (!(unicodeTypes[typePrefix] && unicodeTypes[typePrefix][slug])) {
                    throw new SyntaxError(ERR_UNKNOWN_NAME + fullToken);
                }
            }

            // Switch to the negated form of the referenced Unicode token
            if (item.inverseOf) {
                slug = normalize(item.inverseOf);
                if (!unicode.hasOwnProperty(slug)) {
                    throw new ReferenceError(`${ERR_UNKNOWN_REF + fullToken} -> ${item.inverseOf}`);
                }
                item = unicode[slug];
                isNegated = !isNegated;
            }

            if (!(item.bmp || isAstralMode)) {
                throw new SyntaxError(ERR_ASTRAL_ONLY + fullToken);
            }
            if (isAstralMode) {
                if (scope === 'class') {
                    throw new SyntaxError(ERR_ASTRAL_IN_CLASS);
                }

                return cacheAstral(slug, isNegated);
            }

            return scope === 'class' ?
                (isNegated ? cacheInvertedBmp(slug) : item.bmp) :
                `${(isNegated ? '[^' : '[') + item.bmp}]`;
        },
        {
            scope: 'all',
            optionalFlags: 'A',
            leadChar: '\\'
        }
    );

    /**
     * Adds to the list of Unicode tokens that XRegExp regexes can match via `\p` or `\P`.
     *
     * @memberOf XRegExp
     * @param {Array} data Objects with named character ranges. Each object may have properties
     *   `name`, `alias`, `isBmpLast`, `inverseOf`, `bmp`, and `astral`. All but `name` are
     *   optional, although one of `bmp` or `astral` is required (unless `inverseOf` is set). If
     *   `astral` is absent, the `bmp` data is used for BMP and astral modes. If `bmp` is absent,
     *   the name errors in BMP mode but works in astral mode. If both `bmp` and `astral` are
     *   provided, the `bmp` data only is used in BMP mode, and the combination of `bmp` and
     *   `astral` data is used in astral mode. `isBmpLast` is needed when a token matches orphan
     *   high surrogates *and* uses surrogate pairs to match astral code points. The `bmp` and
     *   `astral` data should be a combination of literal characters and `\xHH` or `\uHHHH` escape
     *   sequences, with hyphens to create ranges. Any regex metacharacters in the data should be
     *   escaped, apart from range-creating hyphens. The `astral` data can additionally use
     *   character classes and alternation, and should use surrogate pairs to represent astral code
     *   points. `inverseOf` can be used to avoid duplicating character data if a Unicode token is
     *   defined as the exact inverse of another token.
     * @param {String} [typePrefix] Enables optionally using this type as a prefix for all of the
     *   provided Unicode tokens, e.g. if given `'Type'`, then `\p{TokenName}` can also be written
     *   as `\p{Type=TokenName}`.
     * @example
     *
     * // Basic use
     * XRegExp.addUnicodeData([{
     *   name: 'XDigit',
     *   alias: 'Hexadecimal',
     *   bmp: '0-9A-Fa-f'
     * }]);
     * XRegExp('\\p{XDigit}:\\p{Hexadecimal}+').test('0:3D'); // -> true
     */
    XRegExp.addUnicodeData = (data, typePrefix) => {
        const ERR_NO_NAME = 'Unicode token requires name';
        const ERR_NO_DATA = 'Unicode token has no character data ';

        if (typePrefix) {
            // Case sensitive to match ES2018
            unicodeTypes[typePrefix] = {};
        }

        for (const item of data) {
            if (!item.name) {
                throw new Error(ERR_NO_NAME);
            }
            if (!(item.inverseOf || item.bmp || item.astral)) {
                throw new Error(ERR_NO_DATA + item.name);
            }

            const normalizedName = normalize(item.name);
            unicode[normalizedName] = item;
            if (typePrefix) {
                unicodeTypes[typePrefix][normalizedName] = true;
            }

            if (item.alias) {
                const normalizedAlias = normalize(item.alias);
                unicode[normalizedAlias] = item;
                if (typePrefix) {
                    unicodeTypes[typePrefix][normalizedAlias] = true;
                }
            }
        }

        // Reset the pattern cache used by the `XRegExp` constructor, since the same pattern and
        // flags might now produce different results
        XRegExp.cache.flush('patterns');
    };

    /**
     * @ignore
     *
     * Return a reference to the internal Unicode definition structure for the given Unicode
     * Property if the given name is a legal Unicode Property for use in XRegExp `\p` or `\P` regex
     * constructs.
     *
     * @memberOf XRegExp
     * @param {String} name Name by which the Unicode Property may be recognized (case-insensitive),
     *   e.g. `'N'` or `'Number'`. The given name is matched against all registered Unicode
     *   Properties and Property Aliases.
     * @returns {Object} Reference to definition structure when the name matches a Unicode Property.
     *
     * @note
     * For more info on Unicode Properties, see also http://unicode.org/reports/tr18/#Categories.
     *
     * @note
     * This method is *not* part of the officially documented API and may change or be removed in
     * the future. It is meant for userland code that wishes to reuse the (large) internal Unicode
     * structures set up by XRegExp.
     */
    XRegExp._getUnicodeProperty = (name) => {
        const slug = normalize(name);
        return unicode[slug];
    };
};
<commit_msg>Fix regex for matching Unicode tokens to support any provided type prefix (#225)<commit_after>/*!
 * XRegExp Unicode Base 4.4.1
 * <xregexp.com>
 * Steven Levithan (c) 2008-present MIT License
 */

export default (XRegExp) => {

    /**
     * Adds base support for Unicode matching:
     * - Adds syntax `\p{..}` for matching Unicode tokens. Tokens can be inverted using `\P{..}` or
     *   `\p{^..}`. Token names ignore case, spaces, hyphens, and underscores. You can omit the
     *   braces for token names that are a single letter (e.g. `\pL` or `PL`).
     * - Adds flag A (astral), which enables 21-bit Unicode support.
     * - Adds the `XRegExp.addUnicodeData` method used by other addons to provide character data.
     *
     * Unicode Base relies on externally provided Unicode character data. Official addons are
     * available to provide data for Unicode categories, scripts, blocks, and properties.
     *
     * @requires XRegExp
     */

    // ==--------------------------==
    // Private stuff
    // ==--------------------------==

    // Storage for Unicode data
    const unicode = {};
    const unicodeTypes = {};

    // Reuse utils
    const dec = XRegExp._dec;
    const hex = XRegExp._hex;
    const pad4 = XRegExp._pad4;

    // Generates a token lookup name: lowercase, with hyphens, spaces, and underscores removed
    function normalize(name) {
        return name.replace(/[- _]+/g, '').toLowerCase();
    }

    // Gets the decimal code of a literal code unit, \xHH, \uHHHH, or a backslash-escaped literal
    function charCode(chr) {
        const esc = /^\\[xu](.+)/.exec(chr);
        return esc ?
            dec(esc[1]) :
            chr.charCodeAt(chr[0] === '\\' ? 1 : 0);
    }

    // Inverts a list of ordered BMP characters and ranges
    function invertBmp(range) {
        let output = '';
        let lastEnd = -1;

        XRegExp.forEach(
            range,
            /(\\x..|\\u....|\\?[\s\S])(?:-(\\x..|\\u....|\\?[\s\S]))?/,
            (m) => {
                const start = charCode(m[1]);
                if (start > (lastEnd + 1)) {
                    output += `\\u${pad4(hex(lastEnd + 1))}`;
                    if (start > (lastEnd + 2)) {
                        output += `-\\u${pad4(hex(start - 1))}`;
                    }
                }
                lastEnd = charCode(m[2] || m[1]);
            }
        );

        if (lastEnd < 0xFFFF) {
            output += `\\u${pad4(hex(lastEnd + 1))}`;
            if (lastEnd < 0xFFFE) {
                output += '-\\uFFFF';
            }
        }

        return output;
    }

    // Generates an inverted BMP range on first use
    function cacheInvertedBmp(slug) {
        const prop = 'b!';
        return (
            unicode[slug][prop] ||
            (unicode[slug][prop] = invertBmp(unicode[slug].bmp))
        );
    }

    // Combines and optionally negates BMP and astral data
    function buildAstral(slug, isNegated) {
        const item = unicode[slug];
        let combined = '';

        if (item.bmp && !item.isBmpLast) {
            combined = `[${item.bmp}]${item.astral ? '|' : ''}`;
        }
        if (item.astral) {
            combined += item.astral;
        }
        if (item.isBmpLast && item.bmp) {
            combined += `${item.astral ? '|' : ''}[${item.bmp}]`;
        }

        // Astral Unicode tokens always match a code point, never a code unit
        return isNegated ?
            `(?:(?!${combined})(?:[\uD800-\uDBFF][\uDC00-\uDFFF]|[\0-\uFFFF]))` :
            `(?:${combined})`;
    }

    // Builds a complete astral pattern on first use
    function cacheAstral(slug, isNegated) {
        const prop = isNegated ? 'a!' : 'a=';
        return (
            unicode[slug][prop] ||
            (unicode[slug][prop] = buildAstral(slug, isNegated))
        );
    }

    // ==--------------------------==
    // Core functionality
    // ==--------------------------==

    /*
     * Add astral mode (flag A) and Unicode token syntax: `\p{..}`, `\P{..}`, `\p{^..}`, `\pC`.
     */
    XRegExp.addToken(
        // Use `*` instead of `+` to avoid capturing `^` as the token name in `\p{^}`
        /\\([pP])(?:{(\^?)(?:(\w+)=)?([^}]*)}|([A-Za-z]))/,
        (match, scope, flags) => {
            const ERR_DOUBLE_NEG = 'Invalid double negation ';
            const ERR_UNKNOWN_NAME = 'Unknown Unicode token ';
            const ERR_UNKNOWN_REF = 'Unicode token missing data ';
            const ERR_ASTRAL_ONLY = 'Astral mode required for Unicode token ';
            const ERR_ASTRAL_IN_CLASS = 'Astral mode does not support Unicode tokens within character classes';
            const [
                fullToken,
                pPrefix,
                caretNegation,
                typePrefix,
                tokenName,
                tokenSingleCharName
            ] = match;
            // Negated via \P{..} or \p{^..}
            let isNegated = pPrefix === 'P' || !!caretNegation;
            // Switch from BMP (0-FFFF) to astral (0-10FFFF) mode via flag A
            const isAstralMode = flags.includes('A');
            // Token lookup name. Check `tokenSingleCharName` first to avoid passing `undefined`
            // via `\p{}`
            let slug = normalize(tokenSingleCharName || tokenName);
            // Token data object
            let item = unicode[slug];

            if (pPrefix === 'P' && caretNegation) {
                throw new SyntaxError(ERR_DOUBLE_NEG + fullToken);
            }
            if (!unicode.hasOwnProperty(slug)) {
                throw new SyntaxError(ERR_UNKNOWN_NAME + fullToken);
            }

            if (typePrefix) {
                if (!(unicodeTypes[typePrefix] && unicodeTypes[typePrefix][slug])) {
                    throw new SyntaxError(ERR_UNKNOWN_NAME + fullToken);
                }
            }

            // Switch to the negated form of the referenced Unicode token
            if (item.inverseOf) {
                slug = normalize(item.inverseOf);
                if (!unicode.hasOwnProperty(slug)) {
                    throw new ReferenceError(`${ERR_UNKNOWN_REF + fullToken} -> ${item.inverseOf}`);
                }
                item = unicode[slug];
                isNegated = !isNegated;
            }

            if (!(item.bmp || isAstralMode)) {
                throw new SyntaxError(ERR_ASTRAL_ONLY + fullToken);
            }
            if (isAstralMode) {
                if (scope === 'class') {
                    throw new SyntaxError(ERR_ASTRAL_IN_CLASS);
                }

                return cacheAstral(slug, isNegated);
            }

            return scope === 'class' ?
                (isNegated ? cacheInvertedBmp(slug) : item.bmp) :
                `${(isNegated ? '[^' : '[') + item.bmp}]`;
        },
        {
            scope: 'all',
            optionalFlags: 'A',
            leadChar: '\\'
        }
    );

    /**
     * Adds to the list of Unicode tokens that XRegExp regexes can match via `\p` or `\P`.
     *
     * @memberOf XRegExp
     * @param {Array} data Objects with named character ranges. Each object may have properties
     *   `name`, `alias`, `isBmpLast`, `inverseOf`, `bmp`, and `astral`. All but `name` are
     *   optional, although one of `bmp` or `astral` is required (unless `inverseOf` is set). If
     *   `astral` is absent, the `bmp` data is used for BMP and astral modes. If `bmp` is absent,
     *   the name errors in BMP mode but works in astral mode. If both `bmp` and `astral` are
     *   provided, the `bmp` data only is used in BMP mode, and the combination of `bmp` and
     *   `astral` data is used in astral mode. `isBmpLast` is needed when a token matches orphan
     *   high surrogates *and* uses surrogate pairs to match astral code points. The `bmp` and
     *   `astral` data should be a combination of literal characters and `\xHH` or `\uHHHH` escape
     *   sequences, with hyphens to create ranges. Any regex metacharacters in the data should be
     *   escaped, apart from range-creating hyphens. The `astral` data can additionally use
     *   character classes and alternation, and should use surrogate pairs to represent astral code
     *   points. `inverseOf` can be used to avoid duplicating character data if a Unicode token is
     *   defined as the exact inverse of another token.
     * @param {String} [typePrefix] Enables optionally using this type as a prefix for all of the
     *   provided Unicode tokens, e.g. if given `'Type'`, then `\p{TokenName}` can also be written
     *   as `\p{Type=TokenName}`.
     * @example
     *
     * // Basic use
     * XRegExp.addUnicodeData([{
     *   name: 'XDigit',
     *   alias: 'Hexadecimal',
     *   bmp: '0-9A-Fa-f'
     * }]);
     * XRegExp('\\p{XDigit}:\\p{Hexadecimal}+').test('0:3D'); // -> true
     */
    XRegExp.addUnicodeData = (data, typePrefix) => {
        const ERR_NO_NAME = 'Unicode token requires name';
        const ERR_NO_DATA = 'Unicode token has no character data ';

        if (typePrefix) {
            // Case sensitive to match ES2018
            unicodeTypes[typePrefix] = {};
        }

        for (const item of data) {
            if (!item.name) {
                throw new Error(ERR_NO_NAME);
            }
            if (!(item.inverseOf || item.bmp || item.astral)) {
                throw new Error(ERR_NO_DATA + item.name);
            }

            const normalizedName = normalize(item.name);
            unicode[normalizedName] = item;
            if (typePrefix) {
                unicodeTypes[typePrefix][normalizedName] = true;
            }

            if (item.alias) {
                const normalizedAlias = normalize(item.alias);
                unicode[normalizedAlias] = item;
                if (typePrefix) {
                    unicodeTypes[typePrefix][normalizedAlias] = true;
                }
            }
        }

        // Reset the pattern cache used by the `XRegExp` constructor, since the same pattern and
        // flags might now produce different results
        XRegExp.cache.flush('patterns');
    };

    /**
     * @ignore
     *
     * Return a reference to the internal Unicode definition structure for the given Unicode
     * Property if the given name is a legal Unicode Property for use in XRegExp `\p` or `\P` regex
     * constructs.
     *
     * @memberOf XRegExp
     * @param {String} name Name by which the Unicode Property may be recognized (case-insensitive),
     *   e.g. `'N'` or `'Number'`. The given name is matched against all registered Unicode
     *   Properties and Property Aliases.
     * @returns {Object} Reference to definition structure when the name matches a Unicode Property.
     *
     * @note
     * For more info on Unicode Properties, see also http://unicode.org/reports/tr18/#Categories.
     *
     * @note
     * This method is *not* part of the officially documented API and may change or be removed in
     * the future. It is meant for userland code that wishes to reuse the (large) internal Unicode
     * structures set up by XRegExp.
     */
    XRegExp._getUnicodeProperty = (name) => {
        const slug = normalize(name);
        return unicode[slug];
    };
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, window, setTimeout */

/**
 * Resizer is a Module utility to inject resizing capabilities to any element
 * inside Brackets.
 *
 * On initialization, Resizer discovers all nodes tagged as "vert-resizable"
 * and "horz-resizable" to add the resizer handler. Additionally, "top-resizer",
 * "bottom-resizer", "left-resizer" and "right-resizer" classes control the
 * position of the resizer on the element.
 *
 * An element can be made resizable at any time using the `makeResizable` API
 *
 * The resizable elements trigger a panelResizeStart, panelResizeUpdate and panelResizeEnd
 * event that can be used to create performance optimizations (such as hiding/showing elements
 * while resizing), custom or internal resizes and save the final resized value into local
 * storage for example.
 *
 * A resizable element can be collapsed/expanded using the `show`, `hide` and `toggle` APIs
 *
 * The resizable elements trigger a panelCollapsed and panelExpanded event when the panel toggles
 * between visible and invisible
 */
define(function (require, exports, module) {
    "use strict";

    var DIRECTION_VERTICAL = "vert";
    var DIRECTION_HORIZONTAL = "horz";

    var POSITION_TOP = "top";
    var POSITION_BOTTOM = "bottom";
    var POSITION_LEFT = "left";
    var POSITION_RIGHT = "right";

    // Minimum size (height or width) for autodiscovered resizable panels
    var DEFAULT_MIN_SIZE = 100;

    // Load dependent modules
    var AppInit                 = require("utils/AppInit"),
        EditorManager           = require("editor/EditorManager");

    var $mainView;

    /**
     * Shows a resizable element.
     * @param {DOMNode} element Html element to show if possible
     */
    function show(element) {
        var showFunc = $(element).data("show");
        if (showFunc) {
            showFunc.apply(element);
        }
    }

    /**
     * Hides a resizable element.
     * @param {DOMNode} element Html element to hide if possible
     */
    function hide(element) {
        var hideFunc = $(element).data("hide");
        if (hideFunc) {
            hideFunc.apply(element);
        }
    }

    /**
     * Changes the visibility state of a resizable element. The toggle
     * functionality is added when an element is made resizable.
     * @param {DOMNode} element Html element to toggle
     */
    function toggle(element) {
        if ($(element).is(":visible")) {
            hide(element);
        } else {
            show(element);
        }
    }

    /**
     * Adds resizing capabilities to a given html element.
     *
     * Resizing can be configured in two directions:
     *  - Vertical ("vert"): Resizes the height of the element
     *  - Horizontal ("horz"): Resizes the width of the element
     *
     * Resizer handlers can be positioned on the element at:
     *  - Top ("top") or bottom ("bottom") for vertical resizing
     *  - Left ("left") or right ("right") for horizontal resizing
     *
     * A resizable element triggers the following events while resizing:
     *  - panelResizeStart: When the resize starts
     *  - panelResizeUpdate: When the resize gets updated
     *  - panelResizeEnds: When the resize ends
     *  - panelCollapsed: When the panel gets collapsed (or hidden)
     *  - panelExpanded: When the panel gets expanded (or shown)
     *
     * @param {DOMNode} element Html element which should be made resizable.
     * @param {string} direction The direction of the resize action. Must be "horz" or "vert".
     * @param {string} position The position of the resizer on the element. Can be "top" or "bottom"
     *                          for vertical resizing and "left" or "right" for horizontal resizing.
     * @param {int} minSize Minimum size (width or height) of the element.
     * @param {boolean} collapsable True indicates the panel is collapsable on double click
     *                              on the resizer.
     */
    function makeResizable(element, direction, position, minSize, collapsable) {

        var $resizer            = $('

'),
            $element            = $(element),
            $resizableElement   = $($element.find(".resizable-content:first")[0]),
            $body               = $(window.document.body),
            animationRequest    = null,
            directionProperty   = direction === DIRECTION_HORIZONTAL ? "clientX" : "clientY",
            elementSizeFunction = direction === DIRECTION_HORIZONTAL ? $element.width : $element.height,
            resizerCSSPosition  = direction === DIRECTION_HORIZONTAL ? "left" : "top",
            contentSizeFunction = null;

        minSize = minSize || 0;
        collapsable = collapsable || false;

        $element.prepend($resizer);

        $element.data("show", function () {
            $element.show();

            if (collapsable) {
                $element.prepend($resizer);
                $resizer.css(resizerCSSPosition, "");
            }

            EditorManager.resizeEditor();
            $element.trigger("panelExpanded");
        });

        $element.data("hide", function () {
            var elementOffset   = $element.offset(),
                elementSize     = elementSizeFunction.apply($element);

            $element.hide();
            if (collapsable) {
                $resizer.insertBefore($element).css(resizerCSSPosition, elementOffset[resizerCSSPosition] + elementSize);
            }

            EditorManager.resizeEditor();
            $element.trigger("panelCollapsed");
        });

        $resizer.on("mousedown", function (e) {
            var $resizeCont     = $("
"),
                startPosition   = e[directionProperty],
                startSize       = $element.is(":visible") ? elementSizeFunction.apply($element) : 0,
                newSize         = startSize,
                baseSize        = 0,
                doResize        = false,
                isMouseDown     = true;

            $body.append($resizeCont);

            $element.trigger("panelResizeStart", [elementSizeFunction.apply($element)]);

            if ($resizableElement !== undefined) {
                $element.children().not(".horz-resizer, .vert-resizer, .resizable-content").each(function (index, child) {
                    if (direction === DIRECTION_HORIZONTAL) {
                        baseSize += $(child).outerWidth();
                    } else {
                        baseSize += $(child).outerHeight();
                    }
                });

                contentSizeFunction = direction === DIRECTION_HORIZONTAL ? $resizableElement.width : $resizableElement.height;
            }

            animationRequest = window.webkitRequestAnimationFrame(function doRedraw() {
                // only run this if the mouse is down so we don't constantly loop even
                // after we're done resizing.
                if (!isMouseDown) {
                    return;
                }

                if (doResize) {
                    // resize the main element to the new size
                    if ($element.is(":visible")) {
                        elementSizeFunction.apply($element, [newSize]);

                        // if there is a content element, its size is the new size
                        // minus the size of the non-resizable elements
                        if ($resizableElement !== undefined) {
                            contentSizeFunction.apply($resizableElement, [newSize - baseSize]);
                        }

                        if (newSize < 10) {
                            toggle($element);
                        }
                    } else if (newSize > 10) {
                        elementSizeFunction.apply($element, [newSize]);
                        toggle($element);
                        $element.trigger("panelResizeStart", [elementSizeFunction.apply($element)]);
                    }

                    EditorManager.resizeEditor();
                }

                animationRequest = window.webkitRequestAnimationFrame(doRedraw);
            });

            $resizeCont.on("mousemove", function (e) {
                doResize = true;
                // calculate newSize adding to startSize the difference
                // between starting and current position, capped at minSize
                newSize = Math.max(startSize + (startPosition - e[directionProperty]), minSize);
                $element.trigger("panelResizeUpdate", [newSize]);
                e.preventDefault();
            });

            // If the element is marked as collapsable, check for double click
            // to toggle the element visibility
            if (collapsable) {
                $resizeCont.on("mousedown", function (e) {
                    toggle($element);
                });
            }

            function endResize(e) {
                if (isMouseDown) {
                    isMouseDown = false;
                    $element.trigger("panelResizeEnd", [elementSizeFunction.apply($element)]);

                    // We wait 100ms to remove the resizer container to capture a mousedown
                    // on the container that would account for double click
                    setTimeout(function () {
                        $resizeCont.off("mousemove");
                        $resizeCont.off("mousedown");
                        $resizeCont.remove();
                    }, 100);
                }
            }

            $resizeCont.one("mouseup", endResize);
            $resizeCont.mouseleave(endResize);

            e.preventDefault();
        });
    }

    // Scan DOM for horz-resizable and vert-resizable classes and make them resizable
    AppInit.htmlReady(function () {
        $mainView = $(".main-view");

        $(".vert-resizable").each(function (index, element) {

            if ($(element).hasClass("top-resizer")) {
                makeResizable(element, DIRECTION_VERTICAL, POSITION_TOP, DEFAULT_MIN_SIZE, $(element).hasClass("collapsable"));
            }

            //if ($(element).hasClass("bottom-resizer")) {
            //    makeResizable(element, DIRECTION_VERTICAL, POSITION_BOTTOM, DEFAULT_MIN_SIZE);
            //}
        });

        $(".horz-resizable").each(function (index, element) {

            //if ($(element).hasClass("left-resizer")) {
            //    makeResizable(element, DIRECTION_HORIZONTAL, POSITION_LEFT, DEFAULT_MIN_SIZE);
            //}

            //if ($(element).hasClass("right-resizer")) {
            //    makeResizable(element, DIRECTION_HORIZONTAL, POSITION_RIGHT, DEFAULT_MIN_SIZE);
            //}
        });
    });

    exports.makeResizable = makeResizable;
    exports.toggle = toggle;
    exports.show = show;
    exports.hide = hide;
});<commit_msg>Used window.setTimeout to clean jslint global<commit_after>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, window */

/**
 * Resizer is a Module utility to inject resizing capabilities to any element
 * inside Brackets.
 *
 * On initialization, Resizer discovers all nodes tagged as "vert-resizable"
 * and "horz-resizable" to add the resizer handler. Additionally, "top-resizer",
 * "bottom-resizer", "left-resizer" and "right-resizer" classes control the
 * position of the resizer on the element.
 *
 * An element can be made resizable at any time using the `makeResizable` API
 *
 * The resizable elements trigger a panelResizeStart, panelResizeUpdate and panelResizeEnd
 * event that can be used to create performance optimizations (such as hiding/showing elements
 * while resizing), custom or internal resizes and save the final resized value into local
 * storage for example.
 *
 * A resizable element can be collapsed/expanded using the `show`, `hide` and `toggle` APIs
 *
 * The resizable elements trigger a panelCollapsed and panelExpanded event when the panel toggles
 * between visible and invisible
 */
define(function (require, exports, module) {
    "use strict";

    var DIRECTION_VERTICAL = "vert";
    var DIRECTION_HORIZONTAL = "horz";

    var POSITION_TOP = "top";
    var POSITION_BOTTOM = "bottom";
    var POSITION_LEFT = "left";
    var POSITION_RIGHT = "right";

    // Minimum size (height or width) for autodiscovered resizable panels
    var DEFAULT_MIN_SIZE = 100;

    // Load dependent modules
    var AppInit                 = require("utils/AppInit"),
        EditorManager           = require("editor/EditorManager");

    var $mainView;

    /**
     * Shows a resizable element.
     * @param {DOMNode} element Html element to show if possible
     */
    function show(element) {
        var showFunc = $(element).data("show");
        if (showFunc) {
            showFunc.apply(element);
        }
    }

    /**
     * Hides a resizable element.
     * @param {DOMNode} element Html element to hide if possible
     */
    function hide(element) {
        var hideFunc = $(element).data("hide");
        if (hideFunc) {
            hideFunc.apply(element);
        }
    }

    /**
     * Changes the visibility state of a resizable element. The toggle
     * functionality is added when an element is made resizable.
     * @param {DOMNode} element Html element to toggle
     */
    function toggle(element) {
        if ($(element).is(":visible")) {
            hide(element);
        } else {
            show(element);
        }
    }

    /**
     * Adds resizing capabilities to a given html element.
     *
     * Resizing can be configured in two directions:
     *  - Vertical ("vert"): Resizes the height of the element
     *  - Horizontal ("horz"): Resizes the width of the element
     *
     * Resizer handlers can be positioned on the element at:
     *  - Top ("top") or bottom ("bottom") for vertical resizing
     *  - Left ("left") or right ("right") for horizontal resizing
     *
     * A resizable element triggers the following events while resizing:
     *  - panelResizeStart: When the resize starts
     *  - panelResizeUpdate: When the resize gets updated
     *  - panelResizeEnds: When the resize ends
     *  - panelCollapsed: When the panel gets collapsed (or hidden)
     *  - panelExpanded: When the panel gets expanded (or shown)
     *
     * @param {DOMNode} element Html element which should be made resizable.
     * @param {string} direction The direction of the resize action. Must be "horz" or "vert".
     * @param {string} position The position of the resizer on the element. Can be "top" or "bottom"
     *                          for vertical resizing and "left" or "right" for horizontal resizing.
     * @param {int} minSize Minimum size (width or height) of the element.
     * @param {boolean} collapsable True indicates the panel is collapsable on double click
     *                              on the resizer.
     */
    function makeResizable(element, direction, position, minSize, collapsable) {

        var $resizer            = $('

'),
            $element            = $(element),
            $resizableElement   = $($element.find(".resizable-content:first")[0]),
            $body               = $(window.document.body),
            animationRequest    = null,
            directionProperty   = direction === DIRECTION_HORIZONTAL ? "clientX" : "clientY",
            elementSizeFunction = direction === DIRECTION_HORIZONTAL ? $element.width : $element.height,
            resizerCSSPosition  = direction === DIRECTION_HORIZONTAL ? "left" : "top",
            contentSizeFunction = null;

        minSize = minSize || 0;
        collapsable = collapsable || false;

        $element.prepend($resizer);

        $element.data("show", function () {
            $element.show();

            if (collapsable) {
                $element.prepend($resizer);
                $resizer.css(resizerCSSPosition, "");
            }

            EditorManager.resizeEditor();
            $element.trigger("panelExpanded");
        });

        $element.data("hide", function () {
            var elementOffset   = $element.offset(),
                elementSize     = elementSizeFunction.apply($element);

            $element.hide();
            if (collapsable) {
                $resizer.insertBefore($element).css(resizerCSSPosition, elementOffset[resizerCSSPosition] + elementSize);
            }

            EditorManager.resizeEditor();
            $element.trigger("panelCollapsed");
        });

        $resizer.on("mousedown", function (e) {
            var $resizeCont     = $("
"),
                startPosition   = e[directionProperty],
                startSize       = $element.is(":visible") ? elementSizeFunction.apply($element) : 0,
                newSize         = startSize,
                baseSize        = 0,
                doResize        = false,
                isMouseDown     = true;

            $body.append($resizeCont);

            $element.trigger("panelResizeStart", [elementSizeFunction.apply($element)]);

            if ($resizableElement !== undefined) {
                $element.children().not(".horz-resizer, .vert-resizer, .resizable-content").each(function (index, child) {
                    if (direction === DIRECTION_HORIZONTAL) {
                        baseSize += $(child).outerWidth();
                    } else {
                        baseSize += $(child).outerHeight();
                    }
                });

                contentSizeFunction = direction === DIRECTION_HORIZONTAL ? $resizableElement.width : $resizableElement.height;
            }

            animationRequest = window.webkitRequestAnimationFrame(function doRedraw() {
                // only run this if the mouse is down so we don't constantly loop even
                // after we're done resizing.
                if (!isMouseDown) {
                    return;
                }

                if (doResize) {
                    // resize the main element to the new size
                    if ($element.is(":visible")) {
                        elementSizeFunction.apply($element, [newSize]);

                        // if there is a content element, its size is the new size
                        // minus the size of the non-resizable elements
                        if ($resizableElement !== undefined) {
                            contentSizeFunction.apply($resizableElement, [newSize - baseSize]);
                        }

                        if (newSize < 10) {
                            toggle($element);
                        }
                    } else if (newSize > 10) {
                        elementSizeFunction.apply($element, [newSize]);
                        toggle($element);
                        $element.trigger("panelResizeStart", [elementSizeFunction.apply($element)]);
                    }

                    EditorManager.resizeEditor();
                }

                animationRequest = window.webkitRequestAnimationFrame(doRedraw);
            });

            $resizeCont.on("mousemove", function (e) {
                doResize = true;
                // calculate newSize adding to startSize the difference
                // between starting and current position, capped at minSize
                newSize = Math.max(startSize + (startPosition - e[directionProperty]), minSize);
                $element.trigger("panelResizeUpdate", [newSize]);
                e.preventDefault();
            });

            // If the element is marked as collapsable, check for double click
            // to toggle the element visibility
            if (collapsable) {
                $resizeCont.on("mousedown", function (e) {
                    toggle($element);
                });
            }

            function endResize(e) {
                if (isMouseDown) {
                    isMouseDown = false;
                    $element.trigger("panelResizeEnd", [elementSizeFunction.apply($element)]);

                    // We wait 100ms to remove the resizer container to capture a mousedown
                    // on the container that would account for double click
                    window.setTimeout(function () {
                        $resizeCont.off("mousemove");
                        $resizeCont.off("mousedown");
                        $resizeCont.remove();
                    }, 100);
                }
            }

            $resizeCont.one("mouseup", endResize);
            $resizeCont.mouseleave(endResize);

            e.preventDefault();
        });
    }

    // Scan DOM for horz-resizable and vert-resizable classes and make them resizable
    AppInit.htmlReady(function () {
        $mainView = $(".main-view");

        $(".vert-resizable").each(function (index, element) {

            if ($(element).hasClass("top-resizer")) {
                makeResizable(element, DIRECTION_VERTICAL, POSITION_TOP, DEFAULT_MIN_SIZE, $(element).hasClass("collapsable"));
            }

            //if ($(element).hasClass("bottom-resizer")) {
            //    makeResizable(element, DIRECTION_VERTICAL, POSITION_BOTTOM, DEFAULT_MIN_SIZE);
            //}
        });

        $(".horz-resizable").each(function (index, element) {

            //if ($(element).hasClass("left-resizer")) {
            //    makeResizable(element, DIRECTION_HORIZONTAL, POSITION_LEFT, DEFAULT_MIN_SIZE);
            //}

            //if ($(element).hasClass("right-resizer")) {
            //    makeResizable(element, DIRECTION_HORIZONTAL, POSITION_RIGHT, DEFAULT_MIN_SIZE);
            //}
        });
    });

    exports.makeResizable = makeResizable;
    exports.toggle = toggle;
    exports.show = show;
    exports.hide = hide;
});<|endoftext|>


<commit_before>/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the 'License'). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

module.exports.experimentList = `

  {{#experiments}}

{{title}}
        Now Active

  {{/experiments}}

View all experiments`;

module.exports.installed = `

FYI: We put an button in your toolbar
 so you can always find Test Pilot.

`;
<commit_msg>Update String in Add-on to 'Enabled' from 'Now active' for Enabled experiments<commit_after>/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the 'License'). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

module.exports.experimentList = `

  {{#experiments}}

{{title}}
        Enabled

  {{/experiments}}

View all experiments`;

module.exports.installed = `

FYI: We put an button in your toolbar
 so you can always find Test Pilot.

`;
<|endoftext|>


<commit_before>/*
 * One Controller per layout view
 */

const express = require('express');
const router = express.Router();
const api = require('../api');
const recurringEventsHelper = require('../helpers/recurringEvents');

/**
 * handle recurring events for fullcalendar.js
 * @param event {Event} - a event which could contain a recurring value
 * @returns events [] - new set of events
 */
const mapRecurringEvent = (event) => {
    if (event.included && event.included[0].attributes.freq == 'WEEKLY') {
        //event.dow = event.included[0].attributes.freq == 'WEEKLY' ?  [recurringEventsHelper.getNumberForFullCalendarWeekday(event.included[0].attributes.wkst)] : '';
        return recurringEventsHelper.createRecurringEvents(event);
    }

    return [event];
};

/**
 * maps jsonapi properties of a event to fit fullcalendar
 * @param event
 */
const mapEventProps = (event) => {
    event.start = new Date(event.attributes.dtstart).getTime();
    event.end = new Date(event.attributes.dtend).getTime();
    event.summary = event.attributes.summary;
    event.title = event.attributes.summary;
    event.location = event.attributes.location;
    event.description = "Test";
};

// secure routes
router.use(require('../helpers/authentication').authChecker);

router.get('/', function (req, res, next) {
    res.render('calendar/calendar', {
        title: 'Kalender'
    });
});

router.get('/events/', function (req, res, next) {
    api(req).get('/calendar/').then(events => {

        events.forEach(mapEventProps);
        events = [].concat.apply([], events.map(mapRecurringEvent));

        return res.json(events);
    }).catch(err => {
        res.json([]);
    });
});

module.exports = router;
<commit_msg>remove comment<commit_after>/*
 * One Controller per layout view
 */

const express = require('express');
const router = express.Router();
const api = require('../api');
const recurringEventsHelper = require('../helpers/recurringEvents');

/**
 * handle recurring events for fullcalendar.js
 * @param event {Event} - a event which could contain a recurring value
 * @returns events [] - new set of events
 */
const mapRecurringEvent = (event) => {
    if (event.included && event.included[0].attributes.freq == 'WEEKLY') {
        return recurringEventsHelper.createRecurringEvents(event);
    }

    return [event];
};

/**
 * maps jsonapi properties of a event to fit fullcalendar
 * @param event
 */
const mapEventProps = (event) => {
    event.start = new Date(event.attributes.dtstart).getTime();
    event.end = new Date(event.attributes.dtend).getTime();
    event.summary = event.attributes.summary;
    event.title = event.attributes.summary;
    event.location = event.attributes.location;
    event.description = "Test";
};

// secure routes
router.use(require('../helpers/authentication').authChecker);

router.get('/', function (req, res, next) {
    res.render('calendar/calendar', {
        title: 'Kalender'
    });
});

router.get('/events/', function (req, res, next) {
    api(req).get('/calendar/').then(events => {

        events.forEach(mapEventProps);
        events = [].concat.apply([], events.map(mapRecurringEvent));

        return res.json(events);
    }).catch(err => {
        res.json([]);
    });
});

module.exports = router;
<|endoftext|>


<commit_before>
import { Map } from 'immutable'

/**
 * The leaf node of a cache tree.
 *
 * An object, so that immutable maps will key it by reference.
 *
 * @type {Object}
 */

const LEAF = {}

/**
 * An unique value used to detect cache misses
 *
 * @type {Object}
 */

const NO_SET = {}

/**
 * Memoize all of the `properties` on a `object`.
 *
 * @param {Object} object
 * @param {Array} properties
 * @return {Record}
 */

function memoize(object, properties) {
  for (const property of properties) {
    const original = object[property]

    if (!original) {
      throw new Error(`Object does not have a property named "${property}".`)
    }

    object[property] = function (...args) {
      const keys = [property, ...args, LEAF]
      this.__cache = this.__cache || new Map()

      const cachedValue = this.__cache.getIn(keys, NO_SET)
      if (cachedValue !== NO_SET) {
        return cachedValue
      }

      const value = original.apply(this, args)
      // If `original` is recursive, it might have changed the cache,
      // so read it from this.__cache
      this.__cache = this.__cache.setIn(keys, value)
      return value
    }
  }
}

/**
 * Export.
 */

export default memoize
<commit_msg>Implement memoize using native Maps<commit_after>
/**
 * The leaf node of a cache tree. Used to support variable argument length.
 *
 * A unique object, so that native Maps will key it by reference.
 *
 * @type {Object}
 */

const LEAF = {}

/**
 * A value to represent a memoized undefined value. Allows efficient
 * value retrieval using Map.get only.
 *
 * @type {Object}
 */

const UNDEFINED = {}

/**
 * Default value for unset keys in native Maps
 */

const UNSET = undefined

/**
 * Memoize all of the `properties` on a `object`.
 *
 * @param {Object} object
 * @param {Array} properties
 * @return {Record}
 */

function memoize(object, properties) {
  for (const property of properties) {
    const original = object[property]

    if (!original) {
      throw new Error(`Object does not have a property named "${property}".`)
    }

    object[property] = function (...args) {
      const keys = [property, ...args]
      this.__cache = this.__cache || new Map()

      const cachedValue = getIn(this.__cache, keys)
      if (cachedValue !== UNSET) {
        return cachedValue === UNDEFINED ? undefined : cachedValue
      }

      const value = original.apply(this, args)
      this.__cache = setIn(this.__cache, keys, value)
      return value
    }
  }
}

/**
 * Set a value at a key path in a tree of Map, creating Maps on the go.
 *
 * @param{Map} map
 * @param{Array} keys
 * @param{Any} value
 * @return {Map}
 */

function setIn(map, keys, value) {
  value = value === undefined ? UNDEFINED : value

  let parentMap = map
  let childMap
  for (const key of keys) {
    childMap = parentMap.get(key)

    if (childMap === UNSET) {
      // This path was not created yet
      childMap = new Map()
      parentMap.set(key, childMap)
    }

    parentMap = childMap
  }
  // The whole map path was created

  // Set the value to the bottom most map
  childMap.set(LEAF, value)

  return map
}

/**
 * Get a value at a key path in a tree of Map.
 * If not set, returns UNSET.
 * If the set value is undefined, returns UNDEFINED
 *
 * @param{Map} map
 * @param{Array} keys
 * @return {Any | UNSET | UNDEFINED}
 */

function getIn(map, keys) {
  let childMap
  for (const key of keys) {
    childMap = map.get(key)
    if (childMap === UNSET) {
      // Not found
      return UNSET
    }
    map = childMap
  }

  return childMap.get(LEAF)
}

/**
 * Export.
 */

export default memoize
<|endoftext|>


<commit_before>var Events = require('../lib/Events.js');

import {equal} from '../lib/utils.js';

/**
 * Update a component.
 *
 * @param {Element} entity - Entity to modify.
 * @param {string} component - Name of the component.
 * @param {string} property - Property name.
 * @param {string|number} value - New value.
 */
export function updateEntity (entity, propertyName, value) {
  if (propertyName.indexOf('.') !== -1) {
    // Multi-prop
    var splitName = propertyName.split('.');
    if (value === null || value === undefined) {
      // Remove property.
      var parameters = entity.getAttribute(splitName[0]);
      delete parameters[splitName[1]];
      entity.setAttribute(splitName[0], parameters);
    } else {
      // Set property.
      entity.setAttribute(splitName[0], splitName[1], value);
    }
  } else {
    if (value === null || value === undefined) {
      // Remove property.
      entity.removeAttribute(propertyName);
    } else {
      // Set property.
      entity.setAttribute(propertyName, value);
    }
  }
  Events.emit('objectchanged', entity.object3D);
}

/**
 * Remove an entity
 * @param  {Element} entity Entity to remove.
 * @param  {boolean} force (Optional) If true it won't ask for confirmation
 */
export function removeEntity (entity, force) {
  if (entity) {
    if (force === true || confirm('Do you really want to remove entity `' + (entity.id || entity.tagName) + '`?')) {
      var closest = findClosestEntity(entity);
      AFRAME.INSPECTOR.removeObject(entity.object3D);
      entity.parentNode.removeChild(entity);
      AFRAME.INSPECTOR.selectEntity(closest);
    }
  }
}

function findClosestEntity (entity) {
  // First we try to find the after the entity
  var nextEntity = entity.nextElementSibling;
  while (nextEntity && (!nextEntity.isEntity || nextEntity.isInspector)) {
    nextEntity = nextEntity.nextElementSibling;
  }

  // Return if we found it
  if (nextEntity && nextEntity.isEntity && !nextEntity.isInspector) {
    return nextEntity;
  }
  // Otherwise try to find before the entity
  var prevEntity = entity.previousElementSibling;
  while (prevEntity && (!prevEntity.isEntity || prevEntity.isInspector)) {
    prevEntity = prevEntity.previousElementSibling;
  }

  // Return if we found it
  if (prevEntity && prevEntity.isEntity && !prevEntity.isInspector) {
    return prevEntity;
  }

  return null;
}

/**
 * Remove the selected entity
 * @param  {boolean} force (Optional) If true it won't ask for confirmation
 */
export function removeSelectedEntity (force) {
  if (AFRAME.INSPECTOR.selectedEntity) {
    removeEntity(AFRAME.INSPECTOR.selectedEntity, force);
  }
}

/**
 * Insert an node after a referenced node.
 * @param  {Element} newNode       Node to insert.
 * @param  {Element} referenceNode Node used as reference to insert after it.
*/
function insertAfter (newNode, referenceNode) {
  if (!referenceNode.parentNode) {
    referenceNode = AFRAME.INSPECTOR.selectedEntity;
  }

  if (!referenceNode) {
    AFRAME.INSPECTOR.sceneEl.appendChild(newNode);
  } else {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  }
}

/**
 * Clone an entity, inserting it after the cloned one.
 * @param  {Element} entity Entity to clone
 */
export function cloneEntity (entity) {
  entity.flushToDOM();
  var copy = entity.cloneNode(true);
  copy.addEventListener('loaded', function (e) {
    AFRAME.INSPECTOR.selectEntity(copy);
  });

  // Get a valid unique ID for the entity
  if (entity.id) {
    copy.id = getUniqueId(entity.id);
  }
  copy.addEventListener('loaded', function () {
    Events.emit('dommodified');
    AFRAME.INSPECTOR.selectEntity(copy);
  });
  insertAfter(copy, entity);
}

/**
 * Clone the selected entity
 */
export function cloneSelectedEntity () {
  if (AFRAME.INSPECTOR.selectedEntity) {
    cloneEntity(AFRAME.INSPECTOR.selectedEntity);
  }
}

/**
 * Returns the clipboard representation to be used to copy to the clipboard
 * @param  {Element} entity Entity to copy to clipboard
 * @return {string}        Entity clipboard representation
 */
export function getClipboardRepresentation (entity) {
  var clone = prepareForSerialization(entity);
  clone.flushToDOM(true);
  return clone.outerHTML;
}

/**
 * Returns a copy of the DOM hierarchy prepared for serialization.
 * The process optimises component representation to avoid values coming from
 * primitive attributes, mixins and defaults.
 *
 * @param {Element} entity Root of the DOM hierarchy.
 * @return {Elment}        Copy of the DOM hierarchy ready for serialization.
 */
function prepareForSerialization(entity) {
  var clone = entity.cloneNode(false);
  var children = entity.childNodes;
  for (var i = 0, l = children.length; i < l; i++) {
    var child = children[i];
    if (child.nodeType !== Node.ELEMENT_NODE ||
        !child.hasAttribute('aframe-injected') &&
        !child.hasAttribute('data-aframe-inspector') &&
        !child.hasAttribute('data-aframe-canvas')) {
      clone.appendChild(prepareForSerialization(children[i]));
    }
  }
  optimizeComponents(clone, entity);
  return clone;
}

/**
 * Removes from copy those components or components' properties that comes from
 * primitive attributes, mixins, injected default components or schema defaults.
 *
 * @param {Element} copy   Destinatary element for the optimization.
 * @param {Element} source Element to be optimized.
 */
function optimizeComponents(copy, source) {
  var components = source.components || {};
  Object.keys(components).forEach(function (name) {
    var component = components[name];
    var implicitValue = getImplicitValue(component, source);
    var currentValue = source.getAttribute(name);
    var optimalUpdate = getOptimalUpdate(component, implicitValue, currentValue);
    if (optimalUpdate === null) {
      copy.removeAttribute(name);
    }
    else {
      copy.setAttribute(name, optimalUpdate, true);
    }
  });
}

/**
 * Computes the value for a component coming from primitive attributes,
 * mixins, primitive defaults, a-frame default components and schema defaults.
 * In this specific order.
 *
 * In other words, it is the value of the component if the author would have not
 * overridden it explicitly.
 *
 * @param {Component} component Component to calculate the value of.
 * @param {Element}   source    Element owning the component.
 * @return                      The computed value for the component of source.
 */
function getImplicitValue(component, source) {
  return (isSingleProperty(component) ? _single : _multi)(component, source);

  function _single() {
    var value = getMixedValue(component, null, source);
    if (value === undefined) {
      value = getInjectedValue(component, null, source);
    }
    if (value === undefined) {
      value = getDefaultValue(component, null, source);
    }
    if (value !== undefined) {
      // XXX: This assumes parse is idempotent
      return component.schema.parse(value);
    }
    return value;
  }

  function _multi() {
    var value;

    Object.keys(component.schema).forEach(function (propertyName) {
      var propertyValue = getFromAttribute(component, propertyName, source);
      if (propertyValue === undefined) {
        propertyValue = getMixedValue(component, propertyName, source);
      }
      if (propertyValue === undefined) {
        propertyValue = getInjectedValue(component, propertyName, source);
      }
      if (propertyValue === undefined) {
        propertyValue = getDefaultValue(component, propertyName, source);
      }
      if (propertyValue !== undefined) {
        var parse = component.schema[propertyName].parse;
        value = value || {};
        // XXX: This assumes parse is idempotent
        value[propertyName] = parse(propertyValue);
      }
    });

    return value;
  }
}

/**
 * Gets the value for the component's property coming from a primitive
 * attribute.
 *
 * Primitives have mappings from attributes to component's properties.
 * The function looks for a present attribute in the source element which
 * maps to the specified component's property.
 *
 * @param  {Component} component    Component to be found.
 * @param  {string}    propertyName Component's property to be found.
 * @param  {Element}   source       Element owning the component.
 * @return {any}                    The value of the component's property coming
 *                                  from the primitive's attribute if any or
 *                                  `undefined`, otherwise.
 */
function getFromAttribute(component, propertyName, source) {
  var value;
  var mappings = source.mappings || {};
  var route = component.name + '.' + propertyName;
  var primitiveAttribute = findAttribute(mappings, route);
  if (primitiveAttribute && source.hasAttribute(primitiveAttribute)) {
    value = source.getAttribute(primitiveAttribute);
  }
  return value;

  function findAttribute(mappings, route) {
    var attributes = Object.keys(mappings);
    for (var i = 0, l = attributes.length; i < l; i++) {
      var attribute = attributes[i];
      if (mappings[attribute] === route) {
        return attribute;
      }
    }
    return undefined;
  }
}

/**
 * Gets the value for a component or component's property coming from mixins of
 * an element.
 *
 * If the component or component's property is not provided by mixins, the
 * functions will return `undefined`.
 *
 * @param {Component} component      Component to be found.
 * @param {string}    [propertyName] If provided, component's property to be
 *                                   found.
 * @param {Element}   source         Element owning the component.
 * @return                           The value of the component or components'
 *                                   property coming from mixins of the source.
 */
function getMixedValue(component, propertyName, source) {
  var value;
  var reversedMixins = source.mixinEls.reverse();
  for (var i = 0; value === undefined && i < reversedMixins.length; i++) {
    var mixin = reversedMixins[i];
    if (mixin.attributes.hasOwnProperty(component.name)) {
      if (!propertyName) {
        value = mixin.getAttribute(component.name);
      }
      else {
        value = mixin.getAttribute(component.name)[propertyName];
      }
    }
  }
  return value;
}

/**
 * Gets the value for a component or component's property coming from primitive
 * defaults or a-frame defaults. In this specific order.
 *
 * @param {Component} component      Component to be found.
 * @param {string}    [propertyName] If provided, component's property to be
 *                                   found.
 * @param {Element}   source         Element owning the component.
 * @return                           The component value coming from the
 *                                   injected default components of source.
 */
function getInjectedValue(component, propertyName, source) {
  var value;
  var primitiveDefaults = source.defaultComponentsFromPrimitive || {};
  var aFrameDefaults = source.defaultComponents || {};
  var defaultSources = [primitiveDefaults, aFrameDefaults];
  for (var i = 0; value === undefined && i < defaultSources.length; i++) {
    var defaults = defaultSources[i];
    if (defaults.hasOwnProperty(component.name)) {
      if (!propertyName) {
        value = defaults[component.name];
      }
      else {
        value = defaults[component.name][propertyName];
      }
    }
  }
  return value;
}

/**
 * Gets the value for a component or component's property coming from schema
 * defaults.
 *
 * @param {Component} component      Component to be found.
 * @param {string}    [propertyName] If provided, component's property to be
 *                                   found.
 * @param {Element}   source         Element owning the component.
 * @return                           The component value coming from the schema
 *                                   default.
 */
function getDefaultValue(component, propertyName, source) {
  if (!propertyName) {
    return component.schema.default;
  }
  return component.schema[propertyName].default;
}

/**
 * Returns the minimum value for a component with an implicit value to equal a
 * reference value. A `null` optimal value means that there is no need for an
 * update since the implicit value and the reference are equal.
 *
 * @param {Component} component Component of the computed value.
 * @param {any}       implicit  The implicit value of the component.
 * @param {any}       reference The reference value for the component.
 * @return                      the minimum value making the component to equal
 *                              the reference value.
 */
function getOptimalUpdate(component, implicit, reference) {
  if (equal(implicit, reference)) {
    return null;
  }
  if (isSingleProperty(component)) {
    return reference;
  }
  var optimal = {};
  Object.keys(reference).forEach(function (key) {
    var needsUpdate = !equal(reference[key], implicit[key]);
    if (needsUpdate) {
      optimal[key] = reference[key];
    }
  });
  return optimal;
}

/**
 * @param {Component} component Component to test if it is single property.
 * @return                      `true` if component is single property.
 */
function isSingleProperty(component) {
  return AFRAME.schema.isSingleProperty(component.schema);
}

/**
 * Detect element's Id collision and returns a valid one
 * @param  {string} baseId Proposed Id
 * @return {string}        Valid Id based on the proposed Id
 */
function getUniqueId (baseId) {
  if (!document.getElementById(baseId)) {
    return baseId;
  }

  var i = 2;
  // If the baseId ends with _#, it extracts the baseId removing the suffix
  var groups = baseId.match(/(\w+)-(\d+)/);
  if (groups) {
    baseId = groups[1];
    i = groups[2];
  }

  while (document.getElementById(baseId + '-' + i)) { i++; }

  return baseId + '-' + i;
}
<commit_msg>Telling inherited components apart form entity-owned components.<commit_after>var Events = require('../lib/Events.js');

import {equal} from '../lib/utils.js';

/**
 * Update a component.
 *
 * @param {Element} entity - Entity to modify.
 * @param {string} component - Name of the component.
 * @param {string} property - Property name.
 * @param {string|number} value - New value.
 */
export function updateEntity (entity, propertyName, value) {
  if (propertyName.indexOf('.') !== -1) {
    // Multi-prop
    var splitName = propertyName.split('.');
    if (value === null || value === undefined) {
      // Remove property.
      var parameters = entity.getAttribute(splitName[0]);
      delete parameters[splitName[1]];
      entity.setAttribute(splitName[0], parameters);
    } else {
      // Set property.
      entity.setAttribute(splitName[0], splitName[1], value);
    }
  } else {
    if (value === null || value === undefined) {
      // Remove property.
      entity.removeAttribute(propertyName);
    } else {
      // Set property.
      entity.setAttribute(propertyName, value);
    }
  }
  Events.emit('objectchanged', entity.object3D);
}

/**
 * Remove an entity
 * @param  {Element} entity Entity to remove.
 * @param  {boolean} force (Optional) If true it won't ask for confirmation
 */
export function removeEntity (entity, force) {
  if (entity) {
    if (force === true || confirm('Do you really want to remove entity `' + (entity.id || entity.tagName) + '`?')) {
      var closest = findClosestEntity(entity);
      AFRAME.INSPECTOR.removeObject(entity.object3D);
      entity.parentNode.removeChild(entity);
      AFRAME.INSPECTOR.selectEntity(closest);
    }
  }
}

function findClosestEntity (entity) {
  // First we try to find the after the entity
  var nextEntity = entity.nextElementSibling;
  while (nextEntity && (!nextEntity.isEntity || nextEntity.isInspector)) {
    nextEntity = nextEntity.nextElementSibling;
  }

  // Return if we found it
  if (nextEntity && nextEntity.isEntity && !nextEntity.isInspector) {
    return nextEntity;
  }
  // Otherwise try to find before the entity
  var prevEntity = entity.previousElementSibling;
  while (prevEntity && (!prevEntity.isEntity || prevEntity.isInspector)) {
    prevEntity = prevEntity.previousElementSibling;
  }

  // Return if we found it
  if (prevEntity && prevEntity.isEntity && !prevEntity.isInspector) {
    return prevEntity;
  }

  return null;
}

/**
 * Remove the selected entity
 * @param  {boolean} force (Optional) If true it won't ask for confirmation
 */
export function removeSelectedEntity (force) {
  if (AFRAME.INSPECTOR.selectedEntity) {
    removeEntity(AFRAME.INSPECTOR.selectedEntity, force);
  }
}

/**
 * Insert an node after a referenced node.
 * @param  {Element} newNode       Node to insert.
 * @param  {Element} referenceNode Node used as reference to insert after it.
*/
function insertAfter (newNode, referenceNode) {
  if (!referenceNode.parentNode) {
    referenceNode = AFRAME.INSPECTOR.selectedEntity;
  }

  if (!referenceNode) {
    AFRAME.INSPECTOR.sceneEl.appendChild(newNode);
  } else {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  }
}

/**
 * Clone an entity, inserting it after the cloned one.
 * @param  {Element} entity Entity to clone
 */
export function cloneEntity (entity) {
  entity.flushToDOM();
  var copy = entity.cloneNode(true);
  copy.addEventListener('loaded', function (e) {
    AFRAME.INSPECTOR.selectEntity(copy);
  });

  // Get a valid unique ID for the entity
  if (entity.id) {
    copy.id = getUniqueId(entity.id);
  }
  copy.addEventListener('loaded', function () {
    Events.emit('dommodified');
    AFRAME.INSPECTOR.selectEntity(copy);
  });
  insertAfter(copy, entity);
}

/**
 * Clone the selected entity
 */
export function cloneSelectedEntity () {
  if (AFRAME.INSPECTOR.selectedEntity) {
    cloneEntity(AFRAME.INSPECTOR.selectedEntity);
  }
}

/**
 * Returns the clipboard representation to be used to copy to the clipboard
 * @param  {Element} entity Entity to copy to clipboard
 * @return {string}        Entity clipboard representation
 */
export function getClipboardRepresentation (entity) {
  var clone = prepareForSerialization(entity);
  clone.flushToDOM(true);
  return clone.outerHTML;
}

/**
 * Returns a copy of the DOM hierarchy prepared for serialization.
 * The process optimises component representation to avoid values coming from
 * primitive attributes, mixins and defaults.
 *
 * @param {Element} entity Root of the DOM hierarchy.
 * @return {Elment}        Copy of the DOM hierarchy ready for serialization.
 */
function prepareForSerialization(entity) {
  var clone = entity.cloneNode(false);
  var children = entity.childNodes;
  for (var i = 0, l = children.length; i < l; i++) {
    var child = children[i];
    if (child.nodeType !== Node.ELEMENT_NODE ||
        !child.hasAttribute('aframe-injected') &&
        !child.hasAttribute('data-aframe-inspector') &&
        !child.hasAttribute('data-aframe-canvas')) {
      clone.appendChild(prepareForSerialization(children[i]));
    }
  }
  optimizeComponents(clone, entity);
  return clone;
}

/**
 * Removes from copy those components or components' properties that comes from
 * primitive attributes, mixins, injected default components or schema defaults.
 *
 * @param {Element} copy   Destinatary element for the optimization.
 * @param {Element} source Element to be optimized.
 */
function optimizeComponents(copy, source) {
  var components = source.components || {};
  Object.keys(components).forEach(function (name) {
    var component = components[name];
    var result = getImplicitValue(component, source);
    var isInherited = result[1];
    var implicitValue = result[0];
    var currentValue = source.getAttribute(name);
    var optimalUpdate = getOptimalUpdate(component, implicitValue, currentValue);
    var doesNotNeedUpdate = optimalUpdate === null;
    if (isInherited && doesNotNeedUpdate) {
      copy.removeAttribute(name);
    }
    else {
      copy.setAttribute(name, optimalUpdate, true);
    }
  });
}

/**
 * Computes the value for a component coming from primitive attributes,
 * mixins, primitive defaults, a-frame default components and schema defaults.
 * In this specific order.
 *
 * In other words, it is the value of the component if the author would have not
 * overridden it explicitly.
 *
 * @param {Component} component Component to calculate the value of.
 * @param {Element}   source    Element owning the component.
 * @return                      A pair with the computed value for the component of source and a flag indicating if the component is completely inherited from other sources (`true`) or genuinely owned by the source entity (`false`).
 */
function getImplicitValue(component, source) {
  var isInherited = false;
  var value = (isSingleProperty(component) ? _single : _multi)();
  return [value, isInherited];

  function _single() {
    var value = getMixedValue(component, null, source);
    if (value === undefined) {
      value = getInjectedValue(component, null, source);
    }
    if (value !== undefined) {
      isInherited = true;
    }
    else {
      value = getDefaultValue(component, null, source);
    }
    if (value !== undefined) {
      // XXX: This assumes parse is idempotent
      return component.schema.parse(value);
    }
    return value;
  }

  function _multi() {
    var value;

    Object.keys(component.schema).forEach(function (propertyName) {
      var propertyValue = getFromAttribute(component, propertyName, source);
      if (propertyValue === undefined) {
        propertyValue = getMixedValue(component, propertyName, source);
      }
      if (propertyValue === undefined) {
        propertyValue = getInjectedValue(component, propertyName, source);
      }
      if (propertyValue !== undefined) {
        isInherited = isInherited || true;
      }
      else {
        propertyValue = getDefaultValue(component, propertyName, source);
      }
      if (propertyValue !== undefined) {
        var parse = component.schema[propertyName].parse;
        value = value || {};
        // XXX: This assumes parse is idempotent
        value[propertyName] = parse(propertyValue);
      }
    });

    return value;
  }
}

/**
 * Gets the value for the component's property coming from a primitive
 * attribute.
 *
 * Primitives have mappings from attributes to component's properties.
 * The function looks for a present attribute in the source element which
 * maps to the specified component's property.
 *
 * @param  {Component} component    Component to be found.
 * @param  {string}    propertyName Component's property to be found.
 * @param  {Element}   source       Element owning the component.
 * @return {any}                    The value of the component's property coming
 *                                  from the primitive's attribute if any or
 *                                  `undefined`, otherwise.
 */
function getFromAttribute(component, propertyName, source) {
  var value;
  var mappings = source.mappings || {};
  var route = component.name + '.' + propertyName;
  var primitiveAttribute = findAttribute(mappings, route);
  if (primitiveAttribute && source.hasAttribute(primitiveAttribute)) {
    value = source.getAttribute(primitiveAttribute);
  }
  return value;

  function findAttribute(mappings, route) {
    var attributes = Object.keys(mappings);
    for (var i = 0, l = attributes.length; i < l; i++) {
      var attribute = attributes[i];
      if (mappings[attribute] === route) {
        return attribute;
      }
    }
    return undefined;
  }
}

/**
 * Gets the value for a component or component's property coming from mixins of
 * an element.
 *
 * If the component or component's property is not provided by mixins, the
 * functions will return `undefined`.
 *
 * @param {Component} component      Component to be found.
 * @param {string}    [propertyName] If provided, component's property to be
 *                                   found.
 * @param {Element}   source         Element owning the component.
 * @return                           The value of the component or components'
 *                                   property coming from mixins of the source.
 */
function getMixedValue(component, propertyName, source) {
  var value;
  var reversedMixins = source.mixinEls.reverse();
  for (var i = 0; value === undefined && i < reversedMixins.length; i++) {
    var mixin = reversedMixins[i];
    if (mixin.attributes.hasOwnProperty(component.name)) {
      if (!propertyName) {
        value = mixin.getAttribute(component.name);
      }
      else {
        value = mixin.getAttribute(component.name)[propertyName];
      }
    }
  }
  return value;
}

/**
 * Gets the value for a component or component's property coming from primitive
 * defaults or a-frame defaults. In this specific order.
 *
 * @param {Component} component      Component to be found.
 * @param {string}    [propertyName] If provided, component's property to be
 *                                   found.
 * @param {Element}   source         Element owning the component.
 * @return                           The component value coming from the
 *                                   injected default components of source.
 */
function getInjectedValue(component, propertyName, source) {
  var value;
  var primitiveDefaults = source.defaultComponentsFromPrimitive || {};
  var aFrameDefaults = source.defaultComponents || {};
  var defaultSources = [primitiveDefaults, aFrameDefaults];
  for (var i = 0; value === undefined && i < defaultSources.length; i++) {
    var defaults = defaultSources[i];
    if (defaults.hasOwnProperty(component.name)) {
      if (!propertyName) {
        value = defaults[component.name];
      }
      else {
        value = defaults[component.name][propertyName];
      }
    }
  }
  return value;
}

/**
 * Gets the value for a component or component's property coming from schema
 * defaults.
 *
 * @param {Component} component      Component to be found.
 * @param {string}    [propertyName] If provided, component's property to be
 *                                   found.
 * @param {Element}   source         Element owning the component.
 * @return                           The component value coming from the schema
 *                                   default.
 */
function getDefaultValue(component, propertyName, source) {
  if (!propertyName) {
    return component.schema.default;
  }
  return component.schema[propertyName].default;
}

/**
 * Returns the minimum value for a component with an implicit value to equal a
 * reference value. A `null` optimal value means that there is no need for an
 * update since the implicit value and the reference are equal.
 *
 * @param {Component} component Component of the computed value.
 * @param {any}       implicit  The implicit value of the component.
 * @param {any}       reference The reference value for the component.
 * @return                      the minimum value making the component to equal
 *                              the reference value.
 */
function getOptimalUpdate(component, implicit, reference) {
  if (equal(implicit, reference)) {
    return null;
  }
  if (isSingleProperty(component)) {
    return reference;
  }
  var optimal = {};
  Object.keys(reference).forEach(function (key) {
    var needsUpdate = !equal(reference[key], implicit[key]);
    if (needsUpdate) {
      optimal[key] = reference[key];
    }
  });
  return optimal;
}

/**
 * @param {Component} component Component to test if it is single property.
 * @return                      `true` if component is single property.
 */
function isSingleProperty(component) {
  return AFRAME.schema.isSingleProperty(component.schema);
}

/**
 * Detect element's Id collision and returns a valid one
 * @param  {string} baseId Proposed Id
 * @return {string}        Valid Id based on the proposed Id
 */
function getUniqueId (baseId) {
  if (!document.getElementById(baseId)) {
    return baseId;
  }

  var i = 2;
  // If the baseId ends with _#, it extracts the baseId removing the suffix
  var groups = baseId.match(/(\w+)-(\d+)/);
  if (groups) {
    baseId = groups[1];
    i = groups[2];
  }

  while (document.getElementById(baseId + '-' + i)) { i++; }

  return baseId + '-' + i;
}
<|endoftext|>