<commit_before>/**
 * angular-locker
 *
 * A simple & configurable abstraction for local/session storage in angular projects.
 *
 * @link https://github.com/tymondesigns/angular-locker
 * @author Sean Tymon @tymondesigns
 * @license MIT License, http://www.opensource.org/licenses/MIT
 */

(function(window, angular, undefined) {

    'use strict';

    angular.module('angular-locker', [])

    .config(function ($provide) {

        /**
         * Decorate the exception handler
         */
        $provide.decorator('$exceptionHandler', ['$log', '$delegate', function($log, $delegate) {
            return function(exception, cause) {
                $log.debug('[angular-locker] - ' + exception.message);
                $delegate(exception, cause);
            };
        }]);
    })

    .provider('locker', function () {

        /**
         * If value is a function then execute, otherwise return
         *
         * @param  {Mixed}  value
         * @return {Mixed}
         */
        var _value = function (value) {
            return angular.isFunction(value) ? value() : value;
        };

        /**
         * Get the key of an object by the value
         *
         * @param  {Object}  obj
         * @param  {Mixed}   value
         * @return {String}
         */
        var _keyByVal = function (obj, value) {
            return Object.keys(obj).filter(function (key) { return obj[key] === value; })[0];
        };

        /**
         * Set the default driver and namespace
         *
         * @type {Object}
         */
        var defaults = {
            driver: 'local',
            namespace: 'locker',
            eventsEnabled: true
        };

        return {

            /**
             * Allow setting of default storage driver via `lockerProvider`
             * e.g. lockerProvider.setDefaultDriver('session');
             *
             * @param {String}  driver
             */
            setDefaultDriver: function (driver) {
                defaults.driver = _value(driver);
                return this;
            },

            /**
             * Get the default driver
             */
            getDefaultDriver: function () {
                return defaults.driver;
            },

            /**
             * Allow setting of default namespace via `lockerProvider`
             * e.g. lockerProvider.setDefaultNamespace('myAppName');
             *
             * @param {String}  namespace
             */
            setDefaultNamespace: function (namespace) {
                defaults.namespace = _value(namespace);
                return this;
            },

            /**
             * Get the default namespace
             */
            getDefaultNamespace: function () {
                return defaults.namespace;
            },

            /**
             * Set whether the events are enabled
             *
             * @param {Boolean}  enabled
             */
            setEventsEnabled: function (enabled) {
                defaults.eventsEnabled = enabled;
                return this;
            },

            /**
             * Get whether the events are enabled
             */
            getEventsEnabled: function () {
                return defaults.eventsEnabled;
            },

            /**
             * The locker service
             */
            $get: ['$window', '$rootScope', '$parse', '$log', function ($window, $rootScope, $parse, $log) {

                /**
                 * Define the Locker class
                 *
                 * @param {Storage}  driver
                 * @param {String}   namespace
                 */
                function Locker (driver, namespace, eventsEnabled) {

                    /**
                     * @type {Object}
                     */
                    this._registeredDrivers = {
                        local: $window.localStorage,
                        session: $window.sessionStorage
                    };

                    /**
                     * Get the Storage instance from the key
                     *
                     * @param  {String}  driver
                     * @return {Storage}
                     */
                    this._resolveDriver = function (driver) {
                        if (! this._registeredDrivers.hasOwnProperty(driver)) {
                            throw new Error('The driver "' + driver + '" was not found. Defaulting to local.');
                        }

                        // fallback gracefully to localStorage
                        return this._registeredDrivers[driver] || this._registeredDrivers.local;
                    };

                    /**
                     * Get the driver key (local/session) by the Storage instance
                     *
                     * @param  {Storage}  driver
                     * @return {String}
                     */
                    this._deriveDriver = function (driver) {
                        return _keyByVal(this._registeredDrivers, driver);
                    };

                    /**
                     * @type {Storage}
                     */
                    this._driver = this._resolveDriver(driver);

                    /**
                     * @type {String}
                     */
                    this._namespace = namespace;

                    /**
                     * @type {Boolean}
                     */
                    this._eventsEnabled = eventsEnabled;

                    /**
                     * Check browser support
                     *
                     * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47
                     * @param  {String}  driver
                     * @return {Boolean}
                     */
                    this._checkSupport = function (driver) {
                        if (typeof this._supported === 'undefined') {
                            var l = 'l';
                            try {
                                this._resolveDriver(driver || 'local').setItem(l, l);
                                this._resolveDriver(driver || 'local').removeItem(l);
                                this._supported = true;
                            } catch (e) {
                                this._supported = false;
                            }
                        }

                        return this._supported;
                    };

                    /**
                     * @type {String}
                     */
                    this._separator = '.';

                    /**
                     * @type {Object}
                     */
                    this._watchers = {};

                    /**
                     * Build the storage key from the namspace
                     *
                     * @param  {String}  key
                     * @return {String}
                     */
                    this._getPrefix = function (key) {
                        return this._namespace + this._separator + key;
                    };

                    /**
                     * Try to encode value as json, or just return the value upon failure
                     *
                     * @param  {Mixed}  value
                     * @return {Mixed}
                     */
                    this._serialize = function (value) {
                        try {
                            return angular.toJson(value);
                        } catch (e) {
                            return value;
                        }
                    };

                    /**
                     * Try to parse value as json, if it fails then it probably isn't json so just return it
                     *
                     * @param  {String}  value
                     * @return {Object|String}
                     */
                    this._unserialize = function (value) {
                        try {
                            return angular.fromJson(value);
                        } catch (e) {
                            return value;
                        }
                    };

                    /**
                     * Trigger an event
                     *
                     * @param  {String} name
                     * @param  {Object} payload
                     * @return {void}
                     */
                    this._event = function (name, payload) {
                        if (this._eventsEnabled) {
                            $rootScope.$emit(name, angular.extend(payload, {
                                driver: this._deriveDriver(this._driver),
                                namespace: this._namespace,
                            }));
                        }
                    };

                    /**
                     * Add to storage
                     *
                     * @param {String}  key
                     * @param {Mixed}  value
                     */
                    this._setItem = function (key, value) {
                        if (this._checkSupport()) {
                            try {
                                var oldVal = this._getItem(key);
                                this._driver.setItem(this._getPrefix(key), this._serialize(value));
                                if (this._exists(key) && ! angular.equals(oldVal, value)) {
                                    this._event('locker.item.updated', { key: key, oldValue: oldVal, newValue: value });
                                } else {
                                    this._event('locker.item.added', { key: key, value: value });
                                }
                            } catch (e) {
                                if (['QUOTA_EXCEEDED_ERR', 'NS_ERROR_DOM_QUOTA_REACHED', 'QuotaExceededError'].indexOf(e.name) !== -1) {
                                    throw new Error('Your browser storage quota has been exceeded');
                                } else {
                                    throw new Error('Could not add item with key "' + key + '"');
                                }
                            }
                        } else {
                            throw new Error('Your browser does not support localStorage');
                        }
                    };

                    /**
                     * Get from storage
                     *
                     * @param  {String}  key
                     * @return {Mixed}
                     */
                    this._getItem = function (key) {
                        if (this._checkSupport()) {
                            return this._unserialize(this._driver.getItem(this._getPrefix(key)));
                        } else {
                            throw new Error('Your browser does not support localStorage');
                        }
                    };

                    /**
                     * Exists in storage
                     *
                     * @param  {String}  key
                     * @return {Boolean}
                     */
                    this._exists = function (key) {
                        if (this._checkSupport()) {
                            return this._driver.hasOwnProperty(this._getPrefix(_value(key)));
                        } else {
                            throw new Error('Your browser does not support localStorage');
                        }
                    };

                    /**
                     * Remove from storage
                     *
                     * @param  {String}  key
                     * @return {Boolean}
                     */
                    this._removeItem = function (key) {
                        if (this._checkSupport()) {
                            if (! this._exists(key)) return false;
                            this._driver.removeItem(this._getPrefix(key));

                            this._event('locker.item.forgotten', { key: key });

                            return true;
                        } else {
                            throw new Error('Your browser does not support localStorage');
                        }
                    };
                }

                /**
                 * Define the public api
                 *
                 * @type {Object}
                 */
                Locker.prototype = {

                    /**
                     * Add a new item to storage (even if it already exists)
                     *
                     * @param  {Mixed}  key
                     * @param  {Mixed}  value
                     * @return {self}
                     */
                    put: function (key, value) {
                        if (! key) return false;
                        key = _value(key);

                        if (angular.isObject(key)) {
                            angular.forEach(key, function (value, key) {
                                this._setItem(key, value);
                            }, this);
                        } else {
                            if (! angular.isDefined(value)) return false;
                            this._setItem(key, _value(value));
                        }

                        return this;
                    },

                    /**
                     * Add an item to storage if it doesn't already exist
                     *
                     * @param  {Mixed}  key
                     * @param  {Mixed}  value
                     * @return {Boolean}
                     */
                    add: function (key, value) {
                        if (! this.has(key)) {
                            this.put(key, value);
                            return true;
                        }

                        return false;
                    },

                    /**
                     * Retrieve the specified item from storage
                     *
                     * @param  {String|Array}  key
                     * @param  {Mixed}  def
                     * @return {Mixed}
                     */
                    get: function (key, def) {
                        if (angular.isArray(key)) {
                            var items = {};
                            angular.forEach(key, function (k) {
                                if (this.has(k)) items[k] = this._getItem(k);
                            }, this);

                            return items;
                        }

                        if (! this.has(key)) return arguments.length === 2 ? def : void 0;

                        return this._getItem(key);
                    },

                    /**
                     * Determine whether the item exists in storage
                     *
                     * @param  {String|Function}  key
                     * @return {Boolean}
                     */
                    has: function (key) {
                        return this._exists(key);
                    },

                    /**
                     * Remove specified item(s) from storage
                     *
                     * @param  {Mixed}  key
                     * @return {Object}
                     */
                    forget: function (key) {
                        key = _value(key);

                        if (angular.isArray(key)) {
                            angular.forEach(key, function (key) {
                                this._removeItem(key);
                            }, this);
                        } else {
                            this._removeItem(key);
                        }

                        return this;
                    },

                    /**
                     * Retrieve the specified item from storage and then remove it
                     *
                     * @param  {String|Array}  key
                     * @param  {Mixed}  def
                     * @return {Mixed}
                     */
                    pull: function (key, def) {
                        var value = this.get(key, def);
                        this.forget(key);

                        return value;
                    },

                    /**
                     * Return all items in storage within the current namespace
                     *
                     * @return {Object}
                     */
                    all: function () {
                        var items = {};
                        angular.forEach(this._driver, function (value, key) {
                            var split = key.split(this._separator);
                            if (split.length > 1 && split[0] === this._namespace) {
                                split.splice(0, 1);
                                key = split.join(this._separator);
                            }
                            if (this.has(key)) items[key] = this.get(key);
                        }, this);

                        return items;
                    },

                    /**
                     * Remove all items set within the current namespace
                     *
                     * @return {self}
                     */
                    clean: function () {
                        this.forget(Object.keys(this.all()));

                        return this;
                    },

                    /**
                     * Empty the current storage driver completely. careful now.
                     *
                     * @return {self}
                     */
                    empty: function () {
                        this._driver.clear();

                        return this;
                    },

                    /**
                     * Get the total number of items within the current namespace
                     *
                     * @return {Integer}
                     */
                    count: function () {
                        return Object.keys(this.all()).length;
                    },

                    /**
                     * Bind a storage key to a $scope property
                     *
                     * @param  {Object}  $scope
                     * @param  {String}  key
                     * @param  {Mixed}   def
                     * @return {self}
                     */
                    bind: function ($scope, key, def) {
                        if (angular.isUndefined( $scope.$eval(key) )) {
                            $parse(key).assign($scope, this.get(key, def));
                        }

                        var self = this;
                        this._watchers[key + $scope.$id] = $scope.$watch(key, function (newVal) {
                            if (angular.isDefined(newVal)) self.put(key, newVal);
                        }, angular.isObject($scope[key]));

                        return this;
                    },

                    /**
                     * Unbind a storage key from a $scope property
                     *
                     * @param  {Object}  $scope
                     * @param  {String}  key
                     * @return {self}
                     */
                    unbind: function ($scope, key) {
                        $parse(key).assign($scope, null);
                        this.forget(key);
                        if (this._watchers[key + $scope]) {
                            delete this._watchers[key + $scope];
                        }

                        return this;
                    },

                    /**
                     * Set the storage driver on a new instance to enable overriding defaults
                     *
                     * @param  {String}  driver
                     * @return {self}
                     */
                    driver: function (driver) {
                        return new Locker(driver, this._namespace, this._eventsEnabled);
                    },

                    /**
                     * Get the currently set driver
                     *
                     * @return {Storage}
                     */
                    getDriver: function () {
                        return this._driver;
                    },

                    /**
                     * Set the namespace on a new instance to enable overriding defaults
                     *
                     * @param  {String}  namespace
                     * @return {self}
                     */
                    namespace: function (namespace) {
                        return new Locker(this._deriveDriver(this._driver), namespace, this._eventsEnabled);
                    },

                    /**
                     * Get the currently set namespace
                     *
                     * @return {String}
                     */
                    getNamespace: function () {
                        return this._namespace;
                    },

                    /**
                     * Check browser support
                     *
                     * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47
                     * @param  {String}  driver
                     * @return {Boolean}
                     */
                    supported: function (driver) {
                        return this._checkSupport(driver);
                    }
                };

                /**
                 * Create the driver instances
                 *
                 * @type {Object}
                 */
                var drivers = {
                    local: new Locker('local', defaults.namespace, defaults.eventsEnabled),
                    session: new Locker('session', defaults.namespace, defaults.eventsEnabled)
                };

                return drivers[defaults.driver];
            }]
        };

    });

})(window, window.angular);
<commit_msg>remove $log<commit_after>/**
 * angular-locker
 *
 * A simple & configurable abstraction for local/session storage in angular projects.
 *
 * @link https://github.com/tymondesigns/angular-locker
 * @author Sean Tymon @tymondesigns
 * @license MIT License, http://www.opensource.org/licenses/MIT
 */

