<commit_before>var Marionette         = require('backbone.marionette')
,   ServicesView       = require('./views/services')
,   ServiceDetailsView = require('./views/service_details')
,   AddServiceView     = require('./views/add_service')
,   EventsView         = require('./views/events')
,   EventDetailsView   = require('./views/event_details')
,   AddEventView       = require('./views/add_event')
,   HomeView           = require('./views/home')
;

module.exports = Controller = Marionette.Controller.extend({
    initialize: function() {
        App.core.vent.trigger('app:log', 'Controller: Initializing');
        window.App.views.servicesView = new ServicesView({ collection: window.App.data.services });
    },

    home: function() {
        App.core.vent.trigger('app:log', 'Controller: "Home" route hit.');
        var view = new HomeView({model: window.App.data.server});
        this.renderView(view);
        window.App.router.navigate('#');
    },

    listServices: function() {
        App.core.vent.trigger('app:log', 'Controller: "List Services" route hit.');
        var view = window.App.views.servicesView;
        this.renderView(view);
        window.App.router.navigate('#listServices');
    },

    serviceDetails: function(id) {
        App.core.vent.trigger('app:log', 'Controller: "Service Details" route hit.');
        var view = new ServiceDetailsView({ model: window.App.data.services.get(id)});
        this.renderView(view);
        window.App.router.navigate('serviceDetails/' + id);
    },

    addService: function() {
        App.core.vent.trigger('app:log', 'Controller: "Add Service" route hit.');
        var view = new AddServiceView();
        this.renderView(view);
        window.App.router.navigate('addService');
    },

    listEvents: function() {
        App.core.vent.trigger('app:log', 'Controller: "List Events" route hit.');
        var view = new EventsView({ collection: window.App.data.services, eventsCol: window.App.data.events });
        this.renderView(view);
        window.App.router.navigate('#listEvents');
    },

    eventDetails : function(id) {
        App.core.vent.trigger('app:log', 'Controller: "Event Details" route hit.');
        var view = new EventDetailsView({ model: window.App.data.events.get(id)});
        this.renderView(view);
        window.App.router.navigate('eventDetails/' + id);
    },
    addEvent: function() {
        App.core.vent.trigger('app:log', 'Controller: "Add Event" route hit.');
        var view = new AddEventView({ collection: window.App.data.services});
        this.renderView(view);
        window.App.router.navigate('addEvent');
    },

    renderView: function(view) {
        this.destroyCurrentView(view);
        App.core.vent.trigger('app:log', 'Controller: Rendering new view.');
        $('#js-epg-app').html(view.render().el);
    },

    destroyCurrentView: function(view) {
        if (!_.isUndefined(window.App.views.currentView)) {
            App.core.vent.trigger('app:log', 'Controller: Destroying existing view.');
            window.App.views.currentView.close();
        }
        window.App.views.currentView = view;
    }
});
<commit_msg>Always fetch when showing server properties<commit_after>var Marionette         = require('backbone.marionette')
,   ServicesView       = require('./views/services')
,   ServiceDetailsView = require('./views/service_details')
,   AddServiceView     = require('./views/add_service')
,   EventsView         = require('./views/events')
,   EventDetailsView   = require('./views/event_details')
,   AddEventView       = require('./views/add_event')
,   HomeView           = require('./views/home')
;

module.exports = Controller = Marionette.Controller.extend({
    initialize: function() {
        App.core.vent.trigger('app:log', 'Controller: Initializing');
        window.App.views.servicesView = new ServicesView({ collection: window.App.data.services });
    },

    home: function() {
        App.core.vent.trigger('app:log', 'Controller: "Home" route hit.');
        var self = this;
        window.App.data.server.fetch().always(function() {
            var view = new HomeView({model: window.App.data.server});
            self.renderView(view);
            window.App.router.navigate('#');
        });
    },

    listServices: function() {
        App.core.vent.trigger('app:log', 'Controller: "List Services" route hit.');
        var view = window.App.views.servicesView;
        this.renderView(view);
        window.App.router.navigate('#listServices');
    },

    serviceDetails: function(id) {
        App.core.vent.trigger('app:log', 'Controller: "Service Details" route hit.');
        var view = new ServiceDetailsView({ model: window.App.data.services.get(id)});
        this.renderView(view);
        window.App.router.navigate('serviceDetails/' + id);
    },

    addService: function() {
        App.core.vent.trigger('app:log', 'Controller: "Add Service" route hit.');
        var view = new AddServiceView();
        this.renderView(view);
        window.App.router.navigate('addService');
    },

    listEvents: function() {
        App.core.vent.trigger('app:log', 'Controller: "List Events" route hit.');
        var view = new EventsView({ collection: window.App.data.services, eventsCol: window.App.data.events });
        this.renderView(view);
        window.App.router.navigate('#listEvents');
    },

    eventDetails : function(id) {
        App.core.vent.trigger('app:log', 'Controller: "Event Details" route hit.');
        var view = new EventDetailsView({ model: window.App.data.events.get(id)});
        this.renderView(view);
        window.App.router.navigate('eventDetails/' + id);
    },
    addEvent: function() {
        App.core.vent.trigger('app:log', 'Controller: "Add Event" route hit.');
        var view = new AddEventView({ collection: window.App.data.services});
        this.renderView(view);
        window.App.router.navigate('addEvent');
    },

    renderView: function(view) {
        this.destroyCurrentView(view);
        App.core.vent.trigger('app:log', 'Controller: Rendering new view.');
        $('#js-epg-app').html(view.render().el);
    },

    destroyCurrentView: function(view) {
        if (!_.isUndefined(window.App.views.currentView)) {
            App.core.vent.trigger('app:log', 'Controller: Destroying existing view.');
            window.App.views.currentView.close();
        }
        window.App.views.currentView = view;
    }
});
<|endoftext|>


