<commit_before>define([
    'jquery',
    'underscore',
    'chaplin',
    'backbone',
    'oroui/js/tools',
    '../app/components/column-renderer-component',
    './util'
], function($, _, Chaplin, Backbone, tools, ColumnRendererComponent, util) {
    'use strict';

    var Row;
    var document = window.document;

    // Cached regex to split keys for `delegate`.
    var delegateEventSplitter = /^(\S+)\s*(.*)$/;

    /**
     * Grid row.
     *
     * Triggers events:
     *  - "clicked" when row is clicked
     *
     * @export  orodatagrid/js/datagrid/row
     * @class   orodatagrid.datagrid.Row
     * @extends Chaplin.CollectionView
     */
    Row = Chaplin.CollectionView.extend({
        tagName: 'tr',
        autoRender: false,
        animationDuration: 0,

        /**
         * Override Chaplin delegate events to use events as function
         * This code supports perfomance fix.
         */
        delegateEvents: Backbone.View.prototype.delegateEvents,
        events: function() {
            var resultEvents = {};

            var events = this.collection.getCellEventList().getEventsMap();
            // prevent CS error 'cause we must completely repeat Backbone behaviour
            for (var key in events) { // jshint forin:false
                var match = key.match(delegateEventSplitter);
                var eventName = match[1];
                var selector = match[2];
                resultEvents[eventName + ' ' + 'td' + (selector ? ' ' + selector : '')] =
                    _.partial(this.delegateEventToCell, key);
            }

            // the order is important, please do not move up
            _.extend(resultEvents, {
                'mousedown': 'onMouseDown',
                'mouseleave': 'onMouseLeave',
                'mouseup': 'onMouseUp',
                'click': 'onClick'
            });
            return resultEvents;
        },

        DOUBLE_CLICK_WAIT_TIMEOUT: 170,

        template: null,

        themeOptions: {
            view: '',
            optionPrefix: 'row',
            className: 'grid-row',
            actionSelector: ''
        },

        /**
         * @inheritDoc
         */
        initialize: function(options) {
            // itemView function is called as new this.itemView
            // it is placed here to pass THIS within closure
            var _this = this;
            _.extend(this, _.pick(options, ['themeOptions', 'template', 'columns']));
            // let descendants override itemView
            if (!this.itemView) {
                this.itemView = function(options) {
                    var column = options.model;
                    var cellOptions = _this.getConfiguredCellOptions(column);
                    cellOptions.model = _this.model;
                    var Cell = column.get('cell');
                    var cell = new Cell(cellOptions);
                    if (column.has('align')) {
                        cell.$el.removeClass('align-left align-center align-right');
                        cell.$el.addClass('align-' + column.get('align'));
                    }
                    if (!_.isUndefined(cell.skipRowClick) && cell.skipRowClick) {
                        cell.$el.addClass('skip-row-click');
                    }
                    return cell;
                };
            }

            // code related to simplified event binding
            var cellEvents = this.collection.getCellEventList();
            this.listenTo(cellEvents, 'change', this.delegateEvents);

            this.listenTo(this.model, 'backgrid:selected', this.onBackgridSelected);
            this.listenTo(this.model, 'change:row_class_name', this.onRowClassNameChanged);

            this.columnRenderer = new ColumnRendererComponent(options);

            Row.__super__.initialize.apply(this, arguments);
            this.cells = this.subviews;
        },

        getConfiguredCellOptions: function(column) {
            var cellOptions = column.get('cellOptions');
            if (!cellOptions) {
                cellOptions = {
                    column: column,
                    themeOptions: {
                        optionPrefix: 'cell',
                        className: 'grid-cell grid-body-cell'
                    }
                };
                if (column.get('name')) {
                    cellOptions.themeOptions.className += ' grid-body-cell-' + column.get('name');
                }
                var Cell = column.get('cell');
                this.columns.trigger('configureInitializeOptions', Cell, cellOptions);
                column.set({
                    cellOptions: cellOptions
                });
            }
            return cellOptions;
        },

        /**
         * Run event handler on cell
         */
        delegateEventToCell: function(key, e) {
            var tdEl = $(e.target).closest('td, th')[0];

            for (var i = 0; i < this.subviews.length; i++) {
                var view = this.subviews[i];
                if (view.el === tdEl && view.events) {
                    // events cannot be function
                    // this kind of cell views are filtered in CellEventList.getEventsMap()
                    var events = view.events;
                    if (key in events) {
                        // run event
                        var method = events[key];
                        if (!_.isFunction(method)) {
                            method = view[events[key]];
                        }
                        if (!method) {
                            break;
                        }
                        var oldTarget = e.delegateTarget;
                        e.delegateTarget = tdEl;
                        method.call(view, e);
                        // must stop immediate propagation because of redelegation
                        if (e.isPropagationStopped()) {
                            e.stopImmediatePropagation();
                        }
                        e.delegateTarget = oldTarget;
                    }
                    break;
                }
            }
        },

        /**
         * Handles row "backgrid:selected" event
         *
         * @param model
         * @param isSelected
         */
        onBackgridSelected: function(model, isSelected) {
            this.$el.toggleClass('row-selected', isSelected);
        },

        onRowClassNameChanged: function(model) {
            var previousClass = model.previous('row_class_name');
            var newClass = _.result(this, 'className');
            if (previousClass) {
                this.$el.removeClass(previousClass);
            }
            if (newClass) {
                this.$el.addClass(newClass);
            }
        },

        className: function() {
            return this.model.get('row_class_name');
        },

        /**
         * @inheritDoc
         */
        dispose: function() {
            if (this.disposed) {
                return;
            }
            if (this.clickTimeout) {
                clearTimeout(this.clickTimeout);
            }
            delete this.columns;
            delete this.cells;
            Row.__super__.dispose.call(this);
        },

        onMouseDown: function(e) {
            if (this.clickTimeout) {
                // if timeout is set, it means that user makes double click
                clearTimeout(this.clickTimeout);
                delete this.clickTimeout;
                // prevent second click handler launch
                this.mouseDownSelection = null;
                this.mouseDownTarget = null;
                // prevent text selection on double click
                if ($(e.target).closest('.prevent-text-selection-on-dblclick').length) {
                    e.preventDefault();
                }
                return;
            }
            // remember selection and target
            this.mouseDownSelection = this.getSelectedText();
            this.mouseDownTarget = $(e.target).closest('td');
            this.$el.addClass('mouse-down');
        },

        onMouseLeave: function(e) {
            this.$el.removeClass('mouse-down');
        },

        onMouseUp: function(e) {
            this.clickPermit = false;
            // remember selection and target
            var $target = this.$(e.target);
            var exclude;
            var allowed;
            if (this.themeOptions.actionSelector) {
                allowed = this.themeOptions.actionSelector;
                if (!$target.is(allowed) && !$target.parents(allowed).length) {
                    return;
                }
            } else {
                exclude = 'a, .dropdown, .skip-row-click';
                // if the target is an action element, skip toggling the email
                if ($target.is(exclude) || $target.parents(exclude).length) {
                    return;
                }
            }

            if (this.mouseDownSelection !== this.getSelectedText()) {
                return;
            }

            if (this.mouseDownTarget[0] !== $target.closest('td')[0]) {
                return;
            }

            this.clickPermit = true;
        },

        onClick: function(e) {
            var _this = this;
            var options = {};
            var clickFunction = function() {
                if (_this.disposed) {
                    return;
                }

                _this.trigger('clicked', _this, options);
                if (_this.disposed) {
                    return;
                }

                for (var i = 0; i < _this.subviews.length; i++) {
                    var cell = _this.subviews[i];
                    if (cell.listenRowClick && _.isFunction(cell.onRowClicked)) {
                        cell.onRowClicked(_this, e);
                    }
                }
                _this.$el.removeClass('mouse-down');
                delete _this.clickTimeout;
            };
            if (!this.clickPermit) {
                return;
            }
            e.preventDefault();
            if (tools.isTargetBlankEvent(e)) {
                options.target = '_blank';
                clickFunction();
                return;
            }
            this.clickTimeout = setTimeout(clickFunction, this.DOUBLE_CLICK_WAIT_TIMEOUT);
        },

        /**
         * Returns selected text is available
         *
         * @return {string}
         */
        getSelectedText: function() {
            var text = '';
            if (_.isFunction(window.getSelection)) {
                text = window.getSelection().toString();
            } else if (!_.isUndefined(document.selection) && document.selection.type === 'Text') {
                text = document.selection.createRange().text;
            }
            return text;
        },

        render: function() {
            this._deferredRender();
            if (this.template) {
                this.renderCustomTemplate();
            } else {
                Row.__super__.render.apply(this, arguments);
            }
            var state = {selected: false};
            this.model.trigger('backgrid:isSelected', this.model, state);
            this.$el.toggleClass('row-selected', state.selected);

            if (this.$el.data('layout') === 'separate') {
                var options = {};
                if (this.$el.data('layout-model')) {
                    options[this.$el.data('layout-model')] = this.model;
                }
                this.initLayout(options).always(_.bind(function() {
                    this._resolveDeferredRender();
                }, this));
            } else {
                this._resolveDeferredRender();
            }

            return this;
        },

        renderCustomTemplate: function() {
            var self = this;
            var $checkbox;
            this.$el.html(this.template({
                model: this.model ? this.model.attributes : {},
                themeOptions: this.themeOptions ? this.themeOptions : {},
                render: function(columnName) {
                    var columnModel = _.find(self.columns.models, function(model) {
                        return model.get('name') === columnName;
                    });
                    if (columnModel) {
                        return self.columnRenderer.getHtml(self.renderItem(columnModel).$el);
                    }
                    return '';
                },
                attributes: function(columnName, additionalAttributes) {
                    var attributes = additionalAttributes || {};
                    var columnModel = _.find(self.columns.models, function(model) {
                        return model.get('name') === columnName;
                    });
                    if (columnModel) {
                        return self.columnRenderer.getRawAttributes(self.renderItem(columnModel).$el, attributes);
                    }
                    return '';
                }
            }));
            $checkbox = this.$('[data-role=select-row]:checkbox');
            if ($checkbox.length) {
                this.listenTo(this.model, 'backgrid:select', function(model, checked) {
                    $checkbox.prop('checked', checked);
                });
                $checkbox.on('change' + this.eventNamespace(), _.bind(function(e) {
                    this.model.trigger('backgrid:selected', this.model, $checkbox.prop('checked'));
                }, this));
                $checkbox.on('click' + this.eventNamespace(), function(e) {
                    e.stopPropagation();
                });
            }
            return this;
        }
    });

    return Row;
});
<commit_msg>BB-5960: FrontStore > Search > Filter > JS error when press on the Clear All Filters link and selected filters are not cleaned  - fix row render<commit_after>define([
    'jquery',
    'underscore',
    'chaplin',
    'backbone',
    'oroui/js/tools',
    '../app/components/column-renderer-component',
    './util'
], function($, _, Chaplin, Backbone, tools, ColumnRendererComponent, util) {
    'use strict';

    var Row;
    var document = window.document;

    // Cached regex to split keys for `delegate`.
    var delegateEventSplitter = /^(\S+)\s*(.*)$/;

    /**
     * Grid row.
     *
     * Triggers events:
     *  - "clicked" when row is clicked
     *
     * @export  orodatagrid/js/datagrid/row
     * @class   orodatagrid.datagrid.Row
     * @extends Chaplin.CollectionView
     */
    Row = Chaplin.CollectionView.extend({
        tagName: 'tr',
        autoRender: false,
        animationDuration: 0,

        /**
         * Override Chaplin delegate events to use events as function
         * This code supports perfomance fix.
         */
        delegateEvents: Backbone.View.prototype.delegateEvents,
        events: function() {
            var resultEvents = {};

            var events = this.collection.getCellEventList().getEventsMap();
            // prevent CS error 'cause we must completely repeat Backbone behaviour
            for (var key in events) { // jshint forin:false
                var match = key.match(delegateEventSplitter);
                var eventName = match[1];
                var selector = match[2];
                resultEvents[eventName + ' ' + 'td' + (selector ? ' ' + selector : '')] =
                    _.partial(this.delegateEventToCell, key);
            }

            // the order is important, please do not move up
            _.extend(resultEvents, {
                'mousedown': 'onMouseDown',
                'mouseleave': 'onMouseLeave',
                'mouseup': 'onMouseUp',
                'click': 'onClick'
            });
            return resultEvents;
        },

        DOUBLE_CLICK_WAIT_TIMEOUT: 170,

        template: null,

        themeOptions: {
            view: '',
            optionPrefix: 'row',
            className: 'grid-row',
            actionSelector: ''
        },

        /**
         * @inheritDoc
         */
        initialize: function(options) {
            // itemView function is called as new this.itemView
            // it is placed here to pass THIS within closure
            var _this = this;
            _.extend(this, _.pick(options, ['themeOptions', 'template', 'columns']));
            // let descendants override itemView
            if (!this.itemView) {
                this.itemView = function(options) {
                    var column = options.model;
                    var cellOptions = _this.getConfiguredCellOptions(column);
                    cellOptions.model = _this.model;
                    var Cell = column.get('cell');
                    var cell = new Cell(cellOptions);
                    if (column.has('align')) {
                        cell.$el.removeClass('align-left align-center align-right');
                        cell.$el.addClass('align-' + column.get('align'));
                    }
                    if (!_.isUndefined(cell.skipRowClick) && cell.skipRowClick) {
                        cell.$el.addClass('skip-row-click');
                    }
                    return cell;
                };
            }

            // code related to simplified event binding
            var cellEvents = this.collection.getCellEventList();
            this.listenTo(cellEvents, 'change', this.delegateEvents);

            this.listenTo(this.model, 'backgrid:selected', this.onBackgridSelected);
            this.listenTo(this.model, 'change:row_class_name', this.onRowClassNameChanged);

            this.columnRenderer = new ColumnRendererComponent(options);

            Row.__super__.initialize.apply(this, arguments);
            this.cells = this.subviews;
        },

        getConfiguredCellOptions: function(column) {
            var cellOptions = column.get('cellOptions');
            if (!cellOptions) {
                cellOptions = {
                    column: column,
                    themeOptions: {
                        optionPrefix: 'cell',
                        className: 'grid-cell grid-body-cell'
                    }
                };
                if (column.get('name')) {
                    cellOptions.themeOptions.className += ' grid-body-cell-' + column.get('name');
                }
                var Cell = column.get('cell');
                this.columns.trigger('configureInitializeOptions', Cell, cellOptions);
                column.set({
                    cellOptions: cellOptions
                });
            }
            return cellOptions;
        },

        /**
         * Run event handler on cell
         */
        delegateEventToCell: function(key, e) {
            var tdEl = $(e.target).closest('td, th')[0];

            for (var i = 0; i < this.subviews.length; i++) {
                var view = this.subviews[i];
                if (view.el === tdEl && view.events) {
                    // events cannot be function
                    // this kind of cell views are filtered in CellEventList.getEventsMap()
                    var events = view.events;
                    if (key in events) {
                        // run event
                        var method = events[key];
                        if (!_.isFunction(method)) {
                            method = view[events[key]];
                        }
                        if (!method) {
                            break;
                        }
                        var oldTarget = e.delegateTarget;
                        e.delegateTarget = tdEl;
                        method.call(view, e);
                        // must stop immediate propagation because of redelegation
                        if (e.isPropagationStopped()) {
                            e.stopImmediatePropagation();
                        }
                        e.delegateTarget = oldTarget;
                    }
                    break;
                }
            }
        },

        /**
         * Handles row "backgrid:selected" event
         *
         * @param model
         * @param isSelected
         */
        onBackgridSelected: function(model, isSelected) {
            this.$el.toggleClass('row-selected', isSelected);
        },

        onRowClassNameChanged: function(model) {
            var previousClass = model.previous('row_class_name');
            var newClass = _.result(this, 'className');
            if (previousClass) {
                this.$el.removeClass(previousClass);
            }
            if (newClass) {
                this.$el.addClass(newClass);
            }
        },

        className: function() {
            return this.model.get('row_class_name');
        },

        /**
         * @inheritDoc
         */
        dispose: function() {
            if (this.disposed) {
                return;
            }
            if (this.clickTimeout) {
                clearTimeout(this.clickTimeout);
            }
            delete this.columns;
            delete this.cells;
            Row.__super__.dispose.call(this);
        },

        onMouseDown: function(e) {
            if (this.clickTimeout) {
                // if timeout is set, it means that user makes double click
                clearTimeout(this.clickTimeout);
                delete this.clickTimeout;
                // prevent second click handler launch
                this.mouseDownSelection = null;
                this.mouseDownTarget = null;
                // prevent text selection on double click
                if ($(e.target).closest('.prevent-text-selection-on-dblclick').length) {
                    e.preventDefault();
                }
                return;
            }
            // remember selection and target
            this.mouseDownSelection = this.getSelectedText();
            this.mouseDownTarget = $(e.target).closest('td');
            this.$el.addClass('mouse-down');
        },

        onMouseLeave: function(e) {
            this.$el.removeClass('mouse-down');
        },

        onMouseUp: function(e) {
            this.clickPermit = false;
            // remember selection and target
            var $target = this.$(e.target);
            var exclude;
            var allowed;
            if (this.themeOptions.actionSelector) {
                allowed = this.themeOptions.actionSelector;
                if (!$target.is(allowed) && !$target.parents(allowed).length) {
                    return;
                }
            } else {
                exclude = 'a, .dropdown, .skip-row-click';
                // if the target is an action element, skip toggling the email
                if ($target.is(exclude) || $target.parents(exclude).length) {
                    return;
                }
            }

            if (this.mouseDownSelection !== this.getSelectedText()) {
                return;
            }

            if (this.mouseDownTarget[0] !== $target.closest('td')[0]) {
                return;
            }

            this.clickPermit = true;
        },

        onClick: function(e) {
            var _this = this;
            var options = {};
            var clickFunction = function() {
                if (_this.disposed) {
                    return;
                }

                _this.trigger('clicked', _this, options);
                if (_this.disposed) {
                    return;
                }

                for (var i = 0; i < _this.subviews.length; i++) {
                    var cell = _this.subviews[i];
                    if (cell.listenRowClick && _.isFunction(cell.onRowClicked)) {
                        cell.onRowClicked(_this, e);
                    }
                }
                _this.$el.removeClass('mouse-down');
                delete _this.clickTimeout;
            };
            if (!this.clickPermit) {
                return;
            }
            e.preventDefault();
            if (tools.isTargetBlankEvent(e)) {
                options.target = '_blank';
                clickFunction();
                return;
            }
            this.clickTimeout = setTimeout(clickFunction, this.DOUBLE_CLICK_WAIT_TIMEOUT);
        },

        /**
         * Returns selected text is available
         *
         * @return {string}
         */
        getSelectedText: function() {
            var text = '';
            if (_.isFunction(window.getSelection)) {
                text = window.getSelection().toString();
            } else if (!_.isUndefined(document.selection) && document.selection.type === 'Text') {
                text = document.selection.createRange().text;
            }
            return text;
        },

        render: function() {
            this._deferredRender();
            Row.__super__.render.apply(this, arguments);
            var state = {selected: false};
            this.model.trigger('backgrid:isSelected', this.model, state);
            this.$el.toggleClass('row-selected', state.selected);

            if (this.$el.data('layout') === 'separate') {
                var options = {};
                if (this.$el.data('layout-model')) {
                    options[this.$el.data('layout-model')] = this.model;
                }
                this.initLayout(options).always(_.bind(function() {
                    this._resolveDeferredRender();
                }, this));
            } else {
                this._resolveDeferredRender();
            }

            return this;
        },

        renderAllItems: function() {
            if (this.template) {
                this.renderCustomTemplate();
            } else {
                return Row.__super__.renderAllItems.apply(this, arguments);
            }
        },

        renderCustomTemplate: function() {
            var self = this;
            var $checkbox;
            this.$el.html(this.template({
                model: this.model ? this.model.attributes : {},
                themeOptions: this.themeOptions ? this.themeOptions : {},
                render: function(columnName) {
                    var columnModel = _.find(self.columns.models, function(model) {
                        return model.get('name') === columnName;
                    });
                    if (columnModel) {
                        return self.columnRenderer.getHtml(self.renderItem(columnModel).$el);
                    }
                    return '';
                },
                attributes: function(columnName, additionalAttributes) {
                    var attributes = additionalAttributes || {};
                    var columnModel = _.find(self.columns.models, function(model) {
                        return model.get('name') === columnName;
                    });
                    if (columnModel) {
                        return self.columnRenderer.getRawAttributes(self.renderItem(columnModel).$el, attributes);
                    }
                    return '';
                }
            }));
            $checkbox = this.$('[data-role=select-row]:checkbox');
            if ($checkbox.length) {
                this.listenTo(this.model, 'backgrid:select', function(model, checked) {
                    $checkbox.prop('checked', checked);
                });
                $checkbox.on('change' + this.eventNamespace(), _.bind(function(e) {
                    this.model.trigger('backgrid:selected', this.model, $checkbox.prop('checked'));
                }, this));
                $checkbox.on('click' + this.eventNamespace(), function(e) {
                    e.stopPropagation();
                });
            }
            return this;
        }
    });

    return Row;
});
<|endoftext|>