(function(window, angular, undefined) {

    'use strict';

    angular.module('angular-locker', [])

    .config(function ($provide) {

        /**
         * Decorate the exception handler
         */
        $provide.decorator('$exceptionHandler', ['$log', '$delegate', function($log, $delegate) {
            return function(exception, cause) {
                $log.debug('[angular-locker] - ' + exception.message);
                $delegate(exception, cause);
            };
        }]);
    })

    .provider('locker', function () {

        /**
         * If value is a function then execute, otherwise return
         *
         * @param  {Mixed}  value
         * @return {Mixed}
         */
        var _value = function (value) {
            return angular.isFunction(value) ? value() : value;
        };

        /**
         * Get the key of an object by the value
         *
         * @param  {Object}  obj
         * @param  {Mixed}   value
         * @return {String}
         */
        var _keyByVal = function (obj, value) {
            return Object.keys(obj).filter(function (key) { return obj[key] === value; })[0];
        };

        /**
         * Set the default driver and namespace
         *
         * @type {Object}
         */
        var defaults = {
            driver: 'local',
            namespace: 'locker',
            eventsEnabled: true
        };

        return {

            /**
             * Allow setting of default storage driver via `lockerProvider`
             * e.g. lockerProvider.setDefaultDriver('session');
             *
             * @param {String}  driver
             */
            setDefaultDriver: function (driver) {
                defaults.driver = _value(driver);
                return this;
            },

            /**
             * Get the default driver
             */
            getDefaultDriver: function () {
                return defaults.driver;
            },

            /**
             * Allow setting of default namespace via `lockerProvider`
             * e.g. lockerProvider.setDefaultNamespace('myAppName');
             *
             * @param {String}  namespace
             */
            setDefaultNamespace: function (namespace) {
                defaults.namespace = _value(namespace);
                return this;
            },

            /**
             * Get the default namespace
             */
            getDefaultNamespace: function () {
                return defaults.namespace;
            },

            /**
             * Set whether the events are enabled
             *
             * @param {Boolean}  enabled
             */
            setEventsEnabled: function (enabled) {
                defaults.eventsEnabled = enabled;
                return this;
            },

            /**
             * Get whether the events are enabled
             */
            getEventsEnabled: function () {
                return defaults.eventsEnabled;
            },

            /**
             * The locker service
             */
            $get: ['$window', '$rootScope', '$parse', function ($window, $rootScope, $parse) {

                /**
                 * Define the Locker class
                 *
                 * @param {Storage}  driver
                 * @param {String}   namespace
                 */
                function Locker (driver, namespace, eventsEnabled) {

                    /**
                     * @type {Object}
                     */
                    this._registeredDrivers = {
                        local: $window.localStorage,
                        session: $window.sessionStorage
                    };

                    /**
                     * Get the Storage instance from the key
                     *
                     * @param  {String}  driver
                     * @return {Storage}
                     */
                    this._resolveDriver = function (driver) {
                        if (! this._registeredDrivers.hasOwnProperty(driver)) {
                            throw new Error('The driver "' + driver + '" was not found. Defaulting to local.');
                        }

                        // fallback gracefully to localStorage
                        return this._registeredDrivers[driver] || this._registeredDrivers.local;
                    };

                    /**
                     * Get the driver key (local/session) by the Storage instance
                     *
                     * @param  {Storage}  driver
                     * @return {String}
                     */
                    this._deriveDriver = function (driver) {
                        return _keyByVal(this._registeredDrivers, driver);
                    };

                    /**
                     * @type {Storage}
                     */
                    this._driver = this._resolveDriver(driver);

                    /**
                     * @type {String}
                     */
                    this._namespace = namespace;

                    /**
                     * @type {Boolean}
                     */
                    this._eventsEnabled = eventsEnabled;

                    /**
                     * Check browser support
                     *
                     * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47
                     * @param  {String}  driver
                     * @return {Boolean}
                     */
                    this._checkSupport = function (driver) {
                        if (typeof this._supported === 'undefined') {
                            var l = 'l';
                            try {
                                this._resolveDriver(driver || 'local').setItem(l, l);
                                this._resolveDriver(driver || 'local').removeItem(l);
                                this._supported = true;
                            } catch (e) {
                                this._supported = false;
                            }
                        }

                        return this._supported;
                    };

                    /**
                     * @type {String}
                     */
                    this._separator = '.';

                    /**
                     * @type {Object}
                     */
                    this._watchers = {};

                    /**
                     * Build the storage key from the namspace
                     *
                     * @param  {String}  key
                     * @return {String}
                     */
                    this._getPrefix = function (key) {
                        return this._namespace + this._separator + key;
                    };

                    /**
                     * Try to encode value as json, or just return the value upon failure
                     *
                     * @param  {Mixed}  value
                     * @return {Mixed}
                     */
                    this._serialize = function (value) {
                        try {
                            return angular.toJson(value);
                        } catch (e) {
                            return value;
                        }
                    };

                    /**
                     * Try to parse value as json, if it fails then it probably isn't json so just return it
                     *
                     * @param  {String}  value
                     * @return {Object|String}
                     */
                    this._unserialize = function (value) {
                        try {
                            return angular.fromJson(value);
                        } catch (e) {
                            return value;
                        }
                    };

                    /**
                     * Trigger an event
                     *
                     * @param  {String} name
                     * @param  {Object} payload
                     * @return {void}
                     */
                    this._event = function (name, payload) {
                        if (this._eventsEnabled) {
                            $rootScope.$emit(name, angular.extend(payload, {
                                driver: this._deriveDriver(this._driver),
                                namespace: this._namespace,
                            }));
                        }
                    };

                    /**
                     * Add to storage
                     *
                     * @param {String}  key
                     * @param {Mixed}  value
                     */
                    this._setItem = function (key, value) {
                        if (this._checkSupport()) {
                            try {
                                var oldVal = this._getItem(key);
                                this._driver.setItem(this._getPrefix(key), this._serialize(value));
                                if (this._exists(key) && ! angular.equals(oldVal, value)) {
                                    this._event('locker.item.updated', { key: key, oldValue: oldVal, newValue: value });
                                } else {
                                    this._event('locker.item.added', { key: key, value: value });
                                }
                            } catch (e) {
                                if (['QUOTA_EXCEEDED_ERR', 'NS_ERROR_DOM_QUOTA_REACHED', 'QuotaExceededError'].indexOf(e.name) !== -1) {
                                    throw new Error('Your browser storage quota has been exceeded');
                                } else {
                                    throw new Error('Could not add item with key "' + key + '"');
                                }
                            }
                        } else {
                            throw new Error('Your browser does not support localStorage');
                        }
                    };

                    /**
                     * Get from storage
                     *
                     * @param  {String}  key
                     * @return {Mixed}
                     */
                    this._getItem = function (key) {
                        if (this._checkSupport()) {
                            return this._unserialize(this._driver.getItem(this._getPrefix(key)));
                        } else {
                            throw new Error('Your browser does not support localStorage');
                        }
                    };

                    /**
                     * Exists in storage
                     *
                     * @param  {String}  key
                     * @return {Boolean}
                     */
                    this._exists = function (key) {
                        if (this._checkSupport()) {
                            return this._driver.hasOwnProperty(this._getPrefix(_value(key)));
                        } else {
                            throw new Error('Your browser does not support localStorage');
                        }
                    };

                    /**
                     * Remove from storage
                     *
                     * @param  {String}  key
                     * @return {Boolean}
                     */
                    this._removeItem = function (key) {
                        if (this._checkSupport()) {
                            if (! this._exists(key)) return false;
                            this._driver.removeItem(this._getPrefix(key));

                            this._event('locker.item.forgotten', { key: key });

                            return true;
                        } else {
                            throw new Error('Your browser does not support localStorage');
                        }
                    };
                }

                /**
                 * Define the public api
                 *
                 * @type {Object}
                 */
                Locker.prototype = {

                    /**
                     * Add a new item to storage (even if it already exists)
                     *
                     * @param  {Mixed}  key
                     * @param  {Mixed}  value
                     * @return {self}
                     */
                    put: function (key, value) {
                        if (! key) return false;
                        key = _value(key);

                        if (angular.isObject(key)) {
                            angular.forEach(key, function (value, key) {
                                this._setItem(key, value);
                            }, this);
                        } else {
                            if (! angular.isDefined(value)) return false;
                            this._setItem(key, _value(value));
                        }

                        return this;
                    },

                    /**
                     * Add an item to storage if it doesn't already exist
                     *
                     * @param  {Mixed}  key
                     * @param  {Mixed}  value
                     * @return {Boolean}
                     */
                    add: function (key, value) {
                        if (! this.has(key)) {
                            this.put(key, value);
                            return true;
                        }

                        return false;
                    },

                    /**
                     * Retrieve the specified item from storage
                     *
                     * @param  {String|Array}  key
                     * @param  {Mixed}  def
                     * @return {Mixed}
                     */
                    get: function (key, def) {
                        if (angular.isArray(key)) {
                            var items = {};
                            angular.forEach(key, function (k) {
                                if (this.has(k)) items[k] = this._getItem(k);
                            }, this);

                            return items;
                        }

                        if (! this.has(key)) return arguments.length === 2 ? def : void 0;

                        return this._getItem(key);
                    },

                    /**
                     * Determine whether the item exists in storage
                     *
                     * @param  {String|Function}  key
                     * @return {Boolean}
                     */
                    has: function (key) {
                        return this._exists(key);
                    },

                    /**
                     * Remove specified item(s) from storage
                     *
                     * @param  {Mixed}  key
                     * @return {Object}
                     */
                    forget: function (key) {
                        key = _value(key);

                        if (angular.isArray(key)) {
                            angular.forEach(key, function (key) {
                                this._removeItem(key);
                            }, this);
                        } else {
                            this._removeItem(key);
                        }

                        return this;
                    },

                    /**
                     * Retrieve the specified item from storage and then remove it
                     *
                     * @param  {String|Array}  key
                     * @param  {Mixed}  def
                     * @return {Mixed}
                     */
                    pull: function (key, def) {
                        var value = this.get(key, def);
                        this.forget(key);

                        return value;
                    },

                    /**
                     * Return all items in storage within the current namespace
                     *
                     * @return {Object}
                     */
                    all: function () {
                        var items = {};
                        angular.forEach(this._driver, function (value, key) {
                            var split = key.split(this._separator);
                            if (split.length > 1 && split[0] === this._namespace) {
                                split.splice(0, 1);
                                key = split.join(this._separator);
                            }
                            if (this.has(key)) items[key] = this.get(key);
                        }, this);

                        return items;
                    },

                    /**
                     * Remove all items set within the current namespace
                     *
                     * @return {self}
                     */
                    clean: function () {
                        this.forget(Object.keys(this.all()));

                        return this;
                    },

                    /**
                     * Empty the current storage driver completely. careful now.
                     *
                     * @return {self}
                     */
                    empty: function () {
                        this._driver.clear();

                        return this;
                    },

                    /**
                     * Get the total number of items within the current namespace
                     *
                     * @return {Integer}
                     */
                    count: function () {
                        return Object.keys(this.all()).length;
                    },

                    /**
                     * Bind a storage key to a $scope property
                     *
                     * @param  {Object}  $scope
                     * @param  {String}  key
                     * @param  {Mixed}   def
                     * @return {self}
                     */
                    bind: function ($scope, key, def) {
                        if (angular.isUndefined( $scope.$eval(key) )) {
                            $parse(key).assign($scope, this.get(key, def));
                        }

                        var self = this;
                        this._watchers[key + $scope.$id] = $scope.$watch(key, function (newVal) {
                            if (angular.isDefined(newVal)) self.put(key, newVal);
                        }, angular.isObject($scope[key]));

                        return this;
                    },

                    /**
                     * Unbind a storage key from a $scope property
                     *
                     * @param  {Object}  $scope
                     * @param  {String}  key
                     * @return {self}
                     */
                    unbind: function ($scope, key) {
                        $parse(key).assign($scope, null);
                        this.forget(key);
                        if (this._watchers[key + $scope]) {
                            delete this._watchers[key + $scope];
                        }

                        return this;
                    },

                    /**
                     * Set the storage driver on a new instance to enable overriding defaults
                     *
                     * @param  {String}  driver
                     * @return {self}
                     */
                    driver: function (driver) {
                        return new Locker(driver, this._namespace, this._eventsEnabled);
                    },

                    /**
                     * Get the currently set driver
                     *
                     * @return {Storage}
                     */
                    getDriver: function () {
                        return this._driver;
                    },

                    /**
                     * Set the namespace on a new instance to enable overriding defaults
                     *
                     * @param  {String}  namespace
                     * @return {self}
                     */
                    namespace: function (namespace) {
                        return new Locker(this._deriveDriver(this._driver), namespace, this._eventsEnabled);
                    },

                    /**
                     * Get the currently set namespace
                     *
                     * @return {String}
                     */
                    getNamespace: function () {
                        return this._namespace;
                    },

                    /**
                     * Check browser support
                     *
                     * @see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js#L38-L47
                     * @param  {String}  driver
                     * @return {Boolean}
                     */
                    supported: function (driver) {
                        return this._checkSupport(driver);
                    }
                };

                /**
                 * Create the driver instances
                 *
                 * @type {Object}
                 */
                var drivers = {
                    local: new Locker('local', defaults.namespace, defaults.eventsEnabled),
                    session: new Locker('session', defaults.namespace, defaults.eventsEnabled)
                };

                return drivers[defaults.driver];
            }]
        };

    });

})(window, window.angular);
<|endoftext|>