<commit_before>define([
    'jquery',
    'underscore',
    'orotranslation/js/translator',
    'oroui/js/app/views/base/view',
    'module',
    'oroui/js/tools',
    'orofilter/js/filter-template',
    'orofilter/js/filter-hint'
], function($, _, __, BaseView, module, tools, FilterTemplate, FilterHint) {
    'use strict';

    var config = module.config();
    config = _.extend({
        placeholder: __('All'),
        labelPrefix: ''
    }, config);

    var AbstractFilter;

    /**
     * Basic grid filter
     *
     * @export  oro/filter/abstract-filter
     * @class   oro.filter.AbstractFilter
     * @extends Backbone.View
     */
    AbstractFilter = BaseView.extend(_.extend({}, FilterTemplate, {
        /**
         * Is filter can be disabled
         *
         * @property {Boolean}
         */
        canDisable: true,

        /**
         * Is filter enabled
         *
         * @property {Boolean}
         */
        enabled: false,

        /**
         * Is filter visible in UI
         *
         * @property {Boolean}
         */
        visible: true,

        /**
         * Is filter enabled by default
         *
         * @property {Boolean}
         */
        defaultEnabled: false,

        /**
         * Name of filter field
         *
         * @property {String}
         */
        name: 'input_name',

        /**
         * Placeholder for default value
         *
         * @property
         */
        placeholder: config.placeholder,

        /**
         * Label of filter
         *
         * @property {String}
         */
        label: __('Input Label'),

        /**
         * Label prefix of filter
         *
         * @property {String}
         */
        labelPrefix: config.labelPrefix,

        /**
         * Is filter label visible
         *
         * @property {Boolean}
         */
        showLabel: true,

        /**
         * Parent element active class
         *
         * @property {String}
         */
        buttonActiveClass: 'open-filter',

        /**
         * Element enclosing a criteria dropdown
         *
         * @property {Array.<string|jQuery|HTMLElement>}
         */
        dropdownFitContainers: ['.ui-dialog-content>*:first-child', '#container', 'body'],

        /**
         * Allow clear selected value
         *
         * @property {Boolean}
         */
        allowClear: true,

        /**
         * Initialize.
         *
         * @param {Object} options
         * @param {Boolean} [options.enabled]
         */
        initialize: function(options) {
            var opts = _.pick(options || {}, 'enabled', 'visible', 'canDisable', 'placeholder', 'showLabel', 'label',
                'templateSelector', 'templateTheme', 'template');
            _.extend(this, opts);

            this._defineTemplate();

            this.defaultEnabled = this.enabled;

            // init empty value object if it was not initialized so far
            if (_.isUndefined(this.emptyValue)) {
                this.emptyValue = {};
            }
            // init raw value of filter
            this.value = tools.deepClone(this.emptyValue);

            AbstractFilter.__super__.initialize.apply(this, arguments);

            var hintView = new FilterHint({
                filter: this
            });

            this.subview('hint', hintView);

            this.listenTo(hintView, 'reset', this.reset);
        },

        isRendered: function() {
            return this._isRendered;
        },

        rendered: function() {
            this._isRendered = true;
            this.subview('hint').render();
        },

        /**
         * @inheritDoc
         */
        dispose: function() {
            if (this.disposed) {
                return;
            }
            delete this.value;
            delete this.emptyValue;
            AbstractFilter.__super__.dispose.call(this);
        },

        /**
         * Enable filter
         *
         * @return {*}
         */
        enable: function() {
            if (!this.enabled) {
                this.enabled = true;
                this.show();
                this.trigger('enable', this);
            }
            return this;
        },

        /**
         * Disable filter
         *
         * @return {*}
         */
        disable: function() {
            if (this.enabled) {
                this.enabled = false;
                this.hide();
                this.trigger('disable', this);
                this.reset();
            }
            return this;
        },

        /**
         * Show filter
         *
         * @return {*}
         */
        show: function() {
            if (this.visible) {
                this.$el.css('display', 'inline-block');
            }
            return this;
        },

        /**
         * Hide filter
         *
         * @return {*}
         */
        hide: function() {
            this.$el.hide();
            return this;
        },

        close: function() {},

        /**
         * Reset filter elements
         *
         * @return {*}
         */
        reset: function() {
            this.setValue(this.emptyValue);
            return this;
        },

        /**
         * Get clone of current raw value
         *
         * @return {Object}
         */
        getValue: function() {
            return tools.deepClone(this.value);
        },

        /**
         * Set raw value to filter
         *
         * @param value
         * @return {*}
         */
        setValue: function(value) {
            if (!tools.isEqualsLoosely(this.value, value)) {
                var oldValue = this.value;
                this.value = tools.deepClone(value);
                this._updateDOMValue();
                this._onValueUpdated(this.value, oldValue);
            }
            return this;
        },

        /**
         * Find element that dropdown of filter should fit to
         *
         * @param {string|jQuery|HTMLElement} element
         * @return {*}
         */
        _findDropdownFitContainer: function(element) {
            element = element || this.$el;
            var $container = $();
            for (var i = 0; i < this.dropdownFitContainers.length && $container.length === 0; i += 1) {
                $container = $(element).closest(this.dropdownFitContainers[i]);
            }
            return $container.length === 0 ? null : $container;
        },

        /**
         * Converts a display value to raw format, e.g. decimal value can be displayed as "5,000,000.00"
         * but raw value is 5000000.0
         *
         * @param {*} value
         * @return {*}
         * @protected
         */
        _formatRawValue: function(value) {
            return value;
        },

        /**
         * Converts a raw value to display format, opposite to _formatRawValue
         *
         * @param {*} value
         * @return {*}
         * @protected
         */
        _formatDisplayValue: function(value) {
            return value;
        },

        /**
         * Triggers when filter value is updated
         *
         * @param {*} newValue
         * @param {*} oldValue
         * @protected
         */
        _onValueUpdated: function(newValue, oldValue) {
            this._updateCriteriaHint();
            this._triggerUpdate(newValue, oldValue);
        },

        /**
         * Updates criteria hint element with actual criteria hint value
         *
         * @protected
         * @return {*}
         */
        _updateCriteriaHint: function() {
            this.subview('hint').update(this._getCriteriaHint());
            this.$el.find('.filter-criteria-selector')
                .toggleClass('filter-default-value', this.isEmptyValue());
            return this;
        },

        /**
         * Triggers update event
         *
         * @param {*} newValue
         * @param {*} oldValue
         * @protected
         */
        _triggerUpdate: function(newValue, oldValue) {
            this.trigger('update');
        },

        /**
         * Compares current value with empty value
         *
         * @return {Boolean}
         */
        isEmpty: function() {
            return tools.isEqualsLoosely(this.getValue(), this.emptyValue);
        },

        /**
         * Determines whether a filter value is empty or not
         * Unlike isEmpty method this method should take in account only data values.
         * For example if a filter has a string value and comparison type, the comparison type
         * should be ignored in this method.
         *
         * @return {Boolean}
         */
        isEmptyValue: function() {
            if (_.has(this.emptyValue, 'value') && _.has(this.value, 'value')) {
                return tools.isEqualsLoosely(this.value.value, this.emptyValue.value);
            }
            return true;
        },

        /**
         * Gets input value. Radio inputs are supported.
         *
         * @param {String|Object} input
         * @return {*}
         * @protected
         */
        _getInputValue: function(input) {
            var result;
            var $input = this.$(input);
            switch ($input.attr('type')) {
                case 'radio':
                    $input.each(function() {
                        if ($(this).is(':checked')) {
                            result = $(this).val();
                        }
                    });
                    break;
                default:
                    result = $input.val();

            }
            return result;
        },

        /**
         * Sets input value. Radio inputs are supported.
         *
         * @param {String|Object} input
         * @param {String} value
         * @protected
         * @return {*}
         */
        _setInputValue: function(input, value) {
            var $input = this.$(input);
            switch ($input.attr('type')) {
                case 'radio':
                    $input.each(function() {
                        var $input = $(this);
                        if ($input.attr('value') === value) {
                            $input.prop('checked', true);
                            $input.click();
                        } else {
                            $input.prop('checked', false);
                        }
                    });
                    break;
                default:
                    $input.val(value);

            }
            return this;
        },

        /**
         * Updated DOM value with current display value
         *
         * @return {*}
         * @protected
         */
        _updateDOMValue: function() {
            return this._writeDOMValue(this._getDisplayValue());
        },

        /**
         * Get criteria hint value
         *
         * @return {String}
         */
        _getCriteriaHint: function() {
            return '';
        },

        /**
         * Get current value formatted to display format
         *
         * @return {*}
         * @protected
         */
        _getDisplayValue: function() {
            var value = (arguments.length > 0) ? arguments[0] : this.getValue();
            return this._formatDisplayValue(value);
        },

        /**
         * Writes values from object into DOM elements
         *
         * @param {Object} value
         * @abstract
         * @protected
         * @return {*}
         */
        _writeDOMValue: function(value) {
            throw new Error('Method _writeDOMValue is abstract and must be implemented');
            //this._setInputValue(inputValueSelector, value.value);
            //return this
        },

        /**
         * Reads value of DOM elements into object
         *
         * @return {Object}
         * @protected
         */
        _readDOMValue: function() {
            throw new Error('Method _readDOMValue is abstract and must be implemented');
            //return { value: this._getInputValue(this.inputValueSelector) }
        },

        /**
         * Set filter button class
         *
         * @param {Object} element
         * @param {Boolean} status
         * @protected
         */
        _setButtonPressed: function(element, status) {
            if (status) {
                element.parent().addClass(this.buttonActiveClass);
            } else {
                element.parent().removeClass(this.buttonActiveClass);
            }
        },

        /**
         * Prevent submit of parent form if any.
         *
         * @param {Event} e
         * @private
         */
        _preventEnterProcessing: function(e) {
            if (e.keyCode === 13) {
                e.preventDefault();
                e.stopPropagation();
            }
        },

        /**
         * Apply changes manually
         *
         * @public
         */
        applyValue: function() {
            this.setValue(this._formatRawValue(this._readDOMValue()));
        },

        getState: function() {
            return {
                label: this.label,
                hint: this._getCriteriaHint()
            };
        }
    }));

    return AbstractFilter;
});
<commit_msg>BB-9153: Admin > Products > Product View page > Product prices section - request "All" missing for Price list in the Manage filters (#11648)<commit_after>define([
    'jquery',
    'underscore',
    'orotranslation/js/translator',
    'oroui/js/app/views/base/view',
    'module',
    'oroui/js/tools',
    'orofilter/js/filter-template',
    'orofilter/js/filter-hint'
], function($, _, __, BaseView, module, tools, FilterTemplate, FilterHint) {
    'use strict';

    var config = module.config();
    config = _.extend({
        placeholder: __('All'),
        labelPrefix: ''
    }, config);

    var AbstractFilter;

    /**
     * Basic grid filter
     *
     * @export  oro/filter/abstract-filter
     * @class   oro.filter.AbstractFilter
     * @extends Backbone.View
     */
    AbstractFilter = BaseView.extend(_.extend({}, FilterTemplate, {
        /**
         * Is filter can be disabled
         *
         * @property {Boolean}
         */
        canDisable: true,

        /**
         * Is filter enabled
         *
         * @property {Boolean}
         */
        enabled: false,

        /**
         * Is filter visible in UI
         *
         * @property {Boolean}
         */
        visible: true,

        /**
         * Is filter enabled by default
         *
         * @property {Boolean}
         */
        defaultEnabled: false,

        /**
         * Name of filter field
         *
         * @property {String}
         */
        name: 'input_name',

        /**
         * Placeholder for default value
         *
         * @property
         */
        placeholder: config.placeholder,

        /**
         * Label of filter
         *
         * @property {String}
         */
        label: __('Input Label'),

        /**
         * Label prefix of filter
         *
         * @property {String}
         */
        labelPrefix: config.labelPrefix,

        /**
         * Is filter label visible
         *
         * @property {Boolean}
         */
        showLabel: true,

        /**
         * Parent element active class
         *
         * @property {String}
         */
        buttonActiveClass: 'open-filter',

        /**
         * Element enclosing a criteria dropdown
         *
         * @property {Array.<string|jQuery|HTMLElement>}
         */
        dropdownFitContainers: ['.ui-dialog-content>*:first-child', '#container', 'body'],

        /**
         * Allow clear selected value
         *
         * @property {Boolean}
         */
        allowClear: true,

        /**
         * Initialize.
         *
         * @param {Object} options
         * @param {Boolean} [options.enabled]
         */
        initialize: function(options) {
            var opts = _.pick(options || {}, 'enabled', 'visible', 'canDisable', 'placeholder', 'showLabel', 'label',
                'templateSelector', 'templateTheme', 'template');
            _.extend(this, opts);

            this._defineTemplate();

            this.defaultEnabled = this.enabled;

            // init empty value object if it was not initialized so far
            if (_.isUndefined(this.emptyValue)) {
                this.emptyValue = {};
            }
            // init raw value of filter if it was not initialized
            if (_.isUndefined(this.value)) {
                this.value = tools.deepClone(this.emptyValue);
            }

            AbstractFilter.__super__.initialize.apply(this, arguments);

            var hintView = new FilterHint({
                filter: this
            });

            this.subview('hint', hintView);

            this.listenTo(hintView, 'reset', this.reset);
        },

        isRendered: function() {
            return this._isRendered;
        },

        rendered: function() {
            this._isRendered = true;
            this.subview('hint').render();
        },

        /**
         * @inheritDoc
         */
        dispose: function() {
            if (this.disposed) {
                return;
            }
            delete this.value;
            delete this.emptyValue;
            AbstractFilter.__super__.dispose.call(this);
        },

        /**
         * Enable filter
         *
         * @return {*}
         */
        enable: function() {
            if (!this.enabled) {
                this.enabled = true;
                this.show();
                this.trigger('enable', this);
            }
            return this;
        },

        /**
         * Disable filter
         *
         * @return {*}
         */
        disable: function() {
            if (this.enabled) {
                this.enabled = false;
                this.hide();
                this.trigger('disable', this);
                this.reset();
            }
            return this;
        },

        /**
         * Show filter
         *
         * @return {*}
         */
        show: function() {
            if (this.visible) {
                this.$el.css('display', 'inline-block');
            }
            return this;
        },

        /**
         * Hide filter
         *
         * @return {*}
         */
        hide: function() {
            this.$el.hide();
            return this;
        },

        close: function() {},

        /**
         * Reset filter elements
         *
         * @return {*}
         */
        reset: function() {
            this.setValue(this.emptyValue);
            return this;
        },

        /**
         * Get clone of current raw value
         *
         * @return {Object}
         */
        getValue: function() {
            return tools.deepClone(this.value);
        },

        /**
         * Set raw value to filter
         *
         * @param value
         * @return {*}
         */
        setValue: function(value) {
            if (!tools.isEqualsLoosely(this.value, value)) {
                var oldValue = this.value;
                this.value = tools.deepClone(value);
                this._updateDOMValue();
                this._onValueUpdated(this.value, oldValue);
            }
            return this;
        },

        /**
         * Find element that dropdown of filter should fit to
         *
         * @param {string|jQuery|HTMLElement} element
         * @return {*}
         */
        _findDropdownFitContainer: function(element) {
            element = element || this.$el;
            var $container = $();
            for (var i = 0; i < this.dropdownFitContainers.length && $container.length === 0; i += 1) {
                $container = $(element).closest(this.dropdownFitContainers[i]);
            }
            return $container.length === 0 ? null : $container;
        },

        /**
         * Converts a display value to raw format, e.g. decimal value can be displayed as "5,000,000.00"
         * but raw value is 5000000.0
         *
         * @param {*} value
         * @return {*}
         * @protected
         */
        _formatRawValue: function(value) {
            return value;
        },

        /**
         * Converts a raw value to display format, opposite to _formatRawValue
         *
         * @param {*} value
         * @return {*}
         * @protected
         */
        _formatDisplayValue: function(value) {
            return value;
        },

        /**
         * Triggers when filter value is updated
         *
         * @param {*} newValue
         * @param {*} oldValue
         * @protected
         */
        _onValueUpdated: function(newValue, oldValue) {
            this._updateCriteriaHint();
            this._triggerUpdate(newValue, oldValue);
        },

        /**
         * Updates criteria hint element with actual criteria hint value
         *
         * @protected
         * @return {*}
         */
        _updateCriteriaHint: function() {
            this.subview('hint').update(this._getCriteriaHint());
            this.$el.find('.filter-criteria-selector')
                .toggleClass('filter-default-value', this.isEmptyValue());
            return this;
        },

        /**
         * Triggers update event
         *
         * @param {*} newValue
         * @param {*} oldValue
         * @protected
         */
        _triggerUpdate: function(newValue, oldValue) {
            this.trigger('update');
        },

        /**
         * Compares current value with empty value
         *
         * @return {Boolean}
         */
        isEmpty: function() {
            return tools.isEqualsLoosely(this.getValue(), this.emptyValue);
        },

        /**
         * Determines whether a filter value is empty or not
         * Unlike isEmpty method this method should take in account only data values.
         * For example if a filter has a string value and comparison type, the comparison type
         * should be ignored in this method.
         *
         * @return {Boolean}
         */
        isEmptyValue: function() {
            if (_.has(this.emptyValue, 'value') && _.has(this.value, 'value')) {
                return tools.isEqualsLoosely(this.value.value, this.emptyValue.value);
            }
            return true;
        },

        /**
         * Gets input value. Radio inputs are supported.
         *
         * @param {String|Object} input
         * @return {*}
         * @protected
         */
        _getInputValue: function(input) {
            var result;
            var $input = this.$(input);
            switch ($input.attr('type')) {
                case 'radio':
                    $input.each(function() {
                        if ($(this).is(':checked')) {
                            result = $(this).val();
                        }
                    });
                    break;
                default:
                    result = $input.val();

            }
            return result;
        },

        /**
         * Sets input value. Radio inputs are supported.
         *
         * @param {String|Object} input
         * @param {String} value
         * @protected
         * @return {*}
         */
        _setInputValue: function(input, value) {
            var $input = this.$(input);
            switch ($input.attr('type')) {
                case 'radio':
                    $input.each(function() {
                        var $input = $(this);
                        if ($input.attr('value') === value) {
                            $input.prop('checked', true);
                            $input.click();
                        } else {
                            $input.prop('checked', false);
                        }
                    });
                    break;
                default:
                    $input.val(value);

            }
            return this;
        },

        /**
         * Updated DOM value with current display value
         *
         * @return {*}
         * @protected
         */
        _updateDOMValue: function() {
            return this._writeDOMValue(this._getDisplayValue());
        },

        /**
         * Get criteria hint value
         *
         * @return {String}
         */
        _getCriteriaHint: function() {
            return '';
        },

        /**
         * Get current value formatted to display format
         *
         * @return {*}
         * @protected
         */
        _getDisplayValue: function() {
            var value = (arguments.length > 0) ? arguments[0] : this.getValue();
            return this._formatDisplayValue(value);
        },

        /**
         * Writes values from object into DOM elements
         *
         * @param {Object} value
         * @abstract
         * @protected
         * @return {*}
         */
        _writeDOMValue: function(value) {
            throw new Error('Method _writeDOMValue is abstract and must be implemented');
            //this._setInputValue(inputValueSelector, value.value);
            //return this
        },

        /**
         * Reads value of DOM elements into object
         *
         * @return {Object}
         * @protected
         */
        _readDOMValue: function() {
            throw new Error('Method _readDOMValue is abstract and must be implemented');
            //return { value: this._getInputValue(this.inputValueSelector) }
        },

        /**
         * Set filter button class
         *
         * @param {Object} element
         * @param {Boolean} status
         * @protected
         */
        _setButtonPressed: function(element, status) {
            if (status) {
                element.parent().addClass(this.buttonActiveClass);
            } else {
                element.parent().removeClass(this.buttonActiveClass);
            }
        },

        /**
         * Prevent submit of parent form if any.
         *
         * @param {Event} e
         * @private
         */
        _preventEnterProcessing: function(e) {
            if (e.keyCode === 13) {
                e.preventDefault();
                e.stopPropagation();
            }
        },

        /**
         * Apply changes manually
         *
         * @public
         */
        applyValue: function() {
            this.setValue(this._formatRawValue(this._readDOMValue()));
        },

        getState: function() {
            return {
                label: this.label,
                hint: this._getCriteriaHint()
            };
        }
    }));

    return AbstractFilter;
});
<|endoftext|>