<commit_before>var React = require('react');
var ReactDOM = require('react-dom');
var Animation = require('./Animation');
var TransitionInfo = require('./TransitionInfo');
var TransitionParser = require('./TransitionParser');

var TransitionChild = React.createClass({
  displayName: 'TransitionChild',

  propTypes: {
    children: React.PropTypes.any,
    childrenAppearStyle: React.PropTypes.object,
    childrenBaseStyle: React.PropTypes.object,
    childrenEnterStyle: React.PropTypes.object,
    childrenLeaveStyle: React.PropTypes.object,
    id: React.PropTypes.oneOfType(
      [React.PropTypes.string, React.PropTypes.number]
    ),
    onChildAppeared: React.PropTypes.func,
    onChildEntered: React.PropTypes.func,
    onChildLeft: React.PropTypes.func,
    onChildStartAppear: React.PropTypes.func,
    onChildStartEnter: React.PropTypes.func,
    onChildStartLeave: React.PropTypes.func,
    style: React.PropTypes.object,
  },

  getInitialState: function () {
    return {
      style: this._computeNewStyle(),
    };
  },

  componentDidMount: function () {
    this._frameIds = [];
  },

  componentWillUnmount: function () {
    var node = ReactDOM.findDOMNode(this);
    if (!node) return;

    node.removeEventListener('transitionend', this._handleReference);

    Animation.cancelFrames(this._frameIds);
  },

  componentWillAppear: function (callback) {
    this._transition(callback, 'appear');

    if (this.props.onChildStartAppear) {
      this.props.onChildStartAppear(this.props.id);
    }
  },

  componentDidAppear: function () {
    if (this.props.onChildAppeared) {
      this.props.onChildAppeared(this.props.id);
    }
  },

  componentWillEnter: function (callback) {
    this._transition(callback, 'enter');

    if (this.props.onChildStartEnter) {
      this.props.onChildStartEnter(this.props.id);
    }
  },

  componentDidEnter: function () {
    if (this.props.onChildEntered) {
      this.props.onChildEntered(this.props.id);
    }
  },

  componentWillLeave: function (callback) {
    this._transition(callback, 'leave');

    if (this.props.onChildStartLeave) {
      this.props.onChildStartLeave(this.props.id);
    }
  },

  componentDidLeave: function () {
    if (this.props.onChildLeft) this.props.onChildLeft(this.props.id);
  },

  _handleTransitionEnd: function (
    node, maxTimeProperty, propertyArray, callback, event
  ) {
    // Check if the element where the transitionend event occurred was the
    // node we are working with and not one of its children.
    if (node === event.target) {
      // TODO: Instead of this huge and ugly if statement expand the shorthand
      // properties and bind the expansion to the handler.
      if (maxTimeProperty === event.propertyName ||
          TransitionInfo.isShorthand(
            maxTimeProperty, event.propertyName, propertyArray
          ) ||
          maxTimeProperty === 'all' && !TransitionInfo.isInPropertyList(
            event.propertyName, propertyArray
          )) {
        node.removeEventListener('transitionend', this._handleReference);
        callback();
      }
    }
  },

  _computeNewStyle: function (phase) {
    var nextStyle;

    if (phase === 'appear') nextStyle = this.props.childrenAppearStyle;
    else if (phase === 'enter') nextStyle = this.props.childrenEnterStyle;
    else if (phase === 'leave') nextStyle = this.props.childrenLeaveStyle;
    else nextStyle = this.props.childrenBaseStyle;

    return Object.assign(
      {}, this.props.style, this.props.childrenBaseStyle, nextStyle
    );
  },

  _transition: function (callback, phase) {
    if ((phase === 'appear' && !this.props.childrenAppearStyle) ||
        (phase === 'enter' && !this.props.childrenEnterStyle) ||
        (phase === 'leave' && !this.props.childrenLeaveStyle)) {
      callback();
    }
    else {
      var frameId = Animation.requestNextFrame((function () {
        this._executeTransition(callback, phase);
      }).bind(this));
      this._frameIds.push(frameId);
    }
  },

  _executeTransition: function (callback, phase) {
    var node = ReactDOM.findDOMNode(this);
    if (!node) return;

    var nextStyle = this._computeNewStyle(phase);
    var transitionValues = TransitionParser.getTransitionValues(nextStyle);

    var maxTimeProperty = TransitionInfo.getMaximumTimeProperty(
      transitionValues
    );

    node.removeEventListener('transitionend', this._handleReference);

    if (maxTimeProperty) {
      // To guarantee the transitionend handler of another phase will not
      // interfere with the handler of the current phase create a new one every
      // time.
      this._handleReference = this._handleTransitionEnd.bind(
        this,
        node,
        maxTimeProperty,
        transitionValues.transitionProperty,
        callback
      );
      node.addEventListener('transitionend', this._handleReference);
    }
    else {
      callback();
    }

    // Using setAttribute() or the functions in CSSPropertyOperations would
    // probably be faster, but stateless components are not working well with
    // this approach.
    this.setState({style: nextStyle});
  },

  render: function () {
    return React.Children.only(
      React.cloneElement(this.props.children, {
        style: this.state.style,
      })
    );
  },

});