<commit_before>/*globals define*/
// TODO: Show an error if not running on the server...
define([
    'common/util/assert',
    '../ComputeClient',
    '../JobResults',
    'child_process',
    'minimatch',
    'module',
    'rimraf',
    'fs',
    'os',
    'path',
], function(
    assert,
    ComputeClient,
    JobResults,
    childProcess,
    minimatch,
    module,
    rimraf,
    fs,
    os,
    path,
) {
    const STDOUT_FILE = 'stdout.txt';
    const spawn = childProcess.spawn;
    const {promisify} = require.nodeRequire('util');
    const mkdir = promisify(fs.mkdir);
    const readdir = promisify(fs.readdir);
    const appendFile = promisify(fs.appendFile);
    const statFile = promisify(fs.stat);
    const rm_rf = promisify(rimraf);
    const writeFile = promisify(fs.writeFile);
    const readFile = promisify(fs.readFile);
    const execFile = promisify(childProcess.execFile);
    const openFile = promisify(fs.open);
    const closeFile = promisify(fs.close);

    // UNZIP must be available on the machine, first ensure that it exists...
    ensureHasUnzip();
    const UNZIP_EXE = '/usr/bin/unzip';  // FIXME: more platform support
    const UNZIP_ARGS = ['-o'];  // FIXME: more platform support
    const DEEPFORGE_ROOT = path.join(path.dirname(module.uri), '..', '..', '..', '..', '..');
    const NODE_MODULES = path.join(DEEPFORGE_ROOT, 'node_modules');
    const symlink = promisify(fs.symlink);
    const touch = async name => await closeFile(await openFile(name, 'w'));

    const LocalExecutor = function() {
        ComputeClient.apply(this, arguments);

        this.jobQueue = [];
        this.currentJob = null;
        this.subprocess = null;
        this.canceled = false;
    };

    LocalExecutor.prototype = Object.create(ComputeClient.prototype);

    LocalExecutor.prototype.cancelJob = function(jobInfo) {
        const {hash} = jobInfo;

        if (this.currentJob === hash) {
            this.canceled = true;
            this.subprocess.kill();
        } else if (this.jobQueue.includes(hash)) {
            const i = this.jobQueue.indexOf(hash);
            this.jobQueue.splice(i, 1);
            this._onJobCompleted(hash, new JobResults(this.CANCELED));
        }
    };

    LocalExecutor.prototype.getStatus = async function(jobInfo) {
        const {hash} = jobInfo;
        if (hash === this.currentJob) {
            return this.RUNNING;
        } else if (this.jobQueue.includes(hash)) {
            return this.QUEUED;
        } else {
            return await this._getJobFile(hash, 'status.txt', 'Job Not Found');
        }
    };

    LocalExecutor.prototype.getConsoleOutput = async function(job) {
        const msg = 'Console output data not found.';
        return await this._getJobFile(job.hash, STDOUT_FILE, msg);
    };

    LocalExecutor.prototype.getResultsInfo = async function(job) {
        const msg = 'Metadata about result types not found.';
        const resultsTxt = await this._getJobFile(job.hash, 'results.json', msg);
        return JSON.parse(resultsTxt);
    };

    LocalExecutor.prototype._getJobFile = async function(hash, name, notFoundMsg) {
        const filename = path.join(this._getWorkingDir(hash), name);
        try {
            return await readFile(filename, 'utf8');
        } catch (err) {
            if (err.code === 'ENOENT') {
                throw new Error(notFoundMsg);
            }
            throw err;
        }
    };

    LocalExecutor.prototype.createJob = async function(hash) {
        this.jobQueue.push(hash);
        this._processNextJob();

        return {hash};
    };

    LocalExecutor.prototype._onJobCompleted = async function(hash, jobResults) {
        if (hash === this.currentJob) {
            this.currentJob = null;
        }

        const tmpdir = this._getWorkingDir(hash);
        //await this._cleanDirectory(tmpdir);
        await writeFile(path.join(tmpdir, 'status.txt'), jobResults.status);

        this.emit('update', hash, jobResults.status);
        this.emit('end', hash, jobResults);
        this._processNextJob();
    };

    LocalExecutor.prototype._cleanDirectory = async function(workdir) {
        const SKIP_FILES = ['results.json', STDOUT_FILE];
        const files = (await readdir(workdir))
            .filter(name => !SKIP_FILES.includes(name))
            .map(name => path.join(workdir, name));

        return Promise.all(files.map(file => rm_rf(file)));
    };

    LocalExecutor.prototype._processNextJob = function() {
        if (this.currentJob) return;

        this.currentJob = this.jobQueue.shift();
        if (this.currentJob) {
            return this._createJob(this.currentJob);
        }
    };

    LocalExecutor.prototype._getWorkingDir = function(hash) {
        return path.join(os.tmpdir(), `deepforge-local-exec-${hash}`);
    };

    LocalExecutor.prototype._createJob = async function(hash) {
        const jobInfo = {hash};
        this.emit('update', jobInfo.hash, this.PENDING);
        const tmpdir = this._getWorkingDir(hash);
        try {
            await mkdir(tmpdir);
        } catch (err) {
            if (err.code === 'EEXIST') {
                await rm_rf(tmpdir);
                await mkdir(tmpdir);
            } else {
                throw err;
            }
        }
        this.logger.info('created working directory at', tmpdir);

        // Fetch the required files from deepforge
        await this.prepareWorkspace(hash, tmpdir);

        // Spin up a subprocess
        const config = JSON.parse(await readFile(tmpdir.replace(path.sep, '/') + '/executor_config.json', 'utf8'));

        const env = process.env;
        env.DEEPFORGE_ROOT = DEEPFORGE_ROOT;
        const options = {
            cwd: tmpdir,
            env,
        };
        this.logger.info(`Running ${config.cmd} ${config.args.join(' ')}`);
        this.subprocess = spawn(config.cmd, config.args, options);
        this.emit('update', jobInfo.hash, this.RUNNING);
        this.subprocess.stdout.on('data', data => this.onConsoleOutput(tmpdir, hash, data));
        this.subprocess.stderr.on('data', data => this.onConsoleOutput(tmpdir, hash, data));

        this.subprocess.on('close', async code => {
            const status = this.canceled ? this.CANCELED :
                (code !== 0 ? this.FAILED : this.SUCCESS);

            const jobResults = new JobResults(status);
            this.canceled = false;

            this._onJobCompleted(hash, jobResults);
        });
    };

    LocalExecutor.prototype.onConsoleOutput = async function(workdir, hash, data) {
        const filename = path.join(workdir, STDOUT_FILE);
        appendFile(filename, data);
        this.emit('data', hash, data);
    };

    LocalExecutor.prototype._getAllFiles = async function(workdir) {
        const dirs = (await readdir(workdir))
            .filter(n => !n.includes('node_modules'))
            .map(name => path.join(workdir, name));
        const files = [];

        // Read each directory
        while (dirs.length) {
            const abspath = dirs.shift();
            const isDirectory = (await statFile(abspath)).isDirectory();
            if (isDirectory) {
                const childpaths = (await readdir(abspath))
                    .map(name => path.join(abspath, name));
                dirs.push.apply(dirs, childpaths);
            } else {
                files.push(abspath);
            }
        }

        return files;
    };

    LocalExecutor.prototype.prepareWorkspace = async function(hash, dirname) {
        this.logger.info('about to fetch job data');
        const content = new Buffer(new Uint8Array(await this.blobClient.getObject(hash)));  // TODO: Handle errors...
        const zipPath = path.join(dirname, `${hash}.zip`);
        await writeFile(zipPath, content);
        this.logger.info(`Fetched job data: ${zipPath}`);

        this.logger.info(`unzipping ${zipPath} in ${dirname}`);
        await unzip(zipPath, dirname);

        // Set up a symbolic link to the node_modules
        await symlink(NODE_MODULES, path.join(dirname, 'node_modules'));

        // Prepare for the stdout
        await touch(path.join(dirname, STDOUT_FILE));
    };

    async function unzip(filename, dirname) {
        const args = UNZIP_ARGS.concat(path.basename(filename));
        await execFile(UNZIP_EXE, args, {cwd: dirname});

        await rm_rf(filename);
    }

    function ensureHasUnzip() {
        // FIXME: check for unzip here!
    }
    // - [ ] emit updates on stdout...

    return LocalExecutor;
});
<commit_msg>Remove extra logs during server execution. Fixes #1428 (#1429)<commit_after>/*globals define*/
// TODO: Show an error if not running on the server...
define([
    'common/util/assert',
    '../ComputeClient',
    '../JobResults',
    'child_process',
    'minimatch',
    'module',
    'rimraf',
    'fs',
    'os',
    'path',
], function(
    assert,
    ComputeClient,
    JobResults,
    childProcess,
    minimatch,
    module,
    rimraf,
    fs,
    os,
    path,
) {
    const STDOUT_FILE = 'stdout.txt';
    const spawn = childProcess.spawn;
    const {promisify} = require.nodeRequire('util');
    const mkdir = promisify(fs.mkdir);
    const readdir = promisify(fs.readdir);
    const appendFile = promisify(fs.appendFile);
    const statFile = promisify(fs.stat);
    const rm_rf = promisify(rimraf);
    const writeFile = promisify(fs.writeFile);
    const readFile = promisify(fs.readFile);
    const execFile = promisify(childProcess.execFile);
    const openFile = promisify(fs.open);
    const closeFile = promisify(fs.close);

    // UNZIP must be available on the machine, first ensure that it exists...
    ensureHasUnzip();
    const UNZIP_EXE = '/usr/bin/unzip';  // FIXME: more platform support
    const UNZIP_ARGS = ['-o'];  // FIXME: more platform support
    const DEEPFORGE_ROOT = path.join(path.dirname(module.uri), '..', '..', '..', '..', '..');
    const NODE_MODULES = path.join(DEEPFORGE_ROOT, 'node_modules');
    const symlink = promisify(fs.symlink);
    const touch = async name => await closeFile(await openFile(name, 'w'));

    const LocalExecutor = function() {
        ComputeClient.apply(this, arguments);

        this.jobQueue = [];
        this.currentJob = null;
        this.subprocess = null;
        this.canceled = false;
    };

    LocalExecutor.prototype = Object.create(ComputeClient.prototype);

    LocalExecutor.prototype.cancelJob = function(jobInfo) {
        const {hash} = jobInfo;

        if (this.currentJob === hash) {
            this.canceled = true;
            this.subprocess.kill();
        } else if (this.jobQueue.includes(hash)) {
            const i = this.jobQueue.indexOf(hash);
            this.jobQueue.splice(i, 1);
            this._onJobCompleted(hash, new JobResults(this.CANCELED));
        }
    };

    LocalExecutor.prototype.getStatus = async function(jobInfo) {
        const {hash} = jobInfo;
        if (hash === this.currentJob) {
            return this.RUNNING;
        } else if (this.jobQueue.includes(hash)) {
            return this.QUEUED;
        } else {
            return await this._getJobFile(hash, 'status.txt', 'Job Not Found');
        }
    };

    LocalExecutor.prototype.getConsoleOutput = async function(job) {
        const msg = 'Console output data not found.';
        return await this._getJobFile(job.hash, STDOUT_FILE, msg);
    };

    LocalExecutor.prototype.getResultsInfo = async function(job) {
        const msg = 'Metadata about result types not found.';
        const resultsTxt = await this._getJobFile(job.hash, 'results.json', msg);
        return JSON.parse(resultsTxt);
    };

    LocalExecutor.prototype._getJobFile = async function(hash, name, notFoundMsg) {
        const filename = path.join(this._getWorkingDir(hash), name);
        try {
            return await readFile(filename, 'utf8');
        } catch (err) {
            if (err.code === 'ENOENT') {
                throw new Error(notFoundMsg);
            }
            throw err;
        }
    };

    LocalExecutor.prototype.createJob = async function(hash) {
        this.jobQueue.push(hash);
        this._processNextJob();

        return {hash};
    };

    LocalExecutor.prototype._onJobCompleted = async function(hash, jobResults) {
        if (hash === this.currentJob) {
            this.currentJob = null;
        }

        const tmpdir = this._getWorkingDir(hash);
        //await this._cleanDirectory(tmpdir);
        await writeFile(path.join(tmpdir, 'status.txt'), jobResults.status);

        this.emit('update', hash, jobResults.status);
        this.emit('end', hash, jobResults);
        this._processNextJob();
    };

    LocalExecutor.prototype._cleanDirectory = async function(workdir) {
        const SKIP_FILES = ['results.json', STDOUT_FILE];
        const files = (await readdir(workdir))
            .filter(name => !SKIP_FILES.includes(name))
            .map(name => path.join(workdir, name));

        return Promise.all(files.map(file => rm_rf(file)));
    };

    LocalExecutor.prototype._processNextJob = function() {
        if (this.currentJob) return;

        this.currentJob = this.jobQueue.shift();
        if (this.currentJob) {
            return this._createJob(this.currentJob);
        }
    };

    LocalExecutor.prototype._getWorkingDir = function(hash) {
        return path.join(os.tmpdir(), `deepforge-local-exec-${hash}`);
    };

    LocalExecutor.prototype._createJob = async function(hash) {
        const jobInfo = {hash};
        this.emit('update', jobInfo.hash, this.PENDING);
        const tmpdir = this._getWorkingDir(hash);
        try {
            await mkdir(tmpdir);
        } catch (err) {
            if (err.code === 'EEXIST') {
                await rm_rf(tmpdir);
                await mkdir(tmpdir);
            } else {
                throw err;
            }
        }
        this.logger.info('created working directory at', tmpdir);

        // Fetch the required files from deepforge
        await this.prepareWorkspace(hash, tmpdir);

        // Spin up a subprocess
        const config = JSON.parse(await readFile(tmpdir.replace(path.sep, '/') + '/executor_config.json', 'utf8'));

        const env = process.env;
        env.DEEPFORGE_ROOT = DEEPFORGE_ROOT;
        const options = {
            cwd: tmpdir,
            env,
        };
        this.logger.info(`Running ${config.cmd} ${config.args.join(' ')}`);
        this.subprocess = spawn(config.cmd, config.args, options);
        this.emit('update', jobInfo.hash, this.RUNNING);
        this.subprocess.stdout.on('data', data => this.onConsoleOutput(tmpdir, hash, data));

        this.subprocess.on('close', async code => {
            const status = this.canceled ? this.CANCELED :
                (code !== 0 ? this.FAILED : this.SUCCESS);

            const jobResults = new JobResults(status);
            this.canceled = false;

            this._onJobCompleted(hash, jobResults);
        });
    };

    LocalExecutor.prototype.onConsoleOutput = async function(workdir, hash, data) {
        const filename = path.join(workdir, STDOUT_FILE);
        appendFile(filename, data);
        this.emit('data', hash, data);
    };

    LocalExecutor.prototype._getAllFiles = async function(workdir) {
        const dirs = (await readdir(workdir))
            .filter(n => !n.includes('node_modules'))
            .map(name => path.join(workdir, name));
        const files = [];

        // Read each directory
        while (dirs.length) {
            const abspath = dirs.shift();
            const isDirectory = (await statFile(abspath)).isDirectory();
            if (isDirectory) {
                const childpaths = (await readdir(abspath))
                    .map(name => path.join(abspath, name));
                dirs.push.apply(dirs, childpaths);
            } else {
                files.push(abspath);
            }
        }

        return files;
    };

    LocalExecutor.prototype.prepareWorkspace = async function(hash, dirname) {
        this.logger.info('about to fetch job data');
        const content = new Buffer(new Uint8Array(await this.blobClient.getObject(hash)));  // TODO: Handle errors...
        const zipPath = path.join(dirname, `${hash}.zip`);
        await writeFile(zipPath, content);
        this.logger.info(`Fetched job data: ${zipPath}`);

        this.logger.info(`unzipping ${zipPath} in ${dirname}`);
        await unzip(zipPath, dirname);

        // Set up a symbolic link to the node_modules
        await symlink(NODE_MODULES, path.join(dirname, 'node_modules'));

        // Prepare for the stdout
        await touch(path.join(dirname, STDOUT_FILE));
    };

    async function unzip(filename, dirname) {
        const args = UNZIP_ARGS.concat(path.basename(filename));
        await execFile(UNZIP_EXE, args, {cwd: dirname});

        await rm_rf(filename);
    }

    function ensureHasUnzip() {
        // FIXME: check for unzip here!
    }
    // - [ ] emit updates on stdout...

    return LocalExecutor;
});
<|endoftext|>