<commit_before>/*globals Spinner, reqwest, store */

var dallinger = (function () {
  var dlgr = {};

  dlgr.skip_experiment = false;

  dlgr.getUrlParameter = function getUrlParameter(sParam) {
    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
      sURLVariables = sPageURL.split('&'),
      sParameterName,
      i;

    for (i = 0; i < sURLVariables.length; i++) {
      sParameterName = sURLVariables[i].split('=');
      if (sParameterName[0] === sParam) {
        return sParameterName[1] === undefined ? true : sParameterName[1];
      }
    }
  };

  dlgr.identity = {
    hitId: dlgr.getUrlParameter('hit_id'),
    workerId: dlgr.getUrlParameter('worker_id'),
    assignmentId: dlgr.getUrlParameter('assignment_id'),
    mode: dlgr.getUrlParameter('mode'),
    participantId: dlgr.getUrlParameter('participant_id')
  };

  dlgr.BusyForm = (function () {
    /**
    Loads a spinner as a visual cue that something is happening
    and disables any jQuery objects passed to freeze().
    **/

    var defaults = {
      spinnerSettings: {scale: 1.5}, // See http://spin.js.org/ for all settings
      spinnerID: 'spinner'  // ID for HTML element where spinner will be inserted
    };

    var BusyForm = function (options) {
      if (!(this instanceof BusyForm)) {
        return new BusyForm(options);
      }
      var settings = $.extend(true, {}, defaults, options);
      this.spinner = new Spinner(settings.spinnerSettings);
      this.target = document.getElementById(settings.spinnerID);
      if (this.target === null) {
        throw new Error(
          'Target HTML element for spinner with ID "' + settings.spinnerID +
          '" does not exist.');
      }
      this.$elements = [];
    };

    BusyForm.prototype.freeze = function ($elements) {
      this.$elements = $elements;
      this.$elements.forEach(function ($element) {
        $element.attr("disabled", true);
      });
      this.spinner.spin(this.target);
    };

    BusyForm.prototype.unfreeze = function () {
      this.$elements.forEach(function ($element) {
        $element.attr("disabled", false);
      });
      this.spinner.stop();
      this.$elements = [];
    };

    return BusyForm;
  }());

  // stop people leaving the page, but only if desired by experiment
  dlgr.allowExitOnce = false;
  dlgr.preventExit = false;
  window.addEventListener('beforeunload', function(e) {
    if (dlgr.preventExit && !dlgr.allowExitOnce) {
      var returnValue = "Warning: the study is not yet finished. " +
        "Closing the window, refreshing the page or navigating elsewhere " +
        "might prevent you from finishing the experiment.";
      e.returnValue = returnValue;
      return returnValue;
    } else {
      dlgr.allowExitOnce = false;
      return undefined;
    }
  });
  // allow actions to leave the page
  dlgr.allowExit = function() {
    dlgr.allowExitOnce = true;
  };

  // advance the participant to a given html page
  dlgr.goToPage = function(page) {
    window.location = "/" + page + "?participant_id=" + dlgr.identity.participantId;
  };

  // AJAX helpers

  var ajax = function (method, route, data) {
    var deferred = $.Deferred();
    var options = {
      url: route,
      method: method,
      type: 'json',
      success: function (resp) { deferred.resolve(resp); },
      error: function (err) {
        console.log(err);
        var errorResponse = JSON.parse(err.response);
        if (errorResponse.hasOwnProperty("html")) {
          $("body").html(errorResponse.html);
        }
        deferred.reject(err);
      }
    };
    if (data !== undefined) {
      options.data = data;
    }
    reqwest(options);
    return deferred;
  };

  dlgr.get = function (route, data) {
    return ajax('get', route, data);
  };

  dlgr.post = function (route, data) {
    return ajax('post', route, data);
  };

  // report assignment complete
  dlgr.submitAssignment = function() {
    var deferred = $.Deferred();
    dlgr.get('/participant/' + dlgr.identity.participantId).done(function (resp) {
      dlgr.identity.mode = resp.participant.mode;
      dlgr.identity.hitId = resp.participant.hit_id;
      dlgr.identity.assignmentId = resp.participant.assignment_id;
      dlgr.identity.workerId = resp.participant.worker_id;
      var workerComplete = '/worker_complete';
      dlgr.get('/worker_complete', {
        'participant_id': dlgr.identity.participantId
      }).done(function (resp) {
        deferred.resolve();
        dallinger.allowExit();
        window.location = "/complete";
      }).fail(function (err) {
        deferred.reject(err);
      });
    }).fail(function (err) {
      deferred.reject(err);
    });
    return deferred;
  };

  // make a new participant
var create_participant = function() {
    var url;

    new Fingerprint2().get(function(result){
      fingerprint_hash = result;
      store.set("fingerprint_hash", fingerprint_hash)
    });

    // check if the local store is available, and if so, use it.
    if (typeof store != "undefined") {
        url = "/participant/" +
            store.get("worker_id") + "/" +
            store.get("hit_id") + "/" +
            store.get("assignment_id") + "/" +
            store.get("mode") + "?fingerprint_hash=" +
            store.get("fingerprint_hash");
    } else {
        url = "/participant/" +
            dlgr.identity.worker_id + "/" +
            dlgr.identity.hit_id + "/" +
            dlgr.identity.assignment_id + "/" +
            dlgr.identity.mode + "?fingerprint_hash=" +
            fingerprint_hash;
    }

    var deferred = $.Deferred();
    if (dlgr.identity.participantId !== undefined && dlgr.identity.participantId !== 'undefined') {
      deferred.resolve();
    } else {
      $(function () {
        $('.btn-success').prop('disabled', true);
        dlgr.post(url).done(function (resp) {
          console.log(resp);
          $('.btn-success').prop('disabled', false);
          dlgr.identity.participantId = resp.participant.id;
          if (resp.quorum && resp.quorum.n !== resp.quorum.q) {
            if (resp.quorum.n > resp.quorum.q) {
              dlgr.skip_experiment = true;
              // reached quorum; resolve immediately
              deferred.resolve();
            } else {
              // wait for quorum, then resolve
              dlgr.updateProgressBar(resp.quorum.n, resp.quorum.q);
              dlgr.waitForQuorum().done(function () {
                deferred.resolve();
              });
            }
          } else {
            // no quorum; resolve immediately
            deferred.resolve();
          }
        });
      });
    }
    return deferred;
  };

  dlgr.createAgent = function () {
    return dlgr.post('/node/' + dallinger.identity.participantId);
  };

  dlgr.createInfo = function (nodeId, data) {
    return dlgr.post('/info/' + nodeId, data);
  };

  dlgr.getExperimentProperty = function (prop) {
    return dlgr.get('/experiment/' + prop);
  };

  dlgr.getInfo = function (nodeId, infoId) {
    return dlgr.get('/info/' + nodeId + '/' + infoId);
  };

  dlgr.getInfos = function (nodeId) {
    return dlgr.get('/node/' + nodeId + '/infos');
  };

  dlgr.getReceivedInfos = function (nodeId) {
    return dlgr.get('/node/' + nodeId + '/received_infos');
  };

  dlgr.getTransmissions = function (nodeId, data) {
    return dlgr.get('/node/' + nodeId + '/transmissions', data);
  };

  dlgr.submitQuestionnaire = function (name) {
    var formSerialized = $("form").serializeArray(),
        spinner = dlgr.BusyForm(),
        formDict = {},
        xhr;

    formSerialized.forEach(function (field) {
      formDict[field.name] = field.value;
    });

    xhr = dlgr.post('/question/' + dlgr.identity.participantId, {
      question: name || "questionnaire",
      number: 1,
      response: JSON.stringify(formDict)
    });
    spinner.freeze([$('form :input')]);
    xhr.done(dlgr.submitAssignment);
    xhr.always(function () { spinner.unfreeze(); });
  };

  dlgr.waitForQuorum = function () {
    var ws_scheme = (window.location.protocol === "https:") ? 'wss://' : 'ws://';
    var socket = new ReconnectingWebSocket(ws_scheme + location.host + "/chat?channel=quorum");
    var deferred = $.Deferred();
    socket.onmessage = function (msg) {
      if (msg.data.indexOf('quorum:') !== 0) { return; }
      var data = JSON.parse(msg.data.substring(7));
      var n = data.n;
      var quorum = data.q;
      dlgr.updateProgressBar(n, quorum);
      if (n === quorum) {
        deferred.resolve();
      }
    };
    return deferred;
  };

  dlgr.updateProgressBar = function (value, total) {
    var percent = Math.round((value / total) * 100.0) + '%';
    $("#waiting-progress-bar").css("width", percent);
    $("#progress-percentage").text(percent);
  };

  return dlgr;
}());
<commit_msg>Include createParticipant in exported dallinger module object, and general cleanup<commit_after>/*globals Spinner, Fingerprint2, ReconnectingWebSocket, reqwest, store */

