<commit_before>var _ = require('../util').lodash,
    Property = require('./property').Property,
    PropertyBase = require('./property-base').PropertyBase,
    VariableList = require('./variable-list').VariableList,
    MutationTracker = require('./mutation-tracker').MutationTracker,

    /**
     * Known variable mutation types.
     *
     * @private
     * @constant
     * @type {Object}
     */
    MUTATIONS = {
        SET: 'set',
        UNSET: 'unset'
    },

    VariableScope;

/**
 * Environment and Globals of postman is exported and imported in a specified data structure. This data structure can be
 * passed on to the constructor parameter of {@link VariableScope} or {@link VariableList} to instantiate an instance of
 * the same with pre-populated values from arguments.
 *
 * @typedef VariableScope~definition
 * @property {String} [id] ID of the scope
 * @property {String} [name] A name of the scope
 * @property {Array.<Variable~definition>} [values] A list of variables defined in an array in form of `{name:String,
 * value:String}`
 *
 * @example
JSON definition of a VariableScope (environment, globals, etc)

 * {
 *   "name": "globals",
 *   "values": [{
 *     "key": "var-1",
 *     "value": "value-1"
 *   }, {
 *     "key": "var-2",
 *     "value": "value-2"
 *   }]
 * }
 */
_.inherit((

    /**
     * VariableScope is a representation of a list of variables in Postman, such as the environment variables or the
     * globals. Using this object, it is easy to perform operations on this list of variables such as get a variable or
     * set a variable.
     *
     * @constructor
     * @extends {Property}
     *
     * @param {VariableScope~definition} definition The constructor accepts an initial set of values for initialising
     * the scope
     * @param {Array<VariableList>=} layers Additional parent scopes to search for and resolve variables
     *
     * @example
Load a environment from file, modify and save back

     * var fs = require('fs'), // assuming NodeJS
     *     env,
     *     sum;
     *
     * // load env from file assuming it has initial data
     * env = new VariableScope(JSON.parse(fs.readFileSync('./my-postman-environment.postman_environment').toString()));
     *
     * // get two variables and add them
     * sum = env.get('one-var') + env.get('another-var');
     *
     * // save it back in environment and write to file
     * env.set('sum', sum, 'number');
     * fs.writeFileSync('./sum-of-vars.postman_environment', JSON.stringify(env.toJSON()));
     */
    VariableScope = function PostmanVariableScope (definition, layers) {
        // in case the definition is an array (legacy format) or existing as list, we convert to actual format
        if (_.isArray(definition) || VariableList.isVariableList(definition)) {
            definition = { values: definition };
        }

        // we accept parent scopes to increase search area. Here we normalize the argument to be an array
        // so we can easily loop though them and add them to the instance.
        layers && !_.isArray(layers) && (layers = [layers]);

        // this constructor is intended to inherit and as such the super constructor is required to be executed
        VariableScope.super_.call(this, definition);

        var values = definition && definition.values, // access the values (need this var to reuse access)

            // enable mutation tracking if `mutations` are in definition (restore the state)
            // or is enabled  through options
            mutations = definition && definition.mutations,
            ii,
            i;

        /**
         * @memberOf VariableScope.prototype
         * @type {VariableList}
         */
        this.values = new VariableList(this, VariableList.isVariableList(values) ? values.toJSON() : values);
        // in above line, we clone the values if it is already a list. there is no point directly using the instance of
        // a variable list since one cannot be created with a parent reference to begin with.

        if (layers) {
            this._layers = [];

            for (i = 0, ii = layers.length; i < ii; i++) {
                VariableList.isVariableList(layers[i]) && this._layers.push(layers[i]);
            }
        }

        // restore previously tracked mutations
        if (mutations) {
            this.mutations = new MutationTracker(mutations);
        }
    }), Property);

/**
 * @note Handling disabled and duplicate variables:
 * | method | single enabled    | single disabled | with duplicates                                                    |
 * |--------|-------------------|-----------------|------------------------------------------------------------------- |
 * | has    | true              | false           | true (if last enabled) OR false (if all disabled)                  |
 * | get    | {Variable}        | undefined       | last enabled {Variable} OR undefined (if all disabled)             |
 * | set    | update {Variable} | new {Variable}  | update last enabled {Variable} OR new {Variable} (if all disabled) |
 * | unset  | delete {Variable} | noop            | delete all enabled {Variable}                                      |
 *
 * @todo Expected behavior of `unset` with duplicates:
 * delete last enabled {Variable} and update the reference with last enabled in rest of the list.
 * This requires unique identifier in the variable list for mutations to work correctly.
 */
_.assign(VariableScope.prototype, /** @lends VariableScope.prototype */ {
    /**
     * Defines whether this property instances requires an id
     * @private
     * @readOnly
     * @type {String}
     */
    _postman_propertyRequiresId: true,

    /**
     * @deprecated since v1.2.5.*
     * Get a copy of all variables in form of a plain object. This is useful for iteration and other use.
     * @returns {Object}
     *
     * @example
Iterate on all variables

     * var VariableScope = require('postman-collection').VariableScope,
     *     env = new VariableScope([{
     *         key: 'var1',
     *         value: 'one'
     *     }, {
     *         key: 'var2',
     *         value: 2,
     *         type: 'number'
     *     }, {
     *         key: 'var3',
     *         value: true,
     *         type: 'boolean'
     *     }]),
     *     obj;
     *
     * // get all variables consolidated as an object
     * obj = env.variables();
     *
     * Object.keys(obj).forEach(function(varname) {
     *     console.log(obj[varname]); // log all variables
     * });
     */
    variables: function () {
        return this.values.syncToObject({});
    },

    /**
     * Converts a list of Variables into an object where key is `_postman_propertyIndexKey` and value is determined
     * by the `valueOf` function
     *
     * @param {Boolean} excludeDisabled
     * @param {Boolean} caseSensitive
     * @return {Object}
     */
    toObject: function (excludeDisabled, caseSensitive) {
        // if the scope has no layers, we simply export the contents of primary store
        if (!this._layers) {
            return this.values.toObject(excludeDisabled, caseSensitive);
        }

        var mergedLayers = {};

        _.forEachRight(this._layers, function (layer) {
            _.assign(mergedLayers, layer.toObject(excludeDisabled, caseSensitive));
        });

        return _.assign(mergedLayers, this.values.toObject(excludeDisabled, caseSensitive));
    },

    /**
     * Determines whether one particular variable is defined in this scope of variables.
     *
     * @param {String} key - The name of the variable to check
     * @returns {Boolean} - Returns true if an enabled variable with given key is present in current or parent scopes,
     *                      false otherwise
     */
    has: function (key) {
        var variable = this.values.oneNormalizedVariable(key),
            i,
            ii;

        // if a variable is disabled or does not exist in local scope,
        // we search all the layers and return the first occurrence.
        if ((!variable || variable.disabled === true) && this._layers) {
            for (i = 0, ii = this._layers.length; i < ii; i++) {
                variable = this._layers[i].oneNormalizedVariable(key);
                if (variable && variable.disabled !== true) { break; }
            }
        }

        return Boolean(variable && variable.disabled !== true);
    },

    /**
     * Fetches a variable from the current scope or from parent scopes if present.
     *
     * @param {String} key - The name of the variable to get.
     * @returns {*} The value of the specified variable across scopes.
     */
    get: function (key) {
        var variable = this.values.oneNormalizedVariable(key),
            i,
            ii;

        // if a variable does not exist in local scope, we search all the layers and return the first occurrence.
        if ((!variable || variable.disabled === true) && this._layers) {
            for (i = 0, ii = this._layers.length; i < ii; i++) {
                variable = this._layers[i].oneNormalizedVariable(key);
                if (variable && variable.disabled !== true) { break; }
            }
        }

        return (variable && variable.disabled !== true) ? variable.valueOf() : undefined;
    },

    /**
     * Creates a new variable, or updates an existing one.
     *
     * @param {String} key - The name of the variable to set.
     * @param {*} value - The value of the variable to be set.
     * @param {Variable.types} [type] - Optionally, the value of the variable can be set to a type
     */
    set: function (key, value, type) {
        var variable = this.values.oneNormalizedVariable(key),

            update = { // create an object that will be used as setter
                key: key,
                value: value
            };

        _.isString(type) && (update.type = type);

        // If a variable by the name key exists, update it's value and return.
        // @note adds new variable if existing is disabled. Disabled variables are not updated.
        if (variable && !variable.disabled) {
            variable.update(update);
        }
        else {
            this.values.add(update);
        }

        // track the change if mutation tracking is enabled
        this._postman_enableTracking && this.mutations.track(MUTATIONS.SET, key, value);
    },

    /**
     * Removes the variable with the specified name.
     *
     * @param {String} key
     */
    unset: function (key) {
        var lastDisabledVariable;

        this.values.remove(function (variable) {
            // bail out if variable name didn't match
            if (variable.key !== key) {
                return false;
            }

            // don't delete disabled variables
            if (variable.disabled) {
                lastDisabledVariable = variable;
                return false;
            }

            // delete all enabled variables
            return true;
        });

        // restore the reference with the last disabled variable
        if (lastDisabledVariable) {
            this.values.reference[key] = lastDisabledVariable;
        }

        // track the change if mutation tracking is enabled
        this._postman_enableTracking && this.mutations.track(MUTATIONS.UNSET, key);
    },

    /**
     * Removes *all* variables from the current scope. This is a destructive action.
     */
    clear: function () {
        var mutations = this.mutations;

        // track the change if mutation tracking is enabled
        // do this before deleting the keys
        if (this._postman_enableTracking) {
            this.values.each(function (variable) {
                mutations.track(MUTATIONS.UNSET, variable.key);
            });
        }

        this.values.clear();
    },

    /**
     * Replace all variable names with their values in the given string or object
     *
     * @param {string|object} obj A string or an object to replace variables in
     * @returns {string|object} The string or object with variables (if any) substituted with their values
     */
    replaceIn: function (obj) {
        var transformed,
            result;

        if (typeof obj !== 'object') {
            obj = [obj];
            transformed = true;
        }

        result = Property.replaceSubstitutionsIn(obj, _.concat(this.values, this._layers));

        return transformed ? result[0] : result;
    },

    /**
     * Enable mutation tracking.
     *
     * @note: Would do nothing if already enabled.
     * @note: Any previously tracked mutations would be reset when starting a new tracking session.
     *
     * @param {MutationTracker~definition} [options] Options for Mutation Tracker. See {@link MutationTracker}
     */
    enableTracking: function (options) {
        // enabled already, do nothing
        if (this._postman_enableTracking) {
            return;
        }

        /**
         * Controls if mutation tracking is enabled
         *
         * @memberof VariableScope.prototype
         *
         * @private
         * @property {Boolean}
         */
        this._postman_enableTracking = true;

        // we don't want to add more mutations to existing mutations
        // that will lead to mutations not capturing the correct state
        // so we reset this with the new instance
        this.mutations = new MutationTracker(options);
    },

    /**
     * Disable mutation tracking.
     */
    disableTracking: function () {
        // disable further tracking but keep the tracked mutations
        this._postman_enableTracking = false;
    },

    /**
     * Apply a mutation instruction on this variable scope.
     *
     * @private
     *
     * @param {String} instruction Instruction identifying the type of the mutation, e.g. `set`, `unset`
     */
    applyMutation: function (instruction, key, value) {
        // we know that `set` and `unset` are the only supported instructions
        // and we know the parameter signature of both is the same as the items in a mutation
        if (this[instruction]) {

            // @todo: use argument spread here, once node v4 support is dropped
            this[instruction](key, value);
        }
    },

    /**
     * Using this function, one can sync the values of this variable list from a reference object.
     *
     * @private
     * @param {Object} obj
     * @param {Boolean=} [track]
     * @returns {Object}
     */
    syncVariablesFrom: function (obj, track) {
        return this.values.syncFromObject(obj, track);
    },

    /**
     * Transfer the variables in this scope to an object
     *
     * @private
     * @param {Object=} [obj]
     * @returns {Object}
     */
    syncVariablesTo: function (obj) {
        return this.values.syncToObject(obj);
    },

    /**
     * Convert this variable scope into a JSON serialisable object. Useful to transport or store, environment and
     * globals as a whole.
     *
     * @returns {Object}
     */
    toJSON: function () {
        var obj = PropertyBase.toJSON(this);

        // @todo - remove this when pluralisation is complete
        if (obj.value) {
            obj.values = obj.value;
            delete obj.value;
        }

        // ensure that the concept of layers is not exported as JSON. JSON cannot retain references and this will end up
        // being a pointless object post JSONification.
        if (obj._layers) {
            delete obj._layers;
        }

        // ensure that tracking flag is not serialized
        // otherwise, it is very easy to let tracking trickle to many instances leading to a snowball effect
        if (obj._postman_enableTracking) {
            delete obj._postman_enableTracking;
        }

        return obj;
    },

    /**
     * Adds a variable list to the current instance in order to increase the surface area of variable resolution.
     * This enables consumers to search across scopes (eg. environment and globals).
     *
     * @private
     * @param {VariableList} [list]
     */
    addLayer: function (list) {
        if (!VariableList.isVariableList(list)) {
            return;
        }

        !this._layers && (this._layers = []); // lazily initialize layers
        this._layers.push(list);
    }
});