<commit_before>#ifndef __lang_Math__
#define __lang_Math__

////////////////////////////////////////////////////////////////////////////////
// Math

/**
 * Returns a random integer number first <= x < second, if two arguments are
 * passed or 0 <= x < first, if there is donly one.
 */
Math.rand = function(first, second) {
	return second == undefined
		? Math.rand(0, first)
		: Math.floor(Math.random() * (max - min) + min);
}

#endif // __lang_Math__<commit_msg>Forgot to rename variables.<commit_after>#ifndef __lang_Math__
#define __lang_Math__

////////////////////////////////////////////////////////////////////////////////
// Math

/**
 * Returns a random integer number first <= x < second, if two arguments are
 * passed or 0 <= x < first, if there is donly one.
 */
Math.rand = function(first, second) {
	return second == undefined
		? Math.rand(0, first)
		: Math.floor(Math.random() * (second - first) + first);
}

#endif // __lang_Math__<|endoftext|>


<commit_before>/**
 * @license Angulartics v0.17.2
 * (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics
 * Universal Analytics update contributed by http://github.com/willmcclellan
 * License: MIT
 */
(function(angular) {
'use strict';

/**
 * @ngdoc overview
 * @name angulartics.google.analytics
 * Enables analytics support for Google Analytics (http://google.com/analytics)
 */
angular.module('angulartics.google.analytics', ['angulartics'])
.config(['$analyticsProvider', function ($analyticsProvider) {

  // GA already supports buffered invocations so we don't need
  // to wrap these inside angulartics.waitForVendorApi

  $analyticsProvider.settings.pageTracking.trackRelativePath = true;

  // Set the default settings for this module
  $analyticsProvider.settings.ga = {
    // array of additional account names (only works for analyticsjs)
    additionalAccountNames: undefined,
    userId: null
  };

  $analyticsProvider.registerPageTrack(function (path) {
    if (window._gaq) {
      _gaq.push(['_trackPageview', path]);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        _gaq.push([accountName + '._trackPageview', path]);
      });
    }
    if (window.ga) {
      if ($analyticsProvider.settings.ga.userId) {
        ga('set', '&uid', $analyticsProvider.settings.ga.userId);
      }
      ga('send', 'pageview', path);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        ga(accountName +'.send', 'pageview', path);
      });
    }
  });

  /**
   * Track Event in GA
   * @name eventTrack
   *
   * @param {string} action Required 'action' (string) associated with the event
   * @param {object} properties Comprised of the mandatory field 'category' (string) and optional  fields 'label' (string), 'value' (integer) and 'noninteraction' (boolean)
   *
   * @link https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#SettingUpEventTracking
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events
   */
  $analyticsProvider.registerEventTrack(function (action, properties) {

    // Google Analytics requires an Event Category
    if (!properties || !properties.category) {
      properties = properties || {};
      properties.category = 'Event';
    }
    // GA requires that eventValue be an integer, see:
    // https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#eventValue
    // https://github.com/luisfarzati/angulartics/issues/81
    if (properties.value) {
      var parsed = parseInt(properties.value, 10);
      properties.value = isNaN(parsed) ? 0 : parsed;
    }

    if (window.ga) {

      var eventOptions = {
        eventCategory: properties.category,
        eventAction: action,
        eventLabel: properties.label,
        eventValue: properties.value,
        nonInteraction: properties.noninteraction,
        page: properties.page || window.location.hash.substring(1),
        userId: $analyticsProvider.settings.ga.userId
      };

      // add custom dimensions and metrics
      for(var idx = 1; idx<=20;idx++) {
      if (properties['dimension' +idx.toString()]) {
        eventOptions['dimension' +idx.toString()] = properties['dimension' +idx.toString()];
      }
      if (properties['metric' +idx.toString()]) {
        eventOptions['metric' +idx.toString()] = properties['metric' +idx.toString()];
        }
      }
      ga('send', 'event', eventOptions);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        ga(accountName +'.send', 'event', eventOptions);
      });
    }

    else if (window._gaq) {
      _gaq.push(['_trackEvent', properties.category, action, properties.label, properties.value, properties.noninteraction]);
    }

  });

  $analyticsProvider.registerSetUsername(function (userId) {
    $analyticsProvider.settings.ga.userId = userId;
  });

}]);
})(angular);
<commit_msg>html5 mode enabled events with current page<commit_after>/**
 * @license Angulartics v0.17.2
 * (c) 2013 Luis Farzati http://luisfarzati.github.io/angulartics
 * Universal Analytics update contributed by http://github.com/willmcclellan
 * License: MIT
 */
