<commit_before>/**
 * @ngdoc service
 * @name $ionicModal
 * @module ionic
 * @description
 *
 * Related: {@link ionic.controller:ionicModal ionicModal controller}.
 *
 * The Modal is a content pane that can go over the user's main view
 * temporarily.  Usually used for making a choice or editing an item.
 *
 * Put the content of the modal inside of an `<ion-modal-view>` element.
 *
 * **Notes:**
 * - A modal will broadcast 'modal.shown', 'modal.hidden', and 'modal.removed' events from its originating
 * scope, passing in itself as an event argument. Both the modal.removed and modal.hidden events are
 * called when the modal is removed.
 *
 * - This example assumes your modal is in your main index file or another template file. If it is in its own
 * template file, remove the script tags and call it by file name.
 *
 * @usage
 * ```html
 * <script id="my-modal.html" type="text/ng-template">
 *   <ion-modal-view>
 *     <ion-header-bar>
 *       <h1 class="title">My Modal title</h1>
 *     </ion-header-bar>
 *     <ion-content>
 *       Hello!
 *     </ion-content>
 *   </ion-modal-view>
 * </script>
 * ```
 * ```js
 * angular.module('testApp', ['ionic'])
 * .controller('MyController', function($scope, $ionicModal) {
 *   $ionicModal.fromTemplateUrl('my-modal.html', {
 *     scope: $scope,
 *     animation: 'slide-in-up'
 *   }).then(function(modal) {
 *     $scope.modal = modal;
 *   });
 *   $scope.openModal = function() {
 *     $scope.modal.show();
 *   };
 *   $scope.closeModal = function() {
 *     $scope.modal.hide();
 *   };
 *   //Cleanup the modal when we're done with it!
 *   $scope.$on('$destroy', function() {
 *     $scope.modal.remove();
 *   });
 *   // Execute action on hide modal
 *   $scope.$on('modal.hidden', function() {
 *     // Execute action
 *   });
 *   // Execute action on remove modal
 *   $scope.$on('modal.removed', function() {
 *     // Execute action
 *   });
 * });
 * ```
 */
IonicModule
.factory('$ionicModal', [
  '$rootScope',
  '$ionicBody',
  '$compile',
  '$timeout',
  '$ionicPlatform',
  '$ionicTemplateLoader',
  '$q',
  '$log',
  '$ionicClickBlock',
function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTemplateLoader, $q, $log, $ionicClickBlock) {

  /**
   * @ngdoc controller
   * @name ionicModal
   * @module ionic
   * @description
   * Instantiated by the {@link ionic.service:$ionicModal} service.
   *
   * Be sure to call [remove()](#remove) when you are done with each modal
   * to clean it up and avoid memory leaks.
   *
   * Note: a modal will broadcast 'modal.shown', 'modal.hidden', and 'modal.removed' events from its originating
   * scope, passing in itself as an event argument. Note: both modal.removed and modal.hidden are
   * called when the modal is removed.
   */
  var ModalView = ionic.views.Modal.inherit({
    /**
     * @ngdoc method
     * @name ionicModal#initialize
     * @description Creates a new modal controller instance.
     * @param {object} options An options object with the following properties:
     *  - `{object=}` `scope` The scope to be a child of.
     *    Default: creates a child of $rootScope.
     *  - `{string=}` `animation` The animation to show & hide with.
     *    Default: 'slide-in-up'
     *  - `{boolean=}` `focusFirstInput` Whether to autofocus the first input of
     *    the modal when shown.  Default: false.
     *  - `{boolean=}` `backdropClickToClose` Whether to close the modal on clicking the backdrop.
     *    Default: true.
     *  - `{boolean=}` `hardwareBackButtonClose` Whether the modal can be closed using the hardware
     *    back button on Android and similar devices.  Default: true.
     */
    initialize: function(opts) {
      ionic.views.Modal.prototype.initialize.call(this, opts);
      this.animation = opts.animation || 'slide-in-up';
    },

    /**
     * @ngdoc method
     * @name ionicModal#show
     * @description Show this modal instance.
     * @returns {promise} A promise which is resolved when the modal is finished animating in.
     */
    show: function(target) {
      var self = this;

      if (self.scope.$$destroyed) {
        $log.error('Cannot call ' +  self.viewType + '.show() after remove(). Please create a new ' +  self.viewType + ' instance.');
        return;
      }

      var modalEl = jqLite(self.modalEl);

      self.el.classList.remove('hide');
      $timeout(function() {
        $ionicBody.addClass(self.viewType + '-open');
      }, 400);

      if (!self.el.parentElement) {
        modalEl.addClass(self.animation);
        $ionicBody.append(self.el);
      }

      if (target && self.positionView) {
        self.positionView(target, modalEl);
        // set up a listener for in case the window size changes
        ionic.on('resize',function() {
          ionic.off('resize',null,window);
          self.positionView(target,modalEl);
        },window);
      }

      modalEl.addClass('ng-enter active')
             .removeClass('ng-leave ng-leave-active');

      self._isShown = true;
      self._deregisterBackButton = $ionicPlatform.registerBackButtonAction(
        self.hardwareBackButtonClose ? angular.bind(self, self.hide) : noop,
        PLATFORM_BACK_BUTTON_PRIORITY_MODAL
      );

      ionic.views.Modal.prototype.show.call(self);

      $timeout(function() {
        modalEl.addClass('ng-enter-active');
        ionic.trigger('resize');
        self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.shown', self);
        self.el.classList.add('active');
        self.scope.$broadcast('$ionicHeader.align');
      }, 20);

      return $timeout(function() {
        //After animating in, allow hide on backdrop click
        self.$el.on('click', function(e) {
          if (self.backdropClickToClose && e.target === self.el) {
            self.hide();
          }
        });
      }, 400);
    },

    /**
     * @ngdoc method
     * @name ionicModal#hide
     * @description Hide this modal instance.
     * @returns {promise} A promise which is resolved when the modal is finished animating out.
     */
    hide: function() {
      var self = this;
      var modalEl = jqLite(self.modalEl);

      // on iOS, clicks will sometimes bleed through/ghost click on underlying
      // elements
      $ionicClickBlock.show(600);

      self.el.classList.remove('active');
      modalEl.addClass('ng-leave');

      $timeout(function() {
        modalEl.addClass('ng-leave-active')
               .removeClass('ng-enter ng-enter-active active');
      }, 20);

      self.$el.off('click');
      self._isShown = false;
      self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.hidden', self);
      self._deregisterBackButton && self._deregisterBackButton();

      ionic.views.Modal.prototype.hide.call(self);

      // clean up event listeners
      if (self.positionView) {
        ionic.off('resize',null,window);
      }

      return $timeout(function() {
        $ionicBody.removeClass(self.viewType + '-open');
        self.el.classList.add('hide');
      }, self.hideDelay || 320);
    },

    /**
     * @ngdoc method
     * @name ionicModal#remove
     * @description Remove this modal instance from the DOM and clean up.
     * @returns {promise} A promise which is resolved when the modal is finished animating out.
     */
    remove: function() {
      var self = this;
      self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.removed', self);

      return self.hide().then(function() {
        self.scope.$destroy();
        self.$el.remove();
      });
    },

    /**
     * @ngdoc method
     * @name ionicModal#isShown
     * @returns boolean Whether this modal is currently shown.
     */
    isShown: function() {
      return !!this._isShown;
    }
  });

  var createModal = function(templateString, options) {
    // Create a new scope for the modal
    var scope = options.scope && options.scope.$new() || $rootScope.$new(true);

    options.viewType = options.viewType || 'modal';

    extend(scope, {
      $hasHeader: false,
      $hasSubheader: false,
      $hasFooter: false,
      $hasSubfooter: false,
      $hasTabs: false,
      $hasTabsTop: false
    });

    // Compile the template
    var element = $compile('<ion-' + options.viewType + '>' + templateString + '</ion-' + options.viewType + '>')(scope);

    options.$el = element;
    options.el = element[0];
    options.modalEl = options.el.querySelector('.' + options.viewType);
    var modal = new ModalView(options);

    modal.scope = scope;

    // If this wasn't a defined scope, we can assign the viewType to the isolated scope
    // we created
    if (!options.scope) {
      scope[ options.viewType ] = modal;
    }

    return modal;
  };

  return {
    /**
     * @ngdoc method
     * @name $ionicModal#fromTemplate
     * @param {string} templateString The template string to use as the modal's
     * content.
     * @param {object} options Options to be passed {@link ionic.controller:ionicModal#initialize ionicModal#initialize} method.
     * @returns {object} An instance of an {@link ionic.controller:ionicModal}
     * controller.
     */
    fromTemplate: function(templateString, options) {
      var modal = createModal(templateString, options || {});
      return modal;
    },
    /**
     * @ngdoc method
     * @name $ionicModal#fromTemplateUrl
     * @param {string} templateUrl The url to load the template from.
     * @param {object} options Options to be passed {@link ionic.controller:ionicModal#initialize ionicModal#initialize} method.
     * options object.
     * @returns {promise} A promise that will be resolved with an instance of
     * an {@link ionic.controller:ionicModal} controller.
     */
    fromTemplateUrl: function(url, options, _) {
      var cb;
      //Deprecated: allow a callback as second parameter. Now we return a promise.
      if (angular.isFunction(options)) {
        cb = options;
        options = _;
      }
      return $ionicTemplateLoader.load(url).then(function(templateString) {
        var modal = createModal(templateString, options || {});
        cb && cb(modal);
        return modal;
      });
    }
  };
}]);
<commit_msg>fix(keyboard): reset modal scroll view on show<commit_after>/**
 * @ngdoc service
 * @name $ionicModal
 * @module ionic
 * @description
 *
 * Related: {@link ionic.controller:ionicModal ionicModal controller}.
 *
 * The Modal is a content pane that can go over the user's main view
 * temporarily.  Usually used for making a choice or editing an item.
 *
 * Put the content of the modal inside of an `<ion-modal-view>` element.
 *
 * **Notes:**
 * - A modal will broadcast 'modal.shown', 'modal.hidden', and 'modal.removed' events from its originating
 * scope, passing in itself as an event argument. Both the modal.removed and modal.hidden events are
 * called when the modal is removed.
 *
 * - This example assumes your modal is in your main index file or another template file. If it is in its own
 * template file, remove the script tags and call it by file name.
 *
 * @usage
 * ```html
 * <script id="my-modal.html" type="text/ng-template">
 *   <ion-modal-view>
 *     <ion-header-bar>
 *       <h1 class="title">My Modal title</h1>
 *     </ion-header-bar>
 *     <ion-content>
 *       Hello!
 *     </ion-content>
 *   </ion-modal-view>
 * </script>
 * ```
 * ```js
 * angular.module('testApp', ['ionic'])
 * .controller('MyController', function($scope, $ionicModal) {
 *   $ionicModal.fromTemplateUrl('my-modal.html', {
 *     scope: $scope,
 *     animation: 'slide-in-up'
 *   }).then(function(modal) {
 *     $scope.modal = modal;
 *   });
 *   $scope.openModal = function() {
 *     $scope.modal.show();
 *   };
 *   $scope.closeModal = function() {
 *     $scope.modal.hide();
 *   };
 *   //Cleanup the modal when we're done with it!
 *   $scope.$on('$destroy', function() {
 *     $scope.modal.remove();
 *   });
 *   // Execute action on hide modal
 *   $scope.$on('modal.hidden', function() {
 *     // Execute action
 *   });
 *   // Execute action on remove modal
 *   $scope.$on('modal.removed', function() {
 *     // Execute action
 *   });
 * });
 * ```
 */