module.exports = TransitionChild;<commit_msg>Make props.style have the highest priority<commit_after>var React = require('react');
var ReactDOM = require('react-dom');
var Animation = require('./Animation');
var TransitionInfo = require('./TransitionInfo');
var TransitionParser = require('./TransitionParser');

var TransitionChild = React.createClass({
  displayName: 'TransitionChild',

  propTypes: {
    children: React.PropTypes.any,
    childrenAppearStyle: React.PropTypes.object,
    childrenBaseStyle: React.PropTypes.object,
    childrenEnterStyle: React.PropTypes.object,
    childrenLeaveStyle: React.PropTypes.object,
    id: React.PropTypes.oneOfType(
      [React.PropTypes.string, React.PropTypes.number]
    ),
    onChildAppeared: React.PropTypes.func,
    onChildEntered: React.PropTypes.func,
    onChildLeft: React.PropTypes.func,
    onChildStartAppear: React.PropTypes.func,
    onChildStartEnter: React.PropTypes.func,
    onChildStartLeave: React.PropTypes.func,
    style: React.PropTypes.object,
  },

  getInitialState: function () {
    return {
      style: this._computeNewStyle(),
    };
  },

  componentDidMount: function () {
    this._frameIds = [];
  },

  componentWillUnmount: function () {
    var node = ReactDOM.findDOMNode(this);
    if (!node) return;

    node.removeEventListener('transitionend', this._handleReference);

    Animation.cancelFrames(this._frameIds);
  },

  componentWillAppear: function (callback) {
    this._transition(callback, 'appear');

    if (this.props.onChildStartAppear) {
      this.props.onChildStartAppear(this.props.id);
    }
  },

  componentDidAppear: function () {
    if (this.props.onChildAppeared) {
      this.props.onChildAppeared(this.props.id);
    }
  },

  componentWillEnter: function (callback) {
    this._transition(callback, 'enter');

    if (this.props.onChildStartEnter) {
      this.props.onChildStartEnter(this.props.id);
    }
  },

  componentDidEnter: function () {
    if (this.props.onChildEntered) {
      this.props.onChildEntered(this.props.id);
    }
  },

  componentWillLeave: function (callback) {
    this._transition(callback, 'leave');

    if (this.props.onChildStartLeave) {
      this.props.onChildStartLeave(this.props.id);
    }
  },

  componentDidLeave: function () {
    if (this.props.onChildLeft) this.props.onChildLeft(this.props.id);
  },

  _handleTransitionEnd: function (
    node, maxTimeProperty, propertyArray, callback, event
  ) {
    // Check if the element where the transitionend event occurred was the
    // node we are working with and not one of its children.
    if (node === event.target) {
      // TODO: Instead of this huge and ugly if statement expand the shorthand
      // properties and bind the expansion to the handler.
      if (maxTimeProperty === event.propertyName ||
          TransitionInfo.isShorthand(
            maxTimeProperty, event.propertyName, propertyArray
          ) ||
          maxTimeProperty === 'all' && !TransitionInfo.isInPropertyList(
            event.propertyName, propertyArray
          )) {
        node.removeEventListener('transitionend', this._handleReference);
        callback();
      }
    }
  },

  _computeNewStyle: function (phase) {
    var nextStyle;

    if (phase === 'appear') nextStyle = this.props.childrenAppearStyle;
    else if (phase === 'enter') nextStyle = this.props.childrenEnterStyle;
    else if (phase === 'leave') nextStyle = this.props.childrenLeaveStyle;
    else nextStyle = this.props.childrenBaseStyle;

    return Object.assign(
      {}, this.props.childrenBaseStyle, nextStyle, this.props.style
    );
  },

  _transition: function (callback, phase) {
    if ((phase === 'appear' && !this.props.childrenAppearStyle) ||
        (phase === 'enter' && !this.props.childrenEnterStyle) ||
        (phase === 'leave' && !this.props.childrenLeaveStyle)) {
      callback();
    }
    else {
      var frameId = Animation.requestNextFrame((function () {
        this._executeTransition(callback, phase);
      }).bind(this));
      this._frameIds.push(frameId);
    }
  },

  _executeTransition: function (callback, phase) {
    var node = ReactDOM.findDOMNode(this);
    if (!node) return;

    var nextStyle = this._computeNewStyle(phase);
    var transitionValues = TransitionParser.getTransitionValues(nextStyle);

    var maxTimeProperty = TransitionInfo.getMaximumTimeProperty(
      transitionValues
    );

    node.removeEventListener('transitionend', this._handleReference);

    if (maxTimeProperty) {
      // To guarantee the transitionend handler of another phase will not
      // interfere with the handler of the current phase create a new one every
      // time.
      this._handleReference = this._handleTransitionEnd.bind(
        this,
        node,
        maxTimeProperty,
        transitionValues.transitionProperty,
        callback
      );
      node.addEventListener('transitionend', this._handleReference);
    }
    else {
      callback();
    }

    // Using setAttribute() or the functions in CSSPropertyOperations would
    // probably be faster, but stateless components are not working well with
    // this approach.
    this.setState({style: nextStyle});
  },

  render: function () {
    return React.Children.only(
      React.cloneElement(this.props.children, {
        style: this.state.style,
      })
    );
  },

});

module.exports = TransitionChild;<|endoftext|>


<commit_before>/**
 * @file This file is the main (i.e. body) javascript file of neural network web worker.
 * It is not an importable module.
 *
 * In module (non-classic) web worker, static import is available. But at the same
 * time, importScripts() will not be avbailable. For solving this problem, using
 * classic (non-module) web worker so that tensorflow.js can be loaded by
 * importScripts(). At the same time, using dynamic import() to load ourselves module
 * because import() can be used in classic (non-module) script.
 */

//import * as Net from "./Net.js";

/**
 * The implementation of a neural network web worker.
 *
 */
class WorkerBody {