(function(angular) {
'use strict';

/**
 * @ngdoc overview
 * @name angulartics.google.analytics
 * Enables analytics support for Google Analytics (http://google.com/analytics)
 */
angular.module('angulartics.google.analytics', ['angulartics'])
.config(['$analyticsProvider', function ($analyticsProvider) {

  // GA already supports buffered invocations so we don't need
  // to wrap these inside angulartics.waitForVendorApi

  $analyticsProvider.settings.pageTracking.trackRelativePath = true;

  // Set the default settings for this module
  $analyticsProvider.settings.ga = {
    // array of additional account names (only works for analyticsjs)
    additionalAccountNames: undefined,
    userId: null
  };

  $analyticsProvider.registerPageTrack(function (path) {
    if (window._gaq) {
      _gaq.push(['_trackPageview', path]);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        _gaq.push([accountName + '._trackPageview', path]);
      });
    }
    if (window.ga) {
      if ($analyticsProvider.settings.ga.userId) {
        ga('set', '&uid', $analyticsProvider.settings.ga.userId);
      }
      ga('send', 'pageview', path);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        ga(accountName +'.send', 'pageview', path);
      });
    }
  });

  /**
   * Track Event in GA
   * @name eventTrack
   *
   * @param {string} action Required 'action' (string) associated with the event
   * @param {object} properties Comprised of the mandatory field 'category' (string) and optional  fields 'label' (string), 'value' (integer) and 'noninteraction' (boolean)
   *
   * @link https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#SettingUpEventTracking
   *
   * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events
   */
  $analyticsProvider.registerEventTrack(function (action, properties) {

    // Google Analytics requires an Event Category
    if (!properties || !properties.category) {
      properties = properties || {};
      properties.category = 'Event';
    }
    // GA requires that eventValue be an integer, see:
    // https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#eventValue
    // https://github.com/luisfarzati/angulartics/issues/81
    if (properties.value) {
      var parsed = parseInt(properties.value, 10);
      properties.value = isNaN(parsed) ? 0 : parsed;
    }

    if (window.ga) {

      var eventOptions = {
        eventCategory: properties.category,
        eventAction: action,
        eventLabel: properties.label,
        eventValue: properties.value,
        nonInteraction: properties.noninteraction,
        page: properties.page || window.location.hash.substring(1) || window.location.pathname,
        userId: $analyticsProvider.settings.ga.userId
      };

      // add custom dimensions and metrics
      for(var idx = 1; idx<=20;idx++) {
      if (properties['dimension' +idx.toString()]) {
        eventOptions['dimension' +idx.toString()] = properties['dimension' +idx.toString()];
      }
      if (properties['metric' +idx.toString()]) {
        eventOptions['metric' +idx.toString()] = properties['metric' +idx.toString()];
        }
      }
      ga('send', 'event', eventOptions);
      angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){
        ga(accountName +'.send', 'event', eventOptions);
      });
    }

    else if (window._gaq) {
      _gaq.push(['_trackEvent', properties.category, action, properties.label, properties.value, properties.noninteraction]);
    }

  });

  $analyticsProvider.registerSetUsername(function (userId) {
    $analyticsProvider.settings.ga.userId = userId;
  });

}]);
})(angular);
<|endoftext|>