var dallinger = (function () {
  var dlgr = {};

  dlgr.skip_experiment = false;

  dlgr.getUrlParameter = function getUrlParameter(sParam) {
    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
      sURLVariables = sPageURL.split('&'),
      sParameterName,
      i;

    for (i = 0; i < sURLVariables.length; i++) {
      sParameterName = sURLVariables[i].split('=');
      if (sParameterName[0] === sParam) {
        return sParameterName[1] === undefined ? true : sParameterName[1];
      }
    }
  };

  dlgr.identity = {
    hitId: dlgr.getUrlParameter('hit_id'),
    workerId: dlgr.getUrlParameter('worker_id'),
    assignmentId: dlgr.getUrlParameter('assignment_id'),
    mode: dlgr.getUrlParameter('mode'),
    participantId: dlgr.getUrlParameter('participant_id')
  };

  dlgr.BusyForm = (function () {
    /**
    Loads a spinner as a visual cue that something is happening
    and disables any jQuery objects passed to freeze().
    **/

    var defaults = {
      spinnerSettings: {scale: 1.5}, // See http://spin.js.org/ for all settings
      spinnerID: 'spinner'  // ID for HTML element where spinner will be inserted
    };

    var BusyForm = function (options) {
      if (!(this instanceof BusyForm)) {
        return new BusyForm(options);
      }
      var settings = $.extend(true, {}, defaults, options);
      this.spinner = new Spinner(settings.spinnerSettings);
      this.target = document.getElementById(settings.spinnerID);
      if (this.target === null) {
        throw new Error(
          'Target HTML element for spinner with ID "' + settings.spinnerID +
          '" does not exist.');
      }
      this.$elements = [];
    };

    BusyForm.prototype.freeze = function ($elements) {
      this.$elements = $elements;
      this.$elements.forEach(function ($element) {
        $element.attr("disabled", true);
      });
      this.spinner.spin(this.target);
    };

    BusyForm.prototype.unfreeze = function () {
      this.$elements.forEach(function ($element) {
        $element.attr("disabled", false);
      });
      this.spinner.stop();
      this.$elements = [];
    };

    return BusyForm;
  }());

  // stop people leaving the page, but only if desired by experiment
  dlgr.allowExitOnce = false;
  dlgr.preventExit = false;
  window.addEventListener('beforeunload', function(e) {
    if (dlgr.preventExit && !dlgr.allowExitOnce) {
      var returnValue = "Warning: the study is not yet finished. " +
        "Closing the window, refreshing the page or navigating elsewhere " +
        "might prevent you from finishing the experiment.";
      e.returnValue = returnValue;
      return returnValue;
    } else {
      dlgr.allowExitOnce = false;
      return undefined;
    }
  });
  // allow actions to leave the page
  dlgr.allowExit = function() {
    dlgr.allowExitOnce = true;
  };

  // advance the participant to a given html page
  dlgr.goToPage = function(page) {
    window.location = "/" + page + "?participant_id=" + dlgr.identity.participantId;
  };

  // AJAX helpers

  var ajax = function (method, route, data) {
    var deferred = $.Deferred();
    var options = {
      url: route,
      method: method,
      type: 'json',
      success: function (resp) { deferred.resolve(resp); },
      error: function (err) {
        console.log(err);
        var errorResponse = JSON.parse(err.response);
        if (errorResponse.hasOwnProperty("html")) {
          $("body").html(errorResponse.html);
        }
        deferred.reject(err);
      }
    };
    if (data !== undefined) {
      options.data = data;
    }
    reqwest(options);
    return deferred;
  };

  dlgr.get = function (route, data) {
    return ajax('get', route, data);
  };

  dlgr.post = function (route, data) {
    return ajax('post', route, data);
  };

  // report assignment complete
  dlgr.submitAssignment = function() {
    var deferred = $.Deferred();
    dlgr.get('/participant/' + dlgr.identity.participantId).done(function (resp) {
      dlgr.identity.mode = resp.participant.mode;
      dlgr.identity.hitId = resp.participant.hit_id;
      dlgr.identity.assignmentId = resp.participant.assignment_id;
      dlgr.identity.workerId = resp.participant.worker_id;
      var workerComplete = '/worker_complete';
      dlgr.get('/worker_complete', {
        'participant_id': dlgr.identity.participantId
      }).done(function (resp) {
        deferred.resolve();
        dallinger.allowExit();
        window.location = "/complete";
      }).fail(function (err) {
        deferred.reject(err);
      });
    }).fail(function (err) {
      deferred.reject(err);
    });
    return deferred;
  };

  // make a new participant
  dlgr.createParticipant = function() {
    var deferred = $.Deferred(),
        fingerprint_hash,
        url;

    new Fingerprint2().get(function(result){
      fingerprint_hash = result;
      store.set("fingerprint_hash", fingerprint_hash);
    });

    // check if the local store is available, and if so, use it.
    if (typeof store !== "undefined") {
        url = "/participant/" +
            store.get("worker_id") + "/" +
            store.get("hit_id") + "/" +
            store.get("assignment_id") + "/" +
            store.get("mode") + "?fingerprint_hash=" +
            store.get("fingerprint_hash");
    } else {
        url = "/participant/" +
            dlgr.identity.worker_id + "/" +
            dlgr.identity.hit_id + "/" +
            dlgr.identity.assignment_id + "/" +
            dlgr.identity.mode + "?fingerprint_hash=" +
            fingerprint_hash;
    }

    if (dlgr.identity.participantId !== undefined && dlgr.identity.participantId !== 'undefined') {
      deferred.resolve();
    } else {
      $(function () {
        $('.btn-success').prop('disabled', true);
        dlgr.post(url).done(function (resp) {
          console.log(resp);
          $('.btn-success').prop('disabled', false);
          dlgr.identity.participantId = resp.participant.id;
          if (resp.quorum && resp.quorum.n !== resp.quorum.q) {
            if (resp.quorum.n > resp.quorum.q) {
              dlgr.skip_experiment = true;
              // reached quorum; resolve immediately
              deferred.resolve();
            } else {
              // wait for quorum, then resolve
              dlgr.updateProgressBar(resp.quorum.n, resp.quorum.q);
              dlgr.waitForQuorum().done(function () {
                deferred.resolve();
              });
            }
          } else {
            // no quorum; resolve immediately
            deferred.resolve();
          }
        });
      });
    }
    return deferred;
  };

  dlgr.createAgent = function () {
    return dlgr.post('/node/' + dallinger.identity.participantId);
  };

  dlgr.createInfo = function (nodeId, data) {
    return dlgr.post('/info/' + nodeId, data);
  };

  dlgr.getExperimentProperty = function (prop) {
    return dlgr.get('/experiment/' + prop);
  };

  dlgr.getInfo = function (nodeId, infoId) {
    return dlgr.get('/info/' + nodeId + '/' + infoId);
  };

  dlgr.getInfos = function (nodeId) {
    return dlgr.get('/node/' + nodeId + '/infos');
  };

  dlgr.getReceivedInfos = function (nodeId) {
    return dlgr.get('/node/' + nodeId + '/received_infos');
  };

  dlgr.getTransmissions = function (nodeId, data) {
    return dlgr.get('/node/' + nodeId + '/transmissions', data);
  };

  dlgr.submitQuestionnaire = function (name) {
    var formSerialized = $("form").serializeArray(),
        spinner = dlgr.BusyForm(),
        formDict = {},
        xhr;

    formSerialized.forEach(function (field) {
      formDict[field.name] = field.value;
    });

    xhr = dlgr.post('/question/' + dlgr.identity.participantId, {
      question: name || "questionnaire",
      number: 1,
      response: JSON.stringify(formDict)
    });
    spinner.freeze([$('form :input')]);
    xhr.done(dlgr.submitAssignment);
    xhr.always(function () { spinner.unfreeze(); });
  };

  dlgr.waitForQuorum = function () {
    var ws_scheme = (window.location.protocol === "https:") ? 'wss://' : 'ws://';
    var socket = new ReconnectingWebSocket(ws_scheme + location.host + "/chat?channel=quorum");
    var deferred = $.Deferred();
    socket.onmessage = function (msg) {
      if (msg.data.indexOf('quorum:') !== 0) { return; }
      var data = JSON.parse(msg.data.substring(7));
      var n = data.n;
      var quorum = data.q;
      dlgr.updateProgressBar(n, quorum);
      if (n === quorum) {
        deferred.resolve();
      }
    };
    return deferred;
  };

  dlgr.updateProgressBar = function (value, total) {
    var percent = Math.round((value / total) * 100.0) + '%';
    $("#waiting-progress-bar").css("width", percent);
    $("#progress-percentage").text(percent);
  };

  return dlgr;
}());
<|endoftext|>