IonicModule
.factory('$ionicModal', [
  '$rootScope',
  '$ionicBody',
  '$compile',
  '$timeout',
  '$ionicPlatform',
  '$ionicTemplateLoader',
  '$q',
  '$log',
  '$ionicClickBlock',
function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTemplateLoader, $q, $log, $ionicClickBlock) {

  /**
   * @ngdoc controller
   * @name ionicModal
   * @module ionic
   * @description
   * Instantiated by the {@link ionic.service:$ionicModal} service.
   *
   * Be sure to call [remove()](#remove) when you are done with each modal
   * to clean it up and avoid memory leaks.
   *
   * Note: a modal will broadcast 'modal.shown', 'modal.hidden', and 'modal.removed' events from its originating
   * scope, passing in itself as an event argument. Note: both modal.removed and modal.hidden are
   * called when the modal is removed.
   */
  var ModalView = ionic.views.Modal.inherit({
    /**
     * @ngdoc method
     * @name ionicModal#initialize
     * @description Creates a new modal controller instance.
     * @param {object} options An options object with the following properties:
     *  - `{object=}` `scope` The scope to be a child of.
     *    Default: creates a child of $rootScope.
     *  - `{string=}` `animation` The animation to show & hide with.
     *    Default: 'slide-in-up'
     *  - `{boolean=}` `focusFirstInput` Whether to autofocus the first input of
     *    the modal when shown.  Default: false.
     *  - `{boolean=}` `backdropClickToClose` Whether to close the modal on clicking the backdrop.
     *    Default: true.
     *  - `{boolean=}` `hardwareBackButtonClose` Whether the modal can be closed using the hardware
     *    back button on Android and similar devices.  Default: true.
     */
    initialize: function(opts) {
      ionic.views.Modal.prototype.initialize.call(this, opts);
      this.animation = opts.animation || 'slide-in-up';
    },

    /**
     * @ngdoc method
     * @name ionicModal#show
     * @description Show this modal instance.
     * @returns {promise} A promise which is resolved when the modal is finished animating in.
     */
    show: function(target) {
      var self = this;

      if (self.scope.$$destroyed) {
        $log.error('Cannot call ' +  self.viewType + '.show() after remove(). Please create a new ' +  self.viewType + ' instance.');
        return;
      }

      var modalEl = jqLite(self.modalEl);

      self.el.classList.remove('hide');
      $timeout(function() {
        $ionicBody.addClass(self.viewType + '-open');
      }, 400);

      if (!self.el.parentElement) {
        modalEl.addClass(self.animation);
        $ionicBody.append(self.el);
      }

      // if modal was closed while the keyboard was up, reset scroll view on
      // next show since we can only resize it once it's visible
      var scrollCtrl = modalEl.data('$$ionicScrollController');
      scrollCtrl && scrollCtrl.resize();

      if (target && self.positionView) {
        self.positionView(target, modalEl);
        // set up a listener for in case the window size changes
        ionic.on('resize',function() {
          ionic.off('resize',null,window);
          self.positionView(target,modalEl);
        },window);
      }

      modalEl.addClass('ng-enter active')
             .removeClass('ng-leave ng-leave-active');

      self._isShown = true;
      self._deregisterBackButton = $ionicPlatform.registerBackButtonAction(
        self.hardwareBackButtonClose ? angular.bind(self, self.hide) : noop,
        PLATFORM_BACK_BUTTON_PRIORITY_MODAL
      );

      ionic.views.Modal.prototype.show.call(self);

      $timeout(function() {
        modalEl.addClass('ng-enter-active');
        ionic.trigger('resize');
        self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.shown', self);
        self.el.classList.add('active');
        self.scope.$broadcast('$ionicHeader.align');
      }, 20);

      return $timeout(function() {
        //After animating in, allow hide on backdrop click
        self.$el.on('click', function(e) {
          if (self.backdropClickToClose && e.target === self.el) {
            self.hide();
          }
        });
      }, 400);
    },

    /**
     * @ngdoc method
     * @name ionicModal#hide
     * @description Hide this modal instance.
     * @returns {promise} A promise which is resolved when the modal is finished animating out.
     */
    hide: function() {
      var self = this;
      var modalEl = jqLite(self.modalEl);

      // on iOS, clicks will sometimes bleed through/ghost click on underlying
      // elements
      $ionicClickBlock.show(600);

      self.el.classList.remove('active');
      modalEl.addClass('ng-leave');

      $timeout(function() {
        modalEl.addClass('ng-leave-active')
               .removeClass('ng-enter ng-enter-active active');
      }, 20);

      self.$el.off('click');
      self._isShown = false;
      self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.hidden', self);
      self._deregisterBackButton && self._deregisterBackButton();

      ionic.views.Modal.prototype.hide.call(self);

      // clean up event listeners
      if (self.positionView) {
        ionic.off('resize',null,window);
      }

      return $timeout(function() {
        $ionicBody.removeClass(self.viewType + '-open');
        self.el.classList.add('hide');
      }, self.hideDelay || 320);
    },

    /**
     * @ngdoc method
     * @name ionicModal#remove
     * @description Remove this modal instance from the DOM and clean up.
     * @returns {promise} A promise which is resolved when the modal is finished animating out.
     */
    remove: function() {
      var self = this;
      self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.removed', self);

      return self.hide().then(function() {
        self.scope.$destroy();
        self.$el.remove();
      });
    },

    /**
     * @ngdoc method
     * @name ionicModal#isShown
     * @returns boolean Whether this modal is currently shown.
     */
    isShown: function() {
      return !!this._isShown;
    }
  });

  var createModal = function(templateString, options) {
    // Create a new scope for the modal
    var scope = options.scope && options.scope.$new() || $rootScope.$new(true);

    options.viewType = options.viewType || 'modal';

    extend(scope, {
      $hasHeader: false,
      $hasSubheader: false,
      $hasFooter: false,
      $hasSubfooter: false,
      $hasTabs: false,
      $hasTabsTop: false
    });

    // Compile the template
    var element = $compile('<ion-' + options.viewType + '>' + templateString + '</ion-' + options.viewType + '>')(scope);

    options.$el = element;
    options.el = element[0];
    options.modalEl = options.el.querySelector('.' + options.viewType);
    var modal = new ModalView(options);

    modal.scope = scope;

    // If this wasn't a defined scope, we can assign the viewType to the isolated scope
    // we created
    if (!options.scope) {
      scope[ options.viewType ] = modal;
    }

    return modal;
  };

  return {
    /**
     * @ngdoc method
     * @name $ionicModal#fromTemplate
     * @param {string} templateString The template string to use as the modal's
     * content.
     * @param {object} options Options to be passed {@link ionic.controller:ionicModal#initialize ionicModal#initialize} method.
     * @returns {object} An instance of an {@link ionic.controller:ionicModal}
     * controller.
     */
    fromTemplate: function(templateString, options) {
      var modal = createModal(templateString, options || {});
      return modal;
    },
    /**
     * @ngdoc method
     * @name $ionicModal#fromTemplateUrl
     * @param {string} templateUrl The url to load the template from.
     * @param {object} options Options to be passed {@link ionic.controller:ionicModal#initialize ionicModal#initialize} method.
     * options object.
     * @returns {promise} A promise that will be resolved with an instance of
     * an {@link ionic.controller:ionicModal} controller.
     */
    fromTemplateUrl: function(url, options, _) {
      var cb;
      //Deprecated: allow a callback as second parameter. Now we return a promise.
      if (angular.isFunction(options)) {
        cb = options;
        options = _;
      }
      return $ionicTemplateLoader.load(url).then(function(templateString) {
        var modal = createModal(templateString, options || {});
        cb && cb(modal);
        return modal;
      });
    }
  };
}]);
<|endoftext|>


<commit_before>module.exports = function (mongoose) {
  'use strict';
  var db,
    config = {
      'USER': process.env.USER || '',
      'PASS': process.env.PASS || '',
      'HOST': process.env.HOST,
      'PORT': process.env.PORT,
      'DATABASE': process.env.DB
    },
    dbPath = ['mongodb://', config.USER, ':',
              config.PASS, '@', config.HOST,
              ':', config.PORT, '/',
              config.DATABASE].join(''),
    greets = 'Welcome to ionic app db';

    function init() {
      mongoose.connect(dbPath);
      return mongoose.connection;
    }

    /*var GreetingSchema = mongoose.Schema({
      sentence: String
    });
    var Greeting = mongoose.model('GreetingSchema', GreetingSchema);*/

    /*db.once('open', function(){
      var greeting;
      Greeting.find(function(err, greetings) {
        if(!greetings) {
          greeting = new Greeting({ sentence: greets });
          greeting.save();
        }
      }, function(err) {
           console.log(err);
      });
    });*/
};
<commit_msg>return func<commit_after>module.exports = function (mongoose) {
  'use strict';
  var db,
    config = {
      'USER': process.env.USER || '',
      'PASS': process.env.PASS || '',
      'HOST': process.env.HOST,
      'PORT': process.env.PORT,
      'DATABASE': process.env.DB
    },
    dbPath = ['mongodb://', config.USER, ':',
              config.PASS, '@', config.HOST,
              ':', config.PORT, '/',
              config.DATABASE].join(''),
    greets = 'Welcome to ionic app db';

    function init() {
      mongoose.connect(dbPath);
      return mongoose.connection;
    }

    return {
      init: init
    }

    /*var GreetingSchema = mongoose.Schema({
      sentence: String
    });
    var Greeting = mongoose.model('GreetingSchema', GreetingSchema);*/

    /*db.once('open', function(){
      var greeting;
      Greeting.find(function(err, greetings) {
        if(!greetings) {
          greeting = new Greeting({ sentence: greets });
          greeting.save();
        }
      }, function(err) {
           console.log(err);
      });
    });*/
};
<|endoftext|>