  /**
   *
   * @param {number} workerId
   *   A non-negative integer represents this worker's id. The id of the first worker
   * should be 0.
   *
   * @param {Net.Config} neuralNetConfig
   *   The configuration of the neural network which will be created by this web worker.
   *
   * @param {string} weightsSpreadsheetId
   *   The Google Sheets spreadsheetId of neural network weights. Every worker will
   * load weights from the spreadsheet to initialize one neural network.
   *
   * @param {string} weightsAPIKey
   *   The API key for accessing the Google Sheets spreadsheet of neural network weights.
   */
  init(
    workerId = 0,
    tensorflowJsURL, neuralNetConfig, weightsSpreadsheetId, weightsAPIKey ) {

    if ( workerId < 0 )
      workerId = 0;

    this.workerId = workerId;
    this.tensorflowJsURL = tensorflowJsURL;
    this.neuralNetConfig = neuralNetConfig;
    this.weightsSpreadsheetId = weightsSpreadsheetId;
    this.weightsAPIKey = weightsAPIKey;

    // Because every web worker will copy the input, there is not necessary to keep input.
    let bKeepInputTensor = false;

//!!! ...unfinished... global scope ? report progress ?

    // If a specific library module does not existed, all libraries might have not yet been loaded. (i.e. this is the first time WorkerBody.init() is called.)
    if ( !globalThis.NeuralNetProgress ) {

      globalThis.NeuralNetProgress = await import( "./NetProgress.js" ); // Load progress library in globalThis.ValueMax scope dynamically.

      this.initProgress = new NeuralNetProgress.InitProgress(); // The progress object could be created only after the progress library has been loaded.
      this.initProgress.libraryDownload.total = 3; // There are 3 libraries should be loaded: NetProgress, tensorflow.js, NeuralNet.

      this.initProgress.weightsDownload.total = 1; // There are ??? 1 weights file will be loaded.

//!!! How to know this? According to neural net architecture? It will be known only after it has been downloaded.
      this.initProgress.weightsParse.total = 10000; // There are ??? weights will be parsed.

//!!! It seems that the libraryDownload and weightsDownload should not be included.
// Because the weightsParse.total is unknown when downloading library and weights, it not possible to set total of every progress at one time.

      ++this.initProgress.libraryDownload.accumulation; // The library NetProgress has been loaded.

//!!! ...unfinished... inform WorkerProxy progress changed.

      importScripts( tensorflowJsURL ); // Load tensorflow javascript library in global scope.
      ++this.initProgress.libraryDownload.accumulation; // The library tensorflow.js has been loaded.

      globalThis.NeuralNet = await import( "./Net.js" ); // Load neural network library in globalThis.NeuralNet scope dynamically.
      ++this.initProgress.libraryDownload.accumulation; // The library NeuralNet has been loaded.
    }

//!!! ...unfinished... the neuralNetConfig is still class NeuralNet.Config? Otherwise, the create_ScaledSourceTensor_from_PixelData() will be lost.

    this.neuralNet = new NeuralNet.Base();
    this.neuralNet.init( neuralNetConfig, bKeepInputTensor );

//!!! ...unfinished...
    // Download and parse neural network weights. Also report downloading and parsing progress.

//       this.initProgress.weightsDownload;
//       this.initProgress.weightsParse;

  }

  disposeWorker() {
    if ( this.neuralNet ) {
      this.neuralNet.disposeTensors();
      this.neuralNet = null;
    }

//!!!??? calling close() when the next worker disposed ?
    close();
  }

//!!! Regular Expression for get text inside html table markup:
//
//  /(?<=
]*>.*)(?<=>)[^<]+(?=<)(?=.*
)/g
//
// It can be used to parsing weights data from Google Sheets published html. Problem: safari browser does not support lookbehind regular expression.

//!!! Regular Expression for get text inside html table data (with or without div) markup:
//
//  let r = RegExp( "]*>(?:
]*>)?([^<]*)(?:
)?	", "g" );
//  let extractedLinesByCells = String( sourceHTMLText ).replace( r, "$1\n" );
//
// The capture group 1 is one cell of one column of google sheet.

//!!! ...unfinished... (2022/08/25)
// Perhaps, use ImageBitmap (transferred object) to transfer image between web worker.
// i.e. using tf.browser.fromPixelsAsync( canvas )

  /**
   * Convert source image data to tensor3d, scale it, transfer scaled source
   * typed-array back to WorkerProxy, compute neural network, pass result back
   * to WorkerProxy.
   *
   * @param {number} processingId
   *   The id of this processing. It is used when reporting processing result so
   * that WorkerProxy could find out corresponding promise.
   *
   * @param {ImageData} sourceImageData
   *   The image data to be processed. The size should be [ sourceImageData.height,
   * sourceImageData.width ] = [ this.neuralNet.sourceImageHeightWidth[ 0 ],
   * this.neuralNet.sourceImageHeightWidth[ 1 ] ]. And it should RGBA 4 channels.
   */
  imageData_transferBack_processTensor( processingId, sourceImageData ) {

    // Create (scaled) source image so that then neural network can process it.
    //
    // Usually, only the first web worker ( workerId == 0 ) is responsible for
    // scaling the source image data to default size. After that, all other web
    // worker received the already scaled typed-array.
    //
    // The reason why not use source ImageData directly is that the next web worker
    // could re-create tensor3d more effficiently.
    //
    // ImageData uses Uint8ClampedArray internally. If it is past directly, the next
    // web worker needs create tensor3d by fromPixels() which internally converts
    // twice: from Uint8ClampedArray to Int32Array and from Int32Array to tensor3d.
    //
    // If passing typed-array (Float32Array), the next web worker could use it to
    // re-create tensord3d directly.
    //
    let scaledSourceTensor
      = this.neuralNetConfig.create_ScaledSourceTensor_from_PixelData( sourceImageData );

//!!! ...unfinished... If the this.neuralNetConfig (which is past through web worker message) is not real NeuralNet.Config, the following should be used.
//    let scaledSourceTensor = NeuralNet.Config.create_ScaledSourceTensor_from_PixelData.call( this.neuralNetConfig, sourceImageData );

    // Download the scaledSourceTensor as typed-array (asynchronously), and transfer it back to WorkerProxy (and inform WorkerProxies).
    //
    // The reason why it is done asynchronously is for not blocking the following computation of neural network.
    this.transferBackSourceScaledTensorAsync( processingId, scaledSourceTensor );

    // At the same time (the scaled source typed-array data is transferring back to WorkerProxy and then WorkerProxies), this worker is
    // still computing the neural network parallelly.
    this.processTensorAndDispose( processingId, scaledSourceTensor );
  }

  /**
   * Transfer scaled source typed-array data back to WorkerProxy, re-create source tensor, compute neural network, pass result back to WorkerProxy.
   *
   * @param {number} processingId
   *   The id of this processing. It is used when reporting processing resultso that WorkerProxy could find back corresponding promise.
   *
   * @param {Float32Array} sourceTypedArray
   *   The source typed-data to be processed. Its shape should be [ height, width, channel ] =
   * [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ].
   */
  typedArray_transferBack_processTensor( processingId, sourceTypedArray ) {
    let shape = [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ];

    // Re-create (scaled) source tensor.
    //
    // This should be done before calling transferBackSourceTypedArray() which will transfer (not copy) the sourceTypedArray and invalid it.
    let scaledSourceTensor = tf.tensor3d( sourceTypedArray, shape );

    // Transfer (not copy) the sourceTypedArray back to WorkerProxy). This will invalid sourceTypedArray.
    this.transferBackSourceTypedArray( processingId, sourceTypedArray );

    // At the same time (the sourceTypedArray is transferring back to WorkerProxy and then WorkerProxies), this worker is still computing
    // the neural network parallelly.
    this.processTensorAndDispose( processingId, scaledSourceTensor );
  }

  /**
   * Re-create source tensor, compute neural network, pass result back to WorkerProxy.
   *
   * It will not transfer source back to WorkerProxy. This is different from typedArray_TransferBack_processTensor().
   *
   * @param {number} processingId
   *   The id of this processing. It is used when reporting processing resultso that WorkerProxy could find back corresponding promise.
   *
   * @param {Float32Array} sourceTypedArray
   *   The source typed-data to be processed. Its shape should be [ height, width, channel ] =
   * [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ].
   */
  typedArray_processTensor( processingId, sourceTypedArray ) {
    let shape = [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ];
    let scaledSourceTensor = tf.tensor3d( sourceTypedArray, shape ); // Re-create (scaled) source tensor.
    this.processTensorAndDispose( processingId, scaledSourceTensor );
  }

  /**
   * Compute neural network, and pass result back to WorkerProxy.
   *
   * @param {number} processingId
   *   The id of this processing. It is used when reporting processing resultso that WorkerProxy could find back corresponding promise.
   *
   * @param {tf.tensor3d} scaledSourceTensor
   *   The source tensor3d to be processed. The scaledSourceTensor.shape should be [ height, width, channel ] =
   * [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ].
   * This tensor will be disposed when processing is done.
   */
  processTensorAndDispose( processingId, scaledSourceTensor ) {

    // Note: scaledSourceTensor will be dispose because this.neuralNet is initialized with ( bKeepInputTensor == false ).
    let resultTensor3d = this.neuralNet.apply_and_destroy_or_keep( scaledSourceTensor, true );

    let resultTypedArray = resultTensor3d.dataSync(); // Download synchronously (because here is web worker).
    resultTensor3d.dispose(); // The result tensor should also be disposed.

    // Pass the output of neural network to WorkerProxy (and inform WorkerProxies).
    let message = { command: "processTensorResult", workerId: this.workerId, processingId: processingId, resultTypedArray: resultTypedArray };
    postMessage( message, [ message.resultTypedArray.buffer ] );
  }

  /**
   * Download the scaledSourceTensor as typed-array (Float32Array) asynchronously. Transfer the typed-array back to WorkerProxy
   * (and inform WorkerProxies) so that it can be past to next web worker.
   *
   * @param {number} processingId
   *   The id of this processing. It is used by WorkerProxy to found back corresponding promise.
   *
   * @param {tf.tensor3d} scaledSourceTensor
   *   The scaled source tensor3d generated by processImageData().
   */
  async transferBackSourceScaledTensorAsync( processingId, scaledSourceTensor ) {
    let sourceTypedArray = await scaledSourceTensor.data(); // Download scaled source tensor3d as typed-array (Float32Array) asynchronously.
    this.transferBackSourceTypedArray( processingId, sourceTypedArray );
  }

  /**
   * Transfer source typed-array back to WorkerProxy (and inform WorkerProxies) so that it can be past to next web worker.
   *
   * @param {number} processingId
   *   The id of this processing. It is used by WorkerProxy to found back corresponding promise.
   *
   * @param {Float32Array} sourceTypedArray
   *   The source typed-array past to processTypedArray(). This will become invalid after this call because it will be transferred (not
   * copied) back to WorkerProxy.
   */
  transferBackSourceTypedArray( processingId, sourceTypedArray ) {
    let message = { command: "transferBackSourceTypedArray", workerId: this.workerId, processingId: processingId, sourceTypedArray: sourceTypedArray };
    postMessage( message, [ message.sourceTypedArray.buffer ] );
  }