<commit_before>'use strict';

var test = require('tape');
var inspect = require('object-inspect');
var SaferBuffer = require('safer-buffer').Buffer;
var forEach = require('for-each');
var utils = require('../lib/utils');

test('merge()', function (t) {
    t.deepEqual(utils.merge(null, true), [null, true], 'merges true into null');

    t.deepEqual(utils.merge(null, [42]), [null, 42], 'merges null into an array');

    t.deepEqual(utils.merge({ a: 'b' }, { a: 'c' }), { a: ['b', 'c'] }, 'merges two objects with the same key');

    var oneMerged = utils.merge({ foo: 'bar' }, { foo: { first: '123' } });
    t.deepEqual(oneMerged, { foo: ['bar', { first: '123' }] }, 'merges a standalone and an object into an array');

    var twoMerged = utils.merge({ foo: ['bar', { first: '123' }] }, { foo: { second: '456' } });
    t.deepEqual(twoMerged, { foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }, 'merges a standalone and two objects into an array');

    var sandwiched = utils.merge({ foo: ['bar', { first: '123', second: '456' }] }, { foo: 'baz' });
    t.deepEqual(sandwiched, { foo: ['bar', { first: '123', second: '456' }, 'baz'] }, 'merges an object sandwiched by two standalones into an array');

    var nestedArrays = utils.merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] });
    t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] });

    var noOptionsNonObjectSource = utils.merge({ foo: 'baz' }, 'bar');
    t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });

    t.test(
        'avoids invoking array setters unnecessarily',
        { skip: typeof Object.defineProperty !== 'function' },
        function (st) {
            var setCount = 0;
            var getCount = 0;
            var observed = [];
            Object.defineProperty(observed, 0, {
                get: function () {
                    getCount += 1;
                    return { bar: 'baz' };
                },
                set: function () { setCount += 1; }
            });
            utils.merge(observed, [null]);
            st.equal(setCount, 0);
            st.equal(getCount, 1);
            observed[0] = observed[0]; // eslint-disable-line no-self-assign
            st.equal(setCount, 1);
            st.equal(getCount, 2);
            st.end();
        }
    );

    t.end();
});

test('assign()', function (t) {
    var target = { a: 1, b: 2 };
    var source = { b: 3, c: 4 };
    var result = utils.assign(target, source);

    t.equal(result, target, 'returns the target');
    t.deepEqual(target, { a: 1, b: 3, c: 4 }, 'target and source are merged');
    t.deepEqual(source, { b: 3, c: 4 }, 'source is untouched');

    t.end();
});

test('combine()', function (t) {
    t.test('both arrays', function (st) {
        var a = [1];
        var b = [2];
        var combined = utils.combine(a, b);

        st.deepEqual(a, [1], 'a is not mutated');
        st.deepEqual(b, [2], 'b is not mutated');
        st.notEqual(a, combined, 'a !== combined');
        st.notEqual(b, combined, 'b !== combined');
        st.deepEqual(combined, [1, 2], 'combined is a + b');

        st.end();
    });

    t.test('one array, one non-array', function (st) {
        var aN = 1;
        var a = [aN];
        var bN = 2;
        var b = [bN];

        var combinedAnB = utils.combine(aN, b);
        st.deepEqual(b, [bN], 'b is not mutated');
        st.notEqual(aN, combinedAnB, 'aN + b !== aN');
        st.notEqual(a, combinedAnB, 'aN + b !== a');
        st.notEqual(bN, combinedAnB, 'aN + b !== bN');
        st.notEqual(b, combinedAnB, 'aN + b !== b');
        st.deepEqual([1, 2], combinedAnB, 'first argument is array-wrapped when not an array');

        var combinedABn = utils.combine(a, bN);
        st.deepEqual(a, [aN], 'a is not mutated');
        st.notEqual(aN, combinedABn, 'a + bN !== aN');
        st.notEqual(a, combinedABn, 'a + bN !== a');
        st.notEqual(bN, combinedABn, 'a + bN !== bN');
        st.notEqual(b, combinedABn, 'a + bN !== b');
        st.deepEqual([1, 2], combinedABn, 'second argument is array-wrapped when not an array');

        st.end();
    });

    t.test('neither is an array', function (st) {
        var combined = utils.combine(1, 2);
        st.notEqual(1, combined, '1 + 2 !== 1');
        st.notEqual(2, combined, '1 + 2 !== 2');
        st.deepEqual([1, 2], combined, 'both arguments are array-wrapped when not an array');

        st.end();
    });

    t.end();
});

test('isBuffer()', function (t) {
    forEach([null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], function () {}, /a/g], function (x) {
        t.equal(utils.isBuffer(x), false, inspect(x) + ' is not a buffer');
    });

    var fakeBuffer = { constructor: Buffer };
    t.equal(utils.isBuffer(fakeBuffer), false, 'fake buffer is not a buffer');

    var saferBuffer = SaferBuffer.from('abc');
    t.equal(utils.isBuffer(saferBuffer), true, 'SaferBuffer instance is a buffer');

    var buffer = Buffer.from ? Buffer.from('abc') : new Buffer('abc');
    t.equal(utils.isBuffer(buffer), true, 'real Buffer instance is a buffer');
    t.end();
});
<commit_msg>[Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray<commit_after>'use strict';

var test = require('tape');
var inspect = require('object-inspect');
var SaferBuffer = require('safer-buffer').Buffer;
var forEach = require('for-each');
var utils = require('../lib/utils');

test('merge()', function (t) {
    t.deepEqual(utils.merge(null, true), [null, true], 'merges true into null');

    t.deepEqual(utils.merge(null, [42]), [null, 42], 'merges null into an array');

    t.deepEqual(utils.merge({ a: 'b' }, { a: 'c' }), { a: ['b', 'c'] }, 'merges two objects with the same key');

    var oneMerged = utils.merge({ foo: 'bar' }, { foo: { first: '123' } });
    t.deepEqual(oneMerged, { foo: ['bar', { first: '123' }] }, 'merges a standalone and an object into an array');

    var twoMerged = utils.merge({ foo: ['bar', { first: '123' }] }, { foo: { second: '456' } });
    t.deepEqual(twoMerged, { foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }, 'merges a standalone and two objects into an array');

    var sandwiched = utils.merge({ foo: ['bar', { first: '123', second: '456' }] }, { foo: 'baz' });
    t.deepEqual(sandwiched, { foo: ['bar', { first: '123', second: '456' }, 'baz'] }, 'merges an object sandwiched by two standalones into an array');

    var nestedArrays = utils.merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] });
    t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] });

    var noOptionsNonObjectSource = utils.merge({ foo: 'baz' }, 'bar');
    t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });

    t.test(
        'avoids invoking array setters unnecessarily',
        { skip: typeof Object.defineProperty !== 'function' },
        function (st) {
            var setCount = 0;
            var getCount = 0;
            var observed = [];
            Object.defineProperty(observed, 0, {
                get: function () {
                    getCount += 1;
                    return { bar: 'baz' };
                },
                set: function () { setCount += 1; }
            });
            utils.merge(observed, [null]);
            st.equal(setCount, 0);
            st.equal(getCount, 1);
            observed[0] = observed[0]; // eslint-disable-line no-self-assign
            st.equal(setCount, 1);
            st.equal(getCount, 2);
            st.end();
        }
    );

    t.end();
});