<commit_before>'use strict';

module.exports = (wallaby) => {
  return {
    compilers: {
      '**/*.js': wallaby.compilers.babel(),
    },
    debug: true,
    env: {
      type: 'node',
    },
    files: [
      './packages/**/*.js',
      './packages/**/*.json',
      '!./packages/**/build/**',
      '!./packages/**/test/**/*-specs.js',
      '!./packages/**/test/**/*.spec.js',
      '!./packages/*/node_modules/**',
      '!./packages/*/gulpfile.js',
      '!./packages/*/scripts/**',
      './packages/*/test/**/fixtures/**/*',
      './packages/*/test/**/mocks.js',
      './packages/*/test/helpers.js',
      './babel.config.json',
      // below this are fixtures
      {
        binary: true,
        pattern: './packages/support/test/unit/assets/sample_binary.plist',
      },
      {
        instrument: false,
        pattern: './packages/support/test/unit/assets/sample_text.plist',
      },
      {
        instrument: false,
        pattern: './packages/base-driver/static/**/*',
      },
      {
        instrument: false,
        pattern: './packages/gulp-plugins/build/**/*',
      },
      // this setup file is special
      {
        instrument: false,
        pattern: './test/setup.js'
      }
    ],
    testFramework: 'mocha',
    tests: [
      './packages/*/test/**/*-specs.js',
      './packages/*/test/unit/**/*.spec.js',
      '!./packages/*/test/**/*-e2e-specs.js',
      '!./packages/*/test/e2e/**/*',
      '!./packages/*/node_modules/**',
      // this is more of an E2E test and it's tedious to run
      '!./packages/gulp-plugins/test/transpile-specs.js',
    ],
    workers: {
      restart: true,
    },
    setup() {
      require('./test/setup');
    },
    runMode: 'onsave'
  };
};
<commit_msg>chore: update wallaby config to avoid local_appium_home<commit_after>'use strict';

module.exports = (wallaby) => {
  return {
    compilers: {
      '**/*.js': wallaby.compilers.babel(),
    },
    debug: true,
    env: {
      type: 'node',
    },
    files: [
      './packages/**/*.js',
      './packages/**/*.json',
      '!./packages/**/build/**',
      '!./packages/**/test/**/*-specs.js',
      '!./packages/**/test/**/*.spec.js',
      '!./packages/*/node_modules/**',
      '!./packages/*/gulpfile.js',
      '!./packages/*/scripts/**',
      './packages/*/test/**/fixtures/**/*',
      './packages/*/test/**/mocks.js',
      './packages/*/test/helpers.js',
      './babel.config.json',
      // below this are fixtures
      {
        binary: true,
        pattern: './packages/support/test/unit/assets/sample_binary.plist',
      },
      {
        instrument: false,
        pattern: './packages/support/test/unit/assets/sample_text.plist',
      },
      {
        instrument: false,
        pattern: './packages/base-driver/static/**/*',
      },
      {
        instrument: false,
        pattern: './packages/gulp-plugins/build/**/*',
      },
      // this setup file is special
      {
        instrument: false,
        pattern: './test/setup.js'
      },
      '!**/local_appium_home/**'
    ],
    testFramework: 'mocha',
    tests: [
      './packages/*/test/**/*-specs.js',
      './packages/*/test/unit/**/*.spec.js',
      '!./packages/*/test/**/*-e2e-specs.js',
      '!./packages/*/test/e2e/**/*',
      '!./packages/*/node_modules/**',
      // this is more of an E2E test and it's tedious to run
      '!./packages/gulp-plugins/test/transpile-specs.js',
      '!**/local_appium_home/**'
    ],
    workers: {
      restart: true,
    },
    setup() {
      require('./test/setup');
    },
    runMode: 'onsave'
  };
};
<|endoftext|>


<commit_before>import React from 'react'
import Place from './Place'