_.assign(VariableScope, /** @lends VariableScope */ {
    /**
     * Defines the name of this property for internal use.
     * @private
     * @readOnly
     * @type {String}
     *
     * @note that this is directly accessed only in case of VariableScope from _.findValue lodash util mixin
     */
    _postman_propertyName: 'VariableScope',

    /**
     * Check whether an object is an instance of {@link VariableScope}.
     *
     * @param {*} obj
     * @returns {Boolean}
     */
    isVariableScope: function (obj) {
        return Boolean(obj) && ((obj instanceof VariableScope) ||
            _.inSuperChain(obj.constructor, '_postman_propertyName', VariableScope._postman_propertyName));
    }
});

module.exports = {
    VariableScope: VariableScope
};
<commit_msg>Rename param `obj` to `template`<commit_after>var _ = require('../util').lodash,
    Property = require('./property').Property,
    PropertyBase = require('./property-base').PropertyBase,
    VariableList = require('./variable-list').VariableList,
    MutationTracker = require('./mutation-tracker').MutationTracker,

    /**
     * Known variable mutation types.
     *
     * @private
     * @constant
     * @type {Object}
     */
    MUTATIONS = {
        SET: 'set',
        UNSET: 'unset'
    },

    VariableScope;

/**
 * Environment and Globals of postman is exported and imported in a specified data structure. This data structure can be
 * passed on to the constructor parameter of {@link VariableScope} or {@link VariableList} to instantiate an instance of
 * the same with pre-populated values from arguments.
 *
 * @typedef VariableScope~definition
 * @property {String} [id] ID of the scope
 * @property {String} [name] A name of the scope
 * @property {Array.<Variable~definition>} [values] A list of variables defined in an array in form of `{name:String,
 * value:String}`
 *
 * @example
JSON definition of a VariableScope (environment, globals, etc)

 * {
 *   "name": "globals",
 *   "values": [{
 *     "key": "var-1",
 *     "value": "value-1"
 *   }, {
 *     "key": "var-2",
 *     "value": "value-2"
 *   }]
 * }
 */
_.inherit((

    /**
     * VariableScope is a representation of a list of variables in Postman, such as the environment variables or the
     * globals. Using this object, it is easy to perform operations on this list of variables such as get a variable or
     * set a variable.
     *
     * @constructor
     * @extends {Property}
     *
     * @param {VariableScope~definition} definition The constructor accepts an initial set of values for initialising
     * the scope
     * @param {Array<VariableList>=} layers Additional parent scopes to search for and resolve variables
     *
     * @example
Load a environment from file, modify and save back

     * var fs = require('fs'), // assuming NodeJS
     *     env,
     *     sum;
     *
     * // load env from file assuming it has initial data
     * env = new VariableScope(JSON.parse(fs.readFileSync('./my-postman-environment.postman_environment').toString()));
     *
     * // get two variables and add them
     * sum = env.get('one-var') + env.get('another-var');
     *
     * // save it back in environment and write to file
     * env.set('sum', sum, 'number');
     * fs.writeFileSync('./sum-of-vars.postman_environment', JSON.stringify(env.toJSON()));
     */
    VariableScope = function PostmanVariableScope (definition, layers) {
        // in case the definition is an array (legacy format) or existing as list, we convert to actual format
        if (_.isArray(definition) || VariableList.isVariableList(definition)) {
            definition = { values: definition };
        }

        // we accept parent scopes to increase search area. Here we normalize the argument to be an array
        // so we can easily loop though them and add them to the instance.
        layers && !_.isArray(layers) && (layers = [layers]);

        // this constructor is intended to inherit and as such the super constructor is required to be executed
        VariableScope.super_.call(this, definition);

        var values = definition && definition.values, // access the values (need this var to reuse access)

            // enable mutation tracking if `mutations` are in definition (restore the state)
            // or is enabled  through options
            mutations = definition && definition.mutations,
            ii,
            i;

        /**
         * @memberOf VariableScope.prototype
         * @type {VariableList}
         */
        this.values = new VariableList(this, VariableList.isVariableList(values) ? values.toJSON() : values);
        // in above line, we clone the values if it is already a list. there is no point directly using the instance of
        // a variable list since one cannot be created with a parent reference to begin with.

        if (layers) {
            this._layers = [];

            for (i = 0, ii = layers.length; i < ii; i++) {
                VariableList.isVariableList(layers[i]) && this._layers.push(layers[i]);
            }
        }

        // restore previously tracked mutations
        if (mutations) {
            this.mutations = new MutationTracker(mutations);
        }
    }), Property);

/**
 * @note Handling disabled and duplicate variables:
 * | method | single enabled    | single disabled | with duplicates                                                    |
 * |--------|-------------------|-----------------|------------------------------------------------------------------- |
 * | has    | true              | false           | true (if last enabled) OR false (if all disabled)                  |
 * | get    | {Variable}        | undefined       | last enabled {Variable} OR undefined (if all disabled)             |
 * | set    | update {Variable} | new {Variable}  | update last enabled {Variable} OR new {Variable} (if all disabled) |
 * | unset  | delete {Variable} | noop            | delete all enabled {Variable}                                      |
 *
 * @todo Expected behavior of `unset` with duplicates:
 * delete last enabled {Variable} and update the reference with last enabled in rest of the list.
 * This requires unique identifier in the variable list for mutations to work correctly.
 */