test('assign()', function (t) {
    var target = { a: 1, b: 2 };
    var source = { b: 3, c: 4 };
    var result = utils.assign(target, source);

    t.equal(result, target, 'returns the target');
    t.deepEqual(target, { a: 1, b: 3, c: 4 }, 'target and source are merged');
    t.deepEqual(source, { b: 3, c: 4 }, 'source is untouched');

    t.end();
});

test('combine()', function (t) {
    t.test('both arrays', function (st) {
        var a = [1];
        var b = [2];
        var combined = utils.combine(a, b);

        st.deepEqual(a, [1], 'a is not mutated');
        st.deepEqual(b, [2], 'b is not mutated');
        st.notEqual(a, combined, 'a !== combined');
        st.notEqual(b, combined, 'b !== combined');
        st.deepEqual(combined, [1, 2], 'combined is a + b');

        st.end();
    });

    t.test('one array, one non-array', function (st) {
        var aN = 1;
        var a = [aN];
        var bN = 2;
        var b = [bN];

        var combinedAnB = utils.combine(aN, b);
        st.deepEqual(b, [bN], 'b is not mutated');
        st.notEqual(aN, combinedAnB, 'aN + b !== aN');
        st.notEqual(a, combinedAnB, 'aN + b !== a');
        st.notEqual(bN, combinedAnB, 'aN + b !== bN');
        st.notEqual(b, combinedAnB, 'aN + b !== b');
        st.deepEqual([1, 2], combinedAnB, 'first argument is array-wrapped when not an array');

        var combinedABn = utils.combine(a, bN);
        st.deepEqual(a, [aN], 'a is not mutated');
        st.notEqual(aN, combinedABn, 'a + bN !== aN');
        st.notEqual(a, combinedABn, 'a + bN !== a');
        st.notEqual(bN, combinedABn, 'a + bN !== bN');
        st.notEqual(b, combinedABn, 'a + bN !== b');
        st.deepEqual([1, 2], combinedABn, 'second argument is array-wrapped when not an array');

        st.end();
    });

    t.test('neither is an array', function (st) {
        var combined = utils.combine(1, 2);
        st.notEqual(1, combined, '1 + 2 !== 1');
        st.notEqual(2, combined, '1 + 2 !== 2');
        st.deepEqual([1, 2], combined, 'both arguments are array-wrapped when not an array');

        st.end();
    });

    t.end();
});