const GlobalFilter = ({
    places,
    mapProperties,
    onPlaceClick,
    onOpacityChange,
    onContourChange,
    onKeyUpSearch,
}) => {

    let input

    // treat opacity tooltip value
    let opacity

    if (!mapProperties) {
        opacity = .5
    } else {
        opacity = mapProperties.opacity
    }
    if(isNaN(opacity)) {
        opacity = .5
    }
    opacity *= 100

    const handleOpacityChange = (e) => {
        onOpacityChange(e.target.value)
    }
    const handleTypeChange = (e) => {
        onContourChange(e.target.value)
    }
    const handleKeyUpSearch = (val) => {
        onKeyUpSearch(val)
    }

    return (

            <form>
                <fieldset className="global-filter-form">

Busca detalhada

                    <fieldset className="global-filter-form--inputsearch">
                        <input
                            id="searchField"
                            type="search"
                            placeholder="Insira o nome da área"
                            ref={node => {input = node;}}
                            onKeyUp={() => {
                                handleKeyUpSearch(input.value)
                            }}
                        />

                    </fieldset>
                    <fieldset className="global-filter-form--selectiontype">
                        Tipo de seleção:

                            <input
                                name="selectionType"
                                type="radio"
                                id="selectionType1"
                                value="borda"
                                defaultChecked="checked"
                                onClick={(e) => handleTypeChange(e)} />
                            Demarcada

                            <input
                                name="selectionType"
                                type="radio"
                                id="selectionType2"
                                value="opaco"
                                onClick={(e) => handleTypeChange(e)} />
                            Isolada

                    </fieldset>

                    <fieldset className="global-filter-form--selectopacity">
                        Opacidade da seleção:

                            <input type="range" min="0" max="10" defaultValue="5" onChange={(e) => handleOpacityChange(e)}></input>
                            {opacity}%

                    </fieldset>
                </fieldset>
            </form>

Áreas dos CRAAIs

                    {places? places.map(p => <Place onPlaceClick={onPlaceClick} key={p.id} place={p}/>) : null}

    )
}

export default GlobalFilter
<commit_msg>remove value in globalFilter.js<commit_after>import React from 'react'
import Place from './Place'

const GlobalFilter = ({
    places,
    mapProperties,
    onPlaceClick,
    onOpacityChange,
    onContourChange,
    onKeyUpSearch,
}) => {

    let input

    // treat opacity tooltip value
    let opacity

    if (!mapProperties) {
        opacity = .5
    } else {
        opacity = mapProperties.opacity
    }
    if(isNaN(opacity)) {
        opacity = .5
    }
    opacity *= 100

    const handleOpacityChange = (e) => {
        onOpacityChange(e.target.value)
    }
    const handleTypeChange = (e) => {
        onContourChange(e.target.value)
    }
    const handleKeyUpSearch = (val) => {
        onKeyUpSearch(val)
    }

    return (

            <form>
                <fieldset className="global-filter-form">

Busca detalhada

                    <fieldset className="global-filter-form--inputsearch">
                        <input
                            id="searchField"
                            type="search"
                            placeholder="Insira o nome da área"
                            ref={node => {input = node;}}
                            onKeyUp={() => {
                                handleKeyUpSearch(input.value)
                            }}
                        />

                    </fieldset>
                    <fieldset className="global-filter-form--selectiontype">
                        Tipo de seleção:

                            <input
                                name="selectionType"
                                type="radio"
                                id="selectionType1"
                                defaultChecked="checked"
                                onClick={(e) => handleTypeChange(e)} />
                            Demarcada

                            <input
                                name="selectionType"
                                type="radio"
                                id="selectionType2"
                                onClick={(e) => handleTypeChange(e)} />
                            Isolada

                    </fieldset>

                    <fieldset className="global-filter-form--selectopacity">
                        Opacidade da seleção:

                            <input type="range" min="0" max="10" defaultValue="5" onChange={(e) => handleOpacityChange(e)}></input>
                            {opacity}%

                    </fieldset>
                </fieldset>
            </form>

Áreas dos CRAAIs

                    {places? places.map(p => <Place onPlaceClick={onPlaceClick} key={p.id} place={p}/>) : null}

    )
}

export default GlobalFilter
<|endoftext|>


<commit_before>/** section: Language, related to: Hash
 *  $H([object]) -> Hash
 *
 *  Creates a `Hash`. This is purely a convenience wrapper around the Hash
 *  constructor, it does not do anything other than pass any argument it's
 *  given into the Hash constructor and return the result.
 **/
function $H(object) {
  return new Hash(object);
};