_.assign(VariableScope.prototype, /** @lends VariableScope.prototype */ {
    /**
     * Defines whether this property instances requires an id
     * @private
     * @readOnly
     * @type {String}
     */
    _postman_propertyRequiresId: true,

    /**
     * @deprecated since v1.2.5.*
     * Get a copy of all variables in form of a plain object. This is useful for iteration and other use.
     * @returns {Object}
     *
     * @example
Iterate on all variables

     * var VariableScope = require('postman-collection').VariableScope,
     *     env = new VariableScope([{
     *         key: 'var1',
     *         value: 'one'
     *     }, {
     *         key: 'var2',
     *         value: 2,
     *         type: 'number'
     *     }, {
     *         key: 'var3',
     *         value: true,
     *         type: 'boolean'
     *     }]),
     *     obj;
     *
     * // get all variables consolidated as an object
     * obj = env.variables();
     *
     * Object.keys(obj).forEach(function(varname) {
     *     console.log(obj[varname]); // log all variables
     * });
     */
    variables: function () {
        return this.values.syncToObject({});
    },

    /**
     * Converts a list of Variables into an object where key is `_postman_propertyIndexKey` and value is determined
     * by the `valueOf` function
     *
     * @param {Boolean} excludeDisabled
     * @param {Boolean} caseSensitive
     * @return {Object}
     */
    toObject: function (excludeDisabled, caseSensitive) {
        // if the scope has no layers, we simply export the contents of primary store
        if (!this._layers) {
            return this.values.toObject(excludeDisabled, caseSensitive);
        }

        var mergedLayers = {};

        _.forEachRight(this._layers, function (layer) {
            _.assign(mergedLayers, layer.toObject(excludeDisabled, caseSensitive));
        });

        return _.assign(mergedLayers, this.values.toObject(excludeDisabled, caseSensitive));
    },

    /**
     * Determines whether one particular variable is defined in this scope of variables.
     *
     * @param {String} key - The name of the variable to check
     * @returns {Boolean} - Returns true if an enabled variable with given key is present in current or parent scopes,
     *                      false otherwise
     */
    has: function (key) {
        var variable = this.values.oneNormalizedVariable(key),
            i,
            ii;

        // if a variable is disabled or does not exist in local scope,
        // we search all the layers and return the first occurrence.
        if ((!variable || variable.disabled === true) && this._layers) {
            for (i = 0, ii = this._layers.length; i < ii; i++) {
                variable = this._layers[i].oneNormalizedVariable(key);
                if (variable && variable.disabled !== true) { break; }
            }
        }

        return Boolean(variable && variable.disabled !== true);
    },

    /**
     * Fetches a variable from the current scope or from parent scopes if present.
     *
     * @param {String} key - The name of the variable to get.
     * @returns {*} The value of the specified variable across scopes.
     */
    get: function (key) {
        var variable = this.values.oneNormalizedVariable(key),
            i,
            ii;

        // if a variable does not exist in local scope, we search all the layers and return the first occurrence.
        if ((!variable || variable.disabled === true) && this._layers) {
            for (i = 0, ii = this._layers.length; i < ii; i++) {
                variable = this._layers[i].oneNormalizedVariable(key);
                if (variable && variable.disabled !== true) { break; }
            }
        }

        return (variable && variable.disabled !== true) ? variable.valueOf() : undefined;
    },

    /**
     * Creates a new variable, or updates an existing one.
     *
     * @param {String} key - The name of the variable to set.
     * @param {*} value - The value of the variable to be set.
     * @param {Variable.types} [type] - Optionally, the value of the variable can be set to a type
     */
    set: function (key, value, type) {
        var variable = this.values.oneNormalizedVariable(key),

            update = { // create an object that will be used as setter
                key: key,
                value: value
            };

        _.isString(type) && (update.type = type);

        // If a variable by the name key exists, update it's value and return.
        // @note adds new variable if existing is disabled. Disabled variables are not updated.
        if (variable && !variable.disabled) {
            variable.update(update);
        }
        else {
            this.values.add(update);
        }

        // track the change if mutation tracking is enabled
        this._postman_enableTracking && this.mutations.track(MUTATIONS.SET, key, value);
    },

    /**
     * Removes the variable with the specified name.
     *
     * @param {String} key
     */
    unset: function (key) {
        var lastDisabledVariable;

        this.values.remove(function (variable) {
            // bail out if variable name didn't match
            if (variable.key !== key) {
                return false;
            }

            // don't delete disabled variables
            if (variable.disabled) {
                lastDisabledVariable = variable;
                return false;
            }

            // delete all enabled variables
            return true;
        });

        // restore the reference with the last disabled variable
        if (lastDisabledVariable) {
            this.values.reference[key] = lastDisabledVariable;
        }

        // track the change if mutation tracking is enabled
        this._postman_enableTracking && this.mutations.track(MUTATIONS.UNSET, key);
    },

    /**
     * Removes *all* variables from the current scope. This is a destructive action.
     */
    clear: function () {
        var mutations = this.mutations;

        // track the change if mutation tracking is enabled
        // do this before deleting the keys
        if (this._postman_enableTracking) {
            this.values.each(function (variable) {
                mutations.track(MUTATIONS.UNSET, variable.key);
            });
        }

        this.values.clear();
    },

    /**
     * Replace all variable names with their values in the given template.
     *
     * @param {string|object} template - A string or an object to replace variables in
     * @returns {string|object} The string or object with variables (if any) substituted with their values
     */
    replaceIn: function (template) {
        var isString = typeof template === 'string',
            result;

        if (isString) {
            template = [template];
        }

        if (typeof template !== 'object') {
            return template;
        }

        result = Property.replaceSubstitutionsIn(template, _.concat(this.values, this._layers));

        return isString ? result[0] : result;
    },

    /**
     * Enable mutation tracking.
     *
     * @note: Would do nothing if already enabled.
     * @note: Any previously tracked mutations would be reset when starting a new tracking session.
     *
     * @param {MutationTracker~definition} [options] Options for Mutation Tracker. See {@link MutationTracker}
     */
    enableTracking: function (options) {
        // enabled already, do nothing
        if (this._postman_enableTracking) {
            return;
        }

        /**
         * Controls if mutation tracking is enabled
         *
         * @memberof VariableScope.prototype
         *
         * @private
         * @property {Boolean}
         */
        this._postman_enableTracking = true;

        // we don't want to add more mutations to existing mutations
        // that will lead to mutations not capturing the correct state
        // so we reset this with the new instance
        this.mutations = new MutationTracker(options);
    },

    /**
     * Disable mutation tracking.
     */
    disableTracking: function () {
        // disable further tracking but keep the tracked mutations
        this._postman_enableTracking = false;
    },

    /**
     * Apply a mutation instruction on this variable scope.
     *
     * @private
     *
     * @param {String} instruction Instruction identifying the type of the mutation, e.g. `set`, `unset`
     */
    applyMutation: function (instruction, key, value) {
        // we know that `set` and `unset` are the only supported instructions
        // and we know the parameter signature of both is the same as the items in a mutation
        if (this[instruction]) {

            // @todo: use argument spread here, once node v4 support is dropped
            this[instruction](key, value);
        }
    },

    /**
     * Using this function, one can sync the values of this variable list from a reference object.
     *
     * @private
     * @param {Object} obj
     * @param {Boolean=} [track]
     * @returns {Object}
     */
    syncVariablesFrom: function (obj, track) {
        return this.values.syncFromObject(obj, track);
    },

    /**
     * Transfer the variables in this scope to an object
     *
     * @private
     * @param {Object=} [obj]
     * @returns {Object}
     */
    syncVariablesTo: function (obj) {
        return this.values.syncToObject(obj);
    },

    /**
     * Convert this variable scope into a JSON serialisable object. Useful to transport or store, environment and
     * globals as a whole.
     *
     * @returns {Object}
     */
    toJSON: function () {
        var obj = PropertyBase.toJSON(this);

        // @todo - remove this when pluralisation is complete
        if (obj.value) {
            obj.values = obj.value;
            delete obj.value;
        }

        // ensure that the concept of layers is not exported as JSON. JSON cannot retain references and this will end up
        // being a pointless object post JSONification.
        if (obj._layers) {
            delete obj._layers;
        }

        // ensure that tracking flag is not serialized
        // otherwise, it is very easy to let tracking trickle to many instances leading to a snowball effect
        if (obj._postman_enableTracking) {
            delete obj._postman_enableTracking;
        }

        return obj;
    },

    /**
     * Adds a variable list to the current instance in order to increase the surface area of variable resolution.
     * This enables consumers to search across scopes (eg. environment and globals).
     *
     * @private
     * @param {VariableList} [list]
     */
    addLayer: function (list) {
        if (!VariableList.isVariableList(list)) {
            return;
        }

        !this._layers && (this._layers = []); // lazily initialize layers
        this._layers.push(list);
    }
});

_.assign(VariableScope, /** @lends VariableScope */ {
    /**
     * Defines the name of this property for internal use.
     * @private
     * @readOnly
     * @type {String}
     *
     * @note that this is directly accessed only in case of VariableScope from _.findValue lodash util mixin
     */
    _postman_propertyName: 'VariableScope',

    /**
     * Check whether an object is an instance of {@link VariableScope}.
     *
     * @param {*} obj
     * @returns {Boolean}
     */
    isVariableScope: function (obj) {
        return Boolean(obj) && ((obj instanceof VariableScope) ||
            _.inSuperChain(obj.constructor, '_postman_propertyName', VariableScope._postman_propertyName));
    }
});

module.exports = {
    VariableScope: VariableScope
};
<|endoftext|>


<commit_before>/* jshint node: true */
'use strict';

var Q = require('q'),
    _ = require('busyman'),
    ZSC = require('zstack-constants'),
    debug = {
        shepherd: require('debug')('zigbee-shepherd'),
        init: require('debug')('zigbee-shepherd:init'),
        request: require('debug')('zigbee-shepherd:request'),
    };

var handlers = {}, event_handlers = {};

event_handlers.attachEventHandlers = function (shepherd) {
    var controller = shepherd.controller,
        hdls = {};

    _.forEach(handlers, function (hdl, key) {
        if (key !== 'attachEventHandlers')
            hdls[key] = hdl.bind(shepherd);
    });

    controller.removeListener('SYS:resetInd',          hdls.resetInd);
    controller.removeListener('ZDO:stateChangeInd',    hdls.stateChangeInd);
    controller.removeListener('ZDO:statusErrorRsp',    hdls.statusErrorRsp);
    controller.removeListener('ZDO:leaveInd',          hdls.leaveInd);

    controller.on('SYS:resetInd',          hdls.resetInd);
    controller.on('ZDO:stateChangeInd',    hdls.stateChangeInd);
    controller.on('ZDO:statusErrorRsp',    hdls.statusErrorRsp);
    controller.on('ZDO:leaveInd',          hdls.leaveInd);
};

/*************************************************************************************************/
/*** Event Handlers                                                                            ***/
/*************************************************************************************************/
handlers.resetInd = async function (msg) {
    if (this.controller.isResetting()) return;

    if (msg !== '_reset'){
        if(!this._enabled){
            debug.shepherd('Skipping software reset (not started)...');
            return
        }
        debug.shepherd('Starting a software reset...');
    }

    if(this._enabled){
        await this.stop()
        await this.start()
    }
    try {
        if (msg === '_reset')
            return this.controller.emit('_reset');
    }catch(err){
        if (msg === '_reset') {
            return this.controller.emit('_reset', err);
        } else {
            debug.shepherd('Reset had an error', err);
            this.emit('error', err);
        }
    }
};

handlers.leaveInd = function (msg) {
    // { srcaddr, extaddr, request, removechildren, rejoin, status }

    if(msg.status) {
        debug.shepherd('Device: %s not leaving due to %d status.', msg.srcaddr, msg.status);
        return
    }

    var dev = this._findDevByAddr(msg.extaddr);

    if (dev) {
        var ieeeAddr = dev.getIeeeAddr(),
            epList = _.cloneDeep(dev.epList);

        if (msg.request)    // request
            this._unregisterDev(dev);
        else    {
            // indication
            dev.update({status: "offline", incomplete: true});
            return Q.ninvoke(this._devbox, 'sync', dev._getId());
        }

        debug.shepherd('Device: %s leave the network.', ieeeAddr);
        this.emit('ind:leaving', epList, msg.nwkaddr, ieeeAddr);
    }else{
        this.emit('ind:leaving', epList, msg.srcaddr, msg.extaddr);
    }
};

handlers.stateChangeInd = function (msg) {
    // { state[, nwkaddr] }
    if (!msg.hasOwnProperty('nwkaddr'))
        return;

    var devStates = msg.state;

    _.forEach(ZSC.ZDO.devStates, function (statesCode, states) {
        if (msg.state === statesCode)
            devStates = states;
    });

    debug.shepherd('Device: %d is now in state: %s', msg.nwkaddr, devStates);
};

handlers.statusErrorRsp = function (msg) {
    // { srcaddr, status }
    debug.shepherd('Device: %d status error: %d', msg.srcaddr, msg.status);
};

module.exports = event_handlers;
<commit_msg>emit leaveInd with extaddr<commit_after>/* jshint node: true */
'use strict';

var Q = require('q'),
    _ = require('busyman'),
    ZSC = require('zstack-constants'),
    debug = {
        shepherd: require('debug')('zigbee-shepherd'),
        init: require('debug')('zigbee-shepherd:init'),
        request: require('debug')('zigbee-shepherd:request'),
    };

var handlers = {}, event_handlers = {};