test('isBuffer()', function (t) {
    forEach([null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], function () {}, /a/g], function (x) {
        t.equal(utils.isBuffer(x), false, inspect(x) + ' is not a buffer');
    });

    var fakeBuffer = { constructor: Buffer };
    t.equal(utils.isBuffer(fakeBuffer), false, 'fake buffer is not a buffer');

    var saferBuffer = SaferBuffer.from('abc');
    t.equal(utils.isBuffer(saferBuffer), true, 'SaferBuffer instance is a buffer');

    var buffer = Buffer.from && Buffer.alloc ? Buffer.from('abc') : new Buffer('abc');
    t.equal(utils.isBuffer(buffer), true, 'real Buffer instance is a buffer');
    t.end();
});
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
({
    /**
     * Iterates over the options in the select element and returns a semicolon-delimited string of the selected values
     */
    getDomElementValue : function (el) {
        var selectedOptions = [];
        for (var i = 0; i < el.options.length; i++) {
            if (el.options[i].selected) {
                selectedOptions.push(el.options[i].value);
            }
        }
        return selectedOptions.join(";");
    },

    /**
     * Returns a package with the array of options (as either an array of components or an array of JS objects)
     * and the strategy to work with that array
     */
    getOptionsWithStrategy: function(cmp) {
        var opts = cmp.get("v.options"),
        	strat = this.optionsStrategy;

        if ($A.util.isEmpty(opts)) {
        	opts = cmp.get("v.body");
        	if ($A.util.isEmpty(opts)) {
        		return null;
        	}
        	strat = this.bodyStrategy;
        }

        return { options : opts, strategy : strat };
    },

    /**
     * Updates all options' "selected" attributes in the select element, based on the semicolon-delimited newValue string
     */
    updateOptionsFromValue: function(cmp) {
    	if (cmp._suspendChangeHandlers) {
    		return;
    	}
        var value = cmp.get("v.value"),
        	optionsPack = this.getOptionsWithStrategy(cmp),
        	selectedOptions = optionsPack.strategy.getSelected(optionsPack.options);

        if (optionsPack.options.length == 0) {
        	cmp._initOptionsFromValue = true;
        	return;
        }

        if (selectedOptions.found && value === selectedOptions.optionValue) {
            return;
        }

        var newValues = (cmp.get("v.value") || "").split(";");

        if (!optionsPack.strategy.updateOptions(optionsPack.options, newValues) && !($A.util.getBooleanValue(cmp.get("v.multiple")) && value == "")) {
        	this.updateValueFromOptions(cmp, optionsPack);
        } else {
        	cmp._suspendChangeHandlers = true;
        	optionsPack.strategy.persistOptions(cmp, optionsPack.options);
        	cmp._suspendChangeHandlers = false;
        }
    },

    /**
     * Updates this component's "value" attribute based on the state of its options' "selected" attributes
     */
    updateValueFromOptions: function(cmp, optionsPack) {
        if (cmp._suspendChangeHandlers) {
        	return;
    	}

        var value = cmp.get("v.value"),
        	isMultiple = $A.util.getBooleanValue(cmp.get("v.multiple")),
        	optionsPack = optionsPack || this.getOptionsWithStrategy(cmp),
        	selectedOptions = optionsPack.strategy.getSelected(optionsPack.options);

        if (!selectedOptions.found || value !== selectedOptions.optionValue) {
        	if (!isMultiple && !selectedOptions.found) {
        		selectedOptions.optionValue = optionsPack.strategy.getValue(optionsPack.options, 0);
        		optionsPack.strategy.setOptionSelected(optionsPack.options, 0, true);

        		cmp._suspendChangeHandlers = true;
            	optionsPack.strategy.persistOptions(cmp, optionsPack.options);
            	cmp._suspendChangeHandlers = false;
        	}
        	cmp.set("v.value", selectedOptions.optionValue, true);
        }
    },

    /**
     * Strategies for working with either an array of option objects or of body components, passed through to the
     * select component either through the "v.options" attribute or through the body in markup.
     * Abstracts the implementation away so that the logic specific to the two data structures can be separated from
     * the main component logic
     *
     * Main functions available:
     *   updateOptions(options, newValues) - updates the list of options based on newValues, which is either an array or a string
     *   	Used for ensuring consistency between "v.value" and the list of options
     *   getValues(options) - returns a ';'-concatenated String of selected values and whether a selected value was found
     *   	Used for seeing which options are selected from the perspective of the options
     *   getText(options, index) - returns the internal text of options[index]
     *   setOptionSelected(options, index, selected) - equivalent to options[index].selected = selected
     *   persistOptions(cmp, options) - persists the array of options into the appropriate component attribute
     */

    /**
     * Strategy object for an array of option objects
     */
    optionsStrategy: {
    	// If an option is in newValues, we want to select it
    	updateOptions : function(options, newValues) {
    		var found = false;

    		$A.util.forEach(options, function(option) {
    			var val = option.value;
    			var selectOption = (newValues.length > 1 && aura.util.arrayIndexOf(newValues, val) > -1) || newValues[0] == val.toString();

    			found = found || selectOption;
    			option.selected = selectOption;
    		}, this);

    		return found;
    	},
    	// If an option is selected, we want to aggregate it into our list
    	getSelected : function(options) {
    		var values = [];

    		$A.util.forEach(options, function(option) {
    			if (option.selected) {
    				values.push(option.value);
    			}
    		}, this);

    		return { found : (values.length > 0), optionValue : values.join(";") };
    	},
    	getValue : function(options, index) {
    		if (!$A.util.isUndefinedOrNull(options[index])) {
    			return options[index].value;
    		}
    		return undefined;
    	},
    	setOptionSelected : function(options, index, selected) {
    		if (!$A.util.isUndefinedOrNull(options[index])) {
    			options[index].selected = selected;
    		} else {
    			// TODO: somehow expose that the option couldn't be set.
    		}
    	},
    	persistOptions : function(cmp, options) {
    		cmp.set("v.options", options);
    	}
    },

    /**]
     * Strategy object for an array of components (used for maintaining support for using inputSelectOption components in the body)
     */
    bodyStrategy: {
    	// Updates options based on their existence in newValues
    	updateOptions : function(options, newValues) {
            var result = { found : false };
            // Perform single option update function on all of our options
    		this.performOperationOnCmps(options, this.updateOption, result, newValues);
    		return result.found;
    	},
    	getSelected : function(bodyCmps) {
    		var values = [];
    		this.performOperationOnCmps(bodyCmps, this.pushIfSelected, values);
    		return { found : (values.length > 0), optionValue : values.join(";") };
    	},
    	getValue : function(options, index) {
    		if (!$A.util.isUndefinedOrNull(options[index])) {
    			return options[index].get("v.text");
    		}
    		return undefined;
    	},
    	setOptionSelected : function(options, index, selected) {
    		if (!$A.util.isUndefinedOrNull(options[index])) {
    			options[index].set("v.value", selected);
    		} else {
    			// TODO: somehow expose that the option couldn't be set.
    		}
    	},
    	persistOptions : function(cmp, options) {
    		cmp.set("v.body", options);
    	},
    	// Performs op on every ui:inputSelectOption in opts, where op = function(optionCmp, resultsObject, optionalArguments)
    	performOperationOnCmps : function(opts, op, result, newValues) {
    		$A.util.forEach(opts, function(cmp) {
        		var descriptor = cmp.getDef().getDescriptor();
        		var cmpName = descriptor.getNamespace() + ":" + descriptor.getName();
        		if (cmpName === "ui:inputSelectOptionGroup") {
        			var groupBody = cmp.get("v.body");
        			if (!$A.util.isEmpty(groupBody)) {
        				$A.util.forEach(groupBody, function(groupBodyCmp) {
        					var descriptor = groupBodyCmp.getDef().getDescriptor();
        					if ((descriptor.getNamespace() + ":" + descriptor.getName()) === "ui:inputSelectOption") {
        						op(groupBodyCmp, result, newValues);
        					}
        				}, this);
        			}
        		} else if (cmpName = "ui:inputSelectOption") {
    				op(cmp, result, newValues);
    			}
        	}, this);
        },
        // Helper function for updateOptions
        // Update optionCmp if it exists in newValues; passes result back in result object
        updateOption : function(optionCmp, result, newValues) {
        	var text = optionCmp.get("v.text");
			var selectOption = (newValues.length > 1 && aura.util.arrayIndexOf(newValues, text) > -1) || newValues[0] === text;

			result.found = result.found || selectOption;
			optionCmp.set("v.value", selectOption);

        },
        // Helper function for getValues
        // Push optionCmp's value into valueList if selected
		pushIfSelected : function(optionCmp, valueList) {
			if ($A.util.getBooleanValue(optionCmp.get("v.value")) === true) {
				var text = optionCmp.get("v.text");
				if (!$A.util.isUndefined(text)) {
					valueList.push(text);
				}
			}
		}
    },

    /**
     * Render the options directly to the DOM for performance
     */
    renderOptions: function(cmp) {
    	var options = cmp.getConcreteComponent().get("v.options"),
			select = cmp.find("select").getElement(),
			optFrag, option, internalText;

		if ($A.util.isEmpty(options)) {
			return;
		}

    	optFrag = document.createDocumentFragment();
    	for (var i = 0; i < options.length; i++) {
    		option = document.createElement("option");
    		internalText = ($A.util.isEmpty(options[i].label) ? options[i].value : options[i].label) || "";

    		option.label = options[i].label || internalText;
    		option.value = options[i].value || internalText;

    		if (options[i]["class"]) {
    			option.setAttribute("class", options[i]["class"]);
    		}

    		if (options[i].selected) {
    			option.selected = "selected";
    		}

    		if (options[i].disabled) {
    			option.disabled = "disabled";
    		}

    		option.appendChild(document.createTextNode(internalText));

    		optFrag.appendChild(option);
    	}

    	while (select.firstChild) {
    		select.removeChild(select.firstChild);
    	}
    	select.appendChild(optFrag);
    }

})<commit_msg>Added checks for no options/body<commit_after>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
({
    /**
     * Iterates over the options in the select element and returns a semicolon-delimited string of the selected values
     */
    getDomElementValue : function (el) {
        var selectedOptions = [];
        for (var i = 0; i < el.options.length; i++) {
            if (el.options[i].selected) {
                selectedOptions.push(el.options[i].value);
            }
        }
        return selectedOptions.join(";");
    },

    /**
     * Returns a package with the array of options (as either an array of components or an array of JS objects)
     * and the strategy to work with that array
     */
    getOptionsWithStrategy: function(cmp) {
        var opts = cmp.get("v.options"),
        	strat = this.optionsStrategy;

        if ($A.util.isEmpty(opts)) {
        	opts = cmp.get("v.body");
        	if ($A.util.isEmpty(opts)) {
        		return null;
        	}
        	strat = this.bodyStrategy;
        }

        return { options : opts, strategy : strat };
    },

    /**
     * Updates all options' "selected" attributes in the select element, based on the semicolon-delimited newValue string
     */
    updateOptionsFromValue: function(cmp) {
    	if (cmp._suspendChangeHandlers) {
    		return;
    	}
        var optionsPack = this.getOptionsWithStrategy(cmp),
        	selectedOptions, value;

        if ($A.util.isUndefinedOrNull(optionsPack)) {
        	$A.warning("Attempted to update options on inputSelect with no options: " + cmp.getGlobalId());
        	return;
        }

        value = cmp.get("v.value");
        selectedOptions = optionsPack.strategy.getSelected(optionsPack.options);

        if (optionsPack.options.length == 0) {
        	cmp._initOptionsFromValue = true;
        	return;
        }

        if (selectedOptions.found && value === selectedOptions.optionValue) {
            return;
        }

        var newValues = (cmp.get("v.value") || "").split(";");

        if (!optionsPack.strategy.updateOptions(optionsPack.options, newValues) && !($A.util.getBooleanValue(cmp.get("v.multiple")) && value == "")) {
        	this.updateValueFromOptions(cmp, optionsPack);
        } else {
        	cmp._suspendChangeHandlers = true;
        	optionsPack.strategy.persistOptions(cmp, optionsPack.options);
        	cmp._suspendChangeHandlers = false;
        }
    },

    /**
     * Updates this component's "value" attribute based on the state of its options' "selected" attributes
     */
    updateValueFromOptions: function(cmp, optionsPack) {
        if (cmp._suspendChangeHandlers) {
        	return;
    	}

        var optionsPack = optionsPack || this.getOptionsWithStrategy(cmp),
        	selectedOptions, value, isMultiple;

        if ($A.util.isUndefinedOrNull(optionsPack)) {
        	$A.warning("Attempted to update value from options on inputSelect with no options: " + cmp.getGlobalId());
        	return;
        }

        value = cmp.get("v.value");
        isMultiple = $A.util.getBooleanValue(cmp.get("v.multiple"));
        selectedOptions = optionsPack.strategy.getSelected(optionsPack.options);

        if (!selectedOptions.found || value !== selectedOptions.optionValue) {
        	if (!isMultiple && !selectedOptions.found) {
        		selectedOptions.optionValue = optionsPack.strategy.getValue(optionsPack.options, 0);
        		optionsPack.strategy.setOptionSelected(optionsPack.options, 0, true);

        		cmp._suspendChangeHandlers = true;
            	optionsPack.strategy.persistOptions(cmp, optionsPack.options);
            	cmp._suspendChangeHandlers = false;
        	}
        	cmp.set("v.value", selectedOptions.optionValue, true);
        }
    },

    /**
     * Strategies for working with either an array of option objects or of body components, passed through to the
     * select component either through the "v.options" attribute or through the body in markup.
     * Abstracts the implementation away so that the logic specific to the two data structures can be separated from
     * the main component logic
     *
     * Main functions available:
     *   updateOptions(options, newValues) - updates the list of options based on newValues, which is either an array or a string
     *   	Used for ensuring consistency between "v.value" and the list of options
     *   getValues(options) - returns a ';'-concatenated String of selected values and whether a selected value was found
     *   	Used for seeing which options are selected from the perspective of the options
     *   getText(options, index) - returns the internal text of options[index]
     *   setOptionSelected(options, index, selected) - equivalent to options[index].selected = selected
     *   persistOptions(cmp, options) - persists the array of options into the appropriate component attribute
     */

    /**
     * Strategy object for an array of option objects
     */
    optionsStrategy: {
    	// If an option is in newValues, we want to select it
    	updateOptions : function(options, newValues) {
    		var found = false;

    		$A.util.forEach(options, function(option) {
    			var val = option.value;
    			var selectOption = (newValues.length > 1 && aura.util.arrayIndexOf(newValues, val) > -1) || newValues[0] == val.toString();

    			found = found || selectOption;
    			option.selected = selectOption;
    		}, this);

    		return found;
    	},
    	// If an option is selected, we want to aggregate it into our list
    	getSelected : function(options) {
    		var values = [];

    		$A.util.forEach(options, function(option) {
    			if (option.selected) {
    				values.push(option.value);
    			}
    		}, this);

    		return { found : (values.length > 0), optionValue : values.join(";") };
    	},
    	getValue : function(options, index) {
    		if (!$A.util.isUndefinedOrNull(options[index])) {
    			return options[index].value;
    		}
    		return undefined;
    	},
    	setOptionSelected : function(options, index, selected) {
    		if (!$A.util.isUndefinedOrNull(options[index])) {
    			options[index].selected = selected;
    		} else {
    			// TODO: somehow expose that the option couldn't be set.
    		}
    	},
    	persistOptions : function(cmp, options) {
    		cmp.set("v.options", options);
    	}
    },

    /**]
     * Strategy object for an array of components (used for maintaining support for using inputSelectOption components in the body)
     */
    bodyStrategy: {
    	// Updates options based on their existence in newValues
    	updateOptions : function(options, newValues) {
            var result = { found : false };
            // Perform single option update function on all of our options
    		this.performOperationOnCmps(options, this.updateOption, result, newValues);
    		return result.found;
    	},
    	getSelected : function(bodyCmps) {
    		var values = [];
    		this.performOperationOnCmps(bodyCmps, this.pushIfSelected, values);
    		return { found : (values.length > 0), optionValue : values.join(";") };
    	},
    	getValue : function(options, index) {
    		if (!$A.util.isUndefinedOrNull(options[index])) {
    			return options[index].get("v.text");
    		}
    		return undefined;
    	},
    	setOptionSelected : function(options, index, selected) {
    		if (!$A.util.isUndefinedOrNull(options[index])) {
    			options[index].set("v.value", selected);
    		} else {
    			// TODO: somehow expose that the option couldn't be set.
    		}
    	},
    	persistOptions : function(cmp, options) {
    		cmp.set("v.body", options);
    	},
    	// Performs op on every ui:inputSelectOption in opts, where op = function(optionCmp, resultsObject, optionalArguments)
    	performOperationOnCmps : function(opts, op, result, newValues) {
    		$A.util.forEach(opts, function(cmp) {
        		var descriptor = cmp.getDef().getDescriptor();
        		var cmpName = descriptor.getNamespace() + ":" + descriptor.getName();
        		if (cmpName === "ui:inputSelectOptionGroup") {
        			var groupBody = cmp.get("v.body");
        			if (!$A.util.isEmpty(groupBody)) {
        				$A.util.forEach(groupBody, function(groupBodyCmp) {
        					var descriptor = groupBodyCmp.getDef().getDescriptor();
        					if ((descriptor.getNamespace() + ":" + descriptor.getName()) === "ui:inputSelectOption") {
        						op(groupBodyCmp, result, newValues);
        					}
        				}, this);
        			}
        		} else if (cmpName = "ui:inputSelectOption") {
    				op(cmp, result, newValues);
    			}
        	}, this);
        },
        // Helper function for updateOptions
        // Update optionCmp if it exists in newValues; passes result back in result object
        updateOption : function(optionCmp, result, newValues) {
        	var text = optionCmp.get("v.text");
			var selectOption = (newValues.length > 1 && aura.util.arrayIndexOf(newValues, text) > -1) || newValues[0] === text;

			result.found = result.found || selectOption;
			optionCmp.set("v.value", selectOption);

        },
        // Helper function for getValues
        // Push optionCmp's value into valueList if selected
		pushIfSelected : function(optionCmp, valueList) {
			if ($A.util.getBooleanValue(optionCmp.get("v.value")) === true) {
				var text = optionCmp.get("v.text");
				if (!$A.util.isUndefined(text)) {
					valueList.push(text);
				}
			}
		}
    },

    /**
     * Render the options directly to the DOM for performance
     */
    renderOptions: function(cmp) {
    	var options = cmp.getConcreteComponent().get("v.options"),
			select = cmp.find("select").getElement(),
			optFrag, option, internalText;

		if ($A.util.isEmpty(options)) {
			return;
		}

    	optFrag = document.createDocumentFragment();
    	for (var i = 0; i < options.length; i++) {
    		option = document.createElement("option");
    		internalText = ($A.util.isEmpty(options[i].label) ? options[i].value : options[i].label) || "";

    		option.label = options[i].label || internalText;
    		option.value = options[i].value || internalText;

    		if (options[i]["class"]) {
    			option.setAttribute("class", options[i]["class"]);
    		}

    		if (options[i].selected) {
    			option.selected = "selected";
    		}

    		if (options[i].disabled) {
    			option.disabled = "disabled";
    		}

    		option.appendChild(document.createTextNode(internalText));

    		optFrag.appendChild(option);
    	}

    	while (select.firstChild) {
    		select.removeChild(select.firstChild);
    	}
    	select.appendChild(optFrag);
    }

})<|endoftext|>