//!!! ...unfinished... How to know and set total of the following:
//       this.initProgress.libraryDownload;
//       this.initProgress.weightsDownload;
//       this.initProgress.weightsParse;

//       this.initProgress.libraryDownload.total = 3; // There are 3 libraries should be loaded: NetProgress, tensorflow.js, NeuralNet.
//       this.initProgress.libraryDownload.accumulation = 0;

  /** Inform WorkerProxy to set total of every progress of initProgress. */
  reportInitProgress_setTotal() {
    let message = { command: "reportInitProgress", subCommand: "setTotal", workerId: this.workerId,
      libraryDownload: this.initProgress.libraryDownload.total,
      weightsDownload: this.initProgress.weightsDownload.total,
      weightsParse: this.initProgress.weightsParse.total,
    };
    postMessage( message );
  }

  /** Inform WorkerProxy to reset initProgress. */
  reportInitProgress_restAccumulation() {
    let message = { command: "reportInitProgress", subCommand: "restAccumulation", workerId: this.workerId };
    postMessage( message );
  }

  /** Inform WorkerProxy to advance initProgress.libraryDownload. */
  reportInitProgress_libraryDownload() {
//    let message = { command: "initProgress_libraryDownload", workerId: this.workerId, total: this.initProgress.libraryDownload.total, accumulation: this.initProgress.libraryDownload.accumulation };
//    let message = { command: "initProgress_libraryDownload", workerId: this.workerId, initProgress: this.initProgress.libraryDownload };
    let message = { command: "reportInitProgress", subCommand: "libraryDownload", workerId: this.workerId, total: this.initProgress.libraryDownload.total, accumulation: this.initProgress.libraryDownload.accumulation };
    postMessage( message );
  }
}

if ( globalThis.document )
  return; // In main document context (Not in worker context). Do nothing. (Should not happen)

// In worker context. Register message handler.

globalThis.onmessage = function( e ) {
  let message = e.data;

  switch ( message.command ) {
    case "init": //{ command: "init", workerId, tensorflowJsURL, neuralNetConfig, weightsSpreadsheetId, weightsAPIKey };
      globalThis.workerBody = new WorkerBody();
      globalThis.workerBody.init(
        message.workerId, message.tensorflowJsURL, message.neuralNetConfig, message.weightsSpreadsheetId, message.weightsAPIKey );
      break;

    case "disposeWorker": //{ command: "disposeWorker" };
      globalThis.workerBody.disposeWorker();
      break;

    case "imageData_transferBack_processTensor": //{ command: "imageData_transferBack_processTensor", processingId, sourceImageData };
      globalThis.workerBody.imageData_transferBack_processTensor( message.processingId, message.sourceImageData );
      break;

    case "typedArray_transferBack_processTensor": //{ command: "typedArray_transferBack_processTensor", processingId, sourceTypedArray };
      globalThis.workerBody.typedArray_transferBack_processTensor( message.processingId, message.sourceTypedArray );
      break;

    case "typedArray_processTensor": //{ command: "typedArray_processTensor", processingId, sourceTypedArray };
      globalThis.workerBody.typedArray_processTensor( message.processingId, message.sourceTypedArray );
      break;
  }
}
<commit_msg>ImageData<commit_after>/**
 * @file This file is the main (i.e. body) javascript file of neural network web worker.
 * It is not an importable module.
 *
 * In module (non-classic) web worker, static import is available. But at the same
 * time, importScripts() will not be avbailable. For solving this problem, using
 * classic (non-module) web worker so that tensorflow.js can be loaded by
 * importScripts(). At the same time, using dynamic import() to load ourselves module
 * because import() can be used in classic (non-module) script.
 */

//import * as Net from "./Net.js";

/**
 * The implementation of a neural network web worker.
 *
 */
class WorkerBody {

  /**
   *
   * @param {number} workerId
   *   A non-negative integer represents this worker's id. The id of the first worker
   * should be 0.
   *
   * @param {Net.Config} neuralNetConfig
   *   The configuration of the neural network which will be created by this web worker.
   *
   * @param {string} weightsSpreadsheetId
   *   The Google Sheets spreadsheetId of neural network weights. Every worker will
   * load weights from the spreadsheet to initialize one neural network.
   *
   * @param {string} weightsAPIKey
   *   The API key for accessing the Google Sheets spreadsheet of neural network weights.
   */
  init(
    workerId = 0,
    tensorflowJsURL, neuralNetConfig, weightsSpreadsheetId, weightsAPIKey ) {

    if ( workerId < 0 )
      workerId = 0;

    this.workerId = workerId;
    this.tensorflowJsURL = tensorflowJsURL;
    this.neuralNetConfig = neuralNetConfig;
    this.weightsSpreadsheetId = weightsSpreadsheetId;
    this.weightsAPIKey = weightsAPIKey;

    // Because every web worker will copy the input, there is not necessary to keep input.
    let bKeepInputTensor = false;

//!!! ...unfinished... global scope ? report progress ?

    // If a specific library module does not existed, all libraries might have not yet been loaded. (i.e. this is the first time WorkerBody.init() is called.)
    if ( !globalThis.NeuralNetProgress ) {

      globalThis.NeuralNetProgress = await import( "./NetProgress.js" ); // Load progress library in globalThis.ValueMax scope dynamically.

      this.initProgress = new NeuralNetProgress.InitProgress(); // The progress object could be created only after the progress library has been loaded.
      this.initProgress.libraryDownload.total = 3; // There are 3 libraries should be loaded: NetProgress, tensorflow.js, NeuralNet.

      this.initProgress.weightsDownload.total = 1; // There are ??? 1 weights file will be loaded.

//!!! How to know this? According to neural net architecture? It will be known only after it has been downloaded.
      this.initProgress.weightsParse.total = 10000; // There are ??? weights will be parsed.

//!!! It seems that the libraryDownload and weightsDownload should not be included.
// Because the weightsParse.total is unknown when downloading library and weights, it not possible to set total of every progress at one time.

      ++this.initProgress.libraryDownload.accumulation; // The library NetProgress has been loaded.

//!!! ...unfinished... inform WorkerProxy progress changed.

      importScripts( tensorflowJsURL ); // Load tensorflow javascript library in global scope.
      ++this.initProgress.libraryDownload.accumulation; // The library tensorflow.js has been loaded.

      globalThis.NeuralNet = await import( "./Net.js" ); // Load neural network library in globalThis.NeuralNet scope dynamically.
      ++this.initProgress.libraryDownload.accumulation; // The library NeuralNet has been loaded.
    }

//!!! ...unfinished... the neuralNetConfig is still class NeuralNet.Config? Otherwise, the create_ScaledSourceTensor_from_PixelData() will be lost.

    this.neuralNet = new NeuralNet.Base();
    this.neuralNet.init( neuralNetConfig, bKeepInputTensor );

//!!! ...unfinished...
    // Download and parse neural network weights. Also report downloading and parsing progress.

//       this.initProgress.weightsDownload;
//       this.initProgress.weightsParse;

  }

  disposeWorker() {
    if ( this.neuralNet ) {
      this.neuralNet.disposeTensors();
      this.neuralNet = null;
    }

//!!!??? calling close() when the next worker disposed ?
    close();
  }

//!!! Regular Expression for get text inside html table markup:
//
//  /(?<=
]*>.*)(?<=>)[^<]+(?=<)(?=.*
)/g
//
// It can be used to parsing weights data from Google Sheets published html. Problem: safari browser does not support lookbehind regular expression.

//!!! Regular Expression for get text inside html table data (with or without div) markup:
//
//  let r = RegExp( "]*>(?:
]*>)?([^<]*)(?:
)?	", "g" );
//  let extractedLinesByCells = String( sourceHTMLText ).replace( r, "$1\n" );
//
// The capture group 1 is one cell of one column of google sheet.