event_handlers.attachEventHandlers = function (shepherd) {
    var controller = shepherd.controller,
        hdls = {};

    _.forEach(handlers, function (hdl, key) {
        if (key !== 'attachEventHandlers')
            hdls[key] = hdl.bind(shepherd);
    });

    controller.removeListener('SYS:resetInd',          hdls.resetInd);
    controller.removeListener('ZDO:stateChangeInd',    hdls.stateChangeInd);
    controller.removeListener('ZDO:statusErrorRsp',    hdls.statusErrorRsp);
    controller.removeListener('ZDO:leaveInd',          hdls.leaveInd);

    controller.on('SYS:resetInd',          hdls.resetInd);
    controller.on('ZDO:stateChangeInd',    hdls.stateChangeInd);
    controller.on('ZDO:statusErrorRsp',    hdls.statusErrorRsp);
    controller.on('ZDO:leaveInd',          hdls.leaveInd);
};

/*************************************************************************************************/
/*** Event Handlers                                                                            ***/
/*************************************************************************************************/
handlers.resetInd = async function (msg) {
    if (this.controller.isResetting()) return;

    if (msg !== '_reset'){
        if(!this._enabled){
            debug.shepherd('Skipping software reset (not started)...');
            return
        }
        debug.shepherd('Starting a software reset...');
    }

    if(this._enabled){
        await this.stop()
        await this.start()
    }
    try {
        if (msg === '_reset')
            return this.controller.emit('_reset');
    }catch(err){
        if (msg === '_reset') {
            return this.controller.emit('_reset', err);
        } else {
            debug.shepherd('Reset had an error', err);
            this.emit('error', err);
        }
    }
};

handlers.leaveInd = function (msg) {
    // { srcaddr, extaddr, request, removechildren, rejoin, status }

    this.emit('ZDO:leaveInd:'+msg.extaddr, msg);

    if(msg.status) {
        debug.shepherd('Device: %s not leaving due to %d status.', msg.srcaddr, msg.status);
        return
    }

    var dev = this._findDevByAddr(msg.extaddr);

    if (dev) {
        var ieeeAddr = dev.getIeeeAddr(),
            epList = _.cloneDeep(dev.epList);

        if (msg.request)    // request
            this._unregisterDev(dev);
        else    {
            // indication
            dev.update({status: "offline", incomplete: true});
            return Q.ninvoke(this._devbox, 'sync', dev._getId());
        }

        debug.shepherd('Device: %s leave the network.', ieeeAddr);
        this.emit('ind:leaving', epList, msg.nwkaddr, ieeeAddr);
    }else{
        this.emit('ind:leaving', epList, msg.srcaddr, msg.extaddr);
    }
};

handlers.stateChangeInd = function (msg) {
    // { state[, nwkaddr] }
    if (!msg.hasOwnProperty('nwkaddr'))
        return;

    var devStates = msg.state;

    _.forEach(ZSC.ZDO.devStates, function (statesCode, states) {
        if (msg.state === statesCode)
            devStates = states;
    });

    debug.shepherd('Device: %d is now in state: %s', msg.nwkaddr, devStates);
};

handlers.statusErrorRsp = function (msg) {
    // { srcaddr, status }
    debug.shepherd('Device: %d status error: %d', msg.srcaddr, msg.status);
};

module.exports = event_handlers;
<|endoftext|>


<commit_before>/*!
 * jQuery Pongstagr.am Plugin
 * Copyright (c) 2013 Pongstr Ordillo
 * Version: 2.0.8
 * Code license under Apache License v2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 * Requires: jQuery v1.9 and Bootstrap 3.2 js
 */

;(function ($, window, document, undefined){

  "use strict";

  function renderHTML( targetElement, request, pager ){
    var galleryList      = '

',
        galleryContainer = '
' + galleryList + '
',
        paginateTarget   = $(targetElement).attr('id'),
        paginateBtn      = '
Load More
';

    $( targetElement ).append( galleryContainer );

    if ( pager === null || pager === true ){
      $( targetElement ).after( paginateBtn );
    }
  }

  function renderModal( imageOwner, imageId, imageTitle, imageUrl, imgUser, video, type ){

    var modal  = '
';
        modal += '
';
        modal += '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>';
        modal += '
';
        modal += '
';
        modal += '
';

        modal += '
';
        modal += ( type !== 'video' ) ? '

' :
                 (!navigator.userAgent.match(/webkit/i) && !navigator.userAgent.match(/(iPod|iPhone|iPad)/) ) ? '

Your browser does not support MPEG-4 Videos
' :
                  '<video controls autoplay width="100%" height="auto"><source src="'+ video +'" type="video/mp4">Your browser does not support html5 video</video>';
        modal += '
';

        modal += '
';
        modal += '
';
        modal += '
';
        modal += '';
        modal += '
' + imageOwner + '
' + imageTitle + '
';
        modal += '
';
        modal += '
';
        modal += '
';

        modal += '
';
        modal += '
';
        modal += '
';

    $('body').append( modal ); //*! Append modal window to body

    $('#' + imageId ).on('hidden', function(){
      $(this).remove(); //*! completely remove modal
      $('body').removeAttr('style');
    });
  }

  function imagePreLoader( imageId ){
    var $image    = $( imageId ),
         spinner  = imageId + '-ldr',
         preloadr = 0,
         total    = $image.length;
    $image.hide();
    $image.load(function(){
      if ( ++preloadr === total ){
        $image.fadeIn('fast');
        $(spinner).fadeOut('fast').remove();
      }
    });
  }

  function ajaxRequest( endpoint, targetElement ){
    $.ajax({
      method   : "GET"    ,
      url      : endpoint ,
      cache    : true     ,
      dataType : "jsonp"  ,
      success  : function(data){

        var injectTo = '#' + $(targetElement).attr('id');

        $.each( data.data, function( key, value ){

          var thumbnail  = value.images.low_resolution.url,
              imgCaption = ( value.caption !== null ) ? ( value.caption.text !== null ) ? value.caption.text : '' : value.user.username,
              comments   = ( value.comments.count !== null ) ? value.comments.count : '0',
              likes      = ( value.likes.count !== null ) ? value.likes.count : '0',
              imageUrl   = value.images.standard_resolution.url,
              imageId    = value.id,
              imgUser    = value.user.profile_picture,
              imageOwner = value.user.username,
              mediaType  = value.type,
              videos     = ( value.videos !== undefined ) ? value.videos.standard_resolution.url : '';

          var thumbBlock  = '
';
              thumbBlock += '
';
              thumbBlock += ( value.type === 'video' ) ? '' : '';
              thumbBlock += '

';
              thumbBlock += '  ' + likes + '';
              thumbBlock += '  ' + comments + '';
              thumbBlock += '';
              thumbBlock += '
';
              thumbBlock += '
';

          // Inject Thumbnaisl to container
          $( injectTo + ' .thumbnails' ).append( thumbBlock );

          // Preload images
          imagePreLoader( '#' + imageId + '-thmb' );

          $('[data-reveal-id="' + imageId + '"]').click(function(){

            $('.modal').attr('id', imageId );
            $('body').css({ overflow: 'hidden' });

            renderModal( imageOwner, imageId, imgCaption, imageUrl, imgUser, videos, mediaType );

            $.each( value.comments.data, function( group, key ){
              var commentBlock  = '
';
                  commentBlock += '';
                  commentBlock += '
';
                  commentBlock += '' + key.from.username + '
';
                  commentBlock +=  key.text;
                  commentBlock += '
'; // end of .media-body
                  commentBlock += '
'; // end of .media

              $('#user-caption').after(commentBlock);
            });

            $('#' + imageId ).modal(); //*! fire modal window
          });
        });

        paginate( data.pagination.next_url, injectTo ); //*! paginate through images
      }
    });
  }

  function paginate( nextUrl, targetElement ){

    var pagBtn = $(targetElement).attr('id');

    if ( nextUrl === undefined || nextUrl === null ) {
      $('[data-paginate="' + pagBtn + '"]').click(function(event){
        event.preventDefault();
        $(this)
          .removeClass('btn-success')
          .addClass('disabled btn-secondary');
      });
    } else {
      $('[data-paginate="' + pagBtn + '"]').click(function(event){
        event.preventDefault();
          ajaxRequest( nextUrl, targetElement );  //*! Load Succeeding Pages.
          $(this).unbind(event);   //*! Unbind all attached events.
      });
    }
  }

  function requestData ( request, count, accessID, accessToken, targetElement, pager ){
    var $apiRequest   = 'https://api.instagram.com/v1/users/',
        $requestCount = ( count !== null ) ?
          '?count=' +  count + '&access_token=' + accessToken :
          '?count=' +    8   + '&access_token=' + accessToken ,
        loadBtnData  = ( request === null ) ? 'recent' : request ;

    if ( request === null || request === 'recent' ){
      var $recentMedia = $apiRequest + accessID + '/media/recent' + $requestCount;
          // Load Recent Media
          ajaxRequest( $recentMedia, targetElement );
    }

    if ( request === 'liked' ){
      var $likedMedia = $apiRequest + 'self/media/liked' + $requestCount;
          // Load Liked Media
          ajaxRequest( $likedMedia, targetElement );
    }

    if ( request === 'feed' ){
      var $feedMedia = $apiRequest + 'self/feed' + $requestCount;
          // Load User Feed
          ajaxRequest( $feedMedia, targetElement );
    }

    renderHTML( targetElement, loadBtnData, pager );
  }

  function accessDetails( accessID, accessToken ){
    if ( accessID !== null || accessToken !== null ) {
        return true;
      } else {
          console.log('Please check whether your Access ID and Access Token if it\'s valid.' );
          console.log('You may visit http://instagram.com/developer/authentication/ for more info.');
        return false;
    }
  }

  $.fn.pongstgrm = function( options ){

    // Plugin Options
    var option  = $.extend({}, $.fn.pongstgrm.defaults, options);

    return this.each( function(i, element){
      if ( accessDetails( option.accessId, option.accessToken ) !== false ){
        requestData( option.show, option.count, option.accessId, option.accessToken, element, option.pager );
      }
    });  //*! end return this.each;
  };     //*! end $.fn.pongstagrm;

  // Pongstagram Default Options
  $.fn.pongstgrm.defaults = {

    // User Authentication
    accessId     : null,  // instsagram user-id
    accessToken  : null,  // instagram access-token

    // Display options
    show         : null,  // string,  options: 'recent', 'feed', 'liked', 'user'
    count        : null,  // integer, options: 1(min) - 40(max), instagram limits the maximum number of photos to 40
    resolution   : null,  // string,  options: 'low_resolution', 'standard_resolution'
    pager        : null   // boolean, options:  true or false (enables/disable load more button)

  };

 })(jQuery, window, document);<commit_msg>added support for tags<commit_after>/*!
 * jQuery Pongstagr.am Plugin
 * Copyright (c) 2013 Pongstr Ordillo
 * Version: 2.0.8
 * Code license under Apache License v2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 * Requires: jQuery v1.9 and Bootstrap 3.2 js
 */