<commit_before>import {EMPTY, combineLatest, distinctUntilChanged, fromEvent, switchMap, take, takeUntil, timer} from 'rxjs'
import {Link} from 'route'
import {compose} from 'compose'
import {download} from 'widget/download'
import {withButtonGroupContext} from './buttonGroup'
import Icon from 'widget/icon'
import Keybinding from './keybinding'
import PropTypes from 'prop-types'
import React from 'react'
import Tooltip from 'widget/tooltip'
import _ from 'lodash'
import lookStyles from 'style/look.module.css'
import styles from './button.module.css'
import withForwardedRef from 'ref'
import withSubscriptions from 'subscription'

const CLICK_HOLD_DURATION_MS = 600
const CLICK_CANCEL_DELAY_MS = 250

const windowMouseUp$ = fromEvent(window, 'mouseup').pipe(distinctUntilChanged())

class _Button extends React.Component {
    constructor(props) {
        super(props)
        const {onClickHold} = props
        this.button = onClickHold && React.createRef()
        this.handleClick = this.handleClick.bind(this)
    }

    stopPropagation() {
        const {route, linkUrl, stopPropagation = !(route || linkUrl)} = this.props
        return stopPropagation
    }

    isActive() {
        const {disabled, busy} = this.props
        return !disabled && !busy
    }

    isLinked() {
        const {onMouseDown, onClick, onClickHold, route, linkUrl, downloadUrl, type} = this.props
        return onMouseDown || onClick || onClickHold || route || linkUrl || downloadUrl || ['submit', 'reset'].includes(type)
    }

    isNonInteractive() {
        return !this.isActive()
    }

    isHoverRequired() {
        const {tooltip, tooltipPanel} = this.props
        return this.isLinked() || tooltip || tooltipPanel
    }

    isHoverDisabled() {
        const {hover} = this.props
        return _.isNil(hover)
            ? !this.isHoverRequired()
            : hover === false
    }

    isHoverForced() {
        const {hover} = this.props
        return _.isNil(hover)
            ? false
            : hover === true
    }

    classNames() {
        const {chromeless, className, additionalClassName, look, size, shape, air, labelStyle, hint,
            alignment, width, onClickHold, disableTransitions, buttonGroupContext: {joinLeft, joinRight} = {}} = this.props
        return className ? className : [
            styles.button,
            styles[`size-${size}`],
            styles[`shape-${shape}`],
            styles[`air-${air}`],
            styles[`alignment-${alignment}`],
            styles[`width-${width}`],
            styles[`labelStyle-${labelStyle}`],
            joinLeft ? styles['join-left'] : null,
            joinRight ? styles['join-right'] : null,
            lookStyles.look,
            lookStyles[look],
            chromeless ? lookStyles.chromeless : null,
            this.isHoverForced() ? lookStyles.hoverForced : null,
            this.isHoverDisabled() ? lookStyles.hoverDisabled : null,
            this.isNonInteractive() ? lookStyles.nonInteractive : null,
            disableTransitions ? lookStyles.noTransitions : null,
            onClickHold ? styles.hold : null,
            hint ? styles.hint : null,
            additionalClassName
        ].join(' ')
    }