//!!! ...unfinished... (2022/08/25)
// Perhaps, use ImageBitmap (transferred object) to transfer image between web worker.
// i.e. using tf.browser.fromPixelsAsync( canvas )
//
// A: No. It is slower to create tf.tensor from ImageBitmap. Use ImageData is faster.
//

  /**
   * Convert source image data to tensor3d, scale it, transfer scaled source
   * typed-array back to WorkerProxy, compute neural network, pass result back
   * to WorkerProxy.
   *
   * @param {number} processingId
   *   The id of this processing. It is used when reporting processing result so
   * that WorkerProxy could find out corresponding promise.
   *
   * @param {ImageData} sourceImageData
   *   The image data to be processed. The size should be [ sourceImageData.height,
   * sourceImageData.width ] = [ this.neuralNet.sourceImageHeightWidth[ 0 ],
   * this.neuralNet.sourceImageHeightWidth[ 1 ] ]. And it should RGBA 4 channels.
   */
  imageData_transferBack_processTensor( processingId, sourceImageData ) {

    // Create (scaled) source image so that then neural network can process it.
    //
    // Usually, only the first web worker ( workerId == 0 ) is responsible for
    // scaling the source image data to default size. After that, all other web
    // worker received the already scaled typed-array.
    //
    // The reason why not use source ImageData directly is that the next web worker
    // could re-create tensor3d more effficiently.
    //
    // ImageData uses Uint8ClampedArray internally. If it is past directly, the next
    // web worker needs create tensor3d by fromPixels() which internally converts
    // twice: from Uint8ClampedArray to Int32Array and from Int32Array to tensor3d.
    //
    // If passing typed-array (Float32Array), the next web worker could use it to
    // re-create tensord3d directly.
    //
    let scaledSourceTensor
      = this.neuralNetConfig.create_ScaledSourceTensor_from_PixelData( sourceImageData );

//!!! ...unfinished... If the this.neuralNetConfig (which is past through web worker message) is not real NeuralNet.Config, the following should be used.
//    let scaledSourceTensor = NeuralNet.Config.create_ScaledSourceTensor_from_PixelData.call( this.neuralNetConfig, sourceImageData );

    // Download the scaledSourceTensor as typed-array (asynchronously), and transfer it back to WorkerProxy (and inform WorkerProxies).
    //
    // The reason why it is done asynchronously is for not blocking the following computation of neural network.
    this.transferBackSourceScaledTensorAsync( processingId, scaledSourceTensor );

    // At the same time (the scaled source typed-array data is transferring back to WorkerProxy and then WorkerProxies), this worker is
    // still computing the neural network parallelly.
    this.processTensorAndDispose( processingId, scaledSourceTensor );
  }

  /**
   * Transfer scaled source typed-array data back to WorkerProxy, re-create source tensor, compute neural network, pass result back to WorkerProxy.
   *
   * @param {number} processingId
   *   The id of this processing. It is used when reporting processing resultso that WorkerProxy could find back corresponding promise.
   *
   * @param {Float32Array} sourceTypedArray
   *   The source typed-data to be processed. Its shape should be [ height, width, channel ] =
   * [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ].
   */
  typedArray_transferBack_processTensor( processingId, sourceTypedArray ) {
    let shape = [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ];

    // Re-create (scaled) source tensor.
    //
    // This should be done before calling transferBackSourceTypedArray() which will transfer (not copy) the sourceTypedArray and invalid it.
    let scaledSourceTensor = tf.tensor3d( sourceTypedArray, shape );

    // Transfer (not copy) the sourceTypedArray back to WorkerProxy). This will invalid sourceTypedArray.
    this.transferBackSourceTypedArray( processingId, sourceTypedArray );

    // At the same time (the sourceTypedArray is transferring back to WorkerProxy and then WorkerProxies), this worker is still computing
    // the neural network parallelly.
    this.processTensorAndDispose( processingId, scaledSourceTensor );
  }

  /**
   * Re-create source tensor, compute neural network, pass result back to WorkerProxy.
   *
   * It will not transfer source back to WorkerProxy. This is different from typedArray_TransferBack_processTensor().
   *
   * @param {number} processingId
   *   The id of this processing. It is used when reporting processing resultso that WorkerProxy could find back corresponding promise.
   *
   * @param {Float32Array} sourceTypedArray
   *   The source typed-data to be processed. Its shape should be [ height, width, channel ] =
   * [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ].
   */
  typedArray_processTensor( processingId, sourceTypedArray ) {
    let shape = [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ];
    let scaledSourceTensor = tf.tensor3d( sourceTypedArray, shape ); // Re-create (scaled) source tensor.
    this.processTensorAndDispose( processingId, scaledSourceTensor );
  }

  /**
   * Compute neural network, and pass result back to WorkerProxy.
   *
   * @param {number} processingId
   *   The id of this processing. It is used when reporting processing resultso that WorkerProxy could find back corresponding promise.
   *
   * @param {tf.tensor3d} scaledSourceTensor
   *   The source tensor3d to be processed. The scaledSourceTensor.shape should be [ height, width, channel ] =
   * [ this.neuralNet.sourceImageHeightWidth[ 0 ], this.neuralNet.sourceImageHeightWidth[ 1 ], this.neuralNet.config.sourceChannelCount ].
   * This tensor will be disposed when processing is done.
   */
  processTensorAndDispose( processingId, scaledSourceTensor ) {

    // Note: scaledSourceTensor will be dispose because this.neuralNet is initialized with ( bKeepInputTensor == false ).
    let resultTensor3d = this.neuralNet.apply_and_destroy_or_keep( scaledSourceTensor, true );

    let resultTypedArray = resultTensor3d.dataSync(); // Download synchronously (because here is web worker).
    resultTensor3d.dispose(); // The result tensor should also be disposed.

    // Pass the output of neural network to WorkerProxy (and inform WorkerProxies).
    let message = { command: "processTensorResult", workerId: this.workerId, processingId: processingId, resultTypedArray: resultTypedArray };
    postMessage( message, [ message.resultTypedArray.buffer ] );
  }

  /**
   * Download the scaledSourceTensor as typed-array (Float32Array) asynchronously. Transfer the typed-array back to WorkerProxy
   * (and inform WorkerProxies) so that it can be past to next web worker.
   *
   * @param {number} processingId
   *   The id of this processing. It is used by WorkerProxy to found back corresponding promise.
   *
   * @param {tf.tensor3d} scaledSourceTensor
   *   The scaled source tensor3d generated by processImageData().
   */
  async transferBackSourceScaledTensorAsync( processingId, scaledSourceTensor ) {
    let sourceTypedArray = await scaledSourceTensor.data(); // Download scaled source tensor3d as typed-array (Float32Array) asynchronously.
    this.transferBackSourceTypedArray( processingId, sourceTypedArray );
  }

  /**
   * Transfer source typed-array back to WorkerProxy (and inform WorkerProxies) so that it can be past to next web worker.
   *
   * @param {number} processingId
   *   The id of this processing. It is used by WorkerProxy to found back corresponding promise.
   *
   * @param {Float32Array} sourceTypedArray
   *   The source typed-array past to processTypedArray(). This will become invalid after this call because it will be transferred (not
   * copied) back to WorkerProxy.
   */
  transferBackSourceTypedArray( processingId, sourceTypedArray ) {
    let message = { command: "transferBackSourceTypedArray", workerId: this.workerId, processingId: processingId, sourceTypedArray: sourceTypedArray };
    postMessage( message, [ message.sourceTypedArray.buffer ] );
  }

//!!! ...unfinished... How to know and set total of the following:
//       this.initProgress.libraryDownload;
//       this.initProgress.weightsDownload;
//       this.initProgress.weightsParse;

//       this.initProgress.libraryDownload.total = 3; // There are 3 libraries should be loaded: NetProgress, tensorflow.js, NeuralNet.
//       this.initProgress.libraryDownload.accumulation = 0;

  /** Inform WorkerProxy to set total of every progress of initProgress. */
  reportInitProgress_setTotal() {
    let message = { command: "reportInitProgress", subCommand: "setTotal", workerId: this.workerId,
      libraryDownload: this.initProgress.libraryDownload.total,
      weightsDownload: this.initProgress.weightsDownload.total,
      weightsParse: this.initProgress.weightsParse.total,
    };
    postMessage( message );
  }

  /** Inform WorkerProxy to reset initProgress. */
  reportInitProgress_restAccumulation() {
    let message = { command: "reportInitProgress", subCommand: "restAccumulation", workerId: this.workerId };
    postMessage( message );
  }

  /** Inform WorkerProxy to advance initProgress.libraryDownload. */
  reportInitProgress_libraryDownload() {
//    let message = { command: "initProgress_libraryDownload", workerId: this.workerId, total: this.initProgress.libraryDownload.total, accumulation: this.initProgress.libraryDownload.accumulation };
//    let message = { command: "initProgress_libraryDownload", workerId: this.workerId, initProgress: this.initProgress.libraryDownload };
    let message = { command: "reportInitProgress", subCommand: "libraryDownload", workerId: this.workerId, total: this.initProgress.libraryDownload.total, accumulation: this.initProgress.libraryDownload.accumulation };
    postMessage( message );
  }
}

if ( globalThis.document )
  return; // In main document context (Not in worker context). Do nothing. (Should not happen)

// In worker context. Register message handler.

globalThis.onmessage = function( e ) {
  let message = e.data;

  switch ( message.command ) {
    case "init": //{ command: "init", workerId, tensorflowJsURL, neuralNetConfig, weightsSpreadsheetId, weightsAPIKey };
      globalThis.workerBody = new WorkerBody();
      globalThis.workerBody.init(
        message.workerId, message.tensorflowJsURL, message.neuralNetConfig, message.weightsSpreadsheetId, message.weightsAPIKey );
      break;

    case "disposeWorker": //{ command: "disposeWorker" };
      globalThis.workerBody.disposeWorker();
      break;

    case "imageData_transferBack_processTensor": //{ command: "imageData_transferBack_processTensor", processingId, sourceImageData };
      globalThis.workerBody.imageData_transferBack_processTensor( message.processingId, message.sourceImageData );
      break;

    case "typedArray_transferBack_processTensor": //{ command: "typedArray_transferBack_processTensor", processingId, sourceTypedArray };
      globalThis.workerBody.typedArray_transferBack_processTensor( message.processingId, message.sourceTypedArray );
      break;

    case "typedArray_processTensor": //{ command: "typedArray_processTensor", processingId, sourceTypedArray };
      globalThis.workerBody.typedArray_processTensor( message.processingId, message.sourceTypedArray );
      break;
  }
}
<|endoftext|>


<commit_before>var ChanceJS = require('chance');
var chance = new ChanceJS();

function TreatController(TrickOrTreatOBJ){
	this.trt = TrickOrTreatOBJ;
}

// ================= REGISTER USER ============================================================

TreatController.prototype.registerCheckUserExists = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}
	if(results.length > 0){
		callback(userObj.name+", you're *already* registered! You don't need to register twice, silly!");
		return;
	}else{
		this.trt.database.insertPlayer(userObj.id,userObj.name,this.registerFinish.bind(this,userObj,callback));
	}
};

TreatController.prototype.registerFinish = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}
	callback(userObj.name+", Welcome to Trick or Treat! Type \"!trt help\" to learn how to use me, or type \"!trt\" again to start playing!");
	return;
};

// ==========================================================================================

TreatController.prototype.goTrickOrTreating = function(userObj,callback){
	this.trt.database.getPlayerById(userObj.id,this.checkUserCanPlay.bind(this,userObj,callback));
};

TreatController.prototype.checkUserCanPlay = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length > 0){
		var numPlayedToday = results[0].numPlayedToday;
		if(numPlayedToday >= 2){
			var timeTillReset = this.trt.timeTillDayReset();
			callback(userObj.name+": "+chance.pick(this.trt.treatStrings.alreadyPlayed)+"\n(Day resets in "+timeTillReset.hoursStr+" "+timeTillReset.minutesStr+")");
			return;
		}
		this.userCanPlay(userObj,callback);
	}else{
		//User doesn't exist, let's register them!
		this.trt.database.getPlayerById(userObj.id,this.registerCheckUserExists.bind(this,userObj,callback));
		return;
	}
};

TreatController.prototype.userCanPlay = function(userObj,callback){
	//This is where you can choose what actually happens in the game, like events or whatnot.
	//For now we'll jusy assume you're rolling for candy, and play for candy.

	if(chance.bool({likelihood: 10})){
		var sugarRush = require('./events/sugarRush.js');
		new sugarRush(this.trt,userObj,callback);
	}else{
		//Normal candyevent.
		this.trt.database.getAllCandies(this.giveUserCandies.bind(this,userObj,callback));
	}

};

TreatController.prototype.giveUserCandies = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length === 0){
		//There's no candy in the database!
		this.trt.log("warning","No candy in the database to give out!");
		callback("I have no candies to give!\n(Did an admin forget to fill up the candy database?)");
		return;
	}

	var amount = 1;
	if(Math.floor(Math.random()*25) === 0){
		amount = 2;
	}
	if(Math.floor(Math.random()*50) === 0){
		amount = 3;
	}

	var candiesGiven = [];
	for(var i=0;i<amount;i++){
		candiesGiven.push(results[chance.natural({min: 0, max: results.length-1})]);
	}

	this.trt.database.giveUserCandies(userObj,candiesGiven,this.giveUserCandiesConfirm.bind(this,userObj,candiesGiven,callback));
};