;(function ($, window, document, undefined){

  "use strict";

  function renderHTML( targetElement, request, pager ){
    var galleryList      = '

',
        galleryContainer = '
' + galleryList + '
',
        paginateTarget   = $(targetElement).attr('id'),
        paginateBtn      = '
Load More
';

    $( targetElement ).append( galleryContainer );

    if ( pager === null || pager === true ){
      $( targetElement ).after( paginateBtn );
    }
  }

  function renderModal( imageOwner, imageId, imageTitle, imageUrl, imgUser, video, type ){

    var modal  = '
';
        modal += '
';
        modal += '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>';
        modal += '
';
        modal += '
';
        modal += '
';

        modal += '
';
        modal += ( type !== 'video' ) ? '

' :
                 (!navigator.userAgent.match(/webkit/i) && !navigator.userAgent.match(/(iPod|iPhone|iPad)/) ) ? '

Your browser does not support MPEG-4 Videos
' :
                  '<video controls autoplay width="100%" height="auto"><source src="'+ video +'" type="video/mp4">Your browser does not support html5 video</video>';
        modal += '
';

        modal += '
';
        modal += '
';
        modal += '
';
        modal += '';
        modal += '
' + imageOwner + '
' + imageTitle + '
';
        modal += '
';
        modal += '
';
        modal += '
';

        modal += '
';
        modal += '
';
        modal += '
';

    $('body').append( modal ); //*! Append modal window to body

    $('#' + imageId ).on('hidden', function(){
      $(this).remove(); //*! completely remove modal
      $('body').removeAttr('style');
    });
  }

  function imagePreLoader( imageId ){
    var $image    = $( imageId ),
         spinner  = imageId + '-ldr',
         preloadr = 0,
         total    = $image.length;
    $image.hide();
    $image.load(function(){
      if ( ++preloadr === total ){
        $image.fadeIn('fast');
        $(spinner).fadeOut('fast').remove();
      }
    });
  }

  function ajaxRequest( endpoint, targetElement ){
    $.ajax({
      method   : "GET"    ,
      url      : endpoint ,
      cache    : true     ,
      dataType : "jsonp"  ,
      success  : function(data){

        var injectTo = '#' + $(targetElement).attr('id');

        $.each( data.data, function( key, value ){

          var thumbnail  = value.images.low_resolution.url,
              imgCaption = ( value.caption !== null ) ? ( value.caption.text !== null ) ? value.caption.text : '' : value.user.username,
              comments   = ( value.comments.count !== null ) ? value.comments.count : '0',
              likes      = ( value.likes.count !== null ) ? value.likes.count : '0',
              imageUrl   = value.images.standard_resolution.url,
              imageId    = value.id,
              imgUser    = value.user.profile_picture,
              imageOwner = value.user.username,
              mediaType  = value.type,
              videos     = ( value.videos !== undefined ) ? value.videos.standard_resolution.url : '';

          var thumbBlock  = '
';
              thumbBlock += '
';
              thumbBlock += ( value.type === 'video' ) ? '' : '';
              thumbBlock += '

';
              thumbBlock += '  ' + likes + '';
              thumbBlock += '  ' + comments + '';
              thumbBlock += '';
              thumbBlock += '
';
              thumbBlock += '
';

          // Inject Thumbnaisl to container
          $( injectTo + ' .thumbnails' ).append( thumbBlock );

          // Preload images
          imagePreLoader( '#' + imageId + '-thmb' );

          $('[data-reveal-id="' + imageId + '"]').click(function(){

            $('.modal').attr('id', imageId );
            $('body').css({ overflow: 'hidden' });

            renderModal( imageOwner, imageId, imgCaption, imageUrl, imgUser, videos, mediaType );

            $.each( value.comments.data, function( group, key ){
              var commentBlock  = '
';
                  commentBlock += '';
                  commentBlock += '
';
                  commentBlock += '' + key.from.username + '
';
                  commentBlock +=  key.text;
                  commentBlock += '
'; // end of .media-body
                  commentBlock += '
'; // end of .media

              $('#user-caption').after(commentBlock);
            });

            $('#' + imageId ).modal(); //*! fire modal window
          });
        });

        paginate( data.pagination.next_url, injectTo ); //*! paginate through images
      }
    });
  }

  function paginate( nextUrl, targetElement ){

    var pagBtn = $(targetElement).attr('id');

    if ( nextUrl === undefined || nextUrl === null ) {
      $('[data-paginate="' + pagBtn + '"]').click(function(event){
        event.preventDefault();
        $(this)
          .removeClass('btn-success')
          .addClass('disabled btn-secondary');
      });
    } else {
      $('[data-paginate="' + pagBtn + '"]').click(function(event){
        event.preventDefault();
          ajaxRequest( nextUrl, targetElement );  //*! Load Succeeding Pages.
          $(this).unbind(event);   //*! Unbind all attached events.
      });
    }
  }

  function requestData ( request, count, accessID, accessToken, targetElement, pager ){
    var $apiRequest   = 'https://api.instagram.com/v1/',
        $requestCount = ( count !== null ) ?
          '?count=' +  count + '&access_token=' + accessToken :
          '?count=' +    8   + '&access_token=' + accessToken ,
        loadBtnData  = ( request === null ) ? 'recent' : request ;

    if ( request === null || request === 'recent' ){
      var $recentMedia =  $apiRequest + 'users/' + accessID + '/media/recent' + $requestCount;
          // Load Recent Media
          ajaxRequest( $recentMedia, targetElement );
    }

     else if ( request === 'liked' ){
      var $likedMedia = $apiRequest + 'users/self/media/liked' + $requestCount;
          // Load Liked Media
          ajaxRequest( $likedMedia, targetElement );
    }

    else if ( request === 'feed' ){
      var $feedMedia = $apiRequest + 'users/self/feed' + $requestCount;
          // Load User Feed
          ajaxRequest( $feedMedia, targetElement );
    }

    else {
      var $feedMedia = $apiRequest + 'tags/' + request + '/media/recent' + $requestCount;
          // Load Tagged Feed
          ajaxRequest( $feedMedia, targetElement );
    }

    renderHTML( targetElement, loadBtnData, pager );
  }

  function accessDetails( accessID, accessToken ){
    if ( accessID !== null || accessToken !== null ) {
        return true;
      } else {
          console.log('Please check whether your Access ID and Access Token if it\'s valid.' );
          console.log('You may visit http://instagram.com/developer/authentication/ for more info.');
        return false;
    }
  }

  $.fn.pongstgrm = function( options ){

    // Plugin Options
    var option  = $.extend({}, $.fn.pongstgrm.defaults, options);

    return this.each( function(i, element){
      if ( accessDetails( option.accessId, option.accessToken ) !== false ){
        requestData( option.show, option.count, option.accessId, option.accessToken, element, option.pager );
      }
    });  //*! end return this.each;
  };     //*! end $.fn.pongstagrm;

  // Pongstagram Default Options
  $.fn.pongstgrm.defaults = {

    // User Authentication
    accessId     : null,  // instsagram user-id
    accessToken  : null,  // instagram access-token

    // Display options
    show         : null,  // string,  options: 'recent', 'feed', 'liked', 'user', 'anytag'
    count        : null,  // integer, options: 1(min) - 40(max), instagram limits the maximum number of photos to 40
    resolution   : null,  // string,  options: 'low_resolution', 'standard_resolution'
    pager        : null   // boolean, options:  true or false (enables/disable load more button)

  };

 })(jQuery, window, document);<|endoftext|>


<commit_before>'use strict';

const { transformAndValidateMeasurements } = require('./validators'),
  log = require('../log');

/**
 * Transforms JSON produced by Luftdaten.info devices to openSenseMap compatible JSON.
 * It looks for the items in the `sensordatavalues` array and tries to match its `value_type`
 * properties to a sensors title.
 *
 * Example in JSON:
 *   {
 *     "software_version": "NRZ-2016-058",
 *     "sensordatavalues": [
 *       {
 *         "value_type": "SDS_P1",
 *         "value": "5.38"
 *       },
 *       {
 *         "value_type": "SDS_P2",
 *         "value": "4.98"
 *       },
 *       {
 *         "value_type": "samples",
 *         "value": "316274"
 *       },
 *       {
 *         "value_type": "min_micro",
 *         "value": "162"
 *       },
 *       {
 *         "value_type": "max_micro",
 *         "value": "394014"
 *       },
 *       {
 *         "value_type": "signal",
 *         "value": "-59 dBm"
 *       }
 *     ]
 *   }
 * Example in JSON PMS7003 with BME280
 *   { "software_version": "NRZ-2017-099",
 *     "sensordatavalues":[
 *       {"value_type":"PMS_P0","value":"1.00"},
 *       {"value_type":"PMS_P1","value":"2.00"},
 *       {"value_type":"PMS_P2","value":"2.00"},
 *       {"value_type":"BME280_temperature","value":"24.98"},
 *       {"value_type":"BME280_humidity","value":"39.60"},
 *       {"value_type":"BME280_pressure","value":"100520.25"},
 *       {"value_type":"samples","value":"580590"},
 *       {"value_type":"min_micro","value":"216"},
 *       {"value_type":"max_micro","value":"3503442"},
 *       {"value_type":"signal","value":"-38"}
 *     ]
 *   }
 *
 * Example out JSON:
 * [
 *   {"sensor_id": "sensorID", "value":"value" },
 *   ...
 * ]
 *
 */

const matchings = {
  p0: ['pm01', 'pm0', 'p1.0', 'p0'],
  p1: ['pm10', 'p10', 'p10', 'p1'],
  p2: ['pm25', 'pm2.5', 'p2.5', 'p25', 'p2'],
  temperature: ['temperatur'],
  humidity: ['rel. luftfeuchte', 'luftfeuchtigkeit', 'luftfeuchte'],
  pressure: ['luftdruck', 'druck'],
  signal: ['stärke', 'signal']
};

const findSensorId = function findSensorId (sensors, value_type) {
  // temperature, humidity and pressure values
  // are named either directly or with a prefix
  // separated by underscores. The last element
  // should be the the desired phenomenon
  let [ vt_sensortype, vt_phenomenon ] = value_type
    .toLowerCase()
    .split('_');

  // DHT11 and DHT22 sensors have no underscore prefix
  if (!vt_phenomenon && ['temperature', 'humidity'].includes(vt_sensortype)) {
    vt_phenomenon = vt_sensortype;
    vt_sensortype = 'dht';
  } else if (!vt_phenomenon && vt_sensortype === 'signal') {
    vt_phenomenon = vt_sensortype;
    vt_sensortype = 'wifi';
  }

  if (matchings[vt_phenomenon]) {
    let sensorId;

    for (const sensor of sensors) {
      const title = sensor.title.toLowerCase();
      const type = sensor.sensorType.toLowerCase();
      if (
        (title === vt_phenomenon || matchings[vt_phenomenon].includes(title) || matchings[vt_phenomenon].some(alias => title.includes(alias)))
        &&
        (type.startsWith(vt_sensortype))
      ) {
        sensorId = sensor._id.toString();
        break;
      }
    }

    return sensorId;
  }
};