/** section: Language
 * class Hash
 *  includes Enumerable
 *
 *  A set of key/value pairs.
 *
 *  `Hash` can be thought of as an associative array, binding unique keys to
 *  values (which are not necessarily unique), though it can not guarantee
 *  consistent order its elements when iterating. Because of the nature of
 *  JavaScript, every object is in fact a hash; but `Hash` adds a number of
 *  methods that let you enumerate keys and values, iterate over key/value
 *  pairs, merge two hashes together, and much more.
 *
 *
Creating a hash

 *
 *  You can create a Hash either via `new Hash()` or the convenience alias
 *  `$H()`; there is **no** difference between them. In either case, you may
 *  optionally pass in an object to seed the `Hash`. If you pass in a `Hash`,
 *  it will be cloned.
 *
**/
var Hash = Class.create(Enumerable, (function() {
  /**
   *  new Hash([object])
   *
   *  Creates a new `Hash`. If `object` is given, the new hash will be populated
   *  with all the object's properties. See [[$H]].
   **/
  function initialize(object) {
    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
  }

  // Docs for #each even though technically it's implemented by Enumerable
  /**
   *  Hash#each(iterator[, context]) -> Hash
   *  - iterator (Function): A function that expects each item in the `Hash`
   *    as the first argument and a numerical index as the second.
   *  - context (Object): The scope in which to call `iterator`. Determines what
   *    `this` means inside `iterator`.
   *
   *  Iterates over the name/value pairs in the hash.
   *
   *  This is actually just the [[Enumerable#each #each]] method from the
   *  mixed-in [[Enumerable]] module. It is documented here to describe the
   *  structure of the elements passed to the iterator and the order of
   *  iteration.
   *
   *  The iterator's first argument (the "item") is an object with two
   *  properties:
   *
   *  - `key`: the key name as a `String`
   *  - `value`: the corresponding value (which may be `undefined`)
   *
   *  The order of iteration is implementation-dependent, as it relies on
   *  the order of the native `for..in` loop. Although most modern
   *  implementations exhibit *ordered* behavior, this is not standardized and
   *  may not always be the case, and so cannot be relied upon.
   *
   *  ### Example
   *
   *      var h = $H({version: 1.6, author: 'The Core Team'});
   *
   *      h.each(function(pair) {
   *        alert(pair.key + ' = "' + pair.value + '"');
   *      });
   *      // Alerts 'version = "1.6"' and 'author = "The Core Team"'
   *      // -or-
   *      // Alerts 'author = "The Core Team"' and 'version = "1.6"'
  **/

  // Our _internal_ each
  function _each(iterator) {
    for (var key in this._object) {
      var value = this._object[key], pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  }

  /**
   *  Hash#set(key, value) -> value
   *
   *  Sets the hash's `key` property to `value` and returns `value`.
  **/
  function set(key, value) {
    return this._object[key] = value;
  }

  /**
   *  Hash#get(key) -> value
   *
   *  Returns the stored value for the given `key`.
   *
   *  ### Examples
   *
   *      var h = new Hash({a: 'apple', b: 'banana', c: 'coconut'});
   *      h.get('a');
   *      // -> 'apple'
  **/
  function get(key) {
    // simulating poorly supported hasOwnProperty
    if (this._object[key] !== Object.prototype[key])
      return this._object[key];
  }

  /**
   *  Hash#unset(key) -> value
   *
   *  Deletes the hash's `key` property and returns its value.
  **/
  function unset(key) {
    var value = this._object[key];
    delete this._object[key];
    return value;
  }

  /**
   *  Hash#toObject() -> Object
   *
   *  Returns a cloned, vanilla object.
  **/
  function toObject() {
    return Object.clone(this._object);
  }

  /**
   *  Hash#keys() -> [String...]
   *
   *  Provides an Array containing the keys for items stored in the hash.
   *
   *  The order of the keys is not guaranteed.
   *
   *  ### Example
   *
   *      var h = $H({one: "uno", two: "due", three: "tre"});
   *      h.keys();
   *      // -> ["one", "three", "two] (these may be in any order)
  **/
  function keys() {
    return this.pluck('key');
  }

  /**
   *  Hash#values() -> Array
   *
   *  Collect the values of a hash and returns them in an array.
  **/
  function values() {
    return this.pluck('value');
  }

  /**
   *  Hash#index(value) -> String
   *
   *  Returns the first key in the hash whose value matches `value`.
   *  Returns `false` if there is no such key.
  **/
  function index(value) {
    var match = this.detect(function(pair) {
      return pair.value === value;
    });
    return match && match.key;
  }

  /**
   *  Hash#merge(object) -> Hash
   *  - object (Object | Hash): The object to merge with this hash to produce
   *    the resulting hash.
   *
   *  Returns a new `Hash` instance with `object`'s key/value pairs merged in;
   *  this hash remains unchanged.
   *
   *  To modify the original hash in place, use [[Hash#update]].
   *
   *  ### Example
   *
   *      var h = $H({one: "uno", two: "due"});
   *      var h2 = h.merge({three: "tre"});
   *      h.keys();
   *      // -> ["one", "two"] (unchanged)
   *      h2.keys();
   *      // -> ["one", "two", "three"] (has merged contents)
  **/
  function merge(object) {
    return this.clone().update(object);
  }

  /**
   *  Hash#update(object) -> Hash
   *
   *  Updates hash with the key/value pairs of `object`.
   *  The original hash will be modified. To return a new hash instead, use
   *  [[Hash#merge]].
  **/
  function update(object) {
    return new Hash(object).inject(this, function(result, pair) {
      result.set(pair.key, pair.value);
      return result;
    });
  }

  // Private. No PDoc necessary.
  function toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  /** related to: String#toQueryParams
   *  Hash#toQueryString() -> String
   *
   *  Turns a hash into its URL-encoded query string representation.
  **/
  function toQueryString() {
    return this.inject([], function(results, pair) {
      var key = encodeURIComponent(pair.key), values = pair.value;

      if (values && typeof values == 'object') {
        if (Object.isArray(values))
          return results.concat(values.map(toQueryPair.curry(key)));
      } else results.push(toQueryPair(key, values));
      return results;
    }).join('&');
  }

  /** related to: Object.inspect
   *  Hash#inspect() -> String
   *
   *  Returns the debug-oriented string representation of the Hash.
  **/
  function inspect() {
    return '#<Hash:{' + this.map(function(pair) {
      return pair.map(Object.inspect).join(': ');
    }).join(', ') + '}>';
  }

  /** related to: Object.toJSON
   *  Hash#toJSON() -> String
   *
   *  Returns a JSON string.
  **/
  function toJSON() {
    return Object.toJSON(this.toObject());
  }

  /**
   *  Hash#clone() -> Hash
   *
   *  Returns a clone of this Hash.
  **/
  function clone() {
    return new Hash(this);
  }

  return {
    initialize:             initialize,
    _each:                  _each,
    set:                    set,
    get:                    get,
    unset:                  unset,
    toObject:               toObject,
    toTemplateReplacements: toObject,
    keys:                   keys,
    values:                 values,
    index:                  index,
    merge:                  merge,
    update:                 update,
    toQueryString:          toQueryString,
    inspect:                inspect,
    toJSON:                 toJSON,
    clone:                  clone
  };
})());

Hash.from = $H;
<commit_msg>doc: Merged and updated old docs for Hash#get<commit_after>/** section: Language, related to: Hash
 *  $H([object]) -> Hash
 *
 *  Creates a `Hash`. This is purely a convenience wrapper around the Hash
 *  constructor, it does not do anything other than pass any argument it's
 *  given into the Hash constructor and return the result.
 **/
function $H(object) {
  return new Hash(object);
};

/** section: Language
 * class Hash
 *  includes Enumerable
 *
 *  A set of key/value pairs.
 *
 *  `Hash` can be thought of as an associative array, binding unique keys to
 *  values (which are not necessarily unique), though it can not guarantee
 *  consistent order its elements when iterating. Because of the nature of
 *  JavaScript, every object is in fact a hash; but `Hash` adds a number of
 *  methods that let you enumerate keys and values, iterate over key/value
 *  pairs, merge two hashes together, and much more.
 *
 *
Creating a hash

 *
 *  You can create a Hash either via `new Hash()` or the convenience alias
 *  `$H()`; there is **no** difference between them. In either case, you may
 *  optionally pass in an object to seed the `Hash`. If you pass in a `Hash`,
 *  it will be cloned.
 *
**/
var Hash = Class.create(Enumerable, (function() {
  /**
   *  new Hash([object])
   *
   *  Creates a new `Hash`. If `object` is given, the new hash will be populated
   *  with all the object's properties. See [[$H]].
   **/
  function initialize(object) {
    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
  }

  // Docs for #each even though technically it's implemented by Enumerable
  /**
   *  Hash#each(iterator[, context]) -> Hash
   *  - iterator (Function): A function that expects each item in the `Hash`
   *    as the first argument and a numerical index as the second.
   *  - context (Object): The scope in which to call `iterator`. Determines what
   *    `this` means inside `iterator`.
   *
   *  Iterates over the name/value pairs in the hash.
   *
   *  This is actually just the [[Enumerable#each #each]] method from the
   *  mixed-in [[Enumerable]] module. It is documented here to describe the
   *  structure of the elements passed to the iterator and the order of
   *  iteration.
   *
   *  The iterator's first argument (the "item") is an object with two
   *  properties:
   *
   *  - `key`: the key name as a `String`
   *  - `value`: the corresponding value (which may be `undefined`)
   *
   *  The order of iteration is implementation-dependent, as it relies on
   *  the order of the native `for..in` loop. Although most modern
   *  implementations exhibit *ordered* behavior, this is not standardized and
   *  may not always be the case, and so cannot be relied upon.
   *
   *  ### Example
   *
   *      var h = $H({version: 1.6, author: 'The Core Team'});
   *
   *      h.each(function(pair) {
   *        alert(pair.key + ' = "' + pair.value + '"');
   *      });
   *      // Alerts 'version = "1.6"' and 'author = "The Core Team"'
   *      // -or-
   *      // Alerts 'author = "The Core Team"' and 'version = "1.6"'
  **/

  // Our _internal_ each
  function _each(iterator) {
    for (var key in this._object) {
      var value = this._object[key], pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  }

  /**
   *  Hash#set(key, value) -> value
   *  - key (String): The key to use for this value.
   *  - value (?): The value to use for this key.
   *
   *  Stores `value` in the hash using the key `key` and returns `value`.
   *
   *  ### Example
   *
   *      var h = $H();
   *      h.keys();
   *      // -> [] (initially empty)
   *      h.set('a', 'apple');
   *      // -> "apple"
   *      h.keys();
   *      // -> ["a"] (has the new entry)
   *      h.get('a');
   *      // -> "apple"
  **/
  function set(key, value) {
    return this._object[key] = value;
  }

  /**
   *  Hash#get(key) -> value
   *
   *  Returns the stored value for the given `key`.
   *
   *  ### Examples
   *
   *      var h = new Hash({a: 'apple', b: 'banana', c: 'coconut'});
   *      h.get('a');
   *      // -> 'apple'
  **/
  function get(key) {
    // simulating poorly supported hasOwnProperty
    if (this._object[key] !== Object.prototype[key])
      return this._object[key];
  }

  /**
   *  Hash#unset(key) -> value
   *
   *  Deletes the hash's `key` property and returns its value.
  **/
  function unset(key) {
    var value = this._object[key];
    delete this._object[key];
    return value;
  }

  /**
   *  Hash#toObject() -> Object
   *
   *  Returns a cloned, vanilla object.
  **/
  function toObject() {
    return Object.clone(this._object);
  }

  /**
   *  Hash#keys() -> [String...]
   *
   *  Provides an Array containing the keys for items stored in the hash.
   *
   *  The order of the keys is not guaranteed.
   *
   *  ### Example
   *
   *      var h = $H({one: "uno", two: "due", three: "tre"});
   *      h.keys();
   *      // -> ["one", "three", "two] (these may be in any order)
  **/
  function keys() {
    return this.pluck('key');
  }

  /**
   *  Hash#values() -> Array
   *
   *  Collect the values of a hash and returns them in an array.
  **/
  function values() {
    return this.pluck('value');
  }

  /**
   *  Hash#index(value) -> String
   *
   *  Returns the first key in the hash whose value matches `value`.
   *  Returns `false` if there is no such key.
  **/
  function index(value) {
    var match = this.detect(function(pair) {
      return pair.value === value;
    });
    return match && match.key;
  }

  /**
   *  Hash#merge(object) -> Hash
   *  - object (Object | Hash): The object to merge with this hash to produce
   *    the resulting hash.
   *
   *  Returns a new `Hash` instance with `object`'s key/value pairs merged in;
   *  this hash remains unchanged.
   *
   *  To modify the original hash in place, use [[Hash#update]].
   *
   *  ### Example
   *
   *      var h = $H({one: "uno", two: "due"});
   *      var h2 = h.merge({three: "tre"});
   *      h.keys();
   *      // -> ["one", "two"] (unchanged)
   *      h2.keys();
   *      // -> ["one", "two", "three"] (has merged contents)
  **/
  function merge(object) {
    return this.clone().update(object);
  }

  /**
   *  Hash#update(object) -> Hash
   *
   *  Updates hash with the key/value pairs of `object`.
   *  The original hash will be modified. To return a new hash instead, use
   *  [[Hash#merge]].
  **/
  function update(object) {
    return new Hash(object).inject(this, function(result, pair) {
      result.set(pair.key, pair.value);
      return result;
    });
  }

  // Private. No PDoc necessary.
  function toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  /** related to: String#toQueryParams
   *  Hash#toQueryString() -> String
   *
   *  Turns a hash into its URL-encoded query string representation.
  **/
  function toQueryString() {
    return this.inject([], function(results, pair) {
      var key = encodeURIComponent(pair.key), values = pair.value;

      if (values && typeof values == 'object') {
        if (Object.isArray(values))
          return results.concat(values.map(toQueryPair.curry(key)));
      } else results.push(toQueryPair(key, values));
      return results;
    }).join('&');
  }

  /** related to: Object.inspect
   *  Hash#inspect() -> String
   *
   *  Returns the debug-oriented string representation of the Hash.
  **/
  function inspect() {
    return '#<Hash:{' + this.map(function(pair) {
      return pair.map(Object.inspect).join(': ');
    }).join(', ') + '}>';
  }

  /** related to: Object.toJSON
   *  Hash#toJSON() -> String
   *
   *  Returns a JSON string.
  **/
  function toJSON() {
    return Object.toJSON(this.toObject());
  }

  /**
   *  Hash#clone() -> Hash
   *
   *  Returns a clone of this Hash.
  **/
  function clone() {
    return new Hash(this);
  }

  return {
    initialize:             initialize,
    _each:                  _each,
    set:                    set,
    get:                    get,
    unset:                  unset,
    toObject:               toObject,
    toTemplateReplacements: toObject,
    keys:                   keys,
    values:                 values,
    index:                  index,
    merge:                  merge,
    update:                 update,
    toQueryString:          toQueryString,
    inspect:                inspect,
    toJSON:                 toJSON,
    clone:                  clone
  };
})());

Hash.from = $H;
<|endoftext|>