TreatController.prototype.giveUserCandiesConfirm = function(userObj,candiesGiven,callback){

	var candyStr = "";
	for(var i=0;i<candiesGiven.length;i++){
		candyStr += (i>0?'*AND* ':'')+'one '+candiesGiven[i].candyIcon+' '+candiesGiven[i].candyName+"\n";
	}

	if(candiesGiven.length >= 3){
		callback("Overflowing Bucket! Jackpot! A house has some extra, so you get "+candiesGiven.length+" candies! \n"+userObj.name+" you received "+candyStr);
	}else{
		callback(userObj.name+", you received "+candyStr);
	}
};

// ==========================================================================================

TreatController.prototype.generateLeaderboard = function(callback){
	this.trt.database.getPlayerCandyTopTen(this.returnGenerateLeaderboard.bind(this,callback));
};

TreatController.prototype.returnGenerateLeaderboard = function(callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length === 0){
		callback("No one has any candy");
		return false;
	}
	var candyTable=[];
	for(var i=0;i<results.length;i++){
		//candyTable+=(results[i].amount>=1000?'*a boatload of*':results[i].amount)+" "+results[i].candyIcon+" "+results[i].candyName+"\n"
		candyTable.push({
			//"title":,
			"value":"#"+(i+1)+": "+results[i].playerName+" = "+results[i].total,
			"short":false
		});
	}
	callback("Top 10 Players:",[{"color": "#f1952a",title:"",fields:candyTable}]);
};

// ==========================================================================================

// ==========================================================================================

TreatController.prototype.showPlayerRank = function(playerObj,callback){
	this.trt.database.getPlayerRank(playerObj.id,this.returnShowPlayerRank.bind(this,callback));
};

TreatController.prototype.returnShowPlayerRank = function(callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length === 0){
		callback("I don't know who you are!");
		return false;
	}

	callback(results[0].playerName+", is rank "+results[0].rank+" with "+results[0].total+" "+this.trt.pluralize("candy",results[0].total));
};

// ==========================================================================================

TreatController.prototype.generateCandyCountAttachment = function(userObj,callback){
	this.trt.database.getCandyCountOfPlayer(userObj.id,this.returnCandyCountAttachment.bind(this,userObj,callback));
};

TreatController.prototype.returnCandyCountAttachment = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length === 0){
		callback(userObj.name+", you don't have any candy! Go get some with !trt");
		return false;
	}
	var candyTable=[];
	for(var i=0;i<results.length;i++){
		//candyTable+=(results[i].amount>=1000?'*a boatload of*':results[i].amount)+" "+results[i].candyIcon+" "+results[i].candyName+"\n"
		candyTable.push({
			//"title":,
			"value":results[i].amount+" "+results[i].candyIcon+" "+results[i].candyName,
			"short":true
		});
	}
	callback(userObj.name+", you have:",[{"color": "#f1952a",title:"",fields:candyTable}]);
};

module.exports = TreatController;
<commit_msg>Reduced event likelihood to 1% Will make event "system" soon instead of hardcoding the events<commit_after>var ChanceJS = require('chance');
var chance = new ChanceJS();

function TreatController(TrickOrTreatOBJ){
	this.trt = TrickOrTreatOBJ;
}

// ================= REGISTER USER ============================================================

TreatController.prototype.registerCheckUserExists = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}
	if(results.length > 0){
		callback(userObj.name+", you're *already* registered! You don't need to register twice, silly!");
		return;
	}else{
		this.trt.database.insertPlayer(userObj.id,userObj.name,this.registerFinish.bind(this,userObj,callback));
	}
};

TreatController.prototype.registerFinish = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}
	callback(userObj.name+", Welcome to Trick or Treat! Type \"!trt help\" to learn how to use me, or type \"!trt\" again to start playing!");
	return;
};

// ==========================================================================================

TreatController.prototype.goTrickOrTreating = function(userObj,callback){
	this.trt.database.getPlayerById(userObj.id,this.checkUserCanPlay.bind(this,userObj,callback));
};

TreatController.prototype.checkUserCanPlay = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length > 0){
		var numPlayedToday = results[0].numPlayedToday;
		if(numPlayedToday >= 2){
			var timeTillReset = this.trt.timeTillDayReset();
			callback(userObj.name+": "+chance.pick(this.trt.treatStrings.alreadyPlayed)+"\n(Day resets in "+timeTillReset.hoursStr+" "+timeTillReset.minutesStr+")");
			return;
		}
		this.userCanPlay(userObj,callback);
	}else{
		//User doesn't exist, let's register them!
		this.trt.database.getPlayerById(userObj.id,this.registerCheckUserExists.bind(this,userObj,callback));
		return;
	}
};

TreatController.prototype.userCanPlay = function(userObj,callback){
	//This is where you can choose what actually happens in the game, like events or whatnot.
	//For now we'll jusy assume you're rolling for candy, and play for candy.

	//Instead of hardcoding this, preload all events, and actualy make this cool.
	//Convert the candy giving into an "event"
	//All outcomes of TRT will be in seperate files (except for general stuff like REGISTER )
	if(chance.bool({likelihood: 1})){
		var sugarRush = require('./events/sugarRush.js');
		new sugarRush(this.trt,userObj,callback);
	}else{
		//Normal candyevent.
		this.trt.database.getAllCandies(this.giveUserCandies.bind(this,userObj,callback));
	}

};

TreatController.prototype.giveUserCandies = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length === 0){
		//There's no candy in the database!
		this.trt.log("warning","No candy in the database to give out!");
		callback("I have no candies to give!\n(Did an admin forget to fill up the candy database?)");
		return;
	}

	var amount = 1;
	if(Math.floor(Math.random()*25) === 0){
		amount = 2;
	}
	if(Math.floor(Math.random()*50) === 0){
		amount = 3;
	}

	var candiesGiven = [];
	for(var i=0;i<amount;i++){
		candiesGiven.push(results[chance.natural({min: 0, max: results.length-1})]);
	}

	this.trt.database.giveUserCandies(userObj,candiesGiven,this.giveUserCandiesConfirm.bind(this,userObj,candiesGiven,callback));
};

TreatController.prototype.giveUserCandiesConfirm = function(userObj,candiesGiven,callback){

	var candyStr = "";
	for(var i=0;i<candiesGiven.length;i++){
		candyStr += (i>0?'*AND* ':'')+'one '+candiesGiven[i].candyIcon+' '+candiesGiven[i].candyName+"\n";
	}

	if(candiesGiven.length >= 3){
		callback("Overflowing Bucket! Jackpot! A house has some extra, so you get "+candiesGiven.length+" candies! \n"+userObj.name+" you received "+candyStr);
	}else{
		callback(userObj.name+", you received "+candyStr);
	}
};

// ==========================================================================================

TreatController.prototype.generateLeaderboard = function(callback){
	this.trt.database.getPlayerCandyTopTen(this.returnGenerateLeaderboard.bind(this,callback));
};

TreatController.prototype.returnGenerateLeaderboard = function(callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length === 0){
		callback("No one has any candy");
		return false;
	}
	var candyTable=[];
	for(var i=0;i<results.length;i++){
		//candyTable+=(results[i].amount>=1000?'*a boatload of*':results[i].amount)+" "+results[i].candyIcon+" "+results[i].candyName+"\n"
		candyTable.push({
			//"title":,
			"value":"#"+(i+1)+": "+results[i].playerName+" = "+results[i].total,
			"short":false
		});
	}
	callback("Top 10 Players:",[{"color": "#f1952a",title:"",fields:candyTable}]);
};

// ==========================================================================================

// ==========================================================================================

TreatController.prototype.showPlayerRank = function(playerObj,callback){
	this.trt.database.getPlayerRank(playerObj.id,this.returnShowPlayerRank.bind(this,callback));
};

TreatController.prototype.returnShowPlayerRank = function(callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length === 0){
		callback("I don't know who you are!");
		return false;
	}

	callback(results[0].playerName+", is rank "+results[0].rank+" with "+results[0].total+" "+this.trt.pluralize("candy",results[0].total));
};

// ==========================================================================================

TreatController.prototype.generateCandyCountAttachment = function(userObj,callback){
	this.trt.database.getCandyCountOfPlayer(userObj.id,this.returnCandyCountAttachment.bind(this,userObj,callback));
};

TreatController.prototype.returnCandyCountAttachment = function(userObj,callback,error,results,fields){
	if(error !== null){ this.trt.error(error); return;}

	if(results.length === 0){
		callback(userObj.name+", you don't have any candy! Go get some with !trt");
		return false;
	}
	var candyTable=[];
	for(var i=0;i<results.length;i++){
		//candyTable+=(results[i].amount>=1000?'*a boatload of*':results[i].amount)+" "+results[i].candyIcon+" "+results[i].candyName+"\n"
		candyTable.push({
			//"title":,
			"value":results[i].amount+" "+results[i].candyIcon+" "+results[i].candyName,
			"short":true
		});
	}
	callback(userObj.name+", you have:",[{"color": "#f1952a",title:"",fields:candyTable}]);
};

module.exports = TreatController;
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Quizz = mongoose.model('Quizz'),
	Question = mongoose.model('Question'),
	_ = require('lodash');

/**
 * Create a quizz
 */
exports.create = function(req, res) {
	var quizz = new Quizz(req.body.infos);

	for (var i = req.body.questions.length - 1; i >= 0; i--) {
		var laQuestion = new Question(req.body.questions[i]);
		quizz.questions.push(laQuestion);
		laQuestion.save();
	}

	quizz.creator = req.user;

	quizz.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(quizz);
		}
	});
};

/**
 * Show the current quizz
 */
exports.read = function(req, res) {
	res.json(req.quizz);
};

/**
 * Update a quizz
 */
exports.update = function(req, res) {
	var quizz = req.quizz;

	quizz = _.extend(quizz, req.body);

	for (var i = quizz.questions.length - 1; i >= 0; i--) {
		var laQuestion = new Question(quizz.questions[i]);
		quizz.questions.push(laQuestion);
		laQuestion.save();
	}

	quizz.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(quizz);
		}
	});
};

/**
 * Delete a quizz
 */
exports.delete = function(req, res) {
	var quizz = req.quizz;
	for (var i = quizz.questions.length - 1; i >= 0; i--) {
		quizz.questions[i].remove();
	}
	quizz.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(quizz);
		}
	});
};

/**
 * List of Quizzs
 */
exports.list = function(req, res) {
	Quizz.find().sort('-created').populate('creator', 'displayName').exec(function(err, quizzs) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(quizzs);
		}
	});
};

/**
 * Quizz middleware
 */
exports.quizzByID = function(req, res, next, id) {
	Quizz.findById(id).populate('creator', 'displayName').populate('questions').exec(function(err, quizz) {
		if (err) return next(err);
		if (!quizz) return next(new Error('Failed to load quizz ' + id));
		req.quizz = quizz;
		next();
	});
};