const transformLuftdatenJson = function transformLuftdatenJson (json, sensors) {
  const outArray = [];

  for (const sdv of json.sensordatavalues) {
    const sensor_id = findSensorId(sensors, sdv.value_type);
    if (sensor_id) {
      outArray.push({ sensor_id, value: sdv.value });
    }
  }

  return outArray;
};

module.exports = {
  decodeMessage: function (message, { sensors }) {
    if (!sensors) {
      return Promise.reject(new Error('luftdaten handler needs sensors'));
    }

    if (message) {
      // try to decode the json, or if already a js object
      // use it as given
      let json;
      if (typeof message === 'object') {
        json = message;
      } else {
        try {
          json = JSON.parse(message);
        } catch (err) {
          log.error(err);

          return Promise.reject(err);
        }
      }

      if (typeof json !== 'undefined') {
        if (!json.sensordatavalues) {
          return Promise.reject(new Error('Invalid luftdaten json. Missing `sensordatavalues`'));
        }

        const transformedMeasurements = transformLuftdatenJson(json, sensors);
        if (transformedMeasurements.length === 0) {
          return Promise.reject(new Error('No applicable values found'));
        }

        return transformAndValidateMeasurements(transformedMeasurements);
      }
    }

    return Promise.reject(new Error('Cannot decode empty message (luftdaten decoder)'));
  }
};

<commit_msg>removed doubles<commit_after>'use strict';

const { transformAndValidateMeasurements } = require('./validators'),
  log = require('../log');

/**
 * Transforms JSON produced by Luftdaten.info devices to openSenseMap compatible JSON.
 * It looks for the items in the `sensordatavalues` array and tries to match its `value_type`
 * properties to a sensors title.
 *
 * Example in JSON:
 *   {
 *     "software_version": "NRZ-2016-058",
 *     "sensordatavalues": [
 *       {
 *         "value_type": "SDS_P1",
 *         "value": "5.38"
 *       },
 *       {
 *         "value_type": "SDS_P2",
 *         "value": "4.98"
 *       },
 *       {
 *         "value_type": "samples",
 *         "value": "316274"
 *       },
 *       {
 *         "value_type": "min_micro",
 *         "value": "162"
 *       },
 *       {
 *         "value_type": "max_micro",
 *         "value": "394014"
 *       },
 *       {
 *         "value_type": "signal",
 *         "value": "-59 dBm"
 *       }
 *     ]
 *   }
 * Example in JSON PMS7003 with BME280
 *   { "software_version": "NRZ-2017-099",
 *     "sensordatavalues":[
 *       {"value_type":"PMS_P0","value":"1.00"},
 *       {"value_type":"PMS_P1","value":"2.00"},
 *       {"value_type":"PMS_P2","value":"2.00"},
 *       {"value_type":"BME280_temperature","value":"24.98"},
 *       {"value_type":"BME280_humidity","value":"39.60"},
 *       {"value_type":"BME280_pressure","value":"100520.25"},
 *       {"value_type":"samples","value":"580590"},
 *       {"value_type":"min_micro","value":"216"},
 *       {"value_type":"max_micro","value":"3503442"},
 *       {"value_type":"signal","value":"-38"}
 *     ]
 *   }
 *
 * Example out JSON:
 * [
 *   {"sensor_id": "sensorID", "value":"value" },
 *   ...
 * ]
 *
 */

const matchings = {
  p0: ['pm01', 'pm0', 'p1.0', 'p0'],
  p1: ['pm10', 'p10', 'p1'],
  p2: ['pm25', 'pm2.5', 'p2.5', 'p25', 'p2'],
  temperature: ['temperatur'],
  humidity: ['rel. luftfeuchte', 'luftfeuchtigkeit', 'luftfeuchte'],
  pressure: ['luftdruck', 'druck'],
  signal: ['stärke', 'signal']
};

const findSensorId = function findSensorId (sensors, value_type) {
  // temperature, humidity and pressure values
  // are named either directly or with a prefix
  // separated by underscores. The last element
  // should be the the desired phenomenon
  let [ vt_sensortype, vt_phenomenon ] = value_type
    .toLowerCase()
    .split('_');

  // DHT11 and DHT22 sensors have no underscore prefix
  if (!vt_phenomenon && ['temperature', 'humidity'].includes(vt_sensortype)) {
    vt_phenomenon = vt_sensortype;
    vt_sensortype = 'dht';
  } else if (!vt_phenomenon && vt_sensortype === 'signal') {
    vt_phenomenon = vt_sensortype;
    vt_sensortype = 'wifi';
  }

  if (matchings[vt_phenomenon]) {
    let sensorId;

    for (const sensor of sensors) {
      const title = sensor.title.toLowerCase();
      const type = sensor.sensorType.toLowerCase();
      if (
        (title === vt_phenomenon || matchings[vt_phenomenon].includes(title) || matchings[vt_phenomenon].some(alias => title.includes(alias)))
        &&
        (type.startsWith(vt_sensortype))
      ) {
        sensorId = sensor._id.toString();
        break;
      }
    }

    return sensorId;
  }
};

const transformLuftdatenJson = function transformLuftdatenJson (json, sensors) {
  const outArray = [];

  for (const sdv of json.sensordatavalues) {
    const sensor_id = findSensorId(sensors, sdv.value_type);
    if (sensor_id) {
      outArray.push({ sensor_id, value: sdv.value });
    }
  }

  return outArray;
};

module.exports = {
  decodeMessage: function (message, { sensors }) {
    if (!sensors) {
      return Promise.reject(new Error('luftdaten handler needs sensors'));
    }

    if (message) {
      // try to decode the json, or if already a js object
      // use it as given
      let json;
      if (typeof message === 'object') {
        json = message;
      } else {
        try {
          json = JSON.parse(message);
        } catch (err) {
          log.error(err);

          return Promise.reject(err);
        }
      }

      if (typeof json !== 'undefined') {
        if (!json.sensordatavalues) {
          return Promise.reject(new Error('Invalid luftdaten json. Missing `sensordatavalues`'));
        }

        const transformedMeasurements = transformLuftdatenJson(json, sensors);
        if (transformedMeasurements.length === 0) {
          return Promise.reject(new Error('No applicable values found'));
        }

        return transformAndValidateMeasurements(transformedMeasurements);
      }
    }

    return Promise.reject(new Error('Cannot decode empty message (luftdaten decoder)'));
  }
};

<|endoftext|>


<commit_before>var postcss = require('postcss');

module.exports = postcss.plugin('postcss-host', function () {
    return function (css) {
        css.walkRules(function (rule) {
            var replaced = [];
            rule.selectors.forEach(function (selector) {
                var idx = selector.indexOf(':host');
                var hostCtxIdx = selector.indexOf(':host-context');
                if (idx === -1 && !(hostCtxIdx === -1 && hostCtxIdx !== idx)) {
                    replaced.push(':host ' + selector);
                } else {
                    replaced.push(selector);
                }
            });
            if ( replaced.length ) {
                rule.selectors = replaced;
            }
       });
    };
});
<commit_msg>missed an indentation space<commit_after>var postcss = require('postcss');

module.exports = postcss.plugin('postcss-host', function () {
    return function (css) {
        css.walkRules(function (rule) {
            var replaced = [];
            rule.selectors.forEach(function (selector) {
                var idx = selector.indexOf(':host');
                var hostCtxIdx = selector.indexOf(':host-context');
                if (idx === -1 && !(hostCtxIdx === -1 && hostCtxIdx !== idx)) {
                    replaced.push(':host ' + selector);
                } else {
                    replaced.push(selector);
                }
            });
            if ( replaced.length ) {
                rule.selectors = replaced;
            }
        });
    };
});
<|endoftext|>


<commit_before>var score = function(table) {
	var result = 0;
	var items = table.items;
	if (!items || items.length === 0) return result;
	for(var i = 0;i < items.length; i++) {
		result += cal(items[i]);
	}
	table.score = result;
	return result;
};

var level = function(table_type, table) {
	if (!table.score) {
			score(table);
		}
	if (table_type == 'caprini') {
		if (table.score <= 1) {
			table.level = '低危';
		} else if (table.score == 2) {
			table.level = '中危';
		} else if (table.score >=3 && table.score <=4) {
			table.level = '高危';
		} else {
			table.level = '极高危';
		}
	} else if (table_type === 'hss_left') {
		//todo
	} else if (table_type == 'hss_right') {
		//todo
	} else if (table_type === 'rapt') {
		if (table.score <= 5) {
			table.level='DVT低风险';
		} else if (table.score >5 && table.score <= 14) {
			table.level = 'DVT中风险';
		} else if (table.score > 14) {
			table.level = 'DVT高风险';
		}
	}
	return table.level;
};

var cal = function(item) {
	if (!item || !item.scored) return 0;
	if (!item.keys || item.keys.length === 0) return 0;
	var result = 0;
	for (var i = 0; i < item.keys.length; i++) {
		var val = item.value[i];
		var ratio = item.ratio || 1;
		if (item.type === 'input') {
			result += (val || 0) * ratio;
		} else {
			result += val ? (selectKey(item.keys) || 0) * ratio:0;
		}
	}
	return result;
};

var selectKey = function(key, keys) {
	for (var i=0; i<keys.length;i++) {
		var item = keys[i];
		if (item.key === key) {
			return item.value;
		}
	}
	return 0;
}

module.exports.score = score;
module.exports.level = level;<commit_msg>bug fixed<commit_after>var score = function(table) {
	var result = 0;
	var items = table.items;
	if (!items || items.length === 0) return result;
	for(var i = 0;i < items.length; i++) {
		result += cal(items[i]);
	}
	table.score = result;
	return result;
};

var level = function(table_type, table) {
	if (!table.score) {
			score(table);
		}
	if (table_type == 'caprini') {
		if (table.score <= 1) {
			table.level = '低危';
		} else if (table.score == 2) {
			table.level = '中危';
		} else if (table.score >=3 && table.score <=4) {
			table.level = '高危';
		} else {
			table.level = '极高危';
		}
	} else if (table_type === 'hss_left') {
		//todo
	} else if (table_type == 'hss_right') {
		//todo
	} else if (table_type === 'rapt') {
		if (table.score <= 5) {
			table.level='DVT低风险';
		} else if (table.score >5 && table.score <= 14) {
			table.level = 'DVT中风险';
		} else if (table.score > 14) {
			table.level = 'DVT高风险';
		}
	}
	return table.level;
};

var cal = function(item) {
	if (!item || !item.scored) return 0;
	if (!item.keys || item.keys.length === 0) return 0;
	var result = 0;
	for (var i = 0; i < item.keys.length; i++) {
		var val = item.value[i];
		var ratio = item.ratio || 1;
		if (item.type === 'input') {
			result += (val || 0) * ratio;
		} else {
			result += val ? (selectKey(val, item.keys) || 0) * ratio:0;
		}
	}
	return result;
};

