<commit_before>define(function(require) {
    'use strict';

    var DataGridComponent;
    var helpers;
    var $ = require('jquery');
    var _ = require('underscore');
    var tools = require('oroui/js/tools');
    var mediator = require('oroui/js/mediator');
    var Backbone = require('backbone');
    var BaseComponent = require('oroui/js/app/components/base/component');
    var PageableCollection = require('orodatagrid/js/pageable-collection');
    var Grid = require('orodatagrid/js/datagrid/grid');
    var mapActionModuleName = require('orodatagrid/js/map-action-module-name');
    var mapCellModuleName = require('orodatagrid/js/map-cell-module-name');
    var gridContentManager = require('orodatagrid/js/content-manager');
    var FloatingHeaderPlugin = require('orodatagrid/js/app/plugins/grid/floating-header-plugin');
    var FullscreenPlugin = require('orodatagrid/js/app/plugins/grid/fullscreen-plugin');
    var ColumnManagerPlugin = require('orodatagrid/js/app/plugins/grid/column-manager-plugin');
    var ToolbarMassActionPlugin = require('orodatagrid/js/app/plugins/grid/toolbar-mass-action-plugin');
    var MetadataModel = require('orodatagrid/js/datagrid/metadata-model');
    var DataGridThemeOptionsManager = require('orodatagrid/js/datagrid-theme-options-manager');

    helpers = {
        cellType: function(type) {
            return type + 'Cell';
        },
        actionType: function(type) {
            return type + 'Action';
        }
    };

    /**
     * Runs passed builder
     *
     * @param {jQuery.Deferred} built
     * @param {Object} options
     * @param {Object} builder
     */
    function runBuilder(built, options, builder) {
        if (!_.has(builder, 'init') || !$.isFunction(builder.init)) {
            built.resolve();
            throw new TypeError('Builder does not have init method');
        }
        _.defer(_.bind(builder.init, builder), built, options);
    }

    /**
     * Processes datagrid options by builder
     *
     * @param {jQuery.Deferred} built
     * @param {Object} options
     * @param {Object} builder
     */
    function processDatagridOptionsByBuilder(built, options, builder) {
        if (!_.has(builder, 'processDatagridOptions')) {
            built.resolve();
            return;
        }
        builder.processDatagridOptions(built, options);
    }

    DataGridComponent = BaseComponent.extend({
        initialize: function(options) {
            if (!options.enableFilters) {
                options.builders = _.reject(options.builders, function(module) {
                    return module === 'orofilter/js/datafilter-builder';
                });
            }
            options.builders.push('orodatagrid/js/inline-editing/builder');

            var self = this;
            this._deferredInit();
            this.built = $.Deferred();

            options = options || {};
            this.fixStates(options);
            this.processOptions(options);

            var optionsProcessedPromises = [];

            // let builders process datagrid options
            _.each(options.builders, function(module) {
                var built = $.Deferred();
                optionsProcessedPromises.push(built.promise());
                require([module], _.partial(processDatagridOptionsByBuilder, built, options));
            });

            $.when.apply($, optionsProcessedPromises).always(_.bind(function() {
                // then init datagrid and run builders
                this.initDataGrid(options);

                var buildersReadyPromises = [this.built.promise()];

                // run related builders
                _.each(options.builders, function(module) {
                    var built = $.Deferred();
                    buildersReadyPromises.push(built.promise());
                    require([module], _.partial(runBuilder, built, options));
                });

                $.when.apply($, buildersReadyPromises).always(function() {
                    self.subComponents = _.compact(arguments);
                    self._resolveDeferredInit();
                    self.$componentEl.find('.view-loading').remove();
                    self.$el.show();
                    self.grid.trigger('shown');
                });
            }, this));
        },

        /**
         * Extends passed options
         *
         * @param options
         */
        processOptions: function(options) {
            if (typeof options.inputName === 'undefined') {
                throw new Error('Option inputName has to be specified');
            }

            options.metadata.options.toolbarOptions =
                $.extend(true, options.metadata.options.toolbarOptions, options.toolbarOptions);
            options.$el = $(options.el);
            options.gridName = options.gridName || options.metadata.options.gridName;
            options.builders = options.builders || [];
            options.builders.push('orodatagrid/js/grid-views-builder');
            options.gridPromise = this.built.promise();
        },

        /**
         * Collects required modules and runs grid builder
         *
         * @param {Object} options
         */
        initDataGrid: function(options) {
            this.$el = $('<div>');
            this.$componentEl = options.$el;
            this.$componentEl.append(this.$el);
            this.gridName = options.gridName;
            this.inputName = options.inputName;
            this.data = options.data;
            this.metadata = _.defaults(options.metadata, {
                columns: [],
                options: {},
                state: {},
                initialState: {},
                rowActions: {},
                massActions: {}
            });
            this.themeOptions = options.themeOptions || {};
            this.metadataModel = new MetadataModel(this.metadata);
            this.modules = {};

            this.collectModules();

            // load all dependencies and build grid
            tools.loadModules(this.modules, this.build, this);

            this.listenTo(this.metadataModel, 'change:massActions', function(model, massActions) {
                this.grid.massActions.reset(this.buildMassActionsOptions(massActions));
            }, this);
        },

        /**
         * Collects required modules
         */
        collectModules: function() {
            var modules = this.modules;
            var metadata = this.metadata;
            // cells
            _.each(metadata.columns, function(column) {
                var type = column.type;
                modules[helpers.cellType(type)] = mapCellModuleName(type);
            });
            // row actions
            _.each(_.values(metadata.rowActions), function(action) {
                var type = action.frontend_type;
                modules[helpers.actionType(type)] = mapActionModuleName(type);
            });
            // mass actions
            _.each(_.values(metadata.massActions), function(action) {
                var type = action.frontend_type;
                modules[helpers.actionType(type)] = mapActionModuleName(type);
            });
        },

        /**
         * Build grid
         */
        build: function() {
            var collectionModels;
            var collectionOptions;
            var grid;

            var collectionName = this.gridName;
            var collection = gridContentManager.get(collectionName);

            collectionModels = {};
            if (this.data && this.data.data) {
                collectionModels = this.data.data;
            }

            collectionOptions = this.combineCollectionOptions();
            if (this.data && this.data.options) {
                _.extend(collectionOptions, this.data.options);
            }

            if (!collection) {
                // otherwise, create collection from metadata
                collection = new PageableCollection(collectionModels, collectionOptions);
            }

            // create grid
            var options = this.combineGridOptions();
            mediator.trigger('datagrid_create_before', options, collection);

            this.$el.hide();
            options.el = this.$el[0];
            options.themeOptionsConfigurator(Grid, options);
            grid = new Grid(_.extend({collection: collection}, options));
            this.grid = grid;
            grid.render();
            mediator.trigger('datagrid:rendered', grid);

            if (options.routerEnabled !== false) {
                // trace collection changes
                gridContentManager.trace(collection);
            }

            var deferredBuilt = this.built;
            if (grid.deferredRender) {
                grid.deferredRender.then(function() {
                    deferredBuilt.resolve(grid);
                });
            } else {
                deferredBuilt.resolve(grid);
            }
        },

        /**
         * Process metadata and combines options for collection
         *
         * @returns {Object}
         */
        combineCollectionOptions: function() {
            var options = _.extend({
                /*
                 * gridName contains extended information "inputName + scopeName"
                 * (allows to differentiate grid instances)
                 */
                inputName: this.gridName,
                parse: true,
                url: '\/user\/json',
                state: _.extend({
                    filters: {},
                    sorters: {},
                    columns: {}
                }, this.metadata.state),
                initialState: this.metadata.initialState,
                mode: this.metadata.mode || 'server'
            }, this.metadata.options);
            return options;
        },

        /**
         * Process metadata and combines options for datagrid
         *
         * @returns {Object}
         */
        combineGridOptions: function() {
            var columns;
            var rowActions = {};
            var defaultOptions = {
                sortable: false
            };
            var modules = this.modules;
            var metadata = this.metadata;
            var plugins = this.metadata.plugins || [];

            // columns
            columns = _.map(metadata.columns, function(cell) {
                var cellOptionKeys = ['name', 'label', 'renderable', 'editable', 'sortable', 'sortingType', 'align',
                    'order', 'manageable', 'required'];
                var cellOptions = _.extend({}, defaultOptions, _.pick.apply(null, [cell].concat(cellOptionKeys)));
                var extendOptions = _.omit.apply(null, [cell].concat(cellOptionKeys.concat('type')));
                var cellType = modules[helpers.cellType(cell.type)];
                if (!_.isEmpty(extendOptions)) {
                    cellType = cellType.extend(extendOptions);
                }
                cellOptions.cell = cellType;
                return cellOptions;
            });

            // row actions
            _.each(metadata.rowActions, function(options, action) {
                rowActions[action] = modules[helpers.actionType(options.frontend_type)].extend(options);
            });

            // mass actions
            var massActions = this.buildMassActionsOptions(this.metadata.massActions);

            if (!this.themeOptions.headerHide) {
                if (tools.isMobile()) {
                    plugins.push(FloatingHeaderPlugin);
                } else if (this.metadata.enableFullScreenLayout) {
                    plugins.push(FullscreenPlugin);
                }
            }

            if (metadata.options.toolbarOptions.addColumnManager) {
                plugins.push(ColumnManagerPlugin);
            }

            if (this.themeOptions.showMassActionOnToolbar) {
                plugins.push(ToolbarMassActionPlugin);
            }

            return {
                name: this.gridName,
                columns: columns,
                rowActions: rowActions,
                massActions: new Backbone.Collection(massActions),
                toolbarOptions: metadata.options.toolbarOptions || {},
                multipleSorting: metadata.options.multipleSorting || false,
                entityHint: metadata.options.entityHint,
                exportOptions: metadata.options.export || {},
                routerEnabled: _.isUndefined(metadata.options.routerEnabled) ? true : metadata.options.routerEnabled,
                multiSelectRowEnabled: metadata.options.multiSelectRowEnabled || massActions.length,
                metadata: this.metadata,
                metadataModel: this.metadataModel,
                plugins: plugins,
                themeOptionsConfigurator: DataGridThemeOptionsManager.createConfigurator(this.themeOptions)
            };
        },

        /**
         * @param {Object} actions
         * @returns {Array}
         */
        buildMassActionsOptions: function(actions) {
            var modules = this.modules;
            var massActions = [];

            _.each(actions, function(options, action) {
                massActions.push({
                    action: action,
                    module: modules[helpers.actionType(options.frontend_type)].extend(options)
                });
            });

            return massActions;
        },

        fixStates: function(options) {
            if (options.metadata) {
                this.fixState(options.metadata.state);
                this.fixState(options.metadata.initialState);
            }
        },

        fixState: function(state) {
            if (_.isArray(state.filters) && _.isEmpty(state.filters)) {
                state.filters = {};
            }

            if (_.isArray(state.sorters) && _.isEmpty(state.sorters)) {
                state.sorters = {};
            }
        },

        dispose: function() {
            // disposes registered sub-components
            if (this.subComponents) {
                _.each(this.subComponents, function(component) {
                    if (component && typeof component.dispose === 'function') {
                        component.dispose();
                    }
                });
                delete this.subComponents;
            }
            DataGridComponent.__super__.dispose.call(this);
        }
    });

    return DataGridComponent;
});
<commit_msg>BAP-10473 Refactor datagrid initialization logic<commit_after>define(function(require) {
    'use strict';

    var DataGridComponent;
    var helpers;
    var $ = require('jquery');
    var _ = require('underscore');
    var tools = require('oroui/js/tools');
    var mediator = require('oroui/js/mediator');
    var Backbone = require('backbone');
    var BaseComponent = require('oroui/js/app/components/base/component');
    var PageableCollection = require('orodatagrid/js/pageable-collection');
    var Grid = require('orodatagrid/js/datagrid/grid');
    var mapActionModuleName = require('orodatagrid/js/map-action-module-name');
    var mapCellModuleName = require('orodatagrid/js/map-cell-module-name');
    var gridContentManager = require('orodatagrid/js/content-manager');
    var FloatingHeaderPlugin = require('orodatagrid/js/app/plugins/grid/floating-header-plugin');
    var FullscreenPlugin = require('orodatagrid/js/app/plugins/grid/fullscreen-plugin');
    var ColumnManagerPlugin = require('orodatagrid/js/app/plugins/grid/column-manager-plugin');
    var ToolbarMassActionPlugin = require('orodatagrid/js/app/plugins/grid/toolbar-mass-action-plugin');
    var MetadataModel = require('orodatagrid/js/datagrid/metadata-model');
    var DataGridThemeOptionsManager = require('orodatagrid/js/datagrid-theme-options-manager');

    helpers = {
        cellType: function(type) {
            return type + 'Cell';
        },
        actionType: function(type) {
            return type + 'Action';
        }
    };

    DataGridComponent = BaseComponent.extend({
        initialize: function(options) {
            if (!options.enableFilters) {
                options.builders = _.reject(options.builders, function(module) {
                    return module === 'orofilter/js/datafilter-builder';
                });
            }
            options.builders.push('orodatagrid/js/inline-editing/builder');

            var self = this;
            this._deferredInit();
            this.built = $.Deferred();

            options = options || {};
            this.fixStates(options);
            this.processOptions(options);

            var optionsProcessedPromises = [];
            var builderImpl = [];

            /**
             * #1. Let builders process datagrid options
             */
            _.each(options.builders, function(module) {
                var built = $.Deferred();
                optionsProcessedPromises.push(built.promise());
                require([module], function(impl) {
                    builderImpl.push(impl);
                    if (!_.has(impl, 'processDatagridOptions')) {
                        built.resolve();
                        return;
                    }
                    impl.processDatagridOptions(built, options);
                });
            });

            $.when.apply($, optionsProcessedPromises).always(_.bind(function() {
                /**
                 * #2. Init datagrid
                 */
                this.initDataGrid(options);

                this.built.then(function() {
                    /**
                     * #3. Run builders
                     */
                    var buildersReadyPromises = [];

                    function throwNoInitMethodError() {
                        throw new TypeError('Builder does not have init method');
                    }
                    // run related builders
                    for (var i = 0; i < builderImpl.length; i++) {
                        var builder = builderImpl[i];
                        var built = $.Deferred();
                        buildersReadyPromises.push(built.promise());

                        if (!_.has(builder, 'init') || !$.isFunction(builder.init)) {
                            built.resolve();
                            _.defer(throwNoInitMethodError);
                            continue;
                        }
                        builder.init(built, options);
                    }

                    $.when.apply($, buildersReadyPromises).always(function() {
                        /**
                         * #4. Done
                         */
                        self.subComponents = _.compact(arguments);
                        self._resolveDeferredInit();
                        self.$componentEl.find('.view-loading').remove();
                        self.$el.show();
                        self.grid.trigger('shown');
                    });
                });
            }, this));
        },

        /**
         * Extends passed options
         *
         * @param options
         */
        processOptions: function(options) {
            if (typeof options.inputName === 'undefined') {
                throw new Error('Option inputName has to be specified');
            }

            options.metadata.options.toolbarOptions =
                $.extend(true, options.metadata.options.toolbarOptions, options.toolbarOptions);
            options.$el = $(options.el);
            options.gridName = options.gridName || options.metadata.options.gridName;
            options.builders = options.builders || [];
            options.builders.push('orodatagrid/js/grid-views-builder');
            options.gridPromise = this.built.promise();
        },

        /**
         * Collects required modules and runs grid builder
         *
         * @param {Object} options
         */
        initDataGrid: function(options) {
            this.$el = $('<div>');
            this.$componentEl = options.$el;
            this.$componentEl.append(this.$el);
            this.gridName = options.gridName;
            this.inputName = options.inputName;
            this.data = options.data;
            this.metadata = _.defaults(options.metadata, {
                columns: [],
                options: {},
                state: {},
                initialState: {},
                rowActions: {},
                massActions: {}
            });
            this.themeOptions = options.themeOptions || {};
            this.metadataModel = new MetadataModel(this.metadata);
            this.modules = {};

            this.collectModules();

            // load all dependencies and build grid
            tools.loadModules(this.modules, this.build, this);

            this.listenTo(this.metadataModel, 'change:massActions', function(model, massActions) {
                this.grid.massActions.reset(this.buildMassActionsOptions(massActions));
            }, this);
        },

        /**
         * Collects required modules
         */
        collectModules: function() {
            var modules = this.modules;
            var metadata = this.metadata;
            // cells
            _.each(metadata.columns, function(column) {
                var type = column.type;
                modules[helpers.cellType(type)] = mapCellModuleName(type);
            });
            // row actions
            _.each(_.values(metadata.rowActions), function(action) {
                var type = action.frontend_type;
                modules[helpers.actionType(type)] = mapActionModuleName(type);
            });
            // mass actions
            _.each(_.values(metadata.massActions), function(action) {
                var type = action.frontend_type;
                modules[helpers.actionType(type)] = mapActionModuleName(type);
            });
        },

        /**
         * Build grid
         */
        build: function() {
            var collectionModels;
            var collectionOptions;
            var grid;

            var collectionName = this.gridName;
            var collection = gridContentManager.get(collectionName);

            collectionModels = {};
            if (this.data && this.data.data) {
                collectionModels = this.data.data;
            }

            collectionOptions = this.combineCollectionOptions();
            if (this.data && this.data.options) {
                _.extend(collectionOptions, this.data.options);
            }

            if (!collection) {
                // otherwise, create collection from metadata
                collection = new PageableCollection(collectionModels, collectionOptions);
            }

            // create grid
            var options = this.combineGridOptions();
            mediator.trigger('datagrid_create_before', options, collection);

            this.$el.hide();
            options.el = this.$el[0];
            options.themeOptionsConfigurator(Grid, options);
            grid = new Grid(_.extend({collection: collection}, options));
            this.grid = grid;
            grid.render();
            mediator.trigger('datagrid:rendered', grid);

            if (options.routerEnabled !== false) {
                // trace collection changes
                gridContentManager.trace(collection);
            }

            var deferredBuilt = this.built;
            if (grid.deferredRender) {
                grid.deferredRender.then(function() {
                    deferredBuilt.resolve(grid);
                });
            } else {
                deferredBuilt.resolve(grid);
            }
        },

        /**
         * Process metadata and combines options for collection
         *
         * @returns {Object}
         */
        combineCollectionOptions: function() {
            var options = _.extend({
                /*
                 * gridName contains extended information "inputName + scopeName"
                 * (allows to differentiate grid instances)
                 */
                inputName: this.gridName,
                parse: true,
                url: '\/user\/json',
                state: _.extend({
                    filters: {},
                    sorters: {},
                    columns: {}
                }, this.metadata.state),
                initialState: this.metadata.initialState,
                mode: this.metadata.mode || 'server'
            }, this.metadata.options);
            return options;
        },

        /**
         * Process metadata and combines options for datagrid
         *
         * @returns {Object}
         */
        combineGridOptions: function() {
            var columns;
            var rowActions = {};
            var defaultOptions = {
                sortable: false
            };
            var modules = this.modules;
            var metadata = this.metadata;
            var plugins = this.metadata.plugins || [];

            // columns
            columns = _.map(metadata.columns, function(cell) {
                var cellOptionKeys = ['name', 'label', 'renderable', 'editable', 'sortable', 'sortingType', 'align',
                    'order', 'manageable', 'required'];
                var cellOptions = _.extend({}, defaultOptions, _.pick.apply(null, [cell].concat(cellOptionKeys)));
                var extendOptions = _.omit.apply(null, [cell].concat(cellOptionKeys.concat('type')));
                var cellType = modules[helpers.cellType(cell.type)];
                if (!_.isEmpty(extendOptions)) {
                    cellType = cellType.extend(extendOptions);
                }
                cellOptions.cell = cellType;
                return cellOptions;
            });

            // row actions
            _.each(metadata.rowActions, function(options, action) {
                rowActions[action] = modules[helpers.actionType(options.frontend_type)].extend(options);
            });

            // mass actions
            var massActions = this.buildMassActionsOptions(this.metadata.massActions);

            if (!this.themeOptions.headerHide) {
                if (tools.isMobile()) {
                    plugins.push(FloatingHeaderPlugin);
                } else if (this.metadata.enableFullScreenLayout) {
                    plugins.push(FullscreenPlugin);
                }
            }

            if (metadata.options.toolbarOptions.addColumnManager) {
                plugins.push(ColumnManagerPlugin);
            }

            if (this.themeOptions.showMassActionOnToolbar) {
                plugins.push(ToolbarMassActionPlugin);
            }

            return {
                name: this.gridName,
                columns: columns,
                rowActions: rowActions,
                massActions: new Backbone.Collection(massActions),
                toolbarOptions: metadata.options.toolbarOptions || {},
                multipleSorting: metadata.options.multipleSorting || false,
                entityHint: metadata.options.entityHint,
                exportOptions: metadata.options.export || {},
                routerEnabled: _.isUndefined(metadata.options.routerEnabled) ? true : metadata.options.routerEnabled,
                multiSelectRowEnabled: metadata.options.multiSelectRowEnabled || massActions.length,
                metadata: this.metadata,
                metadataModel: this.metadataModel,
                plugins: plugins,
                themeOptionsConfigurator: DataGridThemeOptionsManager.createConfigurator(this.themeOptions)
            };
        },

        /**
         * @param {Object} actions
         * @returns {Array}
         */
        buildMassActionsOptions: function(actions) {
            var modules = this.modules;
            var massActions = [];

            _.each(actions, function(options, action) {
                massActions.push({
                    action: action,
                    module: modules[helpers.actionType(options.frontend_type)].extend(options)
                });
            });

            return massActions;
        },

        fixStates: function(options) {
            if (options.metadata) {
                this.fixState(options.metadata.state);
                this.fixState(options.metadata.initialState);
            }
        },

        fixState: function(state) {
            if (_.isArray(state.filters) && _.isEmpty(state.filters)) {
                state.filters = {};
            }

            if (_.isArray(state.sorters) && _.isEmpty(state.sorters)) {
                state.sorters = {};
            }
        },

        dispose: function() {
            // disposes registered sub-components
            if (this.subComponents) {
                _.each(this.subComponents, function(component) {
                    if (component && typeof component.dispose === 'function') {
                        component.dispose();
                    }
                });
                delete this.subComponents;
            }
            DataGridComponent.__super__.dispose.call(this);
        }
    });

    return DataGridComponent;
});
<|endoftext|>