<commit_before>Elm.Native.Show = {};
Elm.Native.Show.make = function(elm) {
    elm.Native = elm.Native || {};
    elm.Native.Show = elm.Native.Show || {};
    if (elm.Native.Show.values) return elm.Native.Show.values;

    var NList = Elm.Native.List.make(elm);
    var List = Elm.List.make(elm);
    var Maybe = Elm.Maybe.make(elm);
    var JS = Elm.JavaScript.make(elm);
    var Dict = Elm.Dict.make(elm);
    var Json = Elm.Json.make(elm);
    var Tuple2 = Elm.Native.Utils.make(elm).Tuple2;

    var toString = function(v) {
        var type = typeof v;
        if (type === "function") {
            var name = v.func ? v.func.name : v.name;
            return '<function' + (name === '' ? '' : ': ') + name + '>';
        } else if (type === "boolean") {
            return v ? "True" : "False";
        } else if (type === "number") {
            return v+"";
        } else if (type === "string") {
            var quote = v.length === 1 ? "'" : '"';
            return quote + addSlashes(v) + quote;
        } else if (type === "object" && '_' in v) {
            var output = [];
            for (var k in v._) {
                for (var i = v._[k].length; i--; ) {
                    output.push(k + " = " + toString(v._[k][i]));
                }
            }
            for (var k in v) {
                if (k === '_') continue;
                output.push(k + " = " + toString(v[k]));
            }
            if (output.length === 0) return "{}";
            return "{ " + output.join(", ") + " }";
        } else if (type === "object" && 'ctor' in v) {
            if (v.ctor.substring(0,6) === "_Tuple") {
                var output = [];
                for (var k in v) {
                    if (k === 'ctor') continue;
                    output.push(toString(v[k]));
                }
                return "(" + output.join(",") + ")";
            } else if (v.ctor === "::") {
                var output = '[' + toString(v._0);
                v = v._1;
                while (v.ctor === "::") {
                    output += "," + toString(v._0);
                    v = v._1;
                }
                return output + ']';
            } else if (v.ctor === "[]") {
                return "[]";
            } else if (v.ctor === "RBNode" || v.ctor === "RBEmpty") {
                var cons = F3(function(k,v,acc){return NList.Cons(Tuple2(k,v),acc)});
                var list = A3(Dict.foldr, cons, NList.Nil, v);
                var name = "Dict";
                if (list.ctor === "::" && list._0._1.ctor === "_Tuple0") {
                    name = "Set";
                    list = A2(List.map, function(x){return x._0}, list);
                }
                return name + ".fromList " + toString(list);
            } else {
                var output = "";
                for (var i in v) {
                    if (i === 'ctor') continue;
                    var str = toString(v[i]);
                    var parenless = str[0] === '{' || str[0] === '<' || str.indexOf(' ') < 0;
                    output += ' ' + (parenless ? str : '(' + str + ')');
                }
                return v.ctor + output;
            }
        }
        if (type === 'object' && 'recv' in v) return '<signal>';
        return "<internal structure>";
    };

    function addSlashes(str) {
        return str.replace(/\n/g, '\\n')
                  .replace(/\t/g, '\\t')
                  .replace(/\r/g, '\\r')
                  .replace(/\v/g, '\\v')
                  .replace(/\0/g, '\\0')
                  .replace(/\'/g, "\\'")
                  .replace(/\"/g, '\\"')
                  .replace(/\\/g, '\\\\')
    }

    return elm.Native.Show.values = { show:toString };
};
<commit_msg>Make sure slashes get converted first so that they don't get added accidentally for other escaped characters<commit_after>Elm.Native.Show = {};
Elm.Native.Show.make = function(elm) {
    elm.Native = elm.Native || {};
    elm.Native.Show = elm.Native.Show || {};
    if (elm.Native.Show.values) return elm.Native.Show.values;

    var NList = Elm.Native.List.make(elm);
    var List = Elm.List.make(elm);
    var Maybe = Elm.Maybe.make(elm);
    var JS = Elm.JavaScript.make(elm);
    var Dict = Elm.Dict.make(elm);
    var Json = Elm.Json.make(elm);
    var Tuple2 = Elm.Native.Utils.make(elm).Tuple2;

    var toString = function(v) {
        var type = typeof v;
        if (type === "function") {
            var name = v.func ? v.func.name : v.name;
            return '<function' + (name === '' ? '' : ': ') + name + '>';
        } else if (type === "boolean") {
            return v ? "True" : "False";
        } else if (type === "number") {
            return v+"";
        } else if (type === "string") {
            var quote = v.length === 1 ? "'" : '"';
            return quote + addSlashes(v) + quote;
        } else if (type === "object" && '_' in v) {
            var output = [];
            for (var k in v._) {
                for (var i = v._[k].length; i--; ) {
                    output.push(k + " = " + toString(v._[k][i]));
                }
            }
            for (var k in v) {
                if (k === '_') continue;
                output.push(k + " = " + toString(v[k]));
            }
            if (output.length === 0) return "{}";
            return "{ " + output.join(", ") + " }";
        } else if (type === "object" && 'ctor' in v) {
            if (v.ctor.substring(0,6) === "_Tuple") {
                var output = [];
                for (var k in v) {
                    if (k === 'ctor') continue;
                    output.push(toString(v[k]));
                }
                return "(" + output.join(",") + ")";
            } else if (v.ctor === "::") {
                var output = '[' + toString(v._0);
                v = v._1;
                while (v.ctor === "::") {
                    output += "," + toString(v._0);
                    v = v._1;
                }
                return output + ']';
            } else if (v.ctor === "[]") {
                return "[]";
            } else if (v.ctor === "RBNode" || v.ctor === "RBEmpty") {
                var cons = F3(function(k,v,acc){return NList.Cons(Tuple2(k,v),acc)});
                var list = A3(Dict.foldr, cons, NList.Nil, v);
                var name = "Dict";
                if (list.ctor === "::" && list._0._1.ctor === "_Tuple0") {
                    name = "Set";
                    list = A2(List.map, function(x){return x._0}, list);
                }
                return name + ".fromList " + toString(list);
            } else {
                var output = "";
                for (var i in v) {
                    if (i === 'ctor') continue;
                    var str = toString(v[i]);
                    var parenless = str[0] === '{' || str[0] === '<' || str.indexOf(' ') < 0;
                    output += ' ' + (parenless ? str : '(' + str + ')');
                }
                return v.ctor + output;
            }
        }
        if (type === 'object' && 'recv' in v) return '<signal>';
        return "<internal structure>";
    };

    function addSlashes(str) {
        return str.replace(/\\/g, '\\\\')
                  .replace(/\n/g, '\\n')
                  .replace(/\t/g, '\\t')
                  .replace(/\r/g, '\\r')
                  .replace(/\v/g, '\\v')
                  .replace(/\0/g, '\\0')
                  .replace(/\'/g, "\\'")
                  .replace(/\"/g, '\\"');
    }

    return elm.Native.Show.values = { show:toString };
};
<|endoftext|>