var selectKey = function(key, keys) {
	for (var i=0; i<keys.length;i++) {
		var item = keys[i];
		if (item.key === key) {
			return item.value;
		}
	}
	return 0;
}

module.exports.score = score;
module.exports.level = level;<|endoftext|>


<commit_before>// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

'use strict';

module.exports = {
    endpoint: require('./lib/endpoint.js'),
    Https: require('./lib/https.js'),
    Message: require('./lib/message.js'),
    SimulatedHttps: require('./lib/https_simulated.js')
};<commit_msg>add ConnectionString to common.js<commit_after>// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

'use strict';

module.exports = {
    ConnectionString: require('./lib/connection_string.js'),
    endpoint: require('./lib/endpoint.js'),
    Https: require('./lib/https.js'),
    Message: require('./lib/message.js'),
    SimulatedHttps: require('./lib/https_simulated.js')
};<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// 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.

'use strict';

var test = require('tape');
var path = require('path');
var exec = require('child_process').exec;

test('running example1.js', function t(assert) {
    spawnExample('example1.js', onChild);

    function onChild(err, stdout, stderr) {
        assert.ifError(err);
        assert.equal(stderr, '');

        assert.ok(stdout.indexOf(
            'func1 responding with a small delay { arg2: \'arg 1\', arg3: \'arg 2\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'err res:  { ok: false, message: \'it failed\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'normal res: { arg2: \'result\', arg3: \'indeed it did\' }'
        ) >= 0);

        assert.end();
    }
});

test('running example2.js', function t(assert) {
    spawnExample('example2.js', onChild);

    function onChild(err, stdout, stderr) {
        assert.ifError(err);
        assert.equal(stderr, '');

        assert.ok(stdout.indexOf(
            'server got ping req from { remoteAddr: \'127.0.0.1:4041\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'ping res from server { arg2: \'pong\', arg3: \'\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'client got ping req from { remoteAddr: \'127.0.0.1:4040\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'ping res from client { arg2: \'pong\', arg3: \'\' }'
        ) >= 0);

        assert.end();
    }
});

test('running as_example.js', function t(assert) {
    spawnExample('as_example1.js', onChild);

    function onChild(err, stdout, stderr) {
        assert.ifError(err);
        assert.equal(stderr, '');

        assert.ok(stdout.indexOf(
            'got resp { ok: true,'
        ) >= 0);
        assert.ok(stdout.indexOf(
            '  head: { head: \'object\' },'
        ) >= 0);
        assert.ok(stdout.indexOf(
            '  body: { body: \'object\' },'
        ) >= 0);
        assert.ok(stdout.indexOf(
            '  headers: { as: \'json\' } }'
        ) >= 0);

        assert.end();
    }
});

test('running send_test.js', function t(assert) {
    spawnExample('send_test.js', onChild);

    function onChild(err, stdout, stderr) {
        assert.ifError(err);
        assert.equal(stderr, '');
        [
            'server got ping req from 127.0.0.1:',
            'func 3 starting response timer',
            'func 1 responding immediately',
            'ping res from client: pong',
            '1 fast res: arg2=result arg3=indeed it did',
            'client got ping req from 127.0.0.1:',
            'ping res server: pong',
            '2 slow res: err=request timed out after',
            'func 3 starting response timer',
            'func 3 starting response timer',
            'func 3 responding now',
            '3 slow res: err=request timed out after',
            '4 slow res: err=request timed out after',
            'func 3 responding now',
            'func 3 responding now'
        ].forEach(function each(str) {
            assert.ok(stdout.indexOf(
                str
            ) >= 0);
        });

        assert.end();
    }
});

function spawnExample(exampleName, cb) {
    var file = path.join(__dirname, '..', 'examples', exampleName);

    console.log(file);
    exec('node ' + file, cb);
}
<commit_msg>remove console.log<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// 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.

'use strict';

var test = require('tape');
var path = require('path');
var exec = require('child_process').exec;

test('running example1.js', function t(assert) {
    spawnExample('example1.js', onChild);

    function onChild(err, stdout, stderr) {
        assert.ifError(err);
        assert.equal(stderr, '');

        assert.ok(stdout.indexOf(
            'func1 responding with a small delay { arg2: \'arg 1\', arg3: \'arg 2\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'err res:  { ok: false, message: \'it failed\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'normal res: { arg2: \'result\', arg3: \'indeed it did\' }'
        ) >= 0);

        assert.end();
    }
});

test('running example2.js', function t(assert) {
    spawnExample('example2.js', onChild);

    function onChild(err, stdout, stderr) {
        assert.ifError(err);
        assert.equal(stderr, '');

        assert.ok(stdout.indexOf(
            'server got ping req from { remoteAddr: \'127.0.0.1:4041\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'ping res from server { arg2: \'pong\', arg3: \'\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'client got ping req from { remoteAddr: \'127.0.0.1:4040\' }'
        ) >= 0);
        assert.ok(stdout.indexOf(
            'ping res from client { arg2: \'pong\', arg3: \'\' }'
        ) >= 0);

        assert.end();
    }
});

test('running as_example.js', function t(assert) {
    spawnExample('as_example1.js', onChild);

    function onChild(err, stdout, stderr) {
        assert.ifError(err);
        assert.equal(stderr, '');

        assert.ok(stdout.indexOf(
            'got resp { ok: true,'
        ) >= 0);
        assert.ok(stdout.indexOf(
            '  head: { head: \'object\' },'
        ) >= 0);
        assert.ok(stdout.indexOf(
            '  body: { body: \'object\' },'
        ) >= 0);
        assert.ok(stdout.indexOf(
            '  headers: { as: \'json\' } }'
        ) >= 0);

        assert.end();
    }
});

test('running send_test.js', function t(assert) {
    spawnExample('send_test.js', onChild);

    function onChild(err, stdout, stderr) {
        assert.ifError(err);
        assert.equal(stderr, '');
        [
            'server got ping req from 127.0.0.1:',
            'func 3 starting response timer',
            'func 1 responding immediately',
            'ping res from client: pong',
            '1 fast res: arg2=result arg3=indeed it did',
            'client got ping req from 127.0.0.1:',
            'ping res server: pong',
            '2 slow res: err=request timed out after',
            'func 3 starting response timer',
            'func 3 starting response timer',
            'func 3 responding now',
            '3 slow res: err=request timed out after',
            '4 slow res: err=request timed out after',
            'func 3 responding now',
            'func 3 responding now'
        ].forEach(function each(str) {
            assert.ok(stdout.indexOf(
                str
            ) >= 0);
        });

        assert.end();
    }
});

function spawnExample(exampleName, cb) {
    var file = path.join(__dirname, '..', 'examples', exampleName);

    exec('node ' + file, cb);
}
<|endoftext|>


<commit_before>var stream = require('stream');
var tar = require('tar');
var mkdirp = require('mkdirp');
var URL = require('url');
var Path = require('path');
var http = require('http');

module.exports = function(notemplate) {
	notemplate.on('ready', function(view, opts) {
		view.archiveCache = {};
	});
	notemplate.on('request', function(view, method, url, status, response) {
		// cache request and response to be able to quickly tar a file
		if (method != "GET") return;
		view.archiveCache[url] = {status: status, response: response};
	});
	notemplate.on('render', function(view, opts) {
		if (!opts.archive) return;
		var sel = (typeof opts.archive == "string" ? opts.archive : 'link,script,img,xhr')
			.split(',').map(Function.prototype.call, String.prototype.trim);
		var xhr = sel.indexOf('xhr');
		if (xhr >= 0) {
			sel.splice(xhr, 1);
			sel = sel.join(',');
			xhr = true;
		}

		var win = view.instance.window;
		var root = opts.root || Path.dirname(win.location.pathname);
		if (root[0] == '/') root = root.substring(1);
		var index = Path.basename(win.location.pathname);
		var match = opts.match || /.*/;
		var tarStream = opts.archiveStream || tar.Pack({ noProprietary: true });

		view.instance.output = tarStream;

		var items = win.$(sel).toArray();
		if (xhr) {
			for (var url in view.archiveCache) {
				var link = win.document.createElement('link');
				link.setAttribute('href', url);
				items.push(link);
			}
		}
		var dirs = {};
		var mangler = opts.mangler || defaultUriMangler;
		mangler.request = function(uri, cb) {
			var cached = view.archiveCache[uri];
			if (cached) {
				if (cached.status < 200 || cached.status >= 300) {
					cb(cached.status);
				} else {
					cb(null, {uri:uri, data:cached.response});
				}
			}	else {
				request(uri, function(err, data) {
					if (!err) {
						view.archiveCache[uri] = {status: 200, response: data};
					} else {
						var status = parseInt(err);
						if (isNaN(status)) status = 404;
						view.archiveCache[uri] = {status: status, response: data};
					}
					cb(err, data);
				});
			}
		};

		(function processItem(err) {
			if (err) console.error(err);
			var item = items.shift();
			if (!item) return finish();
			archive(item, match, mangler, root, tarStream, processItem);
		})();

		function finish() {
			appendToStream(tarStream, view.instance.toString(), index, root);
			tarStream.end();
			view.archiveCache = null;
		}
	});
};

function defaultUriMangler(uri, cb) {
	cb(null, uri);
}

function getPathname(uri) {
	var pathname = URL.parse(uri).pathname;
	if (pathname && pathname[0] == '/') pathname = pathname.substring(1);
	return pathname;
}

function request(uri, cb) {
	http.get(uri, function(res) {
		if (res.statusCode < 200 || res.statusCode > 400) return cb(res.statusCode);
		var chunks = [];
    var bufLen = 0;
    res.on('data', function(chunk) {
			chunks.push(chunk);
			bufLen += chunk.length;
    });
    res.on('end', function() {
			var buf;
			if (chunks.length) {
				if (Buffer.isBuffer(chunks[0])) {
					buf = new Buffer(bufLen);
          var curLen = 0;
          chunks.forEach(function(chunk) {
            chunk.copy(buf, curLen, 0, chunk.length);
            curLen += chunk.length;
          });
				} else {
					buf = chunks.join('');
				}
			}
			cb(null, buf);
		});
	}).on('error', function(err) {
		console.error(err);
		cb(err);
	});
}

function ensureData(obj, requester, cb) {
	if (obj.data) return cb(null, obj);
	if (!obj.uri) return cb(null, obj);
	requester(obj.uri, function(err, buf) {
		if (!err && buf) obj.data = buf;
		cb(err, obj);
	});
}

function archive(elem, match, mangler, root, tarStream, done) {
	var src = elem.getAttribute('src');
	var href = elem.getAttribute('href');
	var uri = src || href; // jsdom has window.location, so it should build the absolute url
	if (!uri || !match.test(uri)) return done();
	uri = elem._ownerDocument.parentWindow.resourceLoader.resolve(elem._ownerDocument, uri);

	tarStream.pause();
	function finish(err) {
		tarStream.resume();
		done(err);
	}
	function mcb(err, mangled) {
		if (err) return finish(err);
		if (!Array.isArray(mangled)) mangled = [mangled];
		var count = mangled.length;
		if (count > 0) mangled.forEach(function(val, i) {
			if (!val || typeof val == "string") val = {uri: val};
			if (val.uri && !val.path) val.path = getPathname(val.uri);
			if (i == 0) {
				if (!val.uri && !val.href && !val.path) {
					if (elem.parentNode) elem.parentNode.removeChild(elem);
				} else {
					if (!val.href) val.href = val.path;
					if (src) elem.setAttribute('src', val.href);
					else if (href) elem.setAttribute('href', val.href);
				}
			}
			ensureData(val, mangler.request, function(err, obj) {
				if (obj.path != null && obj.data != null) appendToStream(tarStream, obj.data, obj.path, root);
				if (--count == 0) finish(err);
			});
		}); else finish();
	}
	mcb.request = mangler.request;
	mangler(uri, mcb);
}

function appendToStream(stream, data, path, root) {
	if (typeof stream.add != "function") {
		stream.write(data);
		return;
	}
	if (typeof data == "string") {
		// tar only understands buffer.length
		data = new Buffer(data);
	}
	var entry = new stream.Readable();
	var allpushed = false;
	entry._read = function(s) {
		if (allpushed) {
			this.push(null);
		} else {
			this.push(data);
			allpushed = true;
		}
	};
	entry.props = {
		type: "File",
		mode: 0644,
		mtime: Date.now() / 1000,
		size: data.length
	};
	entry.path = Path.join(root, path);
	entry.root = root;
	stream.add(entry);
}
<commit_msg>Fix stream variable name<commit_after>var stream = require('stream');
var tar = require('tar');
var mkdirp = require('mkdirp');
var URL = require('url');
var Path = require('path');
var http = require('http');

module.exports = function(notemplate) {
	notemplate.on('ready', function(view, opts) {
		view.archiveCache = {};
	});
	notemplate.on('request', function(view, method, url, status, response) {
		// cache request and response to be able to quickly tar a file
		if (method != "GET") return;
		view.archiveCache[url] = {status: status, response: response};
	});
	notemplate.on('render', function(view, opts) {
		if (!opts.archive) return;
		var sel = (typeof opts.archive == "string" ? opts.archive : 'link,script,img,xhr')
			.split(',').map(Function.prototype.call, String.prototype.trim);
		var xhr = sel.indexOf('xhr');
		if (xhr >= 0) {
			sel.splice(xhr, 1);
			sel = sel.join(',');
			xhr = true;
		}

		var win = view.instance.window;
		var root = opts.root || Path.dirname(win.location.pathname);
		if (root[0] == '/') root = root.substring(1);
		var index = Path.basename(win.location.pathname);
		var match = opts.match || /.*/;
		var tarStream = opts.archiveStream || tar.Pack({ noProprietary: true });

		view.instance.output = tarStream;

		var items = win.$(sel).toArray();
		if (xhr) {
			for (var url in view.archiveCache) {
				var link = win.document.createElement('link');
				link.setAttribute('href', url);
				items.push(link);
			}
		}
		var dirs = {};
		var mangler = opts.mangler || defaultUriMangler;
		mangler.request = function(uri, cb) {
			var cached = view.archiveCache[uri];
			if (cached) {
				if (cached.status < 200 || cached.status >= 300) {
					cb(cached.status);
				} else {
					cb(null, {uri:uri, data:cached.response});
				}
			}	else {
				request(uri, function(err, data) {
					if (!err) {
						view.archiveCache[uri] = {status: 200, response: data};
					} else {
						var status = parseInt(err);
						if (isNaN(status)) status = 404;
						view.archiveCache[uri] = {status: status, response: data};
					}
					cb(err, data);
				});
			}
		};

		(function processItem(err) {
			if (err) console.error(err);
			var item = items.shift();
			if (!item) return finish();
			archive(item, match, mangler, root, tarStream, processItem);
		})();

		function finish() {
			appendToStream(tarStream, view.instance.toString(), index, root);
			tarStream.end();
			view.archiveCache = null;
		}
	});
};

function defaultUriMangler(uri, cb) {
	cb(null, uri);
}

function getPathname(uri) {
	var pathname = URL.parse(uri).pathname;
	if (pathname && pathname[0] == '/') pathname = pathname.substring(1);
	return pathname;
}

function request(uri, cb) {
	http.get(uri, function(res) {
		if (res.statusCode < 200 || res.statusCode > 400) return cb(res.statusCode);
		var chunks = [];
    var bufLen = 0;
    res.on('data', function(chunk) {
			chunks.push(chunk);
			bufLen += chunk.length;
    });
    res.on('end', function() {
			var buf;
			if (chunks.length) {
				if (Buffer.isBuffer(chunks[0])) {
					buf = new Buffer(bufLen);
          var curLen = 0;
          chunks.forEach(function(chunk) {
            chunk.copy(buf, curLen, 0, chunk.length);
            curLen += chunk.length;
          });
				} else {
					buf = chunks.join('');
				}
			}
			cb(null, buf);
		});
	}).on('error', function(err) {
		console.error(err);
		cb(err);
	});
}

function ensureData(obj, requester, cb) {
	if (obj.data) return cb(null, obj);
	if (!obj.uri) return cb(null, obj);
	requester(obj.uri, function(err, buf) {
		if (!err && buf) obj.data = buf;
		cb(err, obj);
	});
}

function archive(elem, match, mangler, root, tarStream, done) {
	var src = elem.getAttribute('src');
	var href = elem.getAttribute('href');
	var uri = src || href; // jsdom has window.location, so it should build the absolute url
	if (!uri || !match.test(uri)) return done();
	uri = elem._ownerDocument.parentWindow.resourceLoader.resolve(elem._ownerDocument, uri);

	tarStream.pause();
	function finish(err) {
		tarStream.resume();
		done(err);
	}
	function mcb(err, mangled) {
		if (err) return finish(err);
		if (!Array.isArray(mangled)) mangled = [mangled];
		var count = mangled.length;
		if (count > 0) mangled.forEach(function(val, i) {
			if (!val || typeof val == "string") val = {uri: val};
			if (val.uri && !val.path) val.path = getPathname(val.uri);
			if (i == 0) {
				if (!val.uri && !val.href && !val.path) {
					if (elem.parentNode) elem.parentNode.removeChild(elem);
				} else {
					if (!val.href) val.href = val.path;
					if (src) elem.setAttribute('src', val.href);
					else if (href) elem.setAttribute('href', val.href);
				}
			}
			ensureData(val, mangler.request, function(err, obj) {
				if (obj.path != null && obj.data != null) appendToStream(tarStream, obj.data, obj.path, root);
				if (--count == 0) finish(err);
			});
		}); else finish();
	}
	mcb.request = mangler.request;
	mangler(uri, mcb);
}

function appendToStream(wStream, data, path, root) {
	if (typeof wStream.add != "function") {
		wStream.write(data);
		return;
	}
	if (typeof data == "string") {
		// tar only understands buffer.length
		data = new Buffer(data);
	}
	var entry = new stream.Readable();
	var allpushed = false;
	entry._read = function(s) {
		if (allpushed) {
			this.push(null);
		} else {
			this.push(data);
			allpushed = true;
		}
	};
	entry.props = {
		type: "File",
		mode: 0644,
		mtime: Date.now() / 1000,
		size: data.length
	};
	entry.path = Path.join(root, path);
	entry.root = root;
	wStream.add(entry);
}
<|endoftext|>


<commit_before>"use strict";
var Email = require('email').Email,
  handlebars = require('handlebars');

var MAILING_LIST,
  SENDER_EMAIL = "plh@w3.org";

if (process.env.NODE_ENV == 'production') {
  MAILING_LIST = "null@noreply.w3.org";
} else {
  MAILING_LIST = "plh@w3.org";
}

var subjectTemplate = handlebars.compile("{{ status }}: {{ title }}"),
  fromTemplate = handlebars.compile("{{ name }} <{{email}}>"),
  bodyTemplate = handlebars.compile("{{ title }}\n\n{{ href }}{{ feedbackDate }}\n\n{{ abstract }}\n\n{{ abstract }}\n\nStatus of the Document\n\n{{ sotd }}");

function notifyWideReview(spec) {
  console.log("Notification for wide review: " + spec.href);
  var context = {
    title: spec.title,
    date: spec.date,
    href: spec.href,
    status: spec.status,
    sotd: spec.sotd,
    abstract: spec.abstract,
    feedbackDate: (spec.feedbackDate === undefined) ?
      "" : "\\nnfeedback due by: " + spec.feedbackDate,
    timestamp: Date.now()
  };

  var msg = new Email({
    messageId: context.href,
    to: [MAILING_LIST],
    body: bodyTemplate(context),
    subject: subjectTemplate(context),
    from: "Notifier <" + SENDER_EMAIL + ">"
  });

  msg.send(function (err) {
    console.log("Failure to notify for wide review for " + spec.href);
    console.log(err);
    console.log(err.stack);
  });
}

exports.notifyWideReview = notifyWideReview;
<commit_msg>Fixed email addresses and abstract<commit_after>"use strict";
var Email = require('email').Email,
  handlebars = require('handlebars');

var MAILING_LIST, SENDER_EMAIL;

if (process.env.NODE_ENV == 'production') {
  MAILING_LIST = "public-review-announce@w3.org";
  SENDER_EMAIL = "webreq@w3.org";
} else {
  MAILING_LIST = "plh@w3.org";
  SENDER_EMAIL = "plh@w3.org";
}

var subjectTemplate = handlebars.compile("{{ status }}: {{ title }}"),
  fromTemplate = handlebars.compile("{{ name }} <{{email}}>"),
  bodyTemplate = handlebars.compile("{{ title }}\n\n{{ href }}{{ feedbackDate }}\n\nAbstract\n\n{{ abstract }}\n\nStatus of the Document\n\n{{ sotd }}");

function notifyWideReview(spec) {
  console.log("Notification for wide review: " + spec.href);
  var context = {
    title: spec.title,
    date: spec.date,
    href: spec.href,
    status: spec.status,
    sotd: spec.sotd,
    abstract: spec.abstract,
    feedbackDate: (spec.feedbackDate === undefined) ?
      "" : "\\nnfeedback due by: " + spec.feedbackDate,
    timestamp: Date.now()
  };

  var msg = new Email({
    messageId: context.href,
    to: [MAILING_LIST],
    body: bodyTemplate(context),
    subject: subjectTemplate(context),
    from: "Notifier <" + SENDER_EMAIL + ">"
  });

  msg.send(function (err) {
    console.log("Failure to notify for wide review for " + spec.href);
    console.log(err);
    console.log(err.stack);
  });
}

exports.notifyWideReview = notifyWideReview;
<|endoftext|>