<commit_before>var hyperHTML = (function () {'use strict';

  /*! (C) 2017 Andrea Giammarchi @WebReflection (MIT) */

  // hyperHTML \o/
  //
  // var render = hyperHTML.bind(document.body);
  // setInterval(() => render`
  //
⚡ hyperHTML ⚡

  //

  //    ${(new Date).toLocaleString()}
  //

  // `, 1000);
  function hyperHTML(statics) {
    return  EXPANDO in this &&
            this[EXPANDO].s === statics ?
              update.apply(this, arguments) :
              upgrade.apply(this, arguments);
  }

  // A wire ➰ is a bridge between a document fragment
  // and its inevitably lost list of rendered nodes
  //
  // var render = hyperHTML.wire();
  // render`
  //
Hello Wired!

  // `;
  //
  // Every single invocation will return that div
  // or the list of elements it contained as Array.
  // This simplifies most task where hyperHTML
  // is used to create the node itself, instead of
  // populating an already known and bound one.
  hyperHTML.wire = function wire(obj, type) {
    return arguments.length < 1 ?
      wireContent('html') :
      (obj == null ?
        wireContent(type || 'html') :
        (wm.get(obj) || wireWeakly(obj, type || 'html'))
      );
  };

  // - - - - - - - - - - - - - - - - - -  - - - - -

  // -------------------------
  // DOM parsing & traversing
  // -------------------------

  // setup attributes for updates
  //
  //

  //
  // Note: always use quotes around attributes, even for events,
  //       booleans, or numbers, otherwise this function fails.
  function attributesSeeker(node, actions) {
    for (var
      attribute,
      value = IE ? uid : uidc,
      attributes = slice.call(node.attributes),
      i = 0,
      length = attributes.length;
      i < length; i++
    ) {
      attribute = attributes[i];
      if (attribute.value === value) {
        // with IE the order doesn't really matter
        // as long as the right attribute is addressed
        actions.push(setAttribute(node, IE ?
          node.getAttributeNode(IEAttributes.shift()) :
          attribute
        ));
      }
    }
  }

  // traverse the whole node in search of editable content
  // decide what each future update should change
  //
  //

  //
${some.HTML}

  //

  //      ${some.text}
  //

  //

  function lukeTreeWalker(node, actions) {
    for (var
      child, text,
      childNodes = slice.call(node.childNodes),
      length = childNodes.length,
      i = 0; i < length; i++
    ) {
      child = childNodes[i];
      switch (child.nodeType) {
        case 1:
          attributesSeeker(child, actions);
          lukeTreeWalker(child, actions);
          break;
        case 8:
          if (child.textContent === uid) {
            if (length === 1) {
              actions.push(setAnyContent(node));
              node.removeChild(child);
            } else if (
              (i < 1 || childNodes[i - 1].nodeType === 1) &&
              (i + 1 === length || childNodes[i + 1].nodeType === 1)
            ) {
              actions.push(setVirtualContent(child));
            } else {
              text = node.ownerDocument.createTextNode('');
              actions.push(setTextContent(text));
              node.replaceChild(text, child);
            }
          }
          break;
      }
    }
  }

  // -------------------------
  // DOM manipulating
  // -------------------------

  // update regular bound nodes
  //
  // var render = hyperHTML.bind(node);
  // function update() {
  //  render`template`;
  // }
  function setAnyContent(node) {
    return function any(value) {
      switch (typeof value) {
        case 'string':
          node.innerHTML = value;
          break;
        case 'number':
        case 'boolean':
          node.textContent = value;
          break;
        default:
          if (Array.isArray(value)) {
            if (value.length === 1) {
              any(value[0]);
            } else if(typeof value[0] === 'string') {
              any(value.join(''));
            } else {
              var i = indexOfDiffereces(node.childNodes, value);
              if (-1 < i) {
                updateViaArray(node, value, i);
              }
            }
          } else {
            populateNode(node, value);
          }
          break;
      }
    };
  }

  // update attributes node
  //
  // render`<a href="${url}" onclick="${click}">${name}</a>`;
  //
  // Note: attributes with `on` prefix are set directly as callbacks.
  //       These won't ever be transformed into strings while other
  //       attributes will be automatically sanitized.
  function setAttribute(node, attribute) {
    var
      name = attribute.name,
      isSpecial = SPECIAL_ATTRIBUTE.test(name)
    ;
    if (isSpecial) node.removeAttribute(name);
    return isSpecial ?
      function event(value) {
        node[name] = value;
      } :
      function attr(value) {
        attribute.value = value;
      };
  }

  // update the "emptiness"
  // this function is used when template literals
  // have sneaky html/fragment capable
  // updates in the wild (no spaces around)
  //
  // render`
  //
Content before
${
  //  'any content in between'
  //  }
Content after

  // `;
  //
  // Note: this is the most expensive
  //       update of them all.
  function setVirtualContent(node) {
    var
      fragment = document.createDocumentFragment(),
      childNodes = []
    ;
    return function any(value) {
      var i, parentNode = node.parentNode;
      switch (typeof value) {
        case 'string':
        case 'number':
        case 'boolean':
          removeNodeList(childNodes, 0);
          injectHTML(fragment, value);
          childNodes = slice.call(fragment.childNodes);
          parentNode.insertBefore(fragment, node);
          break;
        default:
          if (Array.isArray(value)) {
            if (value.length === 0) {
              any(value[0]);
            } else if(typeof value[0] === 'string') {
              any(value.join(''));
            } else {
              i = indexOfDiffereces(childNodes, value);
              if (-1 < i) {
                removeNodeList(childNodes, i);
                value = value.slice(i);
                appendNodes(fragment, value);
                parentNode.insertBefore(fragment, node);
                childNodes.push.apply(childNodes, value);
              }
            }
          } else {
            removeNodeList(childNodes, 0);
            childNodes = value.nodeType === 11 ?
              slice.call(value.childNodes) :
              [value];
            parentNode.insertBefore(value, node);
          }
          break;
      }
    };
  }

  // basic closure to update nodes textContent
  //
  // render`
  //

  //    ${'spaces around means textContent'}
  //
`;
  function setTextContent(node) {
    return function text(value) {
      node.textContent = value;
    };
  }

  // -------------------------
  // Helpers
  // -------------------------

  // it does exactly what it says
  function appendNodes(node, childNodes) {
    for (var
      i = 0,
      length = childNodes.length;
      i < length; i++
    ) {
      node.appendChild(childNodes[i]);
    }
  }

  // given two collections, find
  // the first index that has different content.
  // If the two lists are the same, return -1
  // to indicate no differences were found.
  function indexOfDiffereces(a, b) {
    if (a === b) return -1;
    var
      i = 0,
      aLength = a.length,
      bLength = b.length
    ;
    while (i < aLength) {
      if (i < bLength && a[i] === b[i]) i++;
      else return i;
    }
    return i === bLength ? -1 : i;
  }

  // inject HTML into a template node
  // and populate a fragment with resulting nodes
  //
  // IE9~IE11 are not compatible with the template tag.
  // If the content is a partial part of a table there is a fallback.
  // Not the most elegant/robust way but good enough for common cases.
  // (I don't want to include a whole DOM parser for IE only here).
  function injectHTML(fragment, html) {
    var
      fallback = IE && /^[^\S]*?<(t(?:head|body|foot|r|d|h))/i.test(html),
      template = fragment.ownerDocument.createElement('template')
    ;
    template.innerHTML = fallback ? ('
' + html + '
') : html;
    if (fallback) {
      template = {childNodes: template.querySelectorAll(RegExp.$1)};
    }
    appendNodes(
      fragment,
      slice.call((template.content || template).childNodes)
    );
  }

  // accordingly with the kind of child
  // it put its content into a parent node
  function populateNode(parent, child) {
    switch (child.nodeType) {
      case 1:
        var childNodes = parent.childNodes;
        if (childNodes.length !== 1 || childNodes[0] !== child) {
          resetAndPopulate(parent, child);
        }
        break;
      case 11:
        if (-1 < indexOfDiffereces(parent.childNodes, child.childNodes)) {
          resetAndPopulate(parent, child);
        }
        break;
      case 3:
        parent.textContent = child.textContent;
        break;
    }
  }

  // it does exactly what it says
  function removeNodeList(list, startIndex) {
    var length = list.length, child;
    while (startIndex < length--) {
      child = list[length];
      child.parentNode.removeChild(child);
    }
  }

  // drop all nodes and append a node
  function resetAndPopulate(parent, child) {
    parent.textContent = '';
    parent.appendChild(child);
  }

  // the first time a hyperHTML.wire() is invoked
  // remember the list of nodes that should be updated
  // at every consequent render call.
  // The resulting function might return the very first node
  // or the Array of all nodes that might need updates.
  function setupAndGetContent(node) {
    for (var
      child,
      children = [],
      childNodes = node.childNodes,
      i = 0,
      length = childNodes.length;
      i < length; i++
    ) {
      child = childNodes[i];
      if (
        1 === child.nodeType ||
        0 < trim.call(child.textContent).length
      ) {
        children.push(child);
      }
    }
    length = children.length;
    return length < 2 ?
      ((child = length < 1 ? node : children[0]),
      function () { return child; }) :
      function () { return children; };
  }

  // remove and/or and a list of nodes through an array
  function updateViaArray(node, childNodes, i) {
    var fragment = node.ownerDocument.createDocumentFragment();
    if (0 < i) {
      removeNodeList(node.childNodes, i);
      appendNodes(fragment, childNodes.slice(i));
      node.appendChild(fragment);
    } else {
      appendNodes(fragment, childNodes);
      resetAndPopulate(node, fragment);
    }
  }

  // create a new wire for generic DOM content
  function wireContent(type) {
    var content, container, fragment, render, setup, template;
    return function update(statics) {
      if (template !== statics) {
        setup = true;
        template = statics;
        fragment = document.createDocumentFragment();
        container = type === 'svg' ?
          document.createElementNS('http://www.w3.org/2000/svg', 'svg') :
          fragment;
        render = hyperHTML.bind(container);
      }
      render.apply(null, arguments);
      if (setup) {
        setup = false;
        if (type === 'svg') {
          appendNodes(fragment, slice.call(container.childNodes));
        }
        content = setupAndGetContent(fragment);
      }
      return content();
    };
  }

  // get or create a wired weak reference
  function wireWeakly(obj, type) {
    var wire = wireContent(type);
    wm.set(obj, wire);
    return wire;
  }

  // -------------------------
  // Template setup
  // -------------------------

  // each known hyperHTML update is
  // kept as simple as possible.
  function update() {
    for (var
      i = 1,
      length = arguments.length,
      updates = this[EXPANDO].u;
      i < length; i++
    ) {
      updates[i - 1](arguments[i]);
    }
    return this;
  }

  // but the first time, it needs to be setup.
  // From now on, only update(statics) will be called
  // unless this node won't be used for other renderings.
  function upgrade(statics) {
    var
      updates = [],
      html = statics.join(uidc)
    ;
    if (IE) {
      IEAttributes = [];
      injectHTML(this, html.replace(no, comments));
    } else if (this.nodeType === 1) {
      this.innerHTML = html;
    } else {
      injectHTML(this, html);
    }
    lukeTreeWalker(this, updates);
    this[EXPANDO] = {s: statics, u: updates};
    return update.apply(this, arguments);
  }

  // -------------------------
  // the trash bin
  // -------------------------

  // IE used to suck.
  /*
  // even in a try/catch this throw an error
  // since it's reliable though, I'll keep it around
  function isIE() {
    var p = document.createElement('p');
    p.innerHTML = '';
    return p.childNodes[0].onclick == null;
  }
  //*/

  // remove and/or and a list of nodes through a fragment
  /* temporarily removed until it's demonstrated it's needed
  function updateViaFragment(node, fragment, i) {
    if (0 < i) {
      removeNodeList(node.childNodes, i);
      var slim = fragment.cloneNode();
      appendNodes(slim, slice.call(fragment.childNodes, i));
      node.appendChild(fragment, slim);
    } else {
      resetAndPopulate(node, fragment);
    }
  }
  //*/

  // -------------------------
  // local variables
  // -------------------------

  var
    // decide special attributes behavior
    SPECIAL_ATTRIBUTE = /^(?:on[a-z]+|async|autofocus|autoplay|capture|checked|controls|deferred|disabled|formnovalidate|hidden|loop|multiple|muted|required)$/,
    // avoids WeakMap to avoid memory pressure, use CSS compatible syntax for IE
    EXPANDO = '_hyper_html: ',
    // use a pseudo unique id to avoid conflicts and normalize CSS style for IE
    uid = EXPANDO + ((Math.random() * new Date) | 0) + ';',
    // use comment nodes with pseudo unique content to setup
    uidc = '',
    // threat it differently
    IE = 'documentMode' in document,
    no = IE && new RegExp('([^\\S][a-z]+[a-z0-9_-]*=)([\'"])' + uidc + '\\2', 'g'),
    comments = IE && function ($0, $1, $2) {
      IEAttributes.push($1.slice(1, -1));
      return $1 + $2 + uid + $2;
    },
    // verify empty textContent on .wire() setup
    trim = EXPANDO.trim || function () {
      return this.replace(/^\s+|\s+$/g, '');
    },
    // convert DOM.childNodes into arrays to avoid
    // DOM mutation backfiring on loops
    slice = [].slice,
    // used for weak references
    // if WeakMap is not available
    // it uses a configurable, non enumerable,
    // quick and dirty expando property.
    wm = typeof WeakMap === typeof wm ?
      {
        get: function (obj) { return obj[EXPANDO]; },
        set: function (obj, value) {
          Object.defineProperty(obj, EXPANDO, {
            configurable: true,
            value: value
          });
        }
      } :
      new WeakMap(),
    IEAttributes
  ;

  // Simply to avoid duplicated RegExp in viperHTML
  hyperHTML.SPECIAL_ATTRIBUTE = SPECIAL_ATTRIBUTE;

  // -------------------------
  // ⚡ The End ➰
  // -------------------------
  return hyperHTML;

}());

// umd.KISS
try { module.exports = hyperHTML; } catch(o_O) {}
<commit_msg>feat: add all HTML boolean attributes to SPECIAL_ATTRIBUTE<commit_after>var hyperHTML = (function () {'use strict';

  /*! (C) 2017 Andrea Giammarchi @WebReflection (MIT) */

  // hyperHTML \o/
  //
  // var render = hyperHTML.bind(document.body);
  // setInterval(() => render`
  //
⚡ hyperHTML ⚡

  //

  //    ${(new Date).toLocaleString()}
  //

  // `, 1000);
  function hyperHTML(statics) {
    return  EXPANDO in this &&
            this[EXPANDO].s === statics ?
              update.apply(this, arguments) :
              upgrade.apply(this, arguments);
  }

  // A wire ➰ is a bridge between a document fragment
  // and its inevitably lost list of rendered nodes
  //
  // var render = hyperHTML.wire();
  // render`
  //
Hello Wired!

  // `;
  //
  // Every single invocation will return that div
  // or the list of elements it contained as Array.
  // This simplifies most task where hyperHTML
  // is used to create the node itself, instead of
  // populating an already known and bound one.
  hyperHTML.wire = function wire(obj, type) {
    return arguments.length < 1 ?
      wireContent('html') :
      (obj == null ?
        wireContent(type || 'html') :
        (wm.get(obj) || wireWeakly(obj, type || 'html'))
      );
  };

  // - - - - - - - - - - - - - - - - - -  - - - - -

  // -------------------------
  // DOM parsing & traversing
  // -------------------------

  // setup attributes for updates
  //
  //

  //
  // Note: always use quotes around attributes, even for events,
  //       booleans, or numbers, otherwise this function fails.
  function attributesSeeker(node, actions) {
    for (var
      attribute,
      value = IE ? uid : uidc,
      attributes = slice.call(node.attributes),
      i = 0,
      length = attributes.length;
      i < length; i++
    ) {
      attribute = attributes[i];
      if (attribute.value === value) {
        // with IE the order doesn't really matter
        // as long as the right attribute is addressed
        actions.push(setAttribute(node, IE ?
          node.getAttributeNode(IEAttributes.shift()) :
          attribute
        ));
      }
    }
  }

  // traverse the whole node in search of editable content
  // decide what each future update should change
  //
  //

  //
${some.HTML}

  //

  //      ${some.text}
  //

  //

  function lukeTreeWalker(node, actions) {
    for (var
      child, text,
      childNodes = slice.call(node.childNodes),
      length = childNodes.length,
      i = 0; i < length; i++
    ) {
      child = childNodes[i];
      switch (child.nodeType) {
        case 1:
          attributesSeeker(child, actions);
          lukeTreeWalker(child, actions);
          break;
        case 8:
          if (child.textContent === uid) {
            if (length === 1) {
              actions.push(setAnyContent(node));
              node.removeChild(child);
            } else if (
              (i < 1 || childNodes[i - 1].nodeType === 1) &&
              (i + 1 === length || childNodes[i + 1].nodeType === 1)
            ) {
              actions.push(setVirtualContent(child));
            } else {
              text = node.ownerDocument.createTextNode('');
              actions.push(setTextContent(text));
              node.replaceChild(text, child);
            }
          }
          break;
      }
    }
  }

  // -------------------------
  // DOM manipulating
  // -------------------------

  // update regular bound nodes
  //
  // var render = hyperHTML.bind(node);
  // function update() {
  //  render`template`;
  // }
  function setAnyContent(node) {
    return function any(value) {
      switch (typeof value) {
        case 'string':
          node.innerHTML = value;
          break;
        case 'number':
        case 'boolean':
          node.textContent = value;
          break;
        default:
          if (Array.isArray(value)) {
            if (value.length === 1) {
              any(value[0]);
            } else if(typeof value[0] === 'string') {
              any(value.join(''));
            } else {
              var i = indexOfDiffereces(node.childNodes, value);
              if (-1 < i) {
                updateViaArray(node, value, i);
              }
            }
          } else {
            populateNode(node, value);
          }
          break;
      }
    };
  }

  // update attributes node
  //
  // render`<a href="${url}" onclick="${click}">${name}</a>`;
  //
  // Note: attributes with `on` prefix are set directly as callbacks.
  //       These won't ever be transformed into strings while other
  //       attributes will be automatically sanitized.
  function setAttribute(node, attribute) {
    var
      name = attribute.name,
      isSpecial = SPECIAL_ATTRIBUTE.test(name)
    ;
    if (isSpecial) node.removeAttribute(name);
    return isSpecial ?
      function event(value) {
        node[name] = value;
      } :
      function attr(value) {
        attribute.value = value;
      };
  }

  // update the "emptiness"
  // this function is used when template literals
  // have sneaky html/fragment capable
  // updates in the wild (no spaces around)
  //
  // render`
  //
Content before
${
  //  'any content in between'
  //  }
Content after

  // `;
  //
  // Note: this is the most expensive
  //       update of them all.
  function setVirtualContent(node) {
    var
      fragment = document.createDocumentFragment(),
      childNodes = []
    ;
    return function any(value) {
      var i, parentNode = node.parentNode;
      switch (typeof value) {
        case 'string':
        case 'number':
        case 'boolean':
          removeNodeList(childNodes, 0);
          injectHTML(fragment, value);
          childNodes = slice.call(fragment.childNodes);
          parentNode.insertBefore(fragment, node);
          break;
        default:
          if (Array.isArray(value)) {
            if (value.length === 0) {
              any(value[0]);
            } else if(typeof value[0] === 'string') {
              any(value.join(''));
            } else {
              i = indexOfDiffereces(childNodes, value);
              if (-1 < i) {
                removeNodeList(childNodes, i);
                value = value.slice(i);
                appendNodes(fragment, value);
                parentNode.insertBefore(fragment, node);
                childNodes.push.apply(childNodes, value);
              }
            }
          } else {
            removeNodeList(childNodes, 0);
            childNodes = value.nodeType === 11 ?
              slice.call(value.childNodes) :
              [value];
            parentNode.insertBefore(value, node);
          }
          break;
      }
    };
  }

  // basic closure to update nodes textContent
  //
  // render`
  //

  //    ${'spaces around means textContent'}
  //
`;
  function setTextContent(node) {
    return function text(value) {
      node.textContent = value;
    };
  }

  // -------------------------
  // Helpers
  // -------------------------

  // it does exactly what it says
  function appendNodes(node, childNodes) {
    for (var
      i = 0,
      length = childNodes.length;
      i < length; i++
    ) {
      node.appendChild(childNodes[i]);
    }
  }

  // given two collections, find
  // the first index that has different content.
  // If the two lists are the same, return -1
  // to indicate no differences were found.
  function indexOfDiffereces(a, b) {
    if (a === b) return -1;
    var
      i = 0,
      aLength = a.length,
      bLength = b.length
    ;
    while (i < aLength) {
      if (i < bLength && a[i] === b[i]) i++;
      else return i;
    }
    return i === bLength ? -1 : i;
  }

  // inject HTML into a template node
  // and populate a fragment with resulting nodes
  //
  // IE9~IE11 are not compatible with the template tag.
  // If the content is a partial part of a table there is a fallback.
  // Not the most elegant/robust way but good enough for common cases.
  // (I don't want to include a whole DOM parser for IE only here).
  function injectHTML(fragment, html) {
    var
      fallback = IE && /^[^\S]*?<(t(?:head|body|foot|r|d|h))/i.test(html),
      template = fragment.ownerDocument.createElement('template')
    ;
    template.innerHTML = fallback ? ('
' + html + '
') : html;
    if (fallback) {
      template = {childNodes: template.querySelectorAll(RegExp.$1)};
    }
    appendNodes(
      fragment,
      slice.call((template.content || template).childNodes)
    );
  }

  // accordingly with the kind of child
  // it put its content into a parent node
  function populateNode(parent, child) {
    switch (child.nodeType) {
      case 1:
        var childNodes = parent.childNodes;
        if (childNodes.length !== 1 || childNodes[0] !== child) {
          resetAndPopulate(parent, child);
        }
        break;
      case 11:
        if (-1 < indexOfDiffereces(parent.childNodes, child.childNodes)) {
          resetAndPopulate(parent, child);
        }
        break;
      case 3:
        parent.textContent = child.textContent;
        break;
    }
  }

  // it does exactly what it says
  function removeNodeList(list, startIndex) {
    var length = list.length, child;
    while (startIndex < length--) {
      child = list[length];
      child.parentNode.removeChild(child);
    }
  }

  // drop all nodes and append a node
  function resetAndPopulate(parent, child) {
    parent.textContent = '';
    parent.appendChild(child);
  }

  // the first time a hyperHTML.wire() is invoked
  // remember the list of nodes that should be updated
  // at every consequent render call.
  // The resulting function might return the very first node
  // or the Array of all nodes that might need updates.
  function setupAndGetContent(node) {
    for (var
      child,
      children = [],
      childNodes = node.childNodes,
      i = 0,
      length = childNodes.length;
      i < length; i++
    ) {
      child = childNodes[i];
      if (
        1 === child.nodeType ||
        0 < trim.call(child.textContent).length
      ) {
        children.push(child);
      }
    }
    length = children.length;
    return length < 2 ?
      ((child = length < 1 ? node : children[0]),
      function () { return child; }) :
      function () { return children; };
  }

  // remove and/or and a list of nodes through an array
  function updateViaArray(node, childNodes, i) {
    var fragment = node.ownerDocument.createDocumentFragment();
    if (0 < i) {
      removeNodeList(node.childNodes, i);
      appendNodes(fragment, childNodes.slice(i));
      node.appendChild(fragment);
    } else {
      appendNodes(fragment, childNodes);
      resetAndPopulate(node, fragment);
    }
  }

  // create a new wire for generic DOM content
  function wireContent(type) {
    var content, container, fragment, render, setup, template;
    return function update(statics) {
      if (template !== statics) {
        setup = true;
        template = statics;
        fragment = document.createDocumentFragment();
        container = type === 'svg' ?
          document.createElementNS('http://www.w3.org/2000/svg', 'svg') :
          fragment;
        render = hyperHTML.bind(container);
      }
      render.apply(null, arguments);
      if (setup) {
        setup = false;
        if (type === 'svg') {
          appendNodes(fragment, slice.call(container.childNodes));
        }
        content = setupAndGetContent(fragment);
      }
      return content();
    };
  }

  // get or create a wired weak reference
  function wireWeakly(obj, type) {
    var wire = wireContent(type);
    wm.set(obj, wire);
    return wire;
  }

  // -------------------------
  // Template setup
  // -------------------------

  // each known hyperHTML update is
  // kept as simple as possible.
  function update() {
    for (var
      i = 1,
      length = arguments.length,
      updates = this[EXPANDO].u;
      i < length; i++
    ) {
      updates[i - 1](arguments[i]);
    }
    return this;
  }

  // but the first time, it needs to be setup.
  // From now on, only update(statics) will be called
  // unless this node won't be used for other renderings.
  function upgrade(statics) {
    var
      updates = [],
      html = statics.join(uidc)
    ;
    if (IE) {
      IEAttributes = [];
      injectHTML(this, html.replace(no, comments));
    } else if (this.nodeType === 1) {
      this.innerHTML = html;
    } else {
      injectHTML(this, html);
    }
    lukeTreeWalker(this, updates);
    this[EXPANDO] = {s: statics, u: updates};
    return update.apply(this, arguments);
  }

  // -------------------------
  // the trash bin
  // -------------------------

  // IE used to suck.
  /*
  // even in a try/catch this throw an error
  // since it's reliable though, I'll keep it around
  function isIE() {
    var p = document.createElement('p');
    p.innerHTML = '';
    return p.childNodes[0].onclick == null;
  }
  //*/

  // remove and/or and a list of nodes through a fragment
  /* temporarily removed until it's demonstrated it's needed
  function updateViaFragment(node, fragment, i) {
    if (0 < i) {
      removeNodeList(node.childNodes, i);
      var slim = fragment.cloneNode();
      appendNodes(slim, slice.call(fragment.childNodes, i));
      node.appendChild(fragment, slim);
    } else {
      resetAndPopulate(node, fragment);
    }
  }
  //*/

  // -------------------------
  // local variables
  // -------------------------

  var
    // decide special attributes behavior ("boolean attributes", as per HTML)
    SPECIAL_ATTRIBUTE = /^(?:allow[a-z]+|?:on[a-z]+|async|autofocus|autoplay|capture|checked|controls|default|defer|disabled|formnovalidate|hidden|ismap|itemscope|loop|multiple|muted|nomodule|novalidate|open|playsinline|readonly|required|reversed|selected|truespeed|typemustmatch|usecache)$/,
    // avoids WeakMap to avoid memory pressure, use CSS compatible syntax for IE
    EXPANDO = '_hyper_html: ',
    // use a pseudo unique id to avoid conflicts and normalize CSS style for IE
    uid = EXPANDO + ((Math.random() * new Date) | 0) + ';',
    // use comment nodes with pseudo unique content to setup
    uidc = '',
    // threat it differently
    IE = 'documentMode' in document,
    no = IE && new RegExp('([^\\S][a-z]+[a-z0-9_-]*=)([\'"])' + uidc + '\\2', 'g'),
    comments = IE && function ($0, $1, $2) {
      IEAttributes.push($1.slice(1, -1));
      return $1 + $2 + uid + $2;
    },
    // verify empty textContent on .wire() setup
    trim = EXPANDO.trim || function () {
      return this.replace(/^\s+|\s+$/g, '');
    },
    // convert DOM.childNodes into arrays to avoid
    // DOM mutation backfiring on loops
    slice = [].slice,
    // used for weak references
    // if WeakMap is not available
    // it uses a configurable, non enumerable,
    // quick and dirty expando property.
    wm = typeof WeakMap === typeof wm ?
      {
        get: function (obj) { return obj[EXPANDO]; },
        set: function (obj, value) {
          Object.defineProperty(obj, EXPANDO, {
            configurable: true,
            value: value
          });
        }
      } :
      new WeakMap(),
    IEAttributes
  ;

  // Simply to avoid duplicated RegExp in viperHTML
  hyperHTML.SPECIAL_ATTRIBUTE = SPECIAL_ATTRIBUTE;

  // -------------------------
  // ⚡ The End ➰
  // -------------------------
  return hyperHTML;

}());

// umd.KISS
try { module.exports = hyperHTML; } catch(o_O) {}
<|endoftext|>


<commit_before>// Program that identifies common element(s) that exist within both two arrays

function intersection(firstArray, secondArray) {
	var hashMap = {};
	var commonElements = [];

}

<commit_msg>Create hashmap with elements of first array as keys<commit_after>// Program that identifies common element(s) that exist within both two arrays

function intersection(firstArray, secondArray) {
	var hashMap = {};
	var commonElements = [];

	// create hashmap with elements of first array as keys
	arrOne.forEach(function(element) {
		hashMap[element] = 1;
	})
}

<|endoftext|>


<commit_before><commit_msg>lowercase 'string'<commit_after><|endoftext|>


<commit_before>import React from 'react';
import { CHART_COLORS, CHART_COLORS_EXTENDED } from 'data/constants';
import { assign } from 'app/utils';
import { setChartColors } from 'app/utils/graphs';
import { groupByYear, groupBy, pick } from '../utils';

import Tick from '../tick';

const makeConfig = (data, keys, indicators, yAxisLabel, small) => {
  const names = pick('name', data); // only data name key
  const unit = indicators[0] && indicators[0].unit;
  const chartColors = setChartColors(
    keys.length,
    CHART_COLORS,
    CHART_COLORS_EXTENDED
  );
  return {
    chart: {
      data: pick('value', data), // only data value key
      margin: { top: 50, right: 30, left: 45, bottom: 5 },
      yAxisLabel,
      stackOffset: 'sign',
      unit
    },
    columns: {
      x: ['year'],
      y: keys
    },
    xAxis: small
      ? false
      : {
        dataKey: 'year',
        axisLine: false,
        tickLine: false,
        tick: !small
      },
    yAxis: small
      ? false
      : {
        axisLine: false,
        tickLine: false,
        tick: small
          ? false
          : tick => {
            const tickProps = tick;
            delete tickProps.verticalAnchor; // verticalAnchor prop is not supported
            return tick.index % 2 && <Tick {...tickProps} />;
          }
      },
    cartesianGrid: small
      ? false
      : {
        vertical: false
      },
    theme: keys.reduce(
      (th, k, i) =>
        assign(th, {
          [k]: {
            fill: chartColors[i],
            stroke: ''
          }
        }),
      {}
    ),
    tooltip: small ? null : { unit, names },
    legend: keys.map((k, i) => ({
      color: chartColors[i],
      label: names[0][k]
    }))
  };
};

export const stackBarChart1Data = (
  timeSeries,
  indicators,
  yAxisLabel,
  small
) => {
  const data = groupByYear(timeSeries, 'indicator', indicators);
  const keys = Object.keys(data[0]).filter(k => k !== 'year');
  return makeConfig(data, keys, indicators, yAxisLabel, small);
};

export const stackBarChart2Data = (
  timeseries,
  locations,
  indicators,
  yAxisLabel,
  small
) => {
  const data = groupBy(
    timeseries,
    ['location', 'indicator'],
    [locations, indicators]
  );
  const keys = Object.keys(data[0]).filter(k => k !== 'location');
  const baseConfig = makeConfig(data, keys, indicators, yAxisLabel, small);
  return assign(baseConfig, {
    chart: {
      ...baseConfig.chart,
      layout: 'vertical',
      unit: indicators[0].unit,
      margin: { top: 50, right: 30, left: 40, bottom: 5 }
    },
    cartesianGrid: small
      ? false
      : {
        ...baseConfig.cartesianGrid,
        horizontal: false
      },
    xAxis: {
      type: 'number',
      tick: !small
    },
    yAxis: small
      ? false
      : {
        type: 'category',
        dataKey: 'location'
      }
  });
};
<commit_msg>Apply sorting to stacked chart 1<commit_after>import React from 'react';
import { CHART_COLORS, CHART_COLORS_EXTENDED } from 'data/constants';
import { assign } from 'app/utils';
import { setChartColors } from 'app/utils/graphs';
import { groupByYear, groupBy, pick, orderAlphabetically } from '../utils';

import Tick from '../tick';

const makeConfig = (data, keys, indicators, yAxisLabel, small) => {
  const names = pick('name', data); // only data name key
  const unit = indicators[0] && indicators[0].unit;
  const chartColors = setChartColors(
    keys.length,
    CHART_COLORS,
    CHART_COLORS_EXTENDED
  );

  return {
    chart: {
      data: pick('value', data), // only data value key
      margin: { top: 50, right: 30, left: 45, bottom: 5 },
      yAxisLabel,
      stackOffset: 'sign',
      unit
    },
    columns: {
      x: ['year'],
      y: keys
    },
    xAxis: small
      ? false
      : {
        dataKey: 'year',
        axisLine: false,
        tickLine: false,
        tick: !small
      },
    yAxis: small
      ? false
      : {
        axisLine: false,
        tickLine: false,
        tick: small
          ? false
          : tick => {
            const tickProps = tick;
            delete tickProps.verticalAnchor; // verticalAnchor prop is not supported
            return tick.index % 2 && <Tick {...tickProps} />;
          }
      },
    cartesianGrid: small
      ? false
      : {
        vertical: false
      },
    theme: keys.reduce(
      (th, k, i) =>
        assign(th, {
          [k]: {
            fill: chartColors[i],
            stroke: ''
          }
        }),
      {}
    ),
    tooltip: small ? null : { unit, names },
    legend: keys.map((k, i) => ({
      color: chartColors[i],
      label: names[0][k]
    }))
  };
};

export const stackBarChart1Data = (
  timeSeries,
  indicators,
  yAxisLabel,
  small
) => {
  const data = orderAlphabetically(
    groupByYear(timeSeries, 'indicator', indicators)
  );
  const keys = Object.keys(data[0]).filter(k => k !== 'year');
  return makeConfig(data, keys, indicators, yAxisLabel, small);
};

export const stackBarChart2Data = (
  timeseries,
  locations,
  indicators,
  yAxisLabel,
  small
) => {
  const data = groupBy(
    timeseries,
    ['location', 'indicator'],
    [locations, indicators]
  );
  const keys = Object.keys(data[0]).filter(k => k !== 'location');
  const baseConfig = makeConfig(data, keys, indicators, yAxisLabel, small);
  return assign(baseConfig, {
    chart: {
      ...baseConfig.chart,
      layout: 'vertical',
      unit: indicators[0].unit,
      margin: { top: 50, right: 30, left: 40, bottom: 5 }
    },
    cartesianGrid: small
      ? false
      : {
        ...baseConfig.cartesianGrid,
        horizontal: false
      },
    xAxis: {
      type: 'number',
      tick: !small
    },
    yAxis: small
      ? false
      : {
        type: 'category',
        dataKey: 'location'
      }
  });
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Extra Bitcointip Support on Reddit
// @description  Add a tipping button (/u/bitcointip) and shrinks verifications
// @version      1.3.0
// @license      Public Domain
// @include      http://*.reddit.com/*
// ==/UserScript==

/* Changelog:
 * 1.3.0
 *    New verification and rejection markings
 *    Use the new gettipped API
 *    All new jQuery reddit plugin
 * 1.2.1
 *    More efficient validation marking
 *    Fixed status names
 *    Run on all of reddit, including user pages
 * 1.2
 *    Use the new bitcointip API to get status information
 *    https://github.com/NerdfighterSean/bitcointip/blob/master/src/api/
 * 1.1.4
 *    Don't hide verification when it has children
 *    Don't hide verification when it's the URL target
 * 1.1.3
 *    Fix case insensitivity in regex
 * 1.1.2
 *    Display tipping amount
 * 1.1.1
 *    Support the full tipping syntax
 * 1.1
 *    Hide verification posts, inlining the verification
 * 1.0
 *    Add a "tip bitcoins" button.
 */

var baseTip = '0.01 BTC';
var tipregex = /((\+(bitcointip|bitcoin|tip|btctip|bittip|btc))|((\+((?!0)(\d{1,4})) internet(s)?)|(\+((?!0)(\d{1,4})) point(s)? to (Gryffindor|Slytherin|Ravenclaw|Hufflepuff))))/i;
var rejectTime = 60 * 60 * 1000; // milliseconds
var api = {
    gettips: 'http://bitcointip.net/api/gettips.php?callback=?',
    gettipped: 'http://bitcointip.net/api/gettipped.php?callback=?'
};
var icons = {
    verified: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAt1BMVEX///8AAAAAyAAAuwAAwQcAvAcAvwAAwQYAyAUAxAUAxwQAwgQAvAMAxQYAvwYAxQYAxwU5yT060j460j871T89wUE9wkFGokdGu0hIzExJl09JmE9JxExJxE1K1U9K1k5Ll09LmVNMmVNM2FBNmlRRx1NSzlRTqlVUslZU1ldVq1hVrFdV2FhWrFhX21pZqlphrWJh3WRotGtrqm1stW91sXd2t3h5t3urz6zA2sHA28HG3sf4+PhvgZhQAAAAEXRSTlMAARweJSYoLTM0O0dMU1dYbkVIv+oAAACKSURBVHjaVc7XEoIwEIXhFRED1tBUxBaPFSyxK3n/5zIBb/yv9pudnVky2Ywxm345MHkVXByllPm4W24qrLbzdo1sLPPRepc+XlnSIAuz9DQYPtXnkLhUF/ysrndV3CYLRpbg2VtpxFMwfRfEl8IghEPUhB9t9lEQoke6FnzONfpU5kEIoKOn/z+/pREPWTic38sAAAAASUVORK5CYII=",
    rejected: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAQlBMVEX///+qAAAAAAC/AADIABSaTU3YMDDcPj7cSEjeUFDiZGTld3fmfHzoiorqkJDqlpbupKTuqqr99fX99/f+/Pz////kWqLlAAAABXRSTlMAAwcIM6KYVMQAAABfSURBVHjaXc7JDsAgCEVRsYpIBzro//9qHyHpond3AgkklIuXPKJcqleIIEB6FwEhQEW0t4rlUtt+22ZTMQ09NqZyiK8BtBCvc9iDWegY526hBVRmdcQ9RgD9f/G+P1+JEwRF2vKhRgAAAABJRU5ErkJggg==",
    tipped: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAANCAMAAACq939wAAAA/FBMVEWqVQCqcQCZZgCLXQCdYgDMjACOXACOXwCacQCfcQCqegCwggChggCnfwCwggCthACtgQC9iACleQC+iwDMlgDJlACmfgDDiwDBjADHlQDJnQDFlQDKmAChfRGjgBOmfhKmghKnghOqhBKthxOviROvjB+vjCGvjCOwiRSwihixixWxjSGziBOzkSmzky+0kSa0kiy3jxW8kxS8mCi8n0i8oEq9lBe9mSvOoBbUrTTUrTjbukXcsTDctDrdtj/exHbexXDfwWXfwmvjrBnksRjksx7lrhnqx17qyWTqymrq377rz3nr2qftuiHtvSv67cD67sj+997/+OX///8rcy1sAAAAHXRSTlMDCQoLDRQkKystMDc5Oj0+QUlKS0tMTVFSV15/i6wTI/gAAACWSURBVHjaHcrnAoFQGADQryI7sjNKN0RKZJVNQ8io938YN+f3ASDp1B9NAhD15UzXNH26KhNQXZyDBxZcNlloKadvFIbR56owUOFV9425ai8PrGwZaITQeisXoKHs7k/MP44ZaHYl54U5El8EdmjNEWbEjesf/Ljd9v0S1ETBtD3PNgUxB8nOQIybOGknAKgMy2FsmoIflIEZdK7PshkAAAAASUVORK5CYII="
};

/* Add the "tip bitcoins" button after "give gold". */
var $ = unsafeWindow.$, reddit = unsafeWindow;
var tip = $('tip bitcoins').attr({
    'class': 'tip-bitcoins login-required',
    'href': '#'
});
if (/^\/r\//.test(document.location.pathname)) {
    $('a.give-gold').parent().after($('

').append(tip));
}

/* Tipping button functionality. */
$('.tip-bitcoins').bind('click', function(event) {
    reddit.reply(event.target);
    var form = reddit.comment_reply_for_elem(event.target);
    var textarea = form.find('textarea');
    if (!textarea.val().match(tipregex)) {
        var insert = '+tip ' + baseTip;
        if (textarea.val().length > 0) {
            insert = '\n\n' + insert;
        } else {
            insert = insert + '\n\n';
        }
        textarea.val(textarea.val() + insert);
    }
    return false;
});

/* Reddit jQuery plugin. */
(function($) {
    /** Get the comment div for each element in the current set. */
    $.fn.comment = function() {
        return this.closest('.comment');
    };

    /** Get the comment ID for the first selected comment. */
    $.fn.commentID = function() {
        var full = this.first().find('input[name="thing_id"]').first().val();
        return full.replace(/^t1_/, '');
    };

    /** Get the commenter's name for the first selected comment. */
    $.fn.commentName = function() {
        return this.first().find('.author').first().text();
    };

    /** Get the comment's post time for the first selected comment. */
    $.fn.commentDate = function() {
        return new Date(this.find('.tagline time').first().attr('datetime'));
    };

    /** Get the comment body for the first comment in the current set. */
    $.fn.commentBody = function() {
        return this.find('.md').first();
    };

    /** Get the children comments for each comment in the current set. */
    $.fn.commentChildren = function() {
        return this.find('.comment');
    };

    /** Get the parent comment for each comment in the current set. */
    $.fn.commentParent = function() {
        return this.parent().comment();
    };

    /** Determine whether the first element is the target comment. */
    $.fn.isTarget = function() {
        return this.first().find('form').first().hasClass('border');
    };

    /** Return true if the first comment in the current set has a tip. */
    $.fn.hasTip = function() {
        return this.commentBody().children().is(function() {
            return tipregex.test($(this).text());
        });
    };
})(unsafeWindow.jQuery);

/* Hide verification replies. Note: t2_7vw3n is /u/bitcointip. */
$('a.id-t2_7vw3n').comment().each(function() {
    var $this = $(this);
    if ($this.commentChildren().length === 0 && !$this.isTarget()) {
        reddit.hidecomment($this.find('.expand').first());
    }
});

/* Find all the tip comments. */
var tips = {};
$('div.comment').each(function() {
    var $this = $(this);
    if ($this.hasTip()) {
        tips[$this.commentID()] = $this;
    }
});

/* Get status information about various tips. */
var tipIDs = Object.keys(tips);
if (tipIDs.length > 0) {
    var iconStyle = 'vertical-align: text-bottom; margin-left: 8px;';
    var display = {
        "pending": icons.verified,
        "completed": icons.verified,
        "reversed": icons.verified,
        "cancelled": icons.rejected
    };
    $.getJSON(api.gettips + '&tips=' + tipIDs, function(response) {
        response.forEach(function (tip) {
            var id = tip.fullname.replace(/^t._/, '');
            var tagline = tips[id].find('.tagline').first();
            var icon = $('').attr({href: tip.tx, target: '_blank'});
            tagline.append(icon.append($('').attr({
                src: display[tip.status],
                style: iconStyle,
                title: '+$' + tip.amountUSD + '	→  ' + tip.receiver
            })));
            delete tips[id];
        });

        /* Deal with unanswered tips. */
        for (var id in tips) {
            if (Date.now() - tips[id].commentDate() > rejectTime) {
                var tagline = tips[id].find('.tagline').first();
                tagline.append($('').attr({
                    src: icons.rejected,
                    style: iconStyle,
                    title: 'This tip is invalid.'
                }));
            }
        }
    });

    /* Put receiver information on comments. */
    var comments = {};
    $('div.comment').each(function() {
        var $this = $(this);
        comments[$this.commentID()] = $this;
    });
    var commentIDs = Object.keys(comments);
    $.getJSON(api.gettipped + '&tipped=' + commentIDs, function(response) {
        response.forEach(function (tipped) {
            var id = tipped.fullname.replace(/^t._/, '');
            var comment = comments[id];
            var tagline = comment.find('.tagline').first();
            var plural = tipped.tipQTY > 1;
            var title = comment.commentName() + ' $' + tipped.amountUSD +
                    ' for this comment.';
            if (plural) {
                title = 'redditors have given ' + title;
            } else {
                title = 'a redditor has given ' + title;
            }
            tagline.append($('').attr({
                src: icons.tipped,
                style: iconStyle,
                title: title
            }));
            if (plural) {
                tagline.append('x' + tipped.tipQTY);
            }
        });
    });
}

/* Test URLs:
 *
 * Rejected,
 *   http://www.reddit.com/r/bitcointip/comments/132nhq/t/c7c7iue
 *
 * Rejected flip,
 *   http://www.reddit.com/r/Bitcoin/comments/14i9e7/y/c7dc6w9
 *
 * Combination folding,
 *   http://www.reddit.com/r/bitcointip/comments/13iykn/b/c7dj8ia
 *
 * Multiple tips to one receiver,
 *   http://www.reddit.com/r/bitcointip/comments/12lmut/c7ny177
 *
 * Multiple guilded to one receiver (for comparison),
 *   http://www.reddit.com/r/AdviceAnimals/comments/15mk25/c7ntrcc
 */
<commit_msg>Support tipping posts.<commit_after>// ==UserScript==
// @name         Extra Bitcointip Support on Reddit
// @description  Add a tipping button (/u/bitcointip) and shrinks verifications
// @version      1.3.0
// @license      Public Domain
// @include      http://*.reddit.com/*
// ==/UserScript==

/* Changelog:
 * 1.3.0
 *    New verification and rejection markings
 *    Use the new gettipped API
 *    All new jQuery reddit plugin
 * 1.2.1
 *    More efficient validation marking
 *    Fixed status names
 *    Run on all of reddit, including user pages
 * 1.2
 *    Use the new bitcointip API to get status information
 *    https://github.com/NerdfighterSean/bitcointip/blob/master/src/api/
 * 1.1.4
 *    Don't hide verification when it has children
 *    Don't hide verification when it's the URL target
 * 1.1.3
 *    Fix case insensitivity in regex
 * 1.1.2
 *    Display tipping amount
 * 1.1.1
 *    Support the full tipping syntax
 * 1.1
 *    Hide verification posts, inlining the verification
 * 1.0
 *    Add a "tip bitcoins" button.
 */

var baseTip = '0.01 BTC';
var tipregex = /((\+(bitcointip|bitcoin|tip|btctip|bittip|btc))|((\+((?!0)(\d{1,4})) internet(s)?)|(\+((?!0)(\d{1,4})) point(s)? to (Gryffindor|Slytherin|Ravenclaw|Hufflepuff))))/i;
var rejectTime = 60 * 60 * 1000; // milliseconds
var api = {
    gettips: 'http://bitcointip.net/api/gettips.php?callback=?',
    gettipped: 'http://bitcointip.net/api/gettipped.php?callback=?'
};
var icons = {
    verified: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAt1BMVEX///8AAAAAyAAAuwAAwQcAvAcAvwAAwQYAyAUAxAUAxwQAwgQAvAMAxQYAvwYAxQYAxwU5yT060j460j871T89wUE9wkFGokdGu0hIzExJl09JmE9JxExJxE1K1U9K1k5Ll09LmVNMmVNM2FBNmlRRx1NSzlRTqlVUslZU1ldVq1hVrFdV2FhWrFhX21pZqlphrWJh3WRotGtrqm1stW91sXd2t3h5t3urz6zA2sHA28HG3sf4+PhvgZhQAAAAEXRSTlMAARweJSYoLTM0O0dMU1dYbkVIv+oAAACKSURBVHjaVc7XEoIwEIXhFRED1tBUxBaPFSyxK3n/5zIBb/yv9pudnVky2Ywxm345MHkVXByllPm4W24qrLbzdo1sLPPRepc+XlnSIAuz9DQYPtXnkLhUF/ysrndV3CYLRpbg2VtpxFMwfRfEl8IghEPUhB9t9lEQoke6FnzONfpU5kEIoKOn/z+/pREPWTic38sAAAAASUVORK5CYII=",
    rejected: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAQlBMVEX///+qAAAAAAC/AADIABSaTU3YMDDcPj7cSEjeUFDiZGTld3fmfHzoiorqkJDqlpbupKTuqqr99fX99/f+/Pz////kWqLlAAAABXRSTlMAAwcIM6KYVMQAAABfSURBVHjaXc7JDsAgCEVRsYpIBzro//9qHyHpond3AgkklIuXPKJcqleIIEB6FwEhQEW0t4rlUtt+22ZTMQ09NqZyiK8BtBCvc9iDWegY526hBVRmdcQ9RgD9f/G+P1+JEwRF2vKhRgAAAABJRU5ErkJggg==",
    tipped: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAANCAMAAACq939wAAAA/FBMVEWqVQCqcQCZZgCLXQCdYgDMjACOXACOXwCacQCfcQCqegCwggChggCnfwCwggCthACtgQC9iACleQC+iwDMlgDJlACmfgDDiwDBjADHlQDJnQDFlQDKmAChfRGjgBOmfhKmghKnghOqhBKthxOviROvjB+vjCGvjCOwiRSwihixixWxjSGziBOzkSmzky+0kSa0kiy3jxW8kxS8mCi8n0i8oEq9lBe9mSvOoBbUrTTUrTjbukXcsTDctDrdtj/exHbexXDfwWXfwmvjrBnksRjksx7lrhnqx17qyWTqymrq377rz3nr2qftuiHtvSv67cD67sj+997/+OX///8rcy1sAAAAHXRSTlMDCQoLDRQkKystMDc5Oj0+QUlKS0tMTVFSV15/i6wTI/gAAACWSURBVHjaHcrnAoFQGADQryI7sjNKN0RKZJVNQ8io938YN+f3ASDp1B9NAhD15UzXNH26KhNQXZyDBxZcNlloKadvFIbR56owUOFV9425ai8PrGwZaITQeisXoKHs7k/MP44ZaHYl54U5El8EdmjNEWbEjesf/Ljd9v0S1ETBtD3PNgUxB8nOQIybOGknAKgMy2FsmoIflIEZdK7PshkAAAAASUVORK5CYII="
};
var $ = unsafeWindow.$, reddit = unsafeWindow;

/* Add the "tip bitcoins" button after "give gold". */
var tip = $('tip bitcoins').attr({
    'class': 'tip-bitcoins login-required',
    'href': '#'
});
if (/^\/r\//.test(document.location.pathname)) {
    $('a.give-gold').parent().after($('
').append(tip.clone()));
    $('.link ul.buttons').append($('
').append(tip.clone()));
}

/* Tipping button functionality. */
$('.tip-bitcoins').bind('click', function(event) {
    var $target = $(event.target);
    var form = null;
    if ($target.closest('.link').length > 0) {
        /* Post */
        form = $('.usertext-edit').first();
    } else {
        /* Comment */
        reddit.reply(event.target);
        form = reddit.comment_reply_for_elem(event.target);
    }
    var textarea = form.find('textarea');
    if (!textarea.val().match(tipregex)) {
        var insert = '+tip ' + baseTip;
        if (textarea.val().length > 0) {
            insert = '\n\n' + insert;
        } else {
            insert = insert + '\n\n';
        }
        textarea.val(textarea.val() + insert);
    }
    return false;
});

/* Reddit jQuery plugin. */
(function($) {
    /** Get the comment div for each element in the current set. */
    $.fn.comment = function() {
        return this.closest('.comment');
    };

    /** Get the comment ID for the first selected comment. */
    $.fn.commentID = function() {
        var full = this.first().find('input[name="thing_id"]').first().val();
        return full.replace(/^t1_/, '');
    };

    /** Get the commenter's name for the first selected comment. */
    $.fn.commentName = function() {
        return this.first().find('.author').first().text();
    };

    /** Get the comment's post time for the first selected comment. */
    $.fn.commentDate = function() {
        return new Date(this.find('.tagline time').first().attr('datetime'));
    };

    /** Get the comment body for the first comment in the current set. */
    $.fn.commentBody = function() {
        return this.find('.md').first();
    };

    /** Get the children comments for each comment in the current set. */
    $.fn.commentChildren = function() {
        return this.find('.comment');
    };

    /** Get the parent comment for each comment in the current set. */
    $.fn.commentParent = function() {
        return this.parent().comment();
    };

    /** Determine whether the first element is the target comment. */
    $.fn.isTarget = function() {
        return this.first().find('form').first().hasClass('border');
    };

    /** Return true if the first comment in the current set has a tip. */
    $.fn.hasTip = function() {
        return this.commentBody().children().is(function() {
            return tipregex.test($(this).text());
        });
    };
})(unsafeWindow.jQuery);

/* Hide verification replies. Note: t2_7vw3n is /u/bitcointip. */
$('a.id-t2_7vw3n').comment().each(function() {
    var $this = $(this);
    if ($this.commentChildren().length === 0 && !$this.isTarget()) {
        reddit.hidecomment($this.find('.expand').first());
    }
});

/* Find all the tip comments. */
var tips = {};
$('div.comment').each(function() {
    var $this = $(this);
    if ($this.hasTip()) {
        tips[$this.commentID()] = $this;
    }
});

/* Get status information about various tips. */
var tipIDs = Object.keys(tips);
if (tipIDs.length > 0) {
    var iconStyle = 'vertical-align: text-bottom; margin-left: 8px;';
    var display = {
        "pending": icons.verified,
        "completed": icons.verified,
        "reversed": icons.verified,
        "cancelled": icons.rejected
    };
    $.getJSON(api.gettips + '&tips=' + tipIDs, function(response) {
        response.forEach(function (tip) {
            var id = tip.fullname.replace(/^t._/, '');
            var tagline = tips[id].find('.tagline').first();
            var icon = $('').attr({href: tip.tx, target: '_blank'});
            tagline.append(icon.append($('').attr({
                src: display[tip.status],
                style: iconStyle,
                title: '+$' + tip.amountUSD + '	→  ' + tip.receiver
            })));
            delete tips[id];
        });

        /* Deal with unanswered tips. */
        for (var id in tips) {
            if (Date.now() - tips[id].commentDate() > rejectTime) {
                var tagline = tips[id].find('.tagline').first();
                tagline.append($('').attr({
                    src: icons.rejected,
                    style: iconStyle,
                    title: 'This tip is invalid.'
                }));
            }
        }
    });

    /* Put receiver information on comments. */
    var comments = {};
    $('div.comment').each(function() {
        var $this = $(this);
        comments[$this.commentID()] = $this;
    });
    var commentIDs = Object.keys(comments);
    $.getJSON(api.gettipped + '&tipped=' + commentIDs, function(response) {
        response.forEach(function (tipped) {
            var id = tipped.fullname.replace(/^t._/, '');
            var comment = comments[id];
            var tagline = comment.find('.tagline').first();
            var plural = tipped.tipQTY > 1;
            var title = comment.commentName() + ' $' + tipped.amountUSD +
                    ' for this comment.';
            if (plural) {
                title = 'redditors have given ' + title;
            } else {
                title = 'a redditor has given ' + title;
            }
            tagline.append($('').attr({
                src: icons.tipped,
                style: iconStyle,
                title: title
            }));
            if (plural) {
                tagline.append('x' + tipped.tipQTY);
            }
        });
    });
}

/* Test URLs:
 *
 * Rejected,
 *   http://www.reddit.com/r/bitcointip/comments/132nhq/t/c7c7iue
 *
 * Rejected flip,
 *   http://www.reddit.com/r/Bitcoin/comments/14i9e7/y/c7dc6w9
 *
 * Combination folding,
 *   http://www.reddit.com/r/bitcointip/comments/13iykn/b/c7dj8ia
 *
 * Multiple tips to one receiver,
 *   http://www.reddit.com/r/bitcointip/comments/12lmut/c7ny177
 *
 * Multiple guilded to one receiver (for comparison),
 *   http://www.reddit.com/r/AdviceAnimals/comments/15mk25/c7ntrcc
 */
<|endoftext|>


<commit_before>(function(){
  function StoryNavCtrl($scope){
    var testStory = {
      title: 'TestStory Title',
      description: 'Test of story nav system.',
      summary: 'First story to test story nav system',
      keywords: ['test'],
      system: 'default',
      rating: 'unrated', //TODO set this as default in Rails
      pages: []
    };

    $scope.currentStory = testStory;

    //NOTE Need to have choices properly generate their ID
    var Page = function(args){
      this.parentPage = args.parentPage;
      this.id = args.id;
      this.title = args.title;
      this.summary = args.summary;
      this.content = args.content;
      this.choices = args.choices; //Displayed to user
      this.branches = []; //Ref to other pages
      this.endpoint = args.endpoint;

      this.init = function(){
        if(this.parentPage){
          this.parentPage.branches.push(this);
        }
      }
    };

    var generateBranchIDs = function(){
      var pages = $scope.currentStory.pages;

      console.log('pages',pages);

      pages[0].id = '1';

      for(var i = 1; i < pages.length; i++){
          if(pages[i].parentPage){
            var lastChar = pages[i].parentPage.id[pages[i].parentPage.id.length -1];
            var childIndex = pages[i].parentPage.branches.indexOf(pages[i]);

            if(angular.isNumber(parseInt(lastChar))){
              pages[i].id = pages[i].parentPage.id + indexToAlpha(childIndex);
            }else{
              return pages[i].parentPage.id + (childIndex+1);
            }
        }
      }
    }

    var indexToAlpha = function(index){
      return String.fromCharCode(index + 65);
    }

    $scope.currentStory.pages.push(
      new Page(
        {
          parentPage: null,
          title: 'Initial page', //String
          summary: 'First page', //String
          content: 'You see two doors. Which do you choose?', //String
          choices: [
                        {text: 'Left door', dest: '1A'},
                        {text: 'Right door', dest: '1B'}
                   ],
          endpoint: false //Boolean
        }
      )
    );

    $scope.currentPage = $scope.currentStory.pages[0];

    $scope.currentStory.pages.push(
      new Page(
        {
          parentPage: $scope.currentStory.pages[0],
          title: 'Left door', //String
          summary: 'First page', //String
          content: 'You entered the left door.', //String
          choices: [
            {text: 'Something else', dest: '1A1'}
          ],
          endpoint: true //Boolean
        }
      )
    );

    $scope.currentStory.pages.push(
      new Page(
        {
          parentPage: $scope.currentStory.pages[0],
          title: 'Right door', //String
          summary: 'First page', //String
          content: 'You entered the right door.', //String
          endpoint: true //Boolean
        }
      )
    );

    $scope.currentStory.pages.push(
      new Page(
        {
          parentPage: $scope.currentStory.pages[1],
          title: 'Super last', //String
          summary: 'First page', //String
          content: 'Even more final.', //String
          endpoint: true //Boolean
        }
      )
    );

    $scope.setPage = function(pageID){
      var choices = $scope.currentPage.choices;
      var pages = $scope.currentStory.pages;

      for(var i in pages){
        for(var j in choices){
          if (pages[i].id === choices[j].dest){
            $scope.currentPage = pages[i];
          }
        }
      }

      return null;
    }

    var initializePages = function(){
      for (var i = 0; i < $scope.currentStory.pages.length; i++){
        $scope.currentStory.pages[i].init();
      }
    }

    $scope.$on('$viewContentLoaded', function(){
      initializePages();
      generateBranchIDs();
    });
  }

  angular
    .module('fatebook')
//    .controller('StoryNavCtrl', ['StoryNavSrv', StoryNavCtrl]);
    .controller('StoryNavCtrl', ['$scope', StoryNavCtrl]);
})();<commit_msg>Page ID assignment in nth-level branching implemented<commit_after>(function(){
  function StoryNavCtrl($scope){
    var testStory = {
      title: 'TestStory Title',
      description: 'Test of story nav system.',
      summary: 'First story to test story nav system',
      keywords: ['test'],
      system: 'default',
      rating: 'unrated', //TODO set this as default in Rails
      pages: []
    };

    $scope.currentStory = testStory;

    //NOTE Need to have choices properly generate their ID
    var Page = function(args){
      this.parentPage = args.parentPage;
      this.id = args.id;
      this.title = args.title;
      this.summary = args.summary;
      this.content = args.content;
      this.choices = args.choices; //Displayed to user
      this.branches = []; //Ref to other pages
      this.endpoint = args.endpoint;

      this.init = function(){
        if(this.parentPage){
          this.parentPage.branches.push(this);
        }
      }
    };

    var generateBranchIDs = function(){
      var pages = $scope.currentStory.pages;

      pages[0].id = '1';

      for(var i = 1; i < pages.length; i++){
          if(pages[i].parentPage){
            var lastChar = pages[i].parentPage.id[pages[i].parentPage.id.length -1];
            var childIndex = pages[i].parentPage.branches.indexOf(pages[i]);

            var reg = /^\d+$/;

            if(reg.test(lastChar)){
              pages[i].id = pages[i].parentPage.id + indexToAlpha(childIndex);
            }else{
              pages[i].id = pages[i].parentPage.id + (childIndex+1);
            }
        }
      }
    }

    var indexToAlpha = function(index){
      return String.fromCharCode(index + 65);
    }

    $scope.currentStory.pages.push(
      new Page(
        {
          parentPage: null,
          title: 'Initial page', //String
          summary: 'First page', //String
          content: 'You see two doors. Which do you choose?', //String
          choices: [
                        {text: 'Left door', dest: '1A'},
                        {text: 'Right door', dest: '1B'}
                   ],
          endpoint: false //Boolean
        }
      )
    );

    $scope.currentPage = $scope.currentStory.pages[0];

    $scope.currentStory.pages.push(
      new Page(
        {
          parentPage: $scope.currentStory.pages[0],
          title: 'Left door', //String
          summary: 'First page', //String
          content: 'You entered the left door.', //String
          choices: [
            {text: 'Something else', dest: '1A1'}
          ],
          endpoint: true //Boolean
        }
      )
    );

    $scope.currentStory.pages.push(
      new Page(
        {
          parentPage: $scope.currentStory.pages[0],
          title: 'Right door', //String
          summary: 'First page', //String
          content: 'You entered the right door.', //String
          endpoint: true //Boolean
        }
      )
    );

    $scope.currentStory.pages.push(
      new Page(
        {
          parentPage: $scope.currentStory.pages[1],
          title: 'Super last', //String
          summary: 'First page', //String
          content: 'Even more final.', //String
          endpoint: true //Boolean
        }
      )
    );

    $scope.setPage = function(pageID){
      var choices = $scope.currentPage.choices;
      var pages = $scope.currentStory.pages;

      for(var i in pages){
        for(var j in choices){
          if (pages[i].id === choices[j].dest){
            $scope.currentPage = pages[i];
          }
        }
      }

      return null;
    }

    var initializePages = function(){
      for (var i = 0; i < $scope.currentStory.pages.length; i++){
        $scope.currentStory.pages[i].init();
      }
    }

    $scope.$on('$viewContentLoaded', function(){
      initializePages();
      generateBranchIDs();
    });
  }

  angular
    .module('fatebook')
//    .controller('StoryNavCtrl', ['StoryNavSrv', StoryNavCtrl]);
    .controller('StoryNavCtrl', ['$scope', StoryNavCtrl]);
})();<|endoftext|>


<commit_before>exports = module.exports = {
    app: require('../app'),
    models: require('../app/models'),
    views: require('../app/views'),
    controllers: require('../app/controllers'),
}
<commit_msg>Hiding models<commit_after>exports = module.exports = {
    app: require('../app'),
    // models: require('../app/models'),
    views: require('../app/views'),
    controllers: require('../app/controllers'),
}
<|endoftext|>


<commit_before>// Floating label headings for the contact form
$(function() {
    $("body").on("input propertychange", ".floating-label-form-group", function(e) {
        $(this).toggleClass("floating-label-form-group-with-value", !!$(e.target).val());
    }).on("focus", ".floating-label-form-group", function() {
        $(this).addClass("floating-label-form-group-with-focus");
    }).on("blur", ".floating-label-form-group", function() {
        $(this).removeClass("floating-label-form-group-with-focus");
    });
});
// // Navigation Scripts to Show Header on Scroll-Up
// jQuery(document).ready(function($) {
//     var MQL = 1170;
//
//     //primary navigation slide-in effect
//     if ($(window).width() > MQL) {
//         var headerHeight = $('.navbar-custom').height();
//         $(window).on('scroll', {
//                 previousTop: 0
//             },
//             function() {
//                 var currentTop = $(window).scrollTop();
//                 //check if user is scrolling up
//                 if (currentTop < this.previousTop) {
//                     //if scrolling up...
//                     if (currentTop > 0 && $('.navbar-custom').hasClass('is-fixed')) {
//                         $('.navbar-custom').addClass('is-visible');
//                     } else {
//                         $('.navbar-custom').removeClass('is-visible is-fixed');
//                     }
//                 } else if (currentTop > this.previousTop) {
//                     //if scrolling down...
//                     $('.navbar-custom').removeClass('is-visible');
//                     if (currentTop > headerHeight && !$('.navbar-custom').hasClass('is-fixed')) $('.navbar-custom').addClass('is-fixed');
//                 }
//                 this.previousTop = currentTop;
//             });
//     }
// });
<commit_msg>fixing navbar to always show<commit_after>// Floating label headings for the contact form
$(function() {
    $("body").on("input propertychange", ".floating-label-form-group", function(e) {
        $(this).toggleClass("floating-label-form-group-with-value", !!$(e.target).val());
    }).on("focus", ".floating-label-form-group", function() {
        $(this).addClass("floating-label-form-group-with-focus");
    }).on("blur", ".floating-label-form-group", function() {
        $(this).removeClass("floating-label-form-group-with-focus");
    });
});
// Navigation Scripts to Show Header on Scroll-Up
jQuery(document).ready(function($) {
    var MQL = 1170;

    //primary navigation slide-in effect
    if ($(window).width() > MQL) {
        var headerHeight = $('.navbar-custom').height();
        $(window).on('scroll', {
                previousTop: 0
            },
            function() {
                var currentTop = $(window).scrollTop();
                //check if user is scrolling up
                if (currentTop < this.previousTop) {
                    //if scrolling up...
                    if (currentTop > 0 && $('.navbar-custom').hasClass('is-fixed')) {
                        $('.navbar-custom').addClass('is-visible');
                    } else {
                        $('.navbar-custom').removeClass('is-visible is-fixed');
                    }
                } else if (currentTop > this.previousTop) {
                    //if scrolling down...
                    // $('.navbar-custom').removeClass('is-visible');
                    if (currentTop > headerHeight && !$('.navbar-custom').hasClass('is-fixed')) {
                        $('.navbar-custom').addClass('is-fixed is-visible');
                    }
                }
                this.previousTop = currentTop;
            });
    }
});
<|endoftext|>