/**
 * Quizz authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.quizz.creator.id !== req.user.id) {
		return res.status(403).send({
			message: 'User is not authorized'
		});
	}
	next();
};<commit_msg>Mise en commentaire, bug bizarre<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Quizz = mongoose.model('Quizz'),
	Question = mongoose.model('Question'),
	_ = require('lodash');

/**
 * Create a quizz
 */
exports.create = function(req, res) {
	var quizz = new Quizz(req.body.infos);

	for (var i = req.body.questions.length - 1; i >= 0; i--) {
		var laQuestion = new Question(req.body.questions[i]);
		quizz.questions.push(laQuestion);
		laQuestion.save();
	}

	quizz.creator = req.user;

	quizz.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(quizz);
		}
	});
};

/**
 * Show the current quizz
 */
exports.read = function(req, res) {
	res.json(req.quizz);
};

/**
 * Update a quizz
 */
exports.update = function(req, res) {
	var quizz = req.quizz;
	console.log(quizz);

	/*for (var i = req.quizz.questions.length - 1; i >= 0; i--) {
		var question = _.extend(req.quizz.questions[i], req.body);
		question.save();
	}*/

	quizz.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(quizz);
		}
	});
};

/**
 * Delete a quizz
 */
exports.delete = function(req, res) {
	var quizz = req.quizz;
	for (var i = quizz.questions.length - 1; i >= 0; i--) {
		quizz.questions[i].remove();
	}
	quizz.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(quizz);
		}
	});
};

/**
 * List of Quizzs
 */
exports.list = function(req, res) {
	Quizz.find().sort('-created').populate('creator', 'displayName').exec(function(err, quizzs) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(quizzs);
		}
	});
};

/**
 * Quizz middleware
 */
exports.quizzByID = function(req, res, next, id) {
	Quizz.findById(id).populate('creator', 'displayName').populate('questions').exec(function(err, quizz) {
		if (err) return next(err);
		if (!quizz) return next(new Error('Failed to load quizz ' + id));
		req.quizz = quizz;
		next();
	});
};

/**
 * Quizz authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.quizz.creator.id !== req.user.id) {
		return res.status(403).send({
			message: 'User is not authorized'
		});
	}
	next();
};<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Skill = mongoose.model('Skill'),
	_ = require('lodash');

/**
 * Create a Skill
 */
exports.create = function(req, res) {
	var skill = new Skill(req.body);
	skill.user = req.user;

	skill.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(skill);
		}
	});
};

/**
 * Show the current Skill
 */
exports.read = function(req, res) {
	res.jsonp([req.skill]);
};

/**
 * Update a Skill
 */
exports.update = function(req, res) {
	var skill = req.skill ;

	skill = _.extend(skill , req.body);

	skill.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(skill);
		}
	});
};

/**
 * Delete an Skill
 */
exports.delete = function(req, res) {
	var skill = req.skill ;

	skill.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(skill);
		}
	});
};

/**
 * List of Skills
 */
exports.list = function(req, res) {
	var query = Skill.find();

	if (req.query.jobIds) {
		query = query.in('job', req.query.jobIds.split(';'));
	}

	query.sort('-_id')
		.populate('job', 'name')
		.populate('required_skills.skill', 'name')
		.exec(function(err, skills) {
			if (err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
				res.jsonp(skills);
			}
		});
};

/**
 * Skill middleware
 */
exports.skillByID = function(req, res, next, id) {
	Skill.findById(id)
		.populate('job', 'name')
		.populate('required_skills.skill', 'name').exec(function(err, skill) {
			if (err) return next(err);
			if (! skill) return next(new Error('Failed to load Skill ' + id));
			req.skill = skill ;
			next();
		});
};

/**
 * Skill authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (_.indexOf(req.user.roles, 'admin') === -1) {
		return res.status(403).send('User is not authorized');
	}
	next();
};
<commit_msg>Filter out empty jobIds<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Skill = mongoose.model('Skill'),
	_ = require('lodash');

/**
 * Create a Skill
 */
exports.create = function(req, res) {
	var skill = new Skill(req.body);
	skill.user = req.user;

	skill.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(skill);
		}
	});
};

/**
 * Show the current Skill
 */
exports.read = function(req, res) {
	res.jsonp([req.skill]);
};

/**
 * Update a Skill
 */
exports.update = function(req, res) {
	var skill = req.skill ;

	skill = _.extend(skill , req.body);

	skill.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(skill);
		}
	});
};

/**
 * Delete an Skill
 */
exports.delete = function(req, res) {
	var skill = req.skill ;

	skill.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(skill);
		}
	});
};

/**
 * List of Skills
 */
exports.list = function(req, res) {
	var query = Skill.find();

	if (req.query.jobIds) {
		query = query.in('job', _.filter(req.query.jobIds.split(';')));
	}

	query.sort('-_id')
		.populate('job', 'name')
		.populate('required_skills.skill', 'name')
		.exec(function(err, skills) {
			if (err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
				res.jsonp(skills);
			}
		});
};

/**
 * Skill middleware
 */
exports.skillByID = function(req, res, next, id) {
	Skill.findById(id)
		.populate('job', 'name')
		.populate('required_skills.skill', 'name').exec(function(err, skill) {
			if (err) return next(err);
			if (! skill) return next(new Error('Failed to load Skill ' + id));
			req.skill = skill ;
			next();
		});
};

/**
 * Skill authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (_.indexOf(req.user.roles, 'admin') === -1) {
		return res.status(403).send('User is not authorized');
	}
	next();
};
<|endoftext|>


<commit_before>// # array-item-control component

/*
Render the remove and move buttons for an array field.
*/

'use strict';

import React from 'react';
import cx from 'classnames';

import HelperMixin from '../../mixins/helper';

export default React.createClass({

  displayName: 'ArrayItemControl',

  mixins: [HelperMixin],

  onMoveBack: function () {
    this.props.onMove(this.props.index, this.props.index - 1);
  },

  onMoveForward: function () {
    this.props.onMove(this.props.index, this.props.index + 1);
  },

  onRemove: function () {
    this.props.onRemove(this.props.index);
  },

  render: function () {
    return this.renderWithConfig();
  },

  renderDefault: function () {
    var config = this.props.config;
    var field = this.props.field;

    const isLastItem = field.fieldIndex === 0 && field.parent.value.length === 1;

    const removeItemControl = config.createElement('remove-item', {
      field: field, onClick: this.onRemove, onMaybeRemove: this.props.onMaybeRemove,
      readOnly: isLastItem && !config.isRemovalOfLastArrayItemAllowed(field)
    });

    const moveItemForward = this.props.index < (this.props.numItems - 1) ?
      config.createElement('move-item-forward', { field: field, onClick: this.onMoveForward
      }) :
      null;

    const moveItemBack = this.props.index > 0 ?
      config.createElement('move-item-back', { field: field, onClick: this.onMoveBack
      }) :
      null;

    return (

        {removeItemControl}
        {moveItemBack}
        {moveItemForward}

    );
  }
});
<commit_msg>:lipstick: fix diff again<commit_after>// # array-item-control component

/*
Render the remove and move buttons for an array field.
*/

'use strict';

import React from 'react';
import cx from 'classnames';

import HelperMixin from '../../mixins/helper';

export default React.createClass({

  displayName: 'ArrayItemControl',

  mixins: [HelperMixin],

  onMoveBack: function () {
    this.props.onMove(this.props.index, this.props.index - 1);
  },

  onMoveForward: function () {
    this.props.onMove(this.props.index, this.props.index + 1);
  },

  onRemove: function () {
    this.props.onRemove(this.props.index);
  },

  render: function () {
    return this.renderWithConfig();
  },

  renderDefault: function () {
    var config = this.props.config;
    var field = this.props.field;

    const isLastItem = field.fieldIndex === 0 && field.parent.value.length === 1;

    const removeItemControl = config.createElement('remove-item', {
      field: field, onClick: this.onRemove, onMaybeRemove: this.props.onMaybeRemove,
      readOnly: isLastItem && !config.isRemovalOfLastArrayItemAllowed(field)
    });

    const moveItemForward = this.props.index < (this.props.numItems - 1) ?
      config.createElement('move-item-forward', { field: field, onClick: this.onMoveForward }) :
      null;

    const moveItemBack = this.props.index > 0 ?
      config.createElement('move-item-back', { field: field, onClick: this.onMoveBack }) :
      null;

    return (

        {removeItemControl}
        {moveItemBack}
        {moveItemForward}

    );
  }
});
<|endoftext|>


<commit_before>import coreUtils from '@opentripplanner/core-utils'
import React, { Component } from 'react'
import { connect } from 'react-redux'

import { CircleMarker } from 'react-leaflet'

class ElevationPointMarker extends Component {
  createLeafletElement () {}

  updateLeafletElement () {}

  render () {
    const { diagramLeg, elevationPoint, showElevationProfile } = this.props

    let elevationPointMarker = null
    if (showElevationProfile && diagramLeg && elevationPoint) {
      const pos = coreUtils.itinerary.legLocationAtDistance(
        diagramLeg,
        elevationPoint
      )
      if (pos) {
        elevationPointMarker = (
          <CircleMarker
            center={pos}
            fillColor='#084c8d'
            weight={6}
            color='#555'
            opacity={0.4}
            radius={5}
            fill
            fillOpacity={1}
          />
        )
      }
    }
    return elevationPointMarker
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    diagramLeg: state.otp.ui.diagramLeg,
    elevationPoint: state.otp.ui.elevationPoint,
    showElevationProfile: !!state.otp.config.elevationProfile
  }
}

const mapDispatchToProps = {}

export default connect(mapStateToProps, mapDispatchToProps)(ElevationPointMarker)
<commit_msg>refactor(map): remove unneeded methods and import spacing<commit_after>import coreUtils from '@opentripplanner/core-utils'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { CircleMarker } from 'react-leaflet'

class ElevationPointMarker extends Component {
  render () {
    const { diagramLeg, elevationPoint, showElevationProfile } = this.props

    let elevationPointMarker = null
    if (showElevationProfile && diagramLeg && elevationPoint) {
      const pos = coreUtils.itinerary.legLocationAtDistance(
        diagramLeg,
        elevationPoint
      )
      if (pos) {
        elevationPointMarker = (
          <CircleMarker
            center={pos}
            fillColor='#084c8d'
            weight={6}
            color='#555'
            opacity={0.4}
            radius={5}
            fill
            fillOpacity={1}
          />
        )
      }
    }
    return elevationPointMarker
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    diagramLeg: state.otp.ui.diagramLeg,
    elevationPoint: state.otp.ui.elevationPoint,
    showElevationProfile: !!state.otp.config.elevationProfile
  }
}

const mapDispatchToProps = {}

export default connect(mapStateToProps, mapDispatchToProps)(ElevationPointMarker)
<|endoftext|>