    handleMouseOver(e) {
        const {onMouseOver} = this.props
        onMouseOver && onMouseOver(e)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    handleMouseOut(e) {
        const {onMouseOut} = this.props
        onMouseOut && onMouseOut(e)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    handleMouseDown(e) {
        const {onMouseDown} = this.props
        onMouseDown && onMouseDown(e)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    handleClick(e) {
        const {onClick, downloadUrl, downloadFilename} = this.props
        onClick && onClick(e)
        downloadUrl && download(downloadUrl, downloadFilename)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    handleClickHold(e) {
        const {onClickHold} = this.props
        onClickHold && onClickHold(e)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    getKeymap(keybinding) {
        return _.isArray(keybinding)
            ? _.reduce(keybinding, (keymap, key) => ({...keymap, [key]: this.handleClick}), {})
            : {[keybinding]: this.handleClick}
    }

    renderKeybinding(contents) {
        const {keybinding, hidden} = this.props
        return keybinding
            ? (
                <Keybinding
                    keymap={this.getKeymap(keybinding)}
                    disabled={hidden || !this.isActive()}>
                    {contents}
                </Keybinding>
            )
            : contents
    }

    renderVisible(contents) {
        const {hidden} = this.props
        return hidden ? null : contents
    }

    // The Tooltip component stops propagation of events, thus the ref has to be on a wrapping element.
    renderWrapper(contents) {
        const {onClickHold} = this.props
        const style = {
            '--click-hold-delay-ms': `${CLICK_CANCEL_DELAY_MS}ms`,
            '--click-hold-duration-ms': `${CLICK_HOLD_DURATION_MS - CLICK_CANCEL_DELAY_MS}ms`
        }
        return onClickHold ? (

                {contents}

        ) : contents
    }

    renderLink(contents) {
        const {route, linkUrl} = this.props
        if (!route && !linkUrl) {
            return contents
        }
        if (route && linkUrl) {
            throw Error('Cannot specify route and linkUrl at the same time.')
        }
        if (route) {
            return this.renderRouteLink(contents)
        }
        if (linkUrl) {
            return this.renderPlainLink(contents)
        }
    }

    renderPlainLink(contents) {
        const {linkUrl, linkTarget} = this.props
        return this.isActive() && linkUrl
            ? (
                 e.preventDefault()}>
                    {contents}

            )
            : contents
    }

    renderRouteLink(contents) {
        const {route} = this.props
        return this.isActive() && route
            ? (
                <Link to={route} onMouseDown={e => e.preventDefault()}>
                    {contents}
                </Link>
            )
            : contents
    }

    renderTooltip(contents) {
        const {tooltip, tooltipPanel, tooltipPlacement, tooltipDisabled, tooltipDelay, tooltipOnVisible, tooltipVisible, tooltipClickTrigger} = this.props
        const overlayInnerStyle = tooltipPanel ? {padding: 0} : null
        const message = tooltipPanel || tooltip
        const visibility = _.isNil(tooltipVisible) ? {} : {visible: tooltipVisible}
        return this.isActive() && message ? (
            <Tooltip
                msg={message}
                placement={tooltipPlacement}
                delay={tooltipDelay}
                hoverTrigger={!tooltipPanel}
                clickTrigger={tooltipClickTrigger || !this.isLinked()}
                overlayInnerStyle={overlayInnerStyle}
                overlayStyle={{visibility: tooltipDisabled ? 'hidden' : 'visible'}}
                onVisibleChange={tooltipOnVisible}
                {...visibility}
            >
                {contents}
            </Tooltip>
        ) : contents
    }

    renderButton(contents) {
        const {type, style, tabIndex, onClickHold, forwardedRef} = this.props
        return (
            <button
                ref={forwardedRef}
                type={type}
                className={this.classNames()}
                style={style}
                tabIndex={tabIndex}
                disabled={!this.isActive()}
                onMouseOver={e => this.handleMouseOver(e)}
                onMouseOut={e => this.handleMouseOut(e)}
                onMouseDown={e => this.handleMouseDown(e)}
                onClick={e => onClickHold ? e.stopPropagation() : this.handleClick(e)}
            >
                {contents}
            </button>
        )
    }

    renderIcon() {
        const {busy, icon, iconType, iconVariant, iconDimmed, iconClassName, iconAttributes} = this.props
        return (
            <Icon
                name={busy ? 'spinner' : icon}
                type={iconType}
                variant={iconVariant}
                dimmed={iconDimmed}
                className={iconClassName}
                attributes={iconAttributes}
            />
        )
    }

    renderLabel() {
        const {label} = this.props
        return (
            {label}
        )
    }

    renderContents() {
        const {icon, iconPlacement, label, tail} = this.props
        const content = this.getContent()
        return content ? content : (

                {icon && iconPlacement === 'left' ? this.renderIcon() : null}
                {label ? this.renderLabel() : null}
                {icon && iconPlacement === 'right' ? this.renderIcon() : null}
                {tail}

        )
    }

    render() {
        return (
            this.renderKeybinding(
                this.renderVisible(
                    this.renderWrapper(
                        this.renderLink(
                            this.renderTooltip(
                                this.renderButton(
                                    this.renderContents()
                                )
                            )
                        )
                    )
                )
            )
        )
    }

    // render() {
    //     const {hidden} = this.props
    //     return !hidden ? (
    //         this.renderWrapper(
    //             this.renderLink(
    //                 this.renderTooltip(
    //                     this.renderKeybinding(
    //                         this.renderButton(
    //                             this.renderContents()
    //                         )
    //                     )
    //                 )
    //             )
    //         )
    //     ) : null
    // }

    getContent() {
        const {content, children} = this.props
        return content || children
    }

    componentDidMount() {
        const {onClickHold, addSubscription} = this.props

        if (onClickHold && this.button.current) {
            const button = this.button.current
            const mouseDown$ = fromEvent(button, 'mousedown')
            const mouseUp$ = fromEvent(button, 'mouseup')
            const mouseEnter$ = fromEvent(button, 'mouseenter')
            const cancel$ = windowMouseUp$
            const mouseTrigger$ = combineLatest(mouseDown$, mouseEnter$)
            const mouseActivate$ = mouseUp$

            // Click-hold is triggered if button pressed more than CLICK_HOLD_DELAY_MS.
            const clickHold$ =
                mouseTrigger$.pipe(
                    switchMap(() =>
                        timer(CLICK_HOLD_DURATION_MS).pipe(
                            takeUntil(cancel$),
                            switchMap(() =>
                                mouseActivate$.pipe(
                                    takeUntil(cancel$),
                                    take(1)
                                )
                            )
                        )
                    )
                )

            addSubscription(
                clickHold$.subscribe(e => {
                    const {onClickHold} = this.props
                    if (this.isActive() && onClickHold) {
                        this.handleClickHold(e)
                    }
                })
            )

            // Click event needs to be handled here for two reasons:
            // - to allow cancellation of click-hold without triggering click, when pressed longer than CLICK_CANCEL_DELAY_MS
            // - to avoid concurrent handling of both click and click-hold when pressed longer than CLICK_HOLD_DELAY_MS
            // Click is triggered only if button pressed less than CLICK_CANCEL_DELAY_MS.
            const click$ =
                mouseTrigger$.pipe(
                    switchMap(() =>
                        mouseActivate$.pipe(
                            takeUntil(cancel$),
                            takeUntil(onClickHold ? timer(CLICK_CANCEL_DELAY_MS) : EMPTY),
                            take(1)
                        )
                    )
                )

            addSubscription(
                click$.subscribe(e => {
                    const {onClick} = this.props
                    if (this.isActive() && onClick) {
                        this.handleClick(e)
                    }
                })
            )
        }
    }
}

export const Button = compose(
    _Button,
    withSubscriptions(),
    withButtonGroupContext(),
    withForwardedRef()
)

Button.propTypes = {
    additionalClassName: PropTypes.string,
    air: PropTypes.oneOf(['normal', 'more', 'less', 'none']),
    alignment: PropTypes.oneOf(['left', 'center', 'right']),
    busy: PropTypes.any,
    children: PropTypes.any,
    chromeless: PropTypes.any,
    className: PropTypes.string,
    content: PropTypes.any,
    disabled: PropTypes.any,
    disableTransitions: PropTypes.any,
    downloadFilename: PropTypes.any,
    downloadUrl: PropTypes.any,
    hidden: PropTypes.any,
    hint: PropTypes.any,
    hover: PropTypes.any, // three-state
    icon: PropTypes.string,
    iconAttributes: PropTypes.any,
    iconClassName: PropTypes.any,
    iconDimmed: PropTypes.any,
    iconPlacement: PropTypes.oneOf(['left', 'right']),
    iconType: PropTypes.string,
    iconVariant: PropTypes.string,
    keybinding: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
    label: PropTypes.any,
    labelStyle: PropTypes.oneOf(['default', 'smallcaps', 'smallcaps-highlight']),
    linkTarget: PropTypes.string,
    linkUrl: PropTypes.string,
    look: PropTypes.oneOf(['default', 'highlight', 'selected', 'transparent', 'add', 'apply', 'cancel']),
    route: PropTypes.string,
    shape: PropTypes.oneOf(['rectangle', 'pill', 'circle', 'none']),
    size: PropTypes.oneOf(['x-small', 'small', 'normal', 'large', 'x-large', 'xx-large']),
    stopPropagation: PropTypes.any,
    style: PropTypes.object,
    tabIndex: PropTypes.number,
    tail: PropTypes.any,
    tooltip: PropTypes.any,
    tooltipClickTrigger: PropTypes.any,
    tooltipDelay: PropTypes.number,
    tooltipDisabled: PropTypes.any,
    tooltipOnVisible: PropTypes.func,
    tooltipPanel: PropTypes.any,
    tooltipPlacement: PropTypes.any,
    tooltipVisible: PropTypes.any,
    type: PropTypes.oneOf(['button', 'submit', 'reset']),
    width: PropTypes.oneOf(['fit', 'fill', 'max']),
    onClick: PropTypes.func,
    onClickHold: PropTypes.func,
    onMouseDown: PropTypes.func,
    onMouseOut: PropTypes.func,
    onMouseOver: PropTypes.func
}

Button.defaultProps = {
    air: 'normal',
    alignment: 'center',
    iconPlacement: 'left',
    iconVariant: 'normal',
    labelStyle: 'default',
    linkTarget: '_blank',
    look: 'default',
    shape: 'rectangle',
    size: 'normal',
    type: 'button',
    width: 'fit'
}
<commit_msg>Implemented Button memoization and lazy rendering.<commit_after>import {EMPTY, combineLatest, distinctUntilChanged, fromEvent, switchMap, take, takeUntil, timer} from 'rxjs'
import {Link} from 'route'
import {compose} from 'compose'
import {download} from 'widget/download'
import {withButtonGroupContext} from './buttonGroup'
import Icon from 'widget/icon'
import Keybinding from './keybinding'
import PropTypes from 'prop-types'
import React from 'react'
import Tooltip from 'widget/tooltip'
import _ from 'lodash'
import lookStyles from 'style/look.module.css'
import styles from './button.module.css'
import withForwardedRef from 'ref'
import withSubscriptions from 'subscription'

const CLICK_HOLD_DURATION_MS = 600
const CLICK_CANCEL_DELAY_MS = 250

const windowMouseUp$ = fromEvent(window, 'mouseup').pipe(distinctUntilChanged())

class _Button extends React.Component {
    constructor(props) {
        super(props)
        const {onClickHold} = props
        this.button = onClickHold && React.createRef()
        this.handleClick = this.handleClick.bind(this)
        this.renderKeybinding = this.renderKeybinding.bind(this)
        this.renderVisible = this.renderVisible.bind(this)
        this.renderWrapper = this.renderWrapper.bind(this)
        this.renderLink = this.renderLink.bind(this)
        this.renderTooltip = this.renderTooltip.bind(this)
        this.renderButton = this.renderButton.bind(this)
        this.renderContents = this.renderContents.bind(this)
    }

    stopPropagation() {
        const {route, linkUrl, stopPropagation = !(route || linkUrl)} = this.props
        return stopPropagation
    }

    isActive() {
        const {disabled, busy} = this.props
        return !disabled && !busy
    }

    isLinked() {
        const {onMouseDown, onClick, onClickHold, route, linkUrl, downloadUrl, type} = this.props
        return onMouseDown || onClick || onClickHold || route || linkUrl || downloadUrl || ['submit', 'reset'].includes(type)
    }

    isNonInteractive() {
        return !this.isActive()
    }

    isHoverRequired() {
        const {tooltip, tooltipPanel} = this.props
        return this.isLinked() || tooltip || tooltipPanel
    }

    isHoverDisabled() {
        const {hover} = this.props
        return _.isNil(hover)
            ? !this.isHoverRequired()
            : hover === false
    }

    isHoverForced() {
        const {hover} = this.props
        return _.isNil(hover)
            ? false
            : hover === true
    }

    classNames() {
        const {chromeless, className, additionalClassName, look, size, shape, air, labelStyle, hint,
            alignment, width, onClickHold, disableTransitions, buttonGroupContext: {joinLeft, joinRight} = {}} = this.props
        return className ? className : [
            styles.button,
            styles[`size-${size}`],
            styles[`shape-${shape}`],
            styles[`air-${air}`],
            styles[`alignment-${alignment}`],
            styles[`width-${width}`],
            styles[`labelStyle-${labelStyle}`],
            joinLeft ? styles['join-left'] : null,
            joinRight ? styles['join-right'] : null,
            lookStyles.look,
            lookStyles[look],
            chromeless ? lookStyles.chromeless : null,
            this.isHoverForced() ? lookStyles.hoverForced : null,
            this.isHoverDisabled() ? lookStyles.hoverDisabled : null,
            this.isNonInteractive() ? lookStyles.nonInteractive : null,
            disableTransitions ? lookStyles.noTransitions : null,
            onClickHold ? styles.hold : null,
            hint ? styles.hint : null,
            additionalClassName
        ].join(' ')
    }

    handleMouseOver(e) {
        const {onMouseOver} = this.props
        onMouseOver && onMouseOver(e)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    handleMouseOut(e) {
        const {onMouseOut} = this.props
        onMouseOut && onMouseOut(e)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    handleMouseDown(e) {
        const {onMouseDown} = this.props
        onMouseDown && onMouseDown(e)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    handleClick(e) {
        const {onClick, downloadUrl, downloadFilename} = this.props
        onClick && onClick(e)
        downloadUrl && download(downloadUrl, downloadFilename)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    handleClickHold(e) {
        const {onClickHold} = this.props
        onClickHold && onClickHold(e)
        if (this.stopPropagation()) {
            e.stopPropagation()
        }
    }

    getKeymap(keybinding) {
        return _.isArray(keybinding)
            ? _.reduce(keybinding, (keymap, key) => ({...keymap, [key]: this.handleClick}), {})
            : {[keybinding]: this.handleClick}
    }

    renderKeybinding([current, ...next]) {
        const {keybinding, hidden} = this.props
        return keybinding
            ? (
                <Keybinding
                    keymap={this.getKeymap(keybinding)}
                    disabled={hidden || !this.isActive()}>
                    {current(next)}
                </Keybinding>
            )
            : current(next)
    }

    renderVisible([current, ...next]) {
        const {hidden} = this.props
        return hidden ? null : current(next)
    }

    // The Tooltip component stops propagation of events, thus the ref has to be on a wrapping element.
    renderWrapper([current, ...next]) {
        const {onClickHold} = this.props
        const style = {
            '--click-hold-delay-ms': `${CLICK_CANCEL_DELAY_MS}ms`,
            '--click-hold-duration-ms': `${CLICK_HOLD_DURATION_MS - CLICK_CANCEL_DELAY_MS}ms`
        }
        return onClickHold ? (

                {current(next)}

        ) : current(next)
    }

    renderLink([current, ...next]) {
        const {route, linkUrl} = this.props
        if (!route && !linkUrl) {
            return current(next)
        }
        if (route && linkUrl) {
            throw Error('Cannot specify route and linkUrl at the same time.')
        }
        if (route) {
            return this.renderRouteLink(current(next))
        }
        if (linkUrl) {
            return this.renderPlainLink(current(next))
        }
    }

    renderPlainLink(contents) {
        const {linkUrl, linkTarget} = this.props
        return this.isActive() && linkUrl
            ? (
                 e.preventDefault()}>
                    {contents}

            )
            : contents
    }

    renderRouteLink(contents) {
        const {route} = this.props
        return this.isActive() && route
            ? (
                <Link to={route} onMouseDown={e => e.preventDefault()}>
                    {contents}
                </Link>
            )
            : contents
    }

    renderTooltip([current, ...next]) {
        const {tooltip, tooltipPanel, tooltipPlacement, tooltipDisabled, tooltipDelay, tooltipOnVisible, tooltipVisible, tooltipClickTrigger} = this.props
        const overlayInnerStyle = tooltipPanel ? {padding: 0} : null
        const message = tooltipPanel || tooltip
        const visibility = _.isNil(tooltipVisible) ? {} : {visible: tooltipVisible}
        return this.isActive() && message ? (
            <Tooltip
                msg={message}
                placement={tooltipPlacement}
                delay={tooltipDelay}
                hoverTrigger={!tooltipPanel}
                clickTrigger={tooltipClickTrigger || !this.isLinked()}
                overlayInnerStyle={overlayInnerStyle}
                overlayStyle={{visibility: tooltipDisabled ? 'hidden' : 'visible'}}
                onVisibleChange={tooltipOnVisible}
                {...visibility}
            >
                {current(next)}
            </Tooltip>
        ) : current(next)
    }

    renderButton([current, ...next]) {
        const {type, style, tabIndex, onClickHold, forwardedRef} = this.props
        return (
            <button
                ref={forwardedRef}
                type={type}
                className={this.classNames()}
                style={style}
                tabIndex={tabIndex}
                disabled={!this.isActive()}
                onMouseOver={e => this.handleMouseOver(e)}
                onMouseOut={e => this.handleMouseOut(e)}
                onMouseDown={e => this.handleMouseDown(e)}
                onClick={e => onClickHold ? e.stopPropagation() : this.handleClick(e)}
            >
                {current(next)}
            </button>
        )
    }

    renderIcon() {
        const {busy, icon, iconType, iconVariant, iconDimmed, iconClassName, iconAttributes} = this.props
        return (
            <Icon
                name={busy ? 'spinner' : icon}
                type={iconType}
                variant={iconVariant}
                dimmed={iconDimmed}
                className={iconClassName}
                attributes={iconAttributes}
            />
        )
    }

    renderLabel() {
        const {label} = this.props
        return (
            {label}
        )
    }

    renderContents() {
        const {icon, iconPlacement, label, tail} = this.props
        const content = this.getContent()
        return content ? content : (

                {icon && iconPlacement === 'left' ? this.renderIcon() : null}
                {label ? this.renderLabel() : null}
                {icon && iconPlacement === 'right' ? this.renderIcon() : null}
                {tail}

        )
    }

    render() {
        const [current, ...next] = [
            this.renderKeybinding,
            this.renderVisible,
            this.renderWrapper,
            this.renderLink,
            this.renderTooltip,
            this.renderButton,
            this.renderContents
        ]
        return current(next)
    }

    getContent() {
        const {content, children} = this.props
        return content || children
    }

    componentDidMount() {
        const {onClickHold, addSubscription} = this.props

        if (onClickHold && this.button.current) {
            const button = this.button.current
            const mouseDown$ = fromEvent(button, 'mousedown')
            const mouseUp$ = fromEvent(button, 'mouseup')
            const mouseEnter$ = fromEvent(button, 'mouseenter')
            const cancel$ = windowMouseUp$
            const mouseTrigger$ = combineLatest(mouseDown$, mouseEnter$)
            const mouseActivate$ = mouseUp$

            // Click-hold is triggered if button pressed more than CLICK_HOLD_DELAY_MS.
            const clickHold$ =
                mouseTrigger$.pipe(
                    switchMap(() =>
                        timer(CLICK_HOLD_DURATION_MS).pipe(
                            takeUntil(cancel$),
                            switchMap(() =>
                                mouseActivate$.pipe(
                                    takeUntil(cancel$),
                                    take(1)
                                )
                            )
                        )
                    )
                )

            addSubscription(
                clickHold$.subscribe(e => {
                    const {onClickHold} = this.props
                    if (this.isActive() && onClickHold) {
                        this.handleClickHold(e)
                    }
                })
            )

            // Click event needs to be handled here for two reasons:
            // - to allow cancellation of click-hold without triggering click, when pressed longer than CLICK_CANCEL_DELAY_MS
            // - to avoid concurrent handling of both click and click-hold when pressed longer than CLICK_HOLD_DELAY_MS
            // Click is triggered only if button pressed less than CLICK_CANCEL_DELAY_MS.
            const click$ =
                mouseTrigger$.pipe(
                    switchMap(() =>
                        mouseActivate$.pipe(
                            takeUntil(cancel$),
                            takeUntil(onClickHold ? timer(CLICK_CANCEL_DELAY_MS) : EMPTY),
                            take(1)
                        )
                    )
                )

            addSubscription(
                click$.subscribe(e => {
                    const {onClick} = this.props
                    if (this.isActive() && onClick) {
                        this.handleClick(e)
                    }
                })
            )
        }
    }
}

export const Button =
    compose(
        React.memo(_Button),
        withSubscriptions(),
        withButtonGroupContext(),
        withForwardedRef()
    )

Button.propTypes = {
    additionalClassName: PropTypes.string,
    air: PropTypes.oneOf(['normal', 'more', 'less', 'none']),
    alignment: PropTypes.oneOf(['left', 'center', 'right']),
    busy: PropTypes.any,
    children: PropTypes.any,
    chromeless: PropTypes.any,
    className: PropTypes.string,
    content: PropTypes.any,
    disabled: PropTypes.any,
    disableTransitions: PropTypes.any,
    downloadFilename: PropTypes.any,
    downloadUrl: PropTypes.any,
    hidden: PropTypes.any,
    hint: PropTypes.any,
    hover: PropTypes.any, // three-state
    icon: PropTypes.string,
    iconAttributes: PropTypes.any,
    iconClassName: PropTypes.any,
    iconDimmed: PropTypes.any,
    iconPlacement: PropTypes.oneOf(['left', 'right']),
    iconType: PropTypes.string,
    iconVariant: PropTypes.string,
    keybinding: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
    label: PropTypes.any,
    labelStyle: PropTypes.oneOf(['default', 'smallcaps', 'smallcaps-highlight']),
    linkTarget: PropTypes.string,
    linkUrl: PropTypes.string,
    look: PropTypes.oneOf(['default', 'highlight', 'selected', 'transparent', 'add', 'apply', 'cancel']),
    route: PropTypes.string,
    shape: PropTypes.oneOf(['rectangle', 'pill', 'circle', 'none']),
    size: PropTypes.oneOf(['x-small', 'small', 'normal', 'large', 'x-large', 'xx-large']),
    stopPropagation: PropTypes.any,
    style: PropTypes.object,
    tabIndex: PropTypes.number,
    tail: PropTypes.any,
    tooltip: PropTypes.any,
    tooltipClickTrigger: PropTypes.any,
    tooltipDelay: PropTypes.number,
    tooltipDisabled: PropTypes.any,
    tooltipOnVisible: PropTypes.func,
    tooltipPanel: PropTypes.any,
    tooltipPlacement: PropTypes.any,
    tooltipVisible: PropTypes.any,
    type: PropTypes.oneOf(['button', 'submit', 'reset']),
    width: PropTypes.oneOf(['fit', 'fill', 'max']),
    onClick: PropTypes.func,
    onClickHold: PropTypes.func,
    onMouseDown: PropTypes.func,
    onMouseOut: PropTypes.func,
    onMouseOver: PropTypes.func
}

Button.defaultProps = {
    air: 'normal',
    alignment: 'center',
    iconPlacement: 'left',
    iconVariant: 'normal',
    labelStyle: 'default',
    linkTarget: '_blank',
    look: 'default',
    shape: 'rectangle',
    size: 'normal',
    type: 'button',
    width: 'fit'
}
<|endoftext|>


<commit_before>'use strict';

function draw_logic(){
    // Draw drops.
    buffer.fillStyle = '#aaf';
    for(var drop in drops){
        buffer.fillRect(
          drops[drop]['x'],
          drops[drop]['y'],
          2,
          7
        );
    }

    // Draw object.
    buffer.fillStyle = '#777';
    buffer.fillRect(
      object['x'],
      object['y'],
      200,
      -40
    );

    // Draw number of particles.
    buffer.fillStyle = '#fff';
    buffer.fillText(
      drops.length,
      object['x'],
      object['y']
    );
}

function logic(){
    // Add some randomly placed drops.
    var loop_counter = drop_counter;
    do{
        drops.push({
          'x': Math.floor(Math.random() * width),
          'y': -99,
        });
    }while(loop_counter--);

    // Update drop positions.
    for(var drop in drops){
        drops[drop]['y'] += Math.random() * 9 + 9;

        // Delete drops below bottom of screen
        //   or that collided with the object.
        if(drops[drop]['y'] > height
          || !(
            drops[drop]['x'] <= object['x']
            || drops[drop]['y'] + 40 <= object['y']
            || drops[drop]['x'] - 200 >= object['x']
            || drops[drop]['y'] >= object['y']
          )){
            drops.splice(
              drop,
              1
            );
        }
    }
}

function resize_logic(){
    buffer.font = '23pt sans-serif';
}

var drop_counter = 1;
var drops = [];
var object = {
  'x': 0,
  'y': 0,
};

window.onload = function(e){
    init_canvas();
    init_input(
      {
        187: {
          'todo': function(){
              drop_counter++;
          },
        },
        189: {
          'todo': function(){
              drop_counter = drop_counter > 0
                ? drop_counter - 1
                : 0;
          },
        },
      },
      {
        'mousedown': {
          'todo': function(){
              object['x'] = mouse['x'] - 100;
              object['y'] = mouse['y'];
          },
        },
        'mousemove': {
          'todo': function(){
              if(!mouse['down']){
                  return;
              }

              object['x'] = mouse['x'] - 100;
              object['y'] = mouse['y'];
          },
        },
      }
    );

    object['x'] = width / 2 - 100;
    object['y'] = height / 2;
};
<commit_msg>switched to default font<commit_after>'use strict';

function draw_logic(){
    // Draw drops.
    buffer.fillStyle = '#aaf';
    for(var drop in drops){
        buffer.fillRect(
          drops[drop]['x'],
          drops[drop]['y'],
          2,
          7
        );
    }

    // Draw object.
    buffer.fillStyle = '#777';
    buffer.fillRect(
      object['x'],
      object['y'],
      200,
      -40
    );

    // Draw number of particles.
    buffer.fillStyle = '#fff';
    buffer.fillText(
      drops.length,
      object['x'],
      object['y']
    );
}

function logic(){
    // Add some randomly placed drops.
    var loop_counter = drop_counter;
    do{
        drops.push({
          'x': Math.floor(Math.random() * width),
          'y': -99,
        });
    }while(loop_counter--);

    // Update drop positions.
    for(var drop in drops){
        drops[drop]['y'] += Math.random() * 9 + 9;

        // Delete drops below bottom of screen
        //   or that collided with the object.
        if(drops[drop]['y'] > height
          || !(
            drops[drop]['x'] <= object['x']
            || drops[drop]['y'] + 40 <= object['y']
            || drops[drop]['x'] - 200 >= object['x']
            || drops[drop]['y'] >= object['y']
          )){
            drops.splice(
              drop,
              1
            );
        }
    }
}

function resize_logic(){
    buffer.font = font;
}

var drop_counter = 1;
var drops = [];
var object = {
  'x': 0,
  'y': 0,
};

window.onload = function(e){
    init_canvas();
    init_input(
      {
        187: {
          'todo': function(){
              drop_counter++;
          },
        },
        189: {
          'todo': function(){
              drop_counter = drop_counter > 0
                ? drop_counter - 1
                : 0;
          },
        },
      },
      {
        'mousedown': {
          'todo': function(){
              object['x'] = mouse['x'] - 100;
              object['y'] = mouse['y'];
          },
        },
        'mousemove': {
          'todo': function(){
              if(!mouse['down']){
                  return;
              }

              object['x'] = mouse['x'] - 100;
              object['y'] = mouse['y'];
          },
        },
      }
    );

    object['x'] = width / 2 - 100;
    object['y'] = height / 2;
};
<|endoftext|>


<commit_before>// events.js<commit_msg>created events model<commit_after>// events.js

/*
Some Open Issues:
  - Add some additional validation to these fields:
    - None of these should be null, except maybe currentActivity
    - Capacity should be greater than 0
    - Current size should be >= 0 and <= capacity
  - Add some default values
    - completedStatus: false
    - currentActivity: null?
Note:
  - This will have createdAt and updatedAt fields that we may need to use.
*/

module.exports = function (sequelize, DataTypes) {
  var Event = sequelize.define('Event', {
    locationId: DataTypes.INTEGER,
    plannedTime: DataTypes.DATE,
    capacity: DataTypes.INTEGER,
    currentSize: DataTypes.INTEGER,
    currentActivity: DataTypes.STRING,
    completedStatus: DataTypes.BOOLEAN
  });

  return Event;
};<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 * @author Larry Battle / http://bateru.com/news
 */

var THREE = THREE || { REVISION: '55' };

self.console = self.console || {

	info: function () {},
	log: function () {},
	debug: function () {},
	warn: function () {},
	error: function () {}

};

self.Int32Array = self.Int32Array || Array;
self.Float32Array = self.Float32Array || Array;

// Shims for "startsWith", "endsWith", and "trim" for browsers where this is not yet implemented
// not sure we should have this, or at least not have it here

// http://stackoverflow.com/questions/646628/javascript-startswith
// http://stackoverflow.com/questions/498970/how-do-i-trim-a-string-in-javascript
// http://wiki.ecmascript.org/doku.php?id=harmony%3astring_extras

String.prototype.startsWith = String.prototype.startsWith || function ( str ) {

	return this.slice( 0, str.length ) === str;

};

String.prototype.endsWith = String.prototype.endsWith || function ( str ) {

	var t = String( str );
	var index = this.lastIndexOf( t );
	return ( -1 < index && index ) === (this.length - t.length);

};

String.prototype.trim = String.prototype.trim || function () {

	return this.replace( /^\s+|\s+$/g, '' );

};

// based on http://stackoverflow.com/a/12317051
THREE.extend = function ( target, other ) {

	target = target || {};

	for (var prop in other) {

		if ( typeof other[prop] === 'object' ) {

			target[prop] = THREE.extend( target[prop], other[prop] );

		} else {

			target[prop] = other[prop];

		}

	}

	return target;

};

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel

( function () {

	var lastTime = 0;
	var vendors = [ 'ms', 'moz', 'webkit', 'o' ];

	for ( var x = 0; x < vendors.length && !window.requestAnimationFrame; ++ x ) {

		window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ];
		window.cancelAnimationFrame = window[ vendors[ x ] + 'CancelAnimationFrame' ] || window[ vendors[ x ] + 'CancelRequestAnimationFrame' ];

	}

	if ( window.requestAnimationFrame === undefined ) {

		window.requestAnimationFrame = function ( callback, element ) {

			var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
			var id = window.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
			lastTime = currTime + timeToCall;
			return id;

		};

	}

	window.cancelAnimationFrame = window.cancelAnimationFrame || function ( id ) { window.clearTimeout( id ) };

}() );

// GL STATE CONSTANTS

THREE.CullFaceNone = 0;
THREE.CullFaceBack = 1;
THREE.CullFaceFront = 2;
THREE.CullFaceFrontBack = 3;

THREE.FrontFaceDirectionCW = 0;
THREE.FrontFaceDirectionCCW = 1;

// SHADOWING TYPES

THREE.BasicShadowMap = 0;
THREE.PCFShadowMap = 1;
THREE.PCFSoftShadowMap = 2;

// MATERIAL CONSTANTS

// side

THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;

// shading

THREE.NoShading = 0;
THREE.FlatShading = 1;
THREE.SmoothShading = 2;

// colors

THREE.NoColors = 0;
THREE.FaceColors = 1;
THREE.VertexColors = 2;

// blending modes

THREE.NoBlending = 0;
THREE.NormalBlending = 1;
THREE.AdditiveBlending = 2;
THREE.SubtractiveBlending = 3;
THREE.MultiplyBlending = 4;
THREE.CustomBlending = 5;

// custom blending equations
// (numbers start from 100 not to clash with other
//  mappings to OpenGL constants defined in Texture.js)

THREE.AddEquation = 100;
THREE.SubtractEquation = 101;
THREE.ReverseSubtractEquation = 102;

// custom blending destination factors

THREE.ZeroFactor = 200;
THREE.OneFactor = 201;
THREE.SrcColorFactor = 202;
THREE.OneMinusSrcColorFactor = 203;
THREE.SrcAlphaFactor = 204;
THREE.OneMinusSrcAlphaFactor = 205;
THREE.DstAlphaFactor = 206;
THREE.OneMinusDstAlphaFactor = 207;

// custom blending source factors

//THREE.ZeroFactor = 200;
//THREE.OneFactor = 201;
//THREE.SrcAlphaFactor = 204;
//THREE.OneMinusSrcAlphaFactor = 205;
//THREE.DstAlphaFactor = 206;
//THREE.OneMinusDstAlphaFactor = 207;
THREE.DstColorFactor = 208;
THREE.OneMinusDstColorFactor = 209;
THREE.SrcAlphaSaturateFactor = 210;

// TEXTURE CONSTANTS

THREE.MultiplyOperation = 0;
THREE.MixOperation = 1;
THREE.AddOperation = 2;

// Mapping modes

THREE.UVMapping = function () {};

THREE.CubeReflectionMapping = function () {};
THREE.CubeRefractionMapping = function () {};

THREE.SphericalReflectionMapping = function () {};
THREE.SphericalRefractionMapping = function () {};

// Wrapping modes

THREE.RepeatWrapping = 1000;
THREE.ClampToEdgeWrapping = 1001;
THREE.MirroredRepeatWrapping = 1002;

// Filters

THREE.NearestFilter = 1003;
THREE.NearestMipMapNearestFilter = 1004;
THREE.NearestMipMapLinearFilter = 1005;
THREE.LinearFilter = 1006;
THREE.LinearMipMapNearestFilter = 1007;
THREE.LinearMipMapLinearFilter = 1008;

// Data types

THREE.UnsignedByteType = 1009;
THREE.ByteType = 1010;
THREE.ShortType = 1011;
THREE.UnsignedShortType = 1012;
THREE.IntType = 1013;
THREE.UnsignedIntType = 1014;
THREE.FloatType = 1015;

// Pixel types

//THREE.UnsignedByteType = 1009;
THREE.UnsignedShort4444Type = 1016;
THREE.UnsignedShort5551Type = 1017;
THREE.UnsignedShort565Type = 1018;

// Pixel formats

THREE.AlphaFormat = 1019;
THREE.RGBFormat = 1020;
THREE.RGBAFormat = 1021;
THREE.LuminanceFormat = 1022;
THREE.LuminanceAlphaFormat = 1023;

// Compressed texture formats

THREE.RGB_S3TC_DXT1_Format = 2001;
THREE.RGBA_S3TC_DXT1_Format = 2002;
THREE.RGBA_S3TC_DXT3_Format = 2003;
THREE.RGBA_S3TC_DXT5_Format = 2004;

/*
// Potential future PVRTC compressed texture formats
THREE.RGB_PVRTC_4BPPV1_Format = 2100;
THREE.RGB_PVRTC_2BPPV1_Format = 2101;
THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
*/
<commit_msg>r56dev<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 * @author Larry Battle / http://bateru.com/news
 */

var THREE = THREE || { REVISION: '56dev' };

self.console = self.console || {

	info: function () {},
	log: function () {},
	debug: function () {},
	warn: function () {},
	error: function () {}

};

self.Int32Array = self.Int32Array || Array;
self.Float32Array = self.Float32Array || Array;

// Shims for "startsWith", "endsWith", and "trim" for browsers where this is not yet implemented
// not sure we should have this, or at least not have it here

// http://stackoverflow.com/questions/646628/javascript-startswith
// http://stackoverflow.com/questions/498970/how-do-i-trim-a-string-in-javascript
// http://wiki.ecmascript.org/doku.php?id=harmony%3astring_extras

String.prototype.startsWith = String.prototype.startsWith || function ( str ) {

	return this.slice( 0, str.length ) === str;

};

String.prototype.endsWith = String.prototype.endsWith || function ( str ) {

	var t = String( str );
	var index = this.lastIndexOf( t );
	return ( -1 < index && index ) === (this.length - t.length);

};

String.prototype.trim = String.prototype.trim || function () {

	return this.replace( /^\s+|\s+$/g, '' );

};

// based on http://stackoverflow.com/a/12317051
THREE.extend = function ( target, other ) {

	target = target || {};

	for (var prop in other) {

		if ( typeof other[prop] === 'object' ) {

			target[prop] = THREE.extend( target[prop], other[prop] );

		} else {

			target[prop] = other[prop];

		}

	}

	return target;

};

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel

( function () {

	var lastTime = 0;
	var vendors = [ 'ms', 'moz', 'webkit', 'o' ];

	for ( var x = 0; x < vendors.length && !window.requestAnimationFrame; ++ x ) {

		window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ];
		window.cancelAnimationFrame = window[ vendors[ x ] + 'CancelAnimationFrame' ] || window[ vendors[ x ] + 'CancelRequestAnimationFrame' ];

	}

	if ( window.requestAnimationFrame === undefined ) {

		window.requestAnimationFrame = function ( callback, element ) {

			var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
			var id = window.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
			lastTime = currTime + timeToCall;
			return id;

		};

	}

	window.cancelAnimationFrame = window.cancelAnimationFrame || function ( id ) { window.clearTimeout( id ) };

}() );

// GL STATE CONSTANTS

THREE.CullFaceNone = 0;
THREE.CullFaceBack = 1;
THREE.CullFaceFront = 2;
THREE.CullFaceFrontBack = 3;

THREE.FrontFaceDirectionCW = 0;
THREE.FrontFaceDirectionCCW = 1;

// SHADOWING TYPES

THREE.BasicShadowMap = 0;
THREE.PCFShadowMap = 1;
THREE.PCFSoftShadowMap = 2;

// MATERIAL CONSTANTS

// side

THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;

// shading

THREE.NoShading = 0;
THREE.FlatShading = 1;
THREE.SmoothShading = 2;

// colors

THREE.NoColors = 0;
THREE.FaceColors = 1;
THREE.VertexColors = 2;

// blending modes

THREE.NoBlending = 0;
THREE.NormalBlending = 1;
THREE.AdditiveBlending = 2;
THREE.SubtractiveBlending = 3;
THREE.MultiplyBlending = 4;
THREE.CustomBlending = 5;

// custom blending equations
// (numbers start from 100 not to clash with other
//  mappings to OpenGL constants defined in Texture.js)

THREE.AddEquation = 100;
THREE.SubtractEquation = 101;
THREE.ReverseSubtractEquation = 102;

// custom blending destination factors

THREE.ZeroFactor = 200;
THREE.OneFactor = 201;
THREE.SrcColorFactor = 202;
THREE.OneMinusSrcColorFactor = 203;
THREE.SrcAlphaFactor = 204;
THREE.OneMinusSrcAlphaFactor = 205;
THREE.DstAlphaFactor = 206;
THREE.OneMinusDstAlphaFactor = 207;

// custom blending source factors

//THREE.ZeroFactor = 200;
//THREE.OneFactor = 201;
//THREE.SrcAlphaFactor = 204;
//THREE.OneMinusSrcAlphaFactor = 205;
//THREE.DstAlphaFactor = 206;
//THREE.OneMinusDstAlphaFactor = 207;
THREE.DstColorFactor = 208;
THREE.OneMinusDstColorFactor = 209;
THREE.SrcAlphaSaturateFactor = 210;

// TEXTURE CONSTANTS

THREE.MultiplyOperation = 0;
THREE.MixOperation = 1;
THREE.AddOperation = 2;

// Mapping modes

THREE.UVMapping = function () {};

THREE.CubeReflectionMapping = function () {};
THREE.CubeRefractionMapping = function () {};

THREE.SphericalReflectionMapping = function () {};
THREE.SphericalRefractionMapping = function () {};

// Wrapping modes

THREE.RepeatWrapping = 1000;
THREE.ClampToEdgeWrapping = 1001;
THREE.MirroredRepeatWrapping = 1002;

// Filters

THREE.NearestFilter = 1003;
THREE.NearestMipMapNearestFilter = 1004;
THREE.NearestMipMapLinearFilter = 1005;
THREE.LinearFilter = 1006;
THREE.LinearMipMapNearestFilter = 1007;
THREE.LinearMipMapLinearFilter = 1008;

// Data types

THREE.UnsignedByteType = 1009;
THREE.ByteType = 1010;
THREE.ShortType = 1011;
THREE.UnsignedShortType = 1012;
THREE.IntType = 1013;
THREE.UnsignedIntType = 1014;
THREE.FloatType = 1015;

// Pixel types

//THREE.UnsignedByteType = 1009;
THREE.UnsignedShort4444Type = 1016;
THREE.UnsignedShort5551Type = 1017;
THREE.UnsignedShort565Type = 1018;

// Pixel formats

THREE.AlphaFormat = 1019;
THREE.RGBFormat = 1020;
THREE.RGBAFormat = 1021;
THREE.LuminanceFormat = 1022;
THREE.LuminanceAlphaFormat = 1023;

// Compressed texture formats

THREE.RGB_S3TC_DXT1_Format = 2001;
THREE.RGBA_S3TC_DXT1_Format = 2002;
THREE.RGBA_S3TC_DXT3_Format = 2003;
THREE.RGBA_S3TC_DXT5_Format = 2004;

/*
// Potential future PVRTC compressed texture formats
THREE.RGB_PVRTC_4BPPV1_Format = 2100;
THREE.RGB_PVRTC_2BPPV1_Format = 2101;
THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
*/
<|endoftext|>


<commit_before>var Actions = {
  setActions: function(element, visible) {
    var actions = element.up('.task_list_container, .comment').down('.actions_menu');
    if (actions == null)
      return;
    if (visible)
      actions.show();
    else
      actions.hide();
  },

  setLoading: function(element, loading) {
    var actions = element.up('.task_list_container, .comment').down('.actions_menu');
    if (actions == null)
      return;
    if (loading)
    {
      actions.addClassName('loading');
      actions.down('span.loading').show();
      actions.down('span.actiondate').hide();
    }
    else
    {
      actions.removeClassName('loading');
      actions.down('span.loading').hide();
      actions.down('span.actiondate').show();
    }
  }
};

document.on('mouseover', '.actions_menu', function(e, actions_menu) {
  var comment = actions_menu.up('.comment')

  // My own comments: I can modify them, a later filter will ensure that only for 15 minutes
  if(comment.readAttribute('data-user') == my_user.id) {
    actions_menu.down('.edit').forceShow()
  }

  // Projects where I'm admin: I can destroy comments at any time
  var projects_i_admin = $H(my_projects).select( function(e){ return(e[1].role == 3) } ).collect( function(e) { return e[0] } )
  if(projects_i_admin.include(comment.readAttribute('data-project'))) {
    actions_menu.down('.edit').forceShow()
    actions_menu.down('.delete').forceShow()
  }

  // Disable editing comments 15 minutes after posting them
  var now = new Date()
  var timestamp = comment.readAttribute('data-editable-before'),
      editableBefore = new Date(parseInt(timestamp))
  if (now >= editableBefore) {
    link = actions_menu.down('a.edit')
    if(link) {
      var message = link.readAttribute('data-uneditable-message')
      link.replace(new Element('span').update(message).addClassName('edit'))
    }
  }
})
<commit_msg>Comment actions were affecting task lists<commit_after>var Actions = {
  setActions: function(element, visible) {
    var actions = element.up('.task_list_container, .comment').down('.actions_menu');
    if (actions == null)
      return;
    if (visible)
      actions.show();
    else
      actions.hide();
  },

  setLoading: function(element, loading) {
    var actions = element.up('.task_list_container, .comment').down('.actions_menu');
    if (actions == null)
      return;
    if (loading)
    {
      actions.addClassName('loading');
      actions.down('span.loading').show();
      actions.down('span.actiondate').hide();
    }
    else
    {
      actions.removeClassName('loading');
      actions.down('span.loading').hide();
      actions.down('span.actiondate').show();
    }
  }
};

document.on('mouseover', '.comment .actions_menu', function(e, actions_menu) {
  var comment = actions_menu.up('.comment')

  // My own comments: I can modify them, a later filter will ensure that only for 15 minutes
  if(comment.readAttribute('data-user') == my_user.id) {
    actions_menu.down('.edit').forceShow()
  }

  // Projects where I'm admin: I can destroy comments at any time
  var projects_i_admin = $H(my_projects).select( function(e){ return(e[1].role == 3) } ).collect( function(e) { return e[0] } )
  if(projects_i_admin.include(comment.readAttribute('data-project'))) {
    actions_menu.down('.edit').forceShow()
    actions_menu.down('.delete').forceShow()
  }

  // Disable editing comments 15 minutes after posting them
  var now = new Date()
  var timestamp = comment.readAttribute('data-editable-before'),
      editableBefore = new Date(parseInt(timestamp))
  if (now >= editableBefore) {
    link = actions_menu.down('a.edit')
    if(link) {
      var message = link.readAttribute('data-uneditable-message')
      link.replace(new Element('span').update(message).addClassName('edit'))
    }
  }
})
<|endoftext|>


<commit_before>import id from '../General/id';
import isType from '../General/isType';
import { merge } from '../Obj';
import { curry, compose, deny } from '../Func';

//:: (Number, Number?, Number?) -> [Number]
export const range = (to, from = 1, step = 1) => {
  const result = [];
  for (let i = from; i <= to; i += step) {
    result.push(i);
  }
  return result;
};

//:: (a -> b) -> [a] -> void
export const each = curry((fn, xs) => xs.forEach(fn));

//:: (a -> b) -> [a] -> [b]
export const map = curry((fn, xs) => xs.map(fn));

//:: (a -> Boolean) -> [a] -> [a]
export const filter = curry((fn, xs) => xs.filter(fn));

//:: [a] -> [a]
export const compact = filter(id);

//:: (a -> Boolean) -> [a] -> [a]
export const reject = curry((fn, xs) => xs.filter(deny(fn)));

//:: ((a, b) -> a) -> [b] -> a
export const reduce = curry((fn, initial, xs) => xs.reduce(fn, initial));

//:: (a -> Boolean) -> [a] -> [[a] [a]]
export const partition = curry((fn, xs) => {
  let passed = [];
  let failed = [];
  xs.forEach((x) => (fn(x) ? passed : failed ).push(x));
  return [passed, failed];
});

//:: (a -> Boolean) -> [a] -> a
export const find = curry((fn, [x, ...xs]) => x
  ? fn(x) ? x : find(fn, xs)
  : undefined
);

//:: [a] -> a
export const head = (xs) => xs[0];

//:: [a] -> [a]
export const tail = ([x, ...xs]) => xs;

//:: [a] -> a
export const first = head;

//:: [a] -> a
export const last = (xs) => xs.slice(-1)[0];

//:: [a] -> [a]
export const initial = (xs) => !xs.length ? undefined : xs.slice(0, -1);

//:: [a] -> Boolean
export const empty = (xs) => !xs.length;

//:: [a] -> [a]
export const reverse = (xs) => xs.concat().reverse();

//:: (a -> b) -> [a] -> [a]
export const uniqueBy = curry((f, xs) => {
  const reducer = (acc, x) => merge(acc, { [`K_${x}`]: x });
  return Object.values(xs.map(f).reduce(reducer, {}));
});

//:: [a] -> [a]
export const unique = (xs) => uniqueBy(id, xs);

//:: (b -> a -> b) -> b -> [a] -> b
export const foldl = curry((fn, acc, xs) => xs.reduce(fn, acc));

//:: (a -> a -> a) -> [a] -> a
export const foldl1 = curry((fn, xs) => xs.reduce(fn, 0));

//:: (b -> a -> b) -> b -> [a] -> b
export const foldr = curry((fn, acc, xs) => {
  for (let i = xs.length - 1; i >= 0; i--) {
    acc = fn(xs[i], acc);
  }
  return acc;
});

//:: (a -> a -> a) -> [a] -> a
export const foldr1 = curry((fn, xs) => foldr(fn, 0, xs));

//:: (a -> [b]) -> [a] -> [b]
export const unfoldr = curry((fn, b) => {
  let result = [];
  let x = b;
  let that;
  while ((that = fn(b))) {
    result.push(that[0]);
    x = that[1];
  }
  return result;
});

//:: (a -> b, b -> c) -> [a] -> c
export const mapReduce = curry((mapper, reducer, xs) => xs.map(mapper).reduce(reducer));

//:: [[a]] -> [a]
export const concat = (xss) => [].concat.apply([], xss);

//:: (a -> [b]) -> [a] -> [b]
export const concatMap = curry((fn, xs) => concat(map(fn, xs)));

//:: List -> List
export const flatten = concatMap((xs) => isType('Array', xs) ? flatten(xs) : xs);

//:: ([a], [a], ...) -> [a]
export const difference = (xs, ...yss) =>
  xs.filter((x) => !yss.some(find((y) => y === x)));

//:: ([a], [a], ...) -> [a]
export const intersection = (xs, ...yss) =>
  xs.filter((x) => yss.some(find((y) => y === x)));

//:: ([a], [a], ...) -> [a]
export const union = (xs, ...yss) => unique(xs.concat(flatten(yss)));

//:: (a -> b) -> [a] -> { b: Number }
export const countBy = curry((fn, xs) =>
  xs.reduce((acc, x) => {
    let key = fn(x);
    acc[key] = acc[key] ? acc[key] + 1 : 1;
    return acc;
  }, {}));

//:: (a -> b) -> [a] -> { b: [b] }
export const groupBy = curry((fn, xs) =>
  xs.reduce((acc, x) => {
    let key = fn(x);
    acc[key] = acc[key] ? acc[key].concat([x]) : [x];
    return acc;
  }, {}));

//:: [a] -> Boolean
export const and = reduce((acc, x) => acc && !!x, true);

//:: [a] -> Boolean
export const or = reduce((acc, x) => acc || !!x, false);

//:: (a -> Boolean) -> [a] -> Boolean
export const any = curry((fn, xs) => xs.some(fn));

//:: (a -> Boolean) -> [a] -> Boolean
export const all = deny(any);

// aliases

export const fold = foldl;

export const fold1 = foldl1;
<commit_msg>add List.sort<commit_after>import id from '../General/id';
import isType from '../General/isType';
import { merge } from '../Obj';
import { curry, compose, deny } from '../Func';

//:: (Number, Number?, Number?) -> [Number]
export const range = (to, from = 1, step = 1) => {
  const result = [];
  for (let i = from; i <= to; i += step) {
    result.push(i);
  }
  return result;
};

//:: (a -> b) -> [a] -> void
export const each = curry((fn, xs) => xs.forEach(fn));

//:: (a -> b) -> [a] -> [b]
export const map = curry((fn, xs) => xs.map(fn));

//:: (a -> Boolean) -> [a] -> [a]
export const filter = curry((fn, xs) => xs.filter(fn));

//:: [a] -> [a]
export const compact = filter(id);

//:: (a -> Boolean) -> [a] -> [a]
export const reject = curry((fn, xs) => xs.filter(deny(fn)));

//:: ((a, b) -> a) -> [b] -> a
export const reduce = curry((fn, initial, xs) => xs.reduce(fn, initial));

//:: (a -> Boolean) -> [a] -> [[a] [a]]
export const partition = curry((fn, xs) => {
  let passed = [];
  let failed = [];
  xs.forEach((x) => (fn(x) ? passed : failed ).push(x));
  return [passed, failed];
});

//:: (a -> Boolean) -> [a] -> a
export const find = curry((fn, [x, ...xs]) => x
  ? fn(x) ? x : find(fn, xs)
  : undefined
);

//:: [a] -> a
export const head = (xs) => xs[0];

//:: [a] -> [a]
export const tail = ([x, ...xs]) => xs;

//:: [a] -> a
export const first = head;

//:: [a] -> a
export const last = (xs) => xs.slice(-1)[0];

//:: [a] -> [a]
export const initial = (xs) => !xs.length ? undefined : xs.slice(0, -1);

//:: [a] -> Boolean
export const empty = (xs) => !xs.length;

//:: [a] -> [a]
export const reverse = (xs) => xs.concat().reverse();

//:: (a -> b) -> [a] -> [a]
export const uniqueBy = curry((f, xs) => {
  const reducer = (acc, x) => merge(acc, { [`K_${x}`]: x });
  return Object.values(xs.map(f).reduce(reducer, {}));
});

//:: [a] -> [a]
export const unique = (xs) => uniqueBy(id, xs);

//:: (b -> a -> b) -> b -> [a] -> b
export const foldl = curry((fn, acc, xs) => xs.reduce(fn, acc));

//:: (a -> a -> a) -> [a] -> a
export const foldl1 = curry((fn, xs) => xs.reduce(fn, 0));

//:: (b -> a -> b) -> b -> [a] -> b
export const foldr = curry((fn, acc, xs) => {
  for (let i = xs.length - 1; i >= 0; i--) {
    acc = fn(xs[i], acc);
  }
  return acc;
});

//:: (a -> a -> a) -> [a] -> a
export const foldr1 = curry((fn, xs) => foldr(fn, 0, xs));

//:: (a -> [b]) -> [a] -> [b]
export const unfoldr = curry((fn, b) => {
  let result = [];
  let x = b;
  let that;
  while ((that = fn(b))) {
    result.push(that[0]);
    x = that[1];
  }
  return result;
});

//:: (a -> b, b -> c) -> [a] -> c
export const mapReduce = curry((mapper, reducer, xs) => xs.map(mapper).reduce(reducer));

//:: [[a]] -> [a]
export const concat = (xss) => [].concat.apply([], xss);

//:: (a -> [b]) -> [a] -> [b]
export const concatMap = curry((fn, xs) => concat(map(fn, xs)));

//:: List -> List
export const flatten = concatMap((xs) => isType('Array', xs) ? flatten(xs) : xs);

//:: ([a], [a], ...) -> [a]
export const difference = (xs, ...yss) =>
  xs.filter((x) => !yss.some(find((y) => y === x)));

//:: ([a], [a], ...) -> [a]
export const intersection = (xs, ...yss) =>
  xs.filter((x) => yss.some(find((y) => y === x)));

//:: ([a], [a], ...) -> [a]
export const union = (xs, ...yss) => unique(xs.concat(flatten(yss)));

//:: (a -> b) -> [a] -> { b: Number }
export const countBy = curry((fn, xs) =>
  xs.reduce((acc, x) => {
    let key = fn(x);
    acc[key] = acc[key] ? acc[key] + 1 : 1;
    return acc;
  }, {}));

//:: (a -> b) -> [a] -> { b: [b] }
export const groupBy = curry((fn, xs) =>
  xs.reduce((acc, x) => {
    let key = fn(x);
    acc[key] = acc[key] ? acc[key].concat([x]) : [x];
    return acc;
  }, {}));

//:: [a] -> Boolean
export const and = reduce((acc, x) => acc && !!x, true);

//:: [a] -> Boolean
export const or = reduce((acc, x) => acc || !!x, false);

//:: (a -> Boolean) -> [a] -> Boolean
export const any = curry((fn, xs) => xs.some(fn));

//:: (a -> Boolean) -> [a] -> Boolean
export const all = deny(any);

//:: List -> List
export const sort = (xs) => xs.sort();

// aliases

export const fold = foldl;

export const fold1 = foldl1;
<|endoftext|>


<commit_before>const request = require('request');
const express = require('express');
const paypal = express();

// receive IPN
paypal.post('/', (req, res) => {
  console.log(req, res);
  res.status(200).send();

  replyIPN(req, res).then(() => {
    handlePayment(req);
  });
});

function replyIPN(req, res) {
  console.log('reply ipn');
  return Promise.resolved();
}

/**
 * TODO
 */
function handlePayment(req) {
  console.log('handlePayment');
}

module.exports = paypal;
<commit_msg>chore: add ipn reply<commit_after>const request = require('request');
const express = require('express');
const paypal = express();

// receive IPN
paypal.post('/', (req, res) => {
  console.log(req, res);
  res.status(200).send();

  replyIPN(req, res).then(() => {
    handlePayment(req);
  });
});

function replyIPN(req) {
  console.log('reply ipn');
  console.log(req.get('host'), req.get('body'));

  return new Promise(resolve => {
    request.post({ url: req.get('host'), formData: req.get('body') }, (error, response, body) => {
      console.log(error, response, body);
      resolve();
    });
  });
}

/**
 * TODO
 */
function handlePayment(req) {
  console.log('handlePayment');
}

module.exports = paypal;
<|endoftext|>