<commit_before>// We have to remove node_modules/react to avoid having multiple copies loaded.
// eslint-disable-next-line import/no-unresolved
import React, { PropTypes } from 'react';
import Paneset from '@folio/stripes-components/lib/Paneset';
import Pane from '@folio/stripes-components/lib/Pane';
import PaneMenu from '@folio/stripes-components/lib/PaneMenu';
import { Row, Col } from 'react-bootstrap';
import Button from '@folio/stripes-components/lib/Button';
import TextField from '@folio/stripes-components/lib/TextField';
import Select from '@folio/stripes-components/lib/Select';
import RadioButtonGroup from '@folio/stripes-components/lib/RadioButtonGroup';
import RadioButton from '@folio/stripes-components/lib/RadioButton';
import fetch from 'isomorphic-fetch';

import { Field, reduxForm } from 'redux-form';

const sys = require('stripes-loader'); // eslint-disable-line
const okapiUrl = sys.okapi.url;
const tenant = sys.okapi.tenant;
let okapiToken = '';

function validate(values) {
  const errors = {};

  if (!values.personal || !values.personal.last_name) {
    errors.personal = { last_name: 'Please fill this in to continue' };
  }

  if (!values.username) {
    errors.username = 'Please fill this in to continue';
  }
  return errors;
}

function checkUniqueUserID(username) {
  return fetch(`${okapiUrl}/users?query=(username="${username}")`,
    { headers: Object.assign({}, {
      'X-Okapi-Tenant': tenant,
      'X-Okapi-Token': okapiToken,
      'Content-Type': 'application/json' }),
    },
  );
}

function asyncValidate(values, dispatch, props, blurredField) {
  if (blurredField === 'username' && values.username !== props.initialValues.username) {
    return new Promise((resolve, reject) => {
      // TODO: Should use stripes-connect (dispatching an action and update state)
      checkUniqueUserID(values.username).then((response) => {
        if (response.status >= 400) {
          console.log('Error fetching user');
        } else {
          response.json().then((json) => {
            if (json.total_records > 0)
              reject({ username: 'This User ID has already been taken' });
            else
              resolve();
          });
        }
      });
    });
  }
  return new Promise(resolve => resolve());
}

class UserForm extends React.Component {

  static contextTypes = {
    store: PropTypes.object,
  };

  static propTypes = {
    onClose: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
    newUser: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
    handleSubmit: PropTypes.func.isRequired,
    reset: PropTypes.func,
    pristine: PropTypes.bool,
    submitting: PropTypes.bool,
    onCancel: PropTypes.func,
    initialValues: PropTypes.object,
  };

  constructor(props, context) {
    super(props);
    okapiToken = context.store.getState().okapi.token;
  }

  render() {
    const {
      handleSubmit,
      reset,  // eslint-disable-line no-unused-vars
      pristine,
      submitting,
      onCancel,
      initialValues,
    } = this.props;

    /* Menues for Add User workflow */
    const addUserFirstMenu = <PaneMenu><button onClick={onCancel} title="close" aria-label="Close New User Dialog">×</button></PaneMenu>;
    const addUserLastMenu = <PaneMenu><Button type="submit" title="Create New User" disabled={pristine || submitting} onClick={handleSubmit}>Create User</Button></PaneMenu>;
    const editUserLastMenu = <PaneMenu><Button type="submit" title="Update User" disabled={pristine || submitting} onClick={handleSubmit}>Update User</Button></PaneMenu>;
    const patronGroupOptions = initialValues.available_patron_groups ? initialValues.available_patron_groups.map((g) => { return { label: g.group, value: g._id, selected: initialValues.patron_group === g._id }; }) : [];

    return (
      <form>
        <Paneset>
          <Pane defaultWidth="100%" firstMenu={addUserFirstMenu} lastMenu={initialValues.username ? editUserLastMenu : addUserLastMenu} paneTitle={initialValues.username ? 'Edit User' : 'New User'}>
            <Row>
              <Col sm={5} smOffset={1}>

User Record

                <Field label="User ID" name="username" id="adduser_username" component={TextField} required fullWidth />
                {!initialValues.id ? <Field label="Password" name="creds.password" id="pw" component={TextField} required fullWidth /> : null}
                <Field label="Status" name="active" component={RadioButtonGroup}>
                  <RadioButton label="Active" id="useractiveYesRB" value="true" inline />
                  <RadioButton label="Inactive" id="useractiveNoRB" value="false" inline />
                </Field>
                <fieldset>
                  <legend>Personal Info</legend>
                  <Field label="First Name" name="personal.first_name" id="adduser_firstname" component={TextField} required fullWidth />
                  <Field label="Last Name" name="personal.last_name" id="adduser_lastname" component={TextField} fullWidth />
                  <Field label="Email" name="personal.email" id="adduser_email" component={TextField} required fullWidth />
                </fieldset>
                {/* <Field
                  label="Type"
                  name="type"
                  id="adduser_type"
                  component={Select}
                  fullWidth
                  dataOptions={[{ label: 'Select user type', value: '' },
                                { label: 'Patron', value: 'Patron', selected: 'selected' }]}
                /> */}
                <Field
                  label="Patron Group"
                  name="patron_group"
                  id="adduser_group"
                  component={Select}
                  fullWidth
                  dataOptions={[{ label: 'Select patron group', value: null }, ...patronGroupOptions]}
                />
              </Col>
            </Row>
          </Pane>

        </Paneset>
      </form>
    );
  }
}

export default reduxForm({
  form: 'userForm',
  validate,
  asyncValidate,
  asyncBlurFields: ['username'],
})(UserForm);
<commit_msg>Better handling of empty patron-group list<commit_after>// We have to remove node_modules/react to avoid having multiple copies loaded.
// eslint-disable-next-line import/no-unresolved
import React, { PropTypes } from 'react';
import Paneset from '@folio/stripes-components/lib/Paneset';
import Pane from '@folio/stripes-components/lib/Pane';
import PaneMenu from '@folio/stripes-components/lib/PaneMenu';
import { Row, Col } from 'react-bootstrap';
import Button from '@folio/stripes-components/lib/Button';
import TextField from '@folio/stripes-components/lib/TextField';
import Select from '@folio/stripes-components/lib/Select';
import RadioButtonGroup from '@folio/stripes-components/lib/RadioButtonGroup';
import RadioButton from '@folio/stripes-components/lib/RadioButton';
import fetch from 'isomorphic-fetch';

import { Field, reduxForm } from 'redux-form';

const sys = require('stripes-loader'); // eslint-disable-line
const okapiUrl = sys.okapi.url;
const tenant = sys.okapi.tenant;
let okapiToken = '';

function validate(values) {
  const errors = {};

  if (!values.personal || !values.personal.last_name) {
    errors.personal = { last_name: 'Please fill this in to continue' };
  }

  if (!values.username) {
    errors.username = 'Please fill this in to continue';
  }
  return errors;
}

function checkUniqueUserID(username) {
  return fetch(`${okapiUrl}/users?query=(username="${username}")`,
    { headers: Object.assign({}, {
      'X-Okapi-Tenant': tenant,
      'X-Okapi-Token': okapiToken,
      'Content-Type': 'application/json' }),
    },
  );
}

function asyncValidate(values, dispatch, props, blurredField) {
  if (blurredField === 'username' && values.username !== props.initialValues.username) {
    return new Promise((resolve, reject) => {
      // TODO: Should use stripes-connect (dispatching an action and update state)
      checkUniqueUserID(values.username).then((response) => {
        if (response.status >= 400) {
          console.log('Error fetching user');
        } else {
          response.json().then((json) => {
            if (json.total_records > 0)
              reject({ username: 'This User ID has already been taken' });
            else
              resolve();
          });
        }
      });
    });
  }
  return new Promise(resolve => resolve());
}

class UserForm extends React.Component {

  static contextTypes = {
    store: PropTypes.object,
  };

  static propTypes = {
    onClose: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
    newUser: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
    handleSubmit: PropTypes.func.isRequired,
    reset: PropTypes.func,
    pristine: PropTypes.bool,
    submitting: PropTypes.bool,
    onCancel: PropTypes.func,
    initialValues: PropTypes.object,
  };

  constructor(props, context) {
    super(props);
    okapiToken = context.store.getState().okapi.token;
  }

  render() {
    const {
      handleSubmit,
      reset,  // eslint-disable-line no-unused-vars
      pristine,
      submitting,
      onCancel,
      initialValues,
    } = this.props;

    /* Menues for Add User workflow */
    const addUserFirstMenu = <PaneMenu><button onClick={onCancel} title="close" aria-label="Close New User Dialog">×</button></PaneMenu>;
    const addUserLastMenu = <PaneMenu><Button type="submit" title="Create New User" disabled={pristine || submitting} onClick={handleSubmit}>Create User</Button></PaneMenu>;
    const editUserLastMenu = <PaneMenu><Button type="submit" title="Update User" disabled={pristine || submitting} onClick={handleSubmit}>Update User</Button></PaneMenu>;
    const patronGroupOptions = (initialValues.available_patron_groups || []).map((g) => { return { label: g.group, value: g._id, selected: initialValues.patron_group === g._id }; });

    return (
      <form>
        <Paneset>
          <Pane defaultWidth="100%" firstMenu={addUserFirstMenu} lastMenu={initialValues.username ? editUserLastMenu : addUserLastMenu} paneTitle={initialValues.username ? 'Edit User' : 'New User'}>
            <Row>
              <Col sm={5} smOffset={1}>

User Record

                <Field label="User ID" name="username" id="adduser_username" component={TextField} required fullWidth />
                {!initialValues.id ? <Field label="Password" name="creds.password" id="pw" component={TextField} required fullWidth /> : null}
                <Field label="Status" name="active" component={RadioButtonGroup}>
                  <RadioButton label="Active" id="useractiveYesRB" value="true" inline />
                  <RadioButton label="Inactive" id="useractiveNoRB" value="false" inline />
                </Field>
                <fieldset>
                  <legend>Personal Info</legend>
                  <Field label="First Name" name="personal.first_name" id="adduser_firstname" component={TextField} required fullWidth />
                  <Field label="Last Name" name="personal.last_name" id="adduser_lastname" component={TextField} fullWidth />
                  <Field label="Email" name="personal.email" id="adduser_email" component={TextField} required fullWidth />
                </fieldset>
                {/* <Field
                  label="Type"
                  name="type"
                  id="adduser_type"
                  component={Select}
                  fullWidth
                  dataOptions={[{ label: 'Select user type', value: '' },
                                { label: 'Patron', value: 'Patron', selected: 'selected' }]}
                /> */}
                <Field
                  label="Patron Group"
                  name="patron_group"
                  id="adduser_group"
                  component={Select}
                  fullWidth
                  dataOptions={[{ label: 'Select patron group', value: null }, ...patronGroupOptions]}
                />
              </Col>
            </Row>
          </Pane>

        </Paneset>
      </form>
    );
  }
}

export default reduxForm({
  form: 'userForm',
  validate,
  asyncValidate,
  asyncBlurFields: ['username'],
})(UserForm);
<|endoftext|>


<commit_before>/**
 * Created by lcw on 16/5/31.
 */

//订单详情页->支付页 显示价格total_fee改为real_price
defineClass('OrderInfoViewController', [ ], { pushToPayPageWithOrderId_orderNum:function(orderId,orderNum) {

            var pay = require('PayActionViewController').alloc().init();
            //setter
            pay.setOrderId(orderId);
            pay.setOrderNum(orderNum);
            require('OrderModel')
            var model = self.valueForKey("_orderModel");
            var real_price = model.valueForKey("real_price");
            pay.setSumPrice(real_price);
            var style = model.valueForKey("pay_type");
            pay.setPayStyle(style);

            pay.setLastViewController(self.lastViewController());

            self.navigationController().pushViewController_animated(pay,true);

            } })

//订单列表-> 显示价格total_fee改为real_price

defineClass('OrderViewController', [ ], { clickToAction:function(sender) {

            var actionTyp = sender.valueForKey("actionType");

            if(actionTyp == 1) {

            var mode = sender.valueForKey("aModel");
            var orderId = mode.valueForKey("order_id");
            var orderNu = mode.valueForKey("order_no");
            var sumPri = mode.valueForKey("real_price");
            var paySty = mode.valueForKey("pay_type");

             self.pushToPayPageWithOrderId_orderNum_sumPrice_payStyle(orderId,orderNu,sumPri,paySty);

            }else
            {
             self.ORIGclickToAction(sender);
            }

            }})

defineClass('GPushView', [ ], {

            qingkongshaixuanBtnClicked: function() {

            console.log("xxxxx");
            require('NSMutableDictionary');
            self.setSelectDic(null);
            self.setSelectDic(NSMutableDictionary.dictionaryWithCapacity(1));
            self.tf_low().setText(null);
            self.tf_high().setText(null);

            self.tab1().reloadData();
            self.tab2().reloadData();
            self.tab3().reloadData();
            self.tab4().reloadData();

            }

            })

<commit_msg>lcw<commit_after>/**
 * Created by lcw on 16/5/31.
 */

//订单详情页->支付页 显示价格total_fee改为real_price
defineClass('OrderInfoViewController', [ ], { pushToPayPageWithOrderId_orderNum:function(orderId,orderNum) {

            var pay = require('PayActionViewController').alloc().init();
            //setter
            pay.setOrderId(orderId);
            pay.setOrderNum(orderNum);
            require('OrderModel')
            var model = self.valueForKey("_orderModel");
            var real_price = model.valueForKey("real_price");
            pay.setSumPrice(real_price);
            var style = model.valueForKey("pay_type");
            pay.setPayStyle(style);

            pay.setLastViewController(self.lastViewController());

            self.navigationController().pushViewController_animated(pay,true);

            } })

//订单列表-> 显示价格total_fee改为real_price

defineClass('OrderViewController', [ ], { clickToAction:function(sender) {

            var actionTyp = sender.valueForKey("actionType");

            if(actionTyp == 1) {

            var mode = sender.valueForKey("aModel");
            var orderId = mode.valueForKey("order_id");
            var orderNu = mode.valueForKey("order_no");
            var sumPri = mode.valueForKey("real_price");
            var paySty = mode.valueForKey("pay_type");

             self.pushToPayPageWithOrderId_orderNum_sumPrice_payStyle(orderId,orderNu,sumPri,paySty);

            }else
            {
             self.ORIGclickToAction(sender);
            }

            }})

defineClass('GPushView', [ ], {

            qingkongshaixuanBtnClicked: function() {

            console.log("xxxxx");
            require('NSMutableDictionary');
            self.setSelectDic(null);
            self.setSelectDic(NSMutableDictionary.dictionaryWithCapacity(1));
            self.tf_low().setText(null);
            self.tf_high().setText(null);

            self.tab1().reloadData();
            self.tab2().reloadData();
            self.tab3().reloadData();
            self.tab4().reloadData();

            }

            })

<|endoftext|>


<commit_before>//**************************************************
//**Work inspired by multiple jquery swipe plugins**
//**Done by Yohai Ararat ***************************
//**************************************************

import $ from 'jquery';

var Touch = {};

var startPosX,
    startPosY,
    startTime,
    elapsedTime,
    startEvent,
    isMoving = false,
    didMoved = false;

function onTouchEnd(e) {
  this.removeEventListener('touchmove', onTouchMove);
  this.removeEventListener('touchend', onTouchEnd);

  // If the touch did not moved, consider it as a "tap"
  if (!didMoved) {
    var tapEvent = $.Event('tap', startEvent || e);
    $(this).trigger(tapEvent);
  }

  startEvent = null;
  isMoving = false;
  didMoved = false;
}

function onTouchMove(e) {
  if ($.spotSwipe.preventDefault) { e.preventDefault(); }

  if(isMoving) {
    var x = e.touches[0].pageX;
    var y = e.touches[0].pageY;
    var dx = startPosX - x;
    var dy = startPosY - y;
    var dir;
    didMoved = true;
    elapsedTime = new Date().getTime() - startTime;
    if(Math.abs(dx) >= $.spotSwipe.moveThreshold && elapsedTime <= $.spotSwipe.timeThreshold) {
      dir = dx > 0 ? 'left' : 'right';
    }
    // else if(Math.abs(dy) >= $.spotSwipe.moveThreshold && elapsedTime <= $.spotSwipe.timeThreshold) {
    //   dir = dy > 0 ? 'down' : 'up';
    // }
    if(dir) {
      e.preventDefault();
      onTouchEnd.apply(this, arguments);
      $(this)
        .trigger($.Event('swipe', e), dir)
        .trigger($.Event(`swipe${dir}`, e));
    }
  }

}

function onTouchStart(e) {

  if (e.touches.length == 1) {
    startPosX = e.touches[0].pageX;
    startPosY = e.touches[0].pageY;
    startEvent = e;
    isMoving = true;
    didMoved = false;
    startTime = new Date().getTime();
    this.addEventListener('touchmove', onTouchMove, false);
    this.addEventListener('touchend', onTouchEnd, false);
  }
}

function init() {
  this.addEventListener && this.addEventListener('touchstart', onTouchStart, false);
}

function teardown() {
  this.removeEventListener('touchstart', onTouchStart);
}

class SpotSwipe {
  constructor($) {
    this.version = '1.0.0';
    this.enabled = 'ontouchstart' in document.documentElement;
    this.preventDefault = false;
    this.moveThreshold = 75;
    this.timeThreshold = 200;
    this.$ = $;
    this._init();
  }

  _init() {
    var $ = this.$;
    $.event.special.swipe = { setup: init };
    $.event.special.tap = { setup: init };

    $.each(['left', 'up', 'down', 'right'], function () {
      $.event.special[`swipe${this}`] = { setup: function(){
        $(this).on('swipe', $.noop);
      } };
    });
  }
}

/****************************************************
 * As far as I can tell, both setupSpotSwipe and    *
 * setupTouchHandler should be idempotent,          *
 * because they directly replace functions &        *
 * values, and do not add event handlers directly.  *
 ****************************************************/

Touch.setupSpotSwipe = function($) {
  $.spotSwipe = new SpotSwipe($);
};

/****************************************************
 * Method for adding pseudo drag events to elements *
 ***************************************************/
Touch.setupTouchHandler = function($) {
  $.fn.addTouch = function(){
    this.each(function(i,el){
      $(el).bind('touchstart touchmove touchend touchcancel', function(event)  {
        //we pass the original event object because the jQuery event
        //object is normalized to w3c specs and does not provide the TouchList
        handleTouch(event);
      });
    });

    var handleTouch = function(event){
      var touches = event.changedTouches,
          first = touches[0],
          eventTypes = {
            touchstart: 'mousedown',
            touchmove: 'mousemove',
            touchend: 'mouseup'
          },
          type = eventTypes[event.type],
          simulatedEvent
        ;

      if('MouseEvent' in window && typeof window.MouseEvent === 'function') {
        simulatedEvent = new window.MouseEvent(type, {
          'bubbles': true,
          'cancelable': true,
          'screenX': first.screenX,
          'screenY': first.screenY,
          'clientX': first.clientX,
          'clientY': first.clientY
        });
      } else {
        simulatedEvent = document.createEvent('MouseEvent');
        simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
      }
      first.target.dispatchEvent(simulatedEvent);
    };
  };
};

Touch.init = function ($) {

  if(typeof($.spotSwipe) === 'undefined') {
    Touch.setupSpotSwipe($);
    Touch.setupTouchHandler($);
  }
};

export {Touch};
<commit_msg>docs: fix various typo in Touch methods and Reveal tests (v6.5)<commit_after>//**************************************************
//**Work inspired by multiple jquery swipe plugins**
//**Done by Yohai Ararat ***************************
//**************************************************

import $ from 'jquery';

var Touch = {};

var startPosX,
    startPosY,
    startTime,
    elapsedTime,
    startEvent,
    isMoving = false,
    didMoved = false;

function onTouchEnd(e) {
  this.removeEventListener('touchmove', onTouchMove);
  this.removeEventListener('touchend', onTouchEnd);

  // If the touch did not move, consider it as a "tap"
  if (!didMoved) {
    var tapEvent = $.Event('tap', startEvent || e);
    $(this).trigger(tapEvent);
  }

  startEvent = null;
  isMoving = false;
  didMoved = false;
}

function onTouchMove(e) {
  if ($.spotSwipe.preventDefault) { e.preventDefault(); }

  if(isMoving) {
    var x = e.touches[0].pageX;
    var y = e.touches[0].pageY;
    var dx = startPosX - x;
    var dy = startPosY - y;
    var dir;
    didMoved = true;
    elapsedTime = new Date().getTime() - startTime;
    if(Math.abs(dx) >= $.spotSwipe.moveThreshold && elapsedTime <= $.spotSwipe.timeThreshold) {
      dir = dx > 0 ? 'left' : 'right';
    }
    // else if(Math.abs(dy) >= $.spotSwipe.moveThreshold && elapsedTime <= $.spotSwipe.timeThreshold) {
    //   dir = dy > 0 ? 'down' : 'up';
    // }
    if(dir) {
      e.preventDefault();
      onTouchEnd.apply(this, arguments);
      $(this)
        .trigger($.Event('swipe', e), dir)
        .trigger($.Event(`swipe${dir}`, e));
    }
  }

}

function onTouchStart(e) {

  if (e.touches.length == 1) {
    startPosX = e.touches[0].pageX;
    startPosY = e.touches[0].pageY;
    startEvent = e;
    isMoving = true;
    didMoved = false;
    startTime = new Date().getTime();
    this.addEventListener('touchmove', onTouchMove, false);
    this.addEventListener('touchend', onTouchEnd, false);
  }
}

function init() {
  this.addEventListener && this.addEventListener('touchstart', onTouchStart, false);
}

function teardown() {
  this.removeEventListener('touchstart', onTouchStart);
}

class SpotSwipe {
  constructor($) {
    this.version = '1.0.0';
    this.enabled = 'ontouchstart' in document.documentElement;
    this.preventDefault = false;
    this.moveThreshold = 75;
    this.timeThreshold = 200;
    this.$ = $;
    this._init();
  }

  _init() {
    var $ = this.$;
    $.event.special.swipe = { setup: init };
    $.event.special.tap = { setup: init };

    $.each(['left', 'up', 'down', 'right'], function () {
      $.event.special[`swipe${this}`] = { setup: function(){
        $(this).on('swipe', $.noop);
      } };
    });
  }
}

/****************************************************
 * As far as I can tell, both setupSpotSwipe and    *
 * setupTouchHandler should be idempotent,          *
 * because they directly replace functions &        *
 * values, and do not add event handlers directly.  *
 ****************************************************/

Touch.setupSpotSwipe = function($) {
  $.spotSwipe = new SpotSwipe($);
};

/****************************************************
 * Method for adding pseudo drag events to elements *
 ***************************************************/
Touch.setupTouchHandler = function($) {
  $.fn.addTouch = function(){
    this.each(function(i,el){
      $(el).bind('touchstart touchmove touchend touchcancel', function(event)  {
        //we pass the original event object because the jQuery event
        //object is normalized to w3c specs and does not provide the TouchList
        handleTouch(event);
      });
    });

    var handleTouch = function(event){
      var touches = event.changedTouches,
          first = touches[0],
          eventTypes = {
            touchstart: 'mousedown',
            touchmove: 'mousemove',
            touchend: 'mouseup'
          },
          type = eventTypes[event.type],
          simulatedEvent
        ;

      if('MouseEvent' in window && typeof window.MouseEvent === 'function') {
        simulatedEvent = new window.MouseEvent(type, {
          'bubbles': true,
          'cancelable': true,
          'screenX': first.screenX,
          'screenY': first.screenY,
          'clientX': first.clientX,
          'clientY': first.clientY
        });
      } else {
        simulatedEvent = document.createEvent('MouseEvent');
        simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
      }
      first.target.dispatchEvent(simulatedEvent);
    };
  };
};

Touch.init = function ($) {

  if(typeof($.spotSwipe) === 'undefined') {
    Touch.setupSpotSwipe($);
    Touch.setupTouchHandler($);
  }
};

export {Touch};
<|endoftext|>


<commit_before>const test = require('tap').test
const Promise = require('bluebird')
const datetime = require('../src/datetime')
const is = require('../src/is')
const enums = require('../src/enums')
const tError = require('./test-error')
const tOpts = require('./test-options')
const tData = require('./test-options').tData
const queueProcess = require('../src/queue-process')
const dbReview = require('../src/db-review')
const Queue = require('../src/queue')
const eventHandlers = require('./test-event-handlers')
const testName = 'queue-process'

module.exports = function () {
  return new Promise((resolve, reject) => {
    test(testName, { timeout: 200000 }, (t) => {
      t.plan(409)

      // ---------- Test Setup ----------
      const q = new Queue(tOpts.cxn(), tOpts.default())
      let qGlobalCancel

      let jobs
      let jobDelay = 200
      let repeatDelay = 300
      const noOfJobsToCreate = 10
      const allJobsDelay = jobDelay * (noOfJobsToCreate + 2)

      function resumeProcessPauseGet () {
        return q.resume().delay(jobDelay * 0.6).then(() => {
          return q.pause()
        }).delay(jobDelay).then(() => {
          return q.getJob(jobs)
        }).delay(repeatDelay * 2)
      }

      // ---------- Event Handler Setup ----------
      let state = {
        testName,
        enabled: false,
        ready: 0,
        processing: 48,
        progress: 1,
        pausing: 14,
        paused: 14,
        resumed: 14,
        removed: 0,
        idle: 12,
        reset: 0,
        error: 0,
        reviewed: 3,
        detached: 0,
        stopping: 0,
        stopped: 0,
        dropped: 0,
        added: 37,
        waiting: 0,
        active: 48,
        completed: 42,
        cancelled: 3,
        failed: 3,
        terminated: 1,
        reanimated: 0,
        log: 0,
        updated: 0
      }

      // idle event testing is not part of the test-event-handlers module
      // because it is too difficult to measure. This handler and one
      // test below is to ensure idle is being called.
      function idleEventHandler (qid) {
        t.ok(is.string(qid), `Event: idle`)
      }

      const summaryCompleted = 33
      const summaryCancelled = 3
      const summaryTerminated = 1

      let testTimes = false
      let tryCount = 0
      let updateProgress = false
      let testCancel = false
      let jobProcessTimeoutId = false

      function testHandler (job, next, onCancel) {
        if (testTimes) {
          const testDate = datetime.add.ms(new Date(),
            1000 + job.timeout + (job.retryCount * job.retryDelay))
          t.ok(is.dateBefore(job.dateEnable, testDate), 'Job dateEnable is valid')
          tryCount++
        }

        t.pass(`Job Started: Delay: [${jobDelay}] ID: [${job.id}]`)
        if (testCancel) {
          const cancelErr = new Error(tData)
          cancelErr.cancelJob = tData
          next(cancelErr)
        } else {
          if (updateProgress) {
            setTimeout(function () {
              return job.updateProgress(50).then((result) => {
                t.ok(result, 'Job progress updated: ' + job.id)
              })
            }, jobDelay / 2)
          }

          jobProcessTimeoutId = setTimeout(function () {
            jobProcessTimeoutId = false
            next(null, 'Job Completed: ' + job.id)
            .then((runningJobs) => {
              t.ok(is.integer(runningJobs), `Next call returns running jobs [${runningJobs}]`)
            })
          }, jobDelay)

          return onCancel(job, () => {
            clearTimeout(jobProcessTimeoutId)
            jobProcessTimeoutId = false
            t.pass('onCancel invoked')
            return
          })
        }
      }

      // ---------- Test Setup ----------
      jobs = []
      for (let i = 0; i < noOfJobsToCreate; i++) {
        jobs.push(q.createJob())
      }
      return q.reset().then((resetResult) => {
        t.ok(is.integer(resetResult), 'Queue reset')
        return q.pause()
      }).then(() => {
        eventHandlers.add(t, q, state)

        // ---------- Processing, Pause, and Concurrency Test ----------
        t.comment('queue-process: Process, Pause, and Concurrency')
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, noOfJobsToCreate, `Jobs saved successfully: [${savedJobs.length}]`)
        q._concurrency = 1
        return queueProcess.addHandler(q, testHandler)
      }).delay(jobDelay / 2).then(() => {
        t.equal(q._running, 0, 'Queue not processing jobs')
        return q.resume()
      }).then(() => {
        return queueProcess.addHandler(q, testHandler).then(() => {
          t.fail('Calling queue-process twice should fail and is not')
        }).catch((err) => {
          t.equal(err.message, enums.message.processTwice, 'Calling queue-process twice returns rejected Promise')
        })
      }).delay(jobDelay / 2).then(() => {
        t.equal(q._running, q._concurrency, 'Queue is processing only one job')
        q._concurrency = 3
        return q.pause()
      }).then(() => {
        q.on(enums.status.idle, idleEventHandler)
        return q.resume()
      }).delay(jobDelay / 2).then(() => {
        t.equal(q._running, q._concurrency, 'Queue is processing max concurrent jobs')
      }).delay(jobDelay * 8).then(() => {
        q.removeListener(enums.status.idle, idleEventHandler)
        t.ok(q.idle, 'Queue is idle')

        // ---------- Repeat True Test ----------
        t.comment('queue-process: Repeat True')
        jobs = q.createJob().setRepeat(true).setRepeatDelay(repeatDelay)
        t.ok(jobs.repeat, 'Job repeat is true')
        t.equal(jobs.repeatDelay, repeatDelay, 'Job repeat delay is valid')
        return q.pause()
      }).then(() => {
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, 1, `Jobs saved successfully: [${savedJobs.length}]`)
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 1, 'Repeat job processed once')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 2, 'Repeat job processed twice')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 3, 'Repeat job processed three times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 4, 'Repeat job processed four times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 5, 'Repeat job processed five times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return q.cancelJob(jobs)
      }).then((cancelledJobs) => {
        t.ok(is.uuid(cancelledJobs[0]), 'repeat job cancelled')
        return q.getJob(jobs)
      }).then((cancelledJobs) => {
        t.equal(cancelledJobs[0].processCount, 5, `Job repeated 5 times before cancel`)
        t.equal(cancelledJobs[0].log.length, 12, 'repeat job log count valid')

        // ---------- Repeat Number Test ----------
        t.comment('queue-process: Repeat Number')
        jobs = q.createJob().setRepeat(4).setRepeatDelay(repeatDelay)
        t.equal(jobs.repeat, 4, `Job repeat is 4`)
        t.equal(jobs.repeatDelay, repeatDelay, 'Job repeat delay is valid')
        return q.pause()
      }).then(() => {
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, 1, `Jobs saved successfully: [${savedJobs.length}]`)
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 1, 'Repeat job processed once')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 2, 'Repeat job processed twice')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 3, 'Repeat job processed three times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 4, 'Repeat job processed four times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((completedJobs) => {
        t.equal(completedJobs[0].processCount, 5, 'Repeat job processed five times')
        t.equal(completedJobs[0].status, enums.status.completed, 'Repeat job is completed')
        t.equal(completedJobs[0].log.length, 11, 'repeat job log count valid')
        return q.resume()
      }).then(() => {
        // // TODO - Remove below to run all tests.
        // return q.stop().then(() => Promise.reject())
        // ---------- Processing Restart on Job Add Test ----------
        t.comment('queue-process: Process Restart on Job Add')
        jobs = []
        for (let i = 0; i < noOfJobsToCreate; i++) {
          jobs.push(q.createJob())
        }
        q._concurrency = 10
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, noOfJobsToCreate, `Jobs saved successfully: [${savedJobs.length}]`)
      }).delay(allJobsDelay).then(() => {
        t.ok(q.idle, 'Queue is idle')
        return q.pause()
      }).then(() => {
        //
        // ---------- Processing Restart Test ----------
        t.comment('queue-process: Process Restart')
        jobs = []
        for (let i = 0; i < noOfJobsToCreate; i++) {
          jobs.push(q.createJob())
        }
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, noOfJobsToCreate, `Jobs saved successfully: [${savedJobs.length}]`)
        return q.resume()
      }).then(() => {
        return queueProcess.restart(q)
      }).delay(allJobsDelay).then(() => {
        t.pass('Restart processing succeeded')
        t.ok(q.idle, 'Queue is idle')

        // ---------- Processing with Job Timeout Test ----------
        t.comment('queue-process: Processing with Job Timeout')
        jobs = q.createJob()
        jobs.timeout = 1000
        jobs.retryDelay = 2000
        jobDelay = 1500
        testTimes = true // Enables handler time testing
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, 1, `Jobs saved successfully: [${savedJobs.length}]`)
      }).delay(5200).then(() => {
        return dbReview.runOnce(q)
      }).delay(5200).then(() => {
        return dbReview.runOnce(q)
      }).delay(3200).then(() => {
        jobDelay = 200
        testTimes = false
        t.equal(tryCount, 4, 'Job failed and retried correctly')
      }).then(() => {
        jobs = q.createJob()

        // ---------- Processing with Job Timeout Extended Test ----------
        t.comment('queue-process: Processing with Job Timeout Extended')
        jobs = q.createJob()
        jobs.timeout = 1000
        jobs.retryMax = 0
        jobDelay = 1500
        testTimes = true // Enables handler time testing
        updateProgress = true // Enables the handler job progress updates
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, 1, `Jobs saved successfully: [${savedJobs.length}]`)
      }).delay(5200).then(() => {
        jobDelay = 200
        testTimes = false
        updateProgress = false
        // t.equal(tryCount, 4, 'Job failed and retried correctly')
      }).then(() => {
        testCancel = true

        // ---------- Processing with Cancel Test ----------
        t.comment('queue-process: Processing with Cancel')
        jobs = q.createJob()
        return q.addJob(jobs)
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((cancelledJob) => {
        t.equal(cancelledJob[0].status, enums.status.cancelled, 'Job is cancelled')
      }).then(() => {
        testCancel = false

        // ---------- Processing with Global Cancel Test ----------
        t.comment('queue-process: Processing with Global Cancel')
        qGlobalCancel = new Queue(tOpts.cxn(), tOpts.default())
        jobDelay = 10000
        jobs = q.createJob()
        return q.addJob(jobs)
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((globalCancelJobs1) => {
        t.equal(globalCancelJobs1[0].status, enums.status.active, 'Job status is active')
        t.ok(jobProcessTimeoutId, 'Job is being processed')
        return qGlobalCancel.cancelJob(globalCancelJobs1[0])
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((globalCancelJobs2) => {
        t.equal(globalCancelJobs2[0].status, enums.status.cancelled, 'Job status is cancelled')
        t.notOk(jobProcessTimeoutId, 'Job processing has been stopped')
        jobDelay = 200
        return qGlobalCancel.stop()
      }).then(() => {
        //
        // ---------- Delayed Job Start Test ----------
        t.comment('queue-process: Delayed Job Start')
        jobs = q.createJob()
        jobs.dateEnable = datetime.add.sec(new Date(), 2)
        return q.addJob(jobs)
      }).delay(500).then(() => {
        return queueProcess.restart(q)
      }).delay(500).then(() => {
        return q.getJob(jobs)
      }).then((delayedJobs) => {
        t.equal(delayedJobs[0].status, enums.status.waiting, 'Delayed job has status: added')
      }).delay(2000).then(() => {
        return queueProcess.restart(q)
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((delayedJobs3) => {
        t.equal(delayedJobs3[0].status, enums.status.completed, 'Delayed job has status: completed')

        // ---------- Queue Summary ----------
        t.comment('queue-process: Queue Summary')
        return q.summary()
      }).then((queueSummary) => {
        t.equal(queueSummary.completed, summaryCompleted, `Summary ${summaryCompleted} jobs completed`)
        t.equal(queueSummary.cancelled, summaryCancelled, `Summary ${summaryCancelled} job cancelled`)
        t.equal(queueSummary.terminated, summaryTerminated, `Summary ${summaryTerminated} job terminated`)

        // ---------- Event Summary ----------
        eventHandlers.remove(t, q, state)

        return q.reset()
      }).then((resetResult) => {
        t.ok(resetResult >= 0, 'Queue reset')
        q.stop()
        return resolve(t.end())
      }).catch(err => tError(err, module, t))
    })
  })
}
<commit_msg>Added next() job update support<commit_after>const test = require('tap').test
const Promise = require('bluebird')
const datetime = require('../src/datetime')
const is = require('../src/is')
const enums = require('../src/enums')
const tError = require('./test-error')
const tOpts = require('./test-options')
const tData = require('./test-options').tData
const queueProcess = require('../src/queue-process')
const dbReview = require('../src/db-review')
const Queue = require('../src/queue')
const eventHandlers = require('./test-event-handlers')
const testName = 'queue-process'

module.exports = function () {
  return new Promise((resolve, reject) => {
    test(testName, { timeout: 200000 }, (t) => {
      t.plan(417)

      // ---------- Test Setup ----------
      const q = new Queue(tOpts.cxn(), tOpts.default())
      let qGlobalCancel

      let jobs
      let jobDelay = 200
      let repeatDelay = 300
      const noOfJobsToCreate = 10
      const allJobsDelay = jobDelay * (noOfJobsToCreate + 2)

      function resumeProcessPauseGet () {
        return q.resume().delay(jobDelay * 0.6).then(() => {
          return q.pause()
        }).delay(jobDelay).then(() => {
          return q.getJob(jobs)
        }).delay(repeatDelay * 2)
      }

      // ---------- Event Handler Setup ----------
      let state = {
        testName,
        enabled: false,
        ready: 0,
        processing: 49,
        progress: 1,
        pausing: 14,
        paused: 14,
        resumed: 14,
        removed: 1,
        idle: 12,
        reset: 0,
        error: 0,
        reviewed: 3,
        detached: 0,
        stopping: 0,
        stopped: 0,
        dropped: 0,
        added: 38,
        waiting: 0,
        active: 49,
        completed: 42,
        cancelled: 3,
        failed: 3,
        terminated: 1,
        reanimated: 0,
        log: 0,
        updated: 1
      }

      // idle event testing is not part of the test-event-handlers module
      // because it is too difficult to measure. This handler and one
      // test below is to ensure idle is being called.
      function idleEventHandler (qid) {
        t.ok(is.string(qid), `Event: idle`)
      }

      const summaryCompleted = 33
      const summaryCancelled = 3
      const summaryTerminated = 1

      let testTimes = false
      let tryCount = 0
      let updateProgress = false
      let testCancel = false
      let updateJob = false
      let jobProcessTimeoutId = false

      function testHandler (job, next, onCancel) {
        if (testTimes) {
          const testDate = datetime.add.ms(new Date(),
            1000 + job.timeout + (job.retryCount * job.retryDelay))
          t.ok(is.dateBefore(job.dateEnable, testDate), 'Job dateEnable is valid')
          tryCount++
        }

        t.pass(`Job Started: Delay: [${jobDelay}] ID: [${job.id}]`)
        if (testCancel) {
          const cancelErr = new Error(tData)
          cancelErr.cancelJob = tData
          next(cancelErr)
        } else if (updateJob) {
          job.updateNote = tData
          next(null, job)
        } else {
          if (updateProgress) {
            setTimeout(function () {
              return job.updateProgress(50).then((result) => {
                t.ok(result, 'Job progress updated: ' + job.id)
              })
            }, jobDelay / 2)
          }

          jobProcessTimeoutId = setTimeout(function () {
            jobProcessTimeoutId = false
            next(null, 'Job Completed: ' + job.id)
            .then((runningJobs) => {
              t.ok(is.integer(runningJobs), `Next call returns running jobs [${runningJobs}]`)
            })
          }, jobDelay)

          return onCancel(job, () => {
            clearTimeout(jobProcessTimeoutId)
            jobProcessTimeoutId = false
            t.pass('onCancel invoked')
            return
          })
        }
      }

      // ---------- Test Setup ----------
      jobs = []
      for (let i = 0; i < noOfJobsToCreate; i++) {
        jobs.push(q.createJob())
      }
      return q.reset().then((resetResult) => {
        t.ok(is.integer(resetResult), 'Queue reset')
        return q.pause()
      }).then(() => {
        eventHandlers.add(t, q, state)

        // ---------- Processing, Pause, and Concurrency Test ----------
        t.comment('queue-process: Process, Pause, and Concurrency')
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, noOfJobsToCreate, `Jobs saved successfully: [${savedJobs.length}]`)
        q._concurrency = 1
        return queueProcess.addHandler(q, testHandler)
      }).delay(jobDelay / 2).then(() => {
        t.equal(q._running, 0, 'Queue not processing jobs')
        return q.resume()
      }).then(() => {
        return queueProcess.addHandler(q, testHandler).then(() => {
          t.fail('Calling queue-process twice should fail and is not')
        }).catch((err) => {
          t.equal(err.message, enums.message.processTwice, 'Calling queue-process twice returns rejected Promise')
        })
      }).delay(jobDelay / 2).then(() => {
        t.equal(q._running, q._concurrency, 'Queue is processing only one job')
        q._concurrency = 3
        return q.pause()
      }).then(() => {
        q.on(enums.status.idle, idleEventHandler)
        return q.resume()
      }).delay(jobDelay / 2).then(() => {
        t.equal(q._running, q._concurrency, 'Queue is processing max concurrent jobs')
      }).delay(jobDelay * 8).then(() => {
        q.removeListener(enums.status.idle, idleEventHandler)
        t.ok(q.idle, 'Queue is idle')

        // ---------- Repeat True Test ----------
        t.comment('queue-process: Repeat True')
        jobs = q.createJob().setRepeat(true).setRepeatDelay(repeatDelay)
        t.ok(jobs.repeat, 'Job repeat is true')
        t.equal(jobs.repeatDelay, repeatDelay, 'Job repeat delay is valid')
        return q.pause()
      }).then(() => {
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, 1, `Jobs saved successfully: [${savedJobs.length}]`)
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 1, 'Repeat job processed once')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 2, 'Repeat job processed twice')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 3, 'Repeat job processed three times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 4, 'Repeat job processed four times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 5, 'Repeat job processed five times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return q.cancelJob(jobs)
      }).then((cancelledJobs) => {
        t.ok(is.uuid(cancelledJobs[0]), 'repeat job cancelled')
        return q.getJob(jobs)
      }).then((cancelledJobs) => {
        t.equal(cancelledJobs[0].processCount, 5, `Job repeated 5 times before cancel`)
        t.equal(cancelledJobs[0].log.length, 12, 'repeat job log count valid')

        // ---------- Repeat Number Test ----------
        t.comment('queue-process: Repeat Number')
        jobs = q.createJob().setRepeat(4).setRepeatDelay(repeatDelay)
        t.equal(jobs.repeat, 4, `Job repeat is 4`)
        t.equal(jobs.repeatDelay, repeatDelay, 'Job repeat delay is valid')
        return q.pause()
      }).then(() => {
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, 1, `Jobs saved successfully: [${savedJobs.length}]`)
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 1, 'Repeat job processed once')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 2, 'Repeat job processed twice')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 3, 'Repeat job processed three times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((repeatJob) => {
        t.equal(repeatJob[0].processCount, 4, 'Repeat job processed four times')
        t.equal(repeatJob[0].status, enums.status.waiting, 'Repeat job is waiting')
        return resumeProcessPauseGet()
      }).then((completedJobs) => {
        t.equal(completedJobs[0].processCount, 5, 'Repeat job processed five times')
        t.equal(completedJobs[0].status, enums.status.completed, 'Repeat job is completed')
        t.equal(completedJobs[0].log.length, 11, 'repeat job log count valid')
        return q.resume()
      }).then(() => {
        //
        // ---------- Processing with Job Update Test ----------
        t.comment('queue-process: Processing with Job Update')
        updateJob = true
        jobs = q.createJob()
        return q.addJob(jobs)
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((updatedJob) => {
        t.equal(updatedJob[0].updateNote, tData, 'Job updated in next() call successfully')
        t.equal(updatedJob[0].getLastLog().message, enums.message.jobUpdated, 'Job updated log entry valid')
        return q.removeJob(updatedJob[0])
      }).then(() => {
        updateJob = false
        //
        // ---------- Processing Restart on Job Add Test ----------
        t.comment('queue-process: Process Restart on Job Add')
        jobs = []
        for (let i = 0; i < noOfJobsToCreate; i++) {
          jobs.push(q.createJob())
        }
        q._concurrency = 10
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, noOfJobsToCreate, `Jobs saved successfully: [${savedJobs.length}]`)
      }).delay(allJobsDelay).then(() => {
        t.ok(q.idle, 'Queue is idle')
        return q.pause()
      }).then(() => {
        //
        // ---------- Processing Restart Test ----------
        t.comment('queue-process: Process Restart')
        jobs = []
        for (let i = 0; i < noOfJobsToCreate; i++) {
          jobs.push(q.createJob())
        }
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, noOfJobsToCreate, `Jobs saved successfully: [${savedJobs.length}]`)
        return q.resume()
      }).then(() => {
        return queueProcess.restart(q)
      }).delay(allJobsDelay).then(() => {
        t.pass('Restart processing succeeded')
        t.ok(q.idle, 'Queue is idle')

        // ---------- Processing with Job Timeout Test ----------
        t.comment('queue-process: Processing with Job Timeout')
        jobs = q.createJob()
        jobs.timeout = 1000
        jobs.retryDelay = 2000
        jobDelay = 1500
        testTimes = true // Enables handler time testing
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, 1, `Jobs saved successfully: [${savedJobs.length}]`)
      }).delay(5200).then(() => {
        return dbReview.runOnce(q)
      }).delay(5200).then(() => {
        return dbReview.runOnce(q)
      }).delay(3200).then(() => {
        jobDelay = 200
        testTimes = false
        t.equal(tryCount, 4, 'Job failed and retried correctly')
      }).then(() => {
        jobs = q.createJob()

        // ---------- Processing with Job Timeout Extended Test ----------
        t.comment('queue-process: Processing with Job Timeout Extended')
        jobs = q.createJob()
        jobs.timeout = 1000
        jobs.retryMax = 0
        jobDelay = 1500
        testTimes = true // Enables handler time testing
        updateProgress = true // Enables the handler job progress updates
        return q.addJob(jobs)
      }).then((savedJobs) => {
        t.equal(savedJobs.length, 1, `Jobs saved successfully: [${savedJobs.length}]`)
      }).delay(5200).then(() => {
        jobDelay = 200
        testTimes = false
        updateProgress = false
        // t.equal(tryCount, 4, 'Job failed and retried correctly')
      }).then(() => {
        testCancel = true

        // ---------- Processing with Cancel Test ----------
        t.comment('queue-process: Processing with Cancel')
        jobs = q.createJob()
        return q.addJob(jobs)
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((cancelledJob) => {
        t.equal(cancelledJob[0].status, enums.status.cancelled, 'Job is cancelled')
      }).then(() => {
        testCancel = false

        // ---------- Processing with Global Cancel Test ----------
        t.comment('queue-process: Processing with Global Cancel')
        qGlobalCancel = new Queue(tOpts.cxn(), tOpts.default())
        jobDelay = 10000
        jobs = q.createJob()
        return q.addJob(jobs)
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((globalCancelJobs1) => {
        t.equal(globalCancelJobs1[0].status, enums.status.active, 'Job status is active')
        t.ok(jobProcessTimeoutId, 'Job is being processed')
        return qGlobalCancel.cancelJob(globalCancelJobs1[0])
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((globalCancelJobs2) => {
        t.equal(globalCancelJobs2[0].status, enums.status.cancelled, 'Job status is cancelled')
        t.notOk(jobProcessTimeoutId, 'Job processing has been stopped')
        jobDelay = 200
        return qGlobalCancel.stop()
      }).then(() => {
        //
        // ---------- Delayed Job Start Test ----------
        t.comment('queue-process: Delayed Job Start')
        jobs = q.createJob()
        jobs.dateEnable = datetime.add.sec(new Date(), 2)
        return q.addJob(jobs)
      }).delay(500).then(() => {
        return queueProcess.restart(q)
      }).delay(500).then(() => {
        return q.getJob(jobs)
      }).then((delayedJobs) => {
        t.equal(delayedJobs[0].status, enums.status.waiting, 'Delayed job has status: added')
      }).delay(2000).then(() => {
        return queueProcess.restart(q)
      }).delay(1000).then(() => {
        return q.getJob(jobs)
      }).then((delayedJobs3) => {
        t.equal(delayedJobs3[0].status, enums.status.completed, 'Delayed job has status: completed')

        // ---------- Queue Summary ----------
        t.comment('queue-process: Queue Summary')
        return q.summary()
      }).then((queueSummary) => {
        t.equal(queueSummary.completed, summaryCompleted, `Summary ${summaryCompleted} jobs completed`)
        t.equal(queueSummary.cancelled, summaryCancelled, `Summary ${summaryCancelled} job cancelled`)
        t.equal(queueSummary.terminated, summaryTerminated, `Summary ${summaryTerminated} job terminated`)

        // ---------- Event Summary ----------
        eventHandlers.remove(t, q, state)

        return q.reset()
      }).then((resetResult) => {
        t.ok(resetResult >= 0, 'Queue reset')
        q.stop()
        return resolve(t.end())
      }).catch(err => tError(err, module, t))
    })
  })
}
<|endoftext|>


<commit_before>function draw(data) {

  	// set dimensions and variables
  	var margin = {top: 20, right: 10, bottom: 20, left: 20},
  		width = 500 - margin.left - margin.right,
    	height = 400 - margin.top - margin.bottom;

  	var data_max = 150;

  	var groups = ["Alliansen", "S-V-MP-coalition", "Independent"];

  	// filter and summarize data
  	data_filtered = data.filter(function(d) { return d.valtyp == "R"; });

  	var data_sum = d3.nest()
  		.key(function(d) { return d.parti; })
  		.rollup(function(leaves) { return leaves.length; })
  		.entries(data_filtered);

  	var total = d3.sum(data_sum, function(d) { return d.values; });

  	var y = d3.scale.linear()
  		.domain([0, data_max])
  		.range([height - (margin.top + margin.bottom), 0]),
  		x = d3.scale.ordinal()
  		.domain(d3.range(data_sum.length))
  		.rangeBands([margin.left, width - margin.right], 0.2);

  	// construct bar chart
  	var svg = d3.select("#canvas")
  		.append('svg')
  			.attr("width", width + margin.left + margin.right)
  			.attr("height", height + margin.top + margin.bottom)
  		.append('g')
  			.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
  			.attr('id', 'barchart')
  			.attr('class', 'barchart');

	// tooltip
	var bar_tip = d3.tip()
		.attr('class', 'd3-tip')
		.offset([-10, 0])
		.html(function(d) { return d.values + " seats
" + d3.format('.1%')(d.values / total); });

	svg.call(bar_tip);

  	// make bars
	svg.selectAll('rect')
		.data(data_sum)
		.enter()
		.append('rect')
		.attr("transform", function(d, i){
			return "translate("+x(i) +", 0)"; })
		.attr('y', function(d) { return y(d.values) - (margin.bottom + margin.top); })
		.attr('width', x.rangeBand())
		.attr('height', function(d) { return height - y(d.values); })
		.style('fill', function(d) {
			if (['M', 'C', 'FP', 'KD'].indexOf(d.key) >= 0) { return 'rgb(173,216,230)'; }
			else if (['S', 'V', 'MP'].indexOf(d.key) >= 0) { return 'rgb(230,150,173)'; }
			else { return 'grey'; }
		})
		.on('mouseover', bar_tip.show)
		.on('mouseout', bar_tip.hide)
		.attr('class', function(d) {
			if (['M', 'C', 'FP', 'KD'].indexOf(d.key) >= 0) { return groups[0]; }
			else if (['S', 'V', 'MP'].indexOf(d.key) >= 0) { return groups[1]; }
			else { return groups[2]; }
		});

	// add labels for parties and percentages
	var text = svg.selectAll('text')
		.data(data_sum)
		.enter()

	text.append('text')
		.text(function(d) { return d.key; })
		.attr("text-anchor", "middle")
		.attr('x', function(d, i) {return x.range()[i] + x.rangeBand()/2; })
		.attr('y', function(d) { return height - margin.bottom; })

	// text.append('text')
	// 	.text(function(d) { return d3.format('.1%')(d.values / total); })
	// 	.attr("text-anchor", "middle")
	// 	.attr('x', function(d, i) {return x.range()[i] + x.rangeBand()/2; })
	// 	.attr('y', function(d) { return y(d.values); });

	text.append('text')
		.text("Total seats: 349")
		.attr('x', margin.left + 5)
		.attr('y', margin.top - 5)
		.attr('text-anchor', 'start');

	// axes
	y_axis = d3.svg.axis()
		.scale(y)
		.orient('left')
		.outerTickSize(0);

	svg.append("g")
		.attr('class', 'y axis')
		.attr('transform', 'translate(' + margin.left + ', 0)')
		.call(y_axis);

	// legend
	var legend = svg.append("g")
  		.attr("class", "legend")
		.attr("height", 100)
		.attr("width", 100)
	    .attr('transform', 'translate(50,50)')

    legend.selectAll('rect')
      .data(groups)
      .enter()
      .append("rect")
	  .attr("x", width - 65)
      .attr("y", function(d, i){ return i *  20;})
	  .attr("width", 15)
	  .attr("height", 15)
	  .style("fill", function(d) {
	  	if (d === 'Alliansen') { return 'rgb(173,216,230)'; }
	  	else if (d === 'S-V-MP-coalition') { return 'rgb(230,150,173)'; }
	  	else { return 'grey';}
	  })
	  .attr('class', function(d, i) { return groups[i]; })
	  .on('mouseover', function(d, i) { svg.selectAll("." + groups[i]).style('opacity', .5); })
	  .on('mouseout', function(d, i) { svg.selectAll('rect').style('opacity', 1); });

	legend.selectAll('text')
		.data(groups)
		.enter()
		.append('text')
		.attr("x", width - 75)
		.attr('text-anchor', 'end')
      	.attr("y", function(d, i){ return i * 20 + 12;})
      	.text(function(d) { return d; });

  }

  // load csv file
d3.csv("../assets/nuvarande_ledamoter_RLK.csv", draw);
<commit_msg>Update general-election-2014.js<commit_after>function draw(data) {

  	// set dimensions and variables
  	var margin = {top: 20, right: 10, bottom: 20, left: 20},
  		width = 500 - margin.left - margin.right,
    	height = 400 - margin.top - margin.bottom;

  	var data_max = 150;

  	var groups = ["Alliansen", "S-V-MP-coalition", "Independent"];

  	// filter and summarize data
  	data_filtered = data.filter(function(d) { return d.valtyp == "R"; });

  	var data_sum = d3.nest()
  		.key(function(d) { return d.parti; })
  		.rollup(function(leaves) { return leaves.length; })
  		.entries(data_filtered);

  	var total = d3.sum(data_sum, function(d) { return d.values; });

  	var y = d3.scale.linear()
  		.domain([0, data_max])
  		.range([height - (margin.top + margin.bottom), 0]),
  		x = d3.scale.ordinal()
  		.domain(d3.range(data_sum.length))
  		.rangeBands([margin.left, width - margin.right], 0.2);

  	// construct bar chart
  	var svg = d3.select("#canvas")
  		.append('svg')
  			.attr("width", width + margin.left + margin.right)
  			.attr("height", height + margin.top + margin.bottom)
  		.append('g')
  			.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
  			.attr('id', 'barchart')
  			.attr('class', 'barchart');

	// tooltip
	var bar_tip = d3.tip()
		.attr('class', 'd3-tip')
		.offset([-10, 0])
		.html(function(d) { return d.values + " seats
" + d3.format('.1%')(d.values / total); });

	svg.call(bar_tip);

  	// make bars
	svg.selectAll('rect')
		.data(data_sum)
		.enter()
		.append('rect')
		.attr("transform", function(d, i){
			return "translate("+x(i) +", 0)"; })
		.attr('y', function(d) { return y(d.values) - (margin.bottom + margin.top); })
		.attr('width', x.rangeBand())
		.attr('height', function(d) { return height - y(d.values); })
		.style('fill', function(d) {
			if (['M', 'C', 'FP', 'KD'].indexOf(d.key) >= 0) { return 'rgb(173,216,230)'; }
			else if (['S', 'V', 'MP'].indexOf(d.key) >= 0) { return 'rgb(230,150,173)'; }
			else { return 'grey'; }
		})
		.on('mouseover', bar_tip.show)
		.on('mouseout', bar_tip.hide)
		.attr('class', function(d) {
			if (['M', 'C', 'FP', 'KD'].indexOf(d.key) >= 0) { return groups[0]; }
			else if (['S', 'V', 'MP'].indexOf(d.key) >= 0) { return groups[1]; }
			else { return groups[2]; }
		});

	// add labels for parties and percentages
	var text = svg.selectAll('text')
		.data(data_sum)
		.enter()

	text.append('text')
		.text(function(d) { return d.key; })
		.attr("text-anchor", "middle")
		.attr('x', function(d, i) {return x.range()[i] + x.rangeBand()/2; })
		.attr('y', function(d) { return height - margin.bottom; })

	// text.append('text')
	// 	.text(function(d) { return d3.format('.1%')(d.values / total); })
	// 	.attr("text-anchor", "middle")
	// 	.attr('x', function(d, i) {return x.range()[i] + x.rangeBand()/2; })
	// 	.attr('y', function(d) { return y(d.values); });

	text.append('text')
		.text("Total seats: 349")
		.attr('x', margin.left + 5)
		.attr('y', margin.top - 5)
		.attr('text-anchor', 'start');

	// axes
	y_axis = d3.svg.axis()
		.scale(y)
		.orient('left')
		.outerTickSize(0);

	svg.append("g")
		.attr('class', 'y axis')
		.attr('transform', 'translate(' + margin.left + ', 0)')
		.call(y_axis);

	// legend
	var legend = svg.append("g")
  		.attr("class", "legend")
		.attr("height", 100)
		.attr("width", 100)
	    .attr('transform', 'translate(50,50)')

    legend.selectAll('rect')
      .data(groups)
      .enter()
      .append("rect")
	  .attr("x", width - 65)
      .attr("y", function(d, i){ return i *  20;})
	  .attr("width", 15)
	  .attr("height", 15)
	  .style("fill", function(d) {
	  	if (d === 'Alliansen') { return 'rgb(173,216,230)'; }
	  	else if (d === 'S-V-MP-coalition') { return 'rgb(230,150,173)'; }
	  	else { return 'grey';}
	  })
	  .attr('class', function(d, i) { return groups[i]; })
	  .on('mouseover', function(d, i) { svg.selectAll("." + groups[i]).style('opacity', .5); })
	  .on('mouseout', function(d, i) { svg.selectAll('rect').style('opacity', 1); });

	legend.selectAll('text')
		.data(groups)
		.enter()
		.append('text')
		.attr("x", width - 75)
		.attr('text-anchor', 'end')
      	.attr("y", function(d, i){ return i * 20 + 12;})
      	.text(function(d) { return d; });

  }

  // load csv file
d3.csv("../../../../assets/nuvarande_ledamoter_RLK.csv", draw);
<|endoftext|>


<commit_before>var eventQueue = [];
var svg;
var element;
var drawingArea;
var width;
var height;
var volume = 0.6;
var ULTIMATE_DREAM_KILLER = false;

var scale_factor = 6,
    note_overlap = 2,
    note_timeout = 300,
    current_notes = 0,
    max_life = 20000;

var svg_background_color_online = '#0288D1',
    svg_background_color_offline = '#E91E63'
    svg_text_color = '#FFFFFF',
    newuser_box_color = 'rgb(41, 128, 185)',
    push_color = 'rgb(155, 89, 182)',
    issue_color = 'rgb(46, 204, 113)',
    pull_request_color = 'rgb(46, 204, 113)',
    comment_color = 'rgb(46, 204, 113)'
    edit_color = '#fff',
    total_sounds = 51,
    total_edits = 0;

    var celesta = [],
        clav = [],
        swells = [],
        all_loaded = false;

var socket = io(document.location.hostname);
socket.on('github', function (data) {
  $('.online-users-count').html(data.connected_users);
  data.data.forEach(function(event){
    if(!isEventInQueue(event) || shouldEventBeIgnored(event)){
      eventQueue.push(event);
    }
  });
  // Don't let the eventQueue grow more than 100
  if (eventQueue.length > 100) eventQueue = eventQueue.slice(0, 100);
});

socket.on('connect', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_online);
      $('header').css('background-color', svg_background_color_online);
      $('.offline-text').css('visibility', 'hidden');
      $('.events-remaining-text').css('visibility', 'hidden');
      $('.events-remaining-value').css('visibility', 'hidden');
    }
});

socket.on('disconnect', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_offline);
      $('header').css('background-color', svg_background_color_offline);
      $('.offline-text').css('visibility', 'visible');
      $('.events-remaining-text').css('visibility', 'visible');
      $('.events-remaining-value').css('visibility', 'visible');

    }
});

socket.on('error', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_offline);
      $('header').css('background-color', svg_background_color_offline);
      $('.offline-text').css('visibility', 'visible');
      $('.events-remaining-text').css('visibility', 'visible');
      $('.events-remaining-value').css('visibility', 'visible');
    }
});

/**
* This function checks whether an event is already in the queue
*/
function isEventInQueue(event){
  for(var i=0; i<eventQueue.length; i++){
    if(eventQueue[i].id == event.id)
      return true;
  }
  return false;
}

/**
 * This function adds a filter for events that we don't want to hear.
 *
 * To extend this function, simply add return true for events that should be filtered.
 */
function shouldEventBeIgnored(event){
  // This adds an easter egg to only play closed PRs
  if (!!ULTIMATE_DREAM_KILLER)
    return (event.type !== "PullRequestEvent" || event.action !== "closed");

  return false;
}

$(function(){
  element = document.documentElement;
  drawingArea = document.getElementsByTagName('#area')[0];
  width = window.innerWidth || element.clientWidth || drawingArea.clientWidth;
  height = (window.innerHeight  - $('header').height())|| (element.clientHeight - $('header').height()) || (drawingArea.clientHeight - $('header').height());
  $('svg').css('background-color', svg_background_color_online);
  $('header').css('background-color', svg_background_color_online);
  $('svg text').css('color', svg_text_color);
  $('#volumeSlider').slider({
    'max': 100,
    'min': 0,
    'value': volume*100,
    'slide' : function(event, ui){
      volume = ui.value/100.0;
      Howler.volume(volume);
    },
    'change' : function(event, ui){
      volume = ui.value/100.0;
      Howler.volume(volume);
    }
  });

  $('#eventConsumptionSlider').slider({
    'min': 1,
    'max': 100
  });

  // Main drawing area
  svg = d3.select("#area").append("svg");
  svg.attr({width: width, height: height});
  svg.style('background-color', svg_background_color_online);

  // For window resizes
  var update_window = function() {
      width = window.innerWidth || element.clientWidth || drawingArea.clientWidth;
      height = (window.innerHeight  - $('header').height())|| (element.clientHeight - $('header').height()) || (drawingArea.clientHeight - $('header').height());
      svg.attr("width", width).attr("height", height);
  }
  window.onresize = update_window;
  update_window();

  var loaded_sounds = 0;
  var sound_load = function(r) {
      loaded_sounds += 1;
      if (loaded_sounds == total_sounds) {
          all_loaded = true;
          setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000));
          // Starting the second exchange makes music a bad experience
          // setTimeout(playFromQueueExchange2, Math.floor(Math.random() * 2000));
      }
  }

  // Load sounds
  for (var i = 1; i <= 24; i++) {
      if (i > 9) {
          fn = 'c0' + i;
      } else {
          fn = 'c00' + i;
      }
      celesta.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.mp3'],
          volume : 0.7,
          onload : sound_load(),
          buffer: true,
      }))
      clav.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.mp3'],
          volume : 0.4,
          onload : sound_load(),
          buffer: true,
      }))
  }

  for (var i = 1; i <= 3; i++) {
      swells.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.mp3'],
          volume : 1,
          onload : sound_load(),
          buffer: true,
      }));
  }

  Howler.volume(volume);

});

/**
* Randomly selects a swell sound and plays it
*/
function playRandomSwell() {
    var index = Math.round(Math.random() * (swells.length - 1));
    swells[index].play();
}

/**
* Plays a sound(celesta and clav) based on passed parameters
*/
function playSound(size, type) {
    var max_pitch = 100.0;
    var log_used = 1.0715307808111486871978099;
    var pitch = 100 - Math.min(max_pitch, Math.log(size + log_used) / Math.log(log_used));
    var index = Math.floor(pitch / 100.0 * Object.keys(celesta).length);
    var fuzz = Math.floor(Math.random() * 4) - 2;
    index += fuzz;
    index = Math.min(Object.keys(celesta).length - 1, index);
    index = Math.max(1, index);
    if (current_notes < note_overlap) {
        current_notes++;
        if (type == 'IssuesEvent' || type == 'IssueCommentEvent') {
            clav[index].play();
        } else if(type == 'PushEvent') {
            celesta[index].play();
        }else{
          playRandomSwell();
        }
        setTimeout(function() {
            current_notes--;
        }, note_timeout);
    }
}

// Following are the n numbers of event consumers
// consuming n events each per second with a random delay between them

function playFromQueueExchange1(){
  var event = eventQueue.shift();
  if(event != null && event.message != null && svg != null){
    playSound(event.message.length*1.1, event.type);
    if(!document.hidden)
      drawEvent(event, svg);
  }
  setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000) + 500);
  $('.events-remaining-value').html(eventQueue.length);
}

function playFromQueueExchange2(){
  var event = eventQueue.shift();
  if(event != null && event.message != null && svg != null){
    playSound(event.message.length, event.type);
    if(!document.hidden)
      drawEvent(event, svg);
  }
  setTimeout(playFromQueueExchange2, Math.floor(Math.random() * 800) + 500);
  $('.events-remaining-value').html(eventQueue.length);
}

// This method capitalizes the string in place
String.prototype.capitalize=function(all){
    if(all){
      return this.split(' ').map(function(e){
        return e.capitalize().join(' ');
      });
    }else{
         return this.charAt(0).toUpperCase() + this.slice(1);
    }
}

function drawEvent(data, svg_area) {
    var starting_opacity = 1;
    var opacity = 1 / (100 / data.message.length);
    if (opacity > 0.5) {
        opacity = 0.5;
    }
    var size = data.message.length;
    var label_text;
    var ring_radius = 80;
    var ring_anim_duration = 3000;
    svg_text_color = '#FFFFFF';
    switch(data.type){
      case "PushEvent":
        label_text = data.user.capitalize() + " pushed to " + data.repo_name;
        edit_color = '#B2DFDB';
      break;
      case "PullRequestEvent":
        label_text = data.user.capitalize() + " " +
          data.action + " " + " a PR for " + data.repo_name;
          edit_color = '#C6FF00';
          ring_anim_duration = 10000;
          ring_radius = 600;
      break;
      case "IssuesEvent":
        label_text = data.user.capitalize() + " " +
          data.action + " an issue in " + data.repo_name;
          edit_color = '#FFEB3B';
      break;
      case "IssueCommentEvent":
        label_text = data.user.capitalize() + " commented in " + data.repo_name;
        edit_color = '#FF5722';
      break;
    }
    var csize = size;
    var no_label = false;
    var type = data.type;

    var circle_id = 'd' + ((Math.random() * 100000) | 0);
    var abs_size = Math.abs(size);
    size = Math.max(Math.sqrt(abs_size) * scale_factor, 3);

    Math.seedrandom(data.message)
    var x = Math.random() * (width - size) + size;
    var y = Math.random() * (height - size) + size;

    var circle_group = svg_area.append('g')
        .attr('transform', 'translate(' + x + ', ' + y + ')')
        .attr('fill', edit_color)
        .style('opacity', starting_opacity)

    var ring = circle_group.append('circle');
    ring.attr({r: size, stroke: 'none'});
    ring.transition()
        .attr('r', size + ring_radius)
        .style('opacity', 0)
        .ease(Math.sqrt)
        .duration(ring_anim_duration)
        .remove();

    var circle_container = circle_group.append('a');
    circle_container.attr('xlink:href', 'https://github.com/' + data.repo_name);
    circle_container.attr('target', '_blank');
    circle_container.attr('fill', svg_text_color);

    var circle = circle_container.append('circle');
    circle.classed(type, true);
    circle.attr('r', size)
      .attr('fill', edit_color)
      .transition()
      .duration(max_life)
      .style('opacity', 0)
      .remove();

    circle_container.on('mouseover', function() {
      circle_container.append('text')
          .text(label_text)
          .classed('label', true)
          .attr('text-anchor', 'middle')
          .attr('font-size', '0.8em')
          .transition()
          .delay(1000)
          .style('opacity', 0)
          .duration(2000)
          .each(function() { no_label = true; })
          .remove();
    });

    var text = circle_container.append('text')
        .text(label_text)
        .classed('article-label', true)
        .attr('text-anchor', 'middle')
        .attr('font-size', '0.8em')
        .transition()
        .delay(2000)
        .style('opacity', 0)
        .duration(5000)
        .each(function() { no_label = true; })
        .remove();

  // Remove HTML of decayed events
  // Keep it less than 50
  if($('#area svg g').length > 50){
    $('#area svg g:lt(10)').remove();
  }
}
<commit_msg>Filter events before playing instead of before queuing<commit_after>var eventQueue = [];
var svg;
var element;
var drawingArea;
var width;
var height;
var volume = 0.6;
var ULTIMATE_DREAM_KILLER = false;

var scale_factor = 6,
    note_overlap = 2,
    note_timeout = 300,
    current_notes = 0,
    max_life = 20000;

var svg_background_color_online = '#0288D1',
    svg_background_color_offline = '#E91E63'
    svg_text_color = '#FFFFFF',
    newuser_box_color = 'rgb(41, 128, 185)',
    push_color = 'rgb(155, 89, 182)',
    issue_color = 'rgb(46, 204, 113)',
    pull_request_color = 'rgb(46, 204, 113)',
    comment_color = 'rgb(46, 204, 113)'
    edit_color = '#fff',
    total_sounds = 51,
    total_edits = 0;

    var celesta = [],
        clav = [],
        swells = [],
        all_loaded = false;

var socket = io(document.location.hostname);
socket.on('github', function (data) {
  $('.online-users-count').html(data.connected_users);
  data.data.forEach(function(event){
    if(!isEventInQueue(event)){
      eventQueue.push(event);
    }
  });
  // Don't let the eventQueue grow more than 100
  if (eventQueue.length > 100) eventQueue = eventQueue.slice(0, 100);
});

socket.on('connect', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_online);
      $('header').css('background-color', svg_background_color_online);
      $('.offline-text').css('visibility', 'hidden');
      $('.events-remaining-text').css('visibility', 'hidden');
      $('.events-remaining-value').css('visibility', 'hidden');
    }
});

socket.on('disconnect', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_offline);
      $('header').css('background-color', svg_background_color_offline);
      $('.offline-text').css('visibility', 'visible');
      $('.events-remaining-text').css('visibility', 'visible');
      $('.events-remaining-value').css('visibility', 'visible');

    }
});

socket.on('error', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_offline);
      $('header').css('background-color', svg_background_color_offline);
      $('.offline-text').css('visibility', 'visible');
      $('.events-remaining-text').css('visibility', 'visible');
      $('.events-remaining-value').css('visibility', 'visible');
    }
});

/**
* This function checks whether an event is already in the queue
*/
function isEventInQueue(event){
  for(var i=0; i<eventQueue.length; i++){
    if(eventQueue[i].id == event.id)
      return true;
  }
  return false;
}

/**
 * This function adds a filter for events that we don't want to hear.
 *
 * To extend this function, simply add return true for events that should be filtered.
 */
function shouldEventBeIgnored(event){
  // This adds an easter egg to only play closed PRs
  if (!!ULTIMATE_DREAM_KILLER)
    return (event.type !== "PullRequestEvent" || event.action !== "closed");

  return false;
}

$(function(){
  element = document.documentElement;
  drawingArea = document.getElementsByTagName('#area')[0];
  width = window.innerWidth || element.clientWidth || drawingArea.clientWidth;
  height = (window.innerHeight  - $('header').height())|| (element.clientHeight - $('header').height()) || (drawingArea.clientHeight - $('header').height());
  $('svg').css('background-color', svg_background_color_online);
  $('header').css('background-color', svg_background_color_online);
  $('svg text').css('color', svg_text_color);
  $('#volumeSlider').slider({
    'max': 100,
    'min': 0,
    'value': volume*100,
    'slide' : function(event, ui){
      volume = ui.value/100.0;
      Howler.volume(volume);
    },
    'change' : function(event, ui){
      volume = ui.value/100.0;
      Howler.volume(volume);
    }
  });

  $('#eventConsumptionSlider').slider({
    'min': 1,
    'max': 100
  });

  // Main drawing area
  svg = d3.select("#area").append("svg");
  svg.attr({width: width, height: height});
  svg.style('background-color', svg_background_color_online);

  // For window resizes
  var update_window = function() {
      width = window.innerWidth || element.clientWidth || drawingArea.clientWidth;
      height = (window.innerHeight  - $('header').height())|| (element.clientHeight - $('header').height()) || (drawingArea.clientHeight - $('header').height());
      svg.attr("width", width).attr("height", height);
  }
  window.onresize = update_window;
  update_window();

  var loaded_sounds = 0;
  var sound_load = function(r) {
      loaded_sounds += 1;
      if (loaded_sounds == total_sounds) {
          all_loaded = true;
          setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000));
          // Starting the second exchange makes music a bad experience
          // setTimeout(playFromQueueExchange2, Math.floor(Math.random() * 2000));
      }
  }

  // Load sounds
  for (var i = 1; i <= 24; i++) {
      if (i > 9) {
          fn = 'c0' + i;
      } else {
          fn = 'c00' + i;
      }
      celesta.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.mp3'],
          volume : 0.7,
          onload : sound_load(),
          buffer: true,
      }))
      clav.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.mp3'],
          volume : 0.4,
          onload : sound_load(),
          buffer: true,
      }))
  }

  for (var i = 1; i <= 3; i++) {
      swells.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.mp3'],
          volume : 1,
          onload : sound_load(),
          buffer: true,
      }));
  }

  Howler.volume(volume);

});

/**
* Randomly selects a swell sound and plays it
*/
function playRandomSwell() {
    var index = Math.round(Math.random() * (swells.length - 1));
    swells[index].play();
}

/**
* Plays a sound(celesta and clav) based on passed parameters
*/
function playSound(size, type) {
    var max_pitch = 100.0;
    var log_used = 1.0715307808111486871978099;
    var pitch = 100 - Math.min(max_pitch, Math.log(size + log_used) / Math.log(log_used));
    var index = Math.floor(pitch / 100.0 * Object.keys(celesta).length);
    var fuzz = Math.floor(Math.random() * 4) - 2;
    index += fuzz;
    index = Math.min(Object.keys(celesta).length - 1, index);
    index = Math.max(1, index);
    if (current_notes < note_overlap) {
        current_notes++;
        if (type == 'IssuesEvent' || type == 'IssueCommentEvent') {
            clav[index].play();
        } else if(type == 'PushEvent') {
            celesta[index].play();
        }else{
          playRandomSwell();
        }
        setTimeout(function() {
            current_notes--;
        }, note_timeout);
    }
}

// Following are the n numbers of event consumers
// consuming n events each per second with a random delay between them

function playFromQueueExchange1(){
  var event = eventQueue.shift();
  if(event != null && event.message != null && !shouldEventBeIgnored(event) && svg != null){
    playSound(event.message.length*1.1, event.type);
    if(!document.hidden)
      drawEvent(event, svg);
  }
  setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000) + 500);
  $('.events-remaining-value').html(eventQueue.length);
}

function playFromQueueExchange2(){
  var event = eventQueue.shift();
  if(event != null && event.message != null && !shouldEventBeIgnored(event) && svg != null){
    playSound(event.message.length, event.type);
    if(!document.hidden)
      drawEvent(event, svg);
  }
  setTimeout(playFromQueueExchange2, Math.floor(Math.random() * 800) + 500);
  $('.events-remaining-value').html(eventQueue.length);
}

// This method capitalizes the string in place
String.prototype.capitalize=function(all){
    if(all){
      return this.split(' ').map(function(e){
        return e.capitalize().join(' ');
      });
    }else{
         return this.charAt(0).toUpperCase() + this.slice(1);
    }
}

function drawEvent(data, svg_area) {
    var starting_opacity = 1;
    var opacity = 1 / (100 / data.message.length);
    if (opacity > 0.5) {
        opacity = 0.5;
    }
    var size = data.message.length;
    var label_text;
    var ring_radius = 80;
    var ring_anim_duration = 3000;
    svg_text_color = '#FFFFFF';
    switch(data.type){
      case "PushEvent":
        label_text = data.user.capitalize() + " pushed to " + data.repo_name;
        edit_color = '#B2DFDB';
      break;
      case "PullRequestEvent":
        label_text = data.user.capitalize() + " " +
          data.action + " " + " a PR for " + data.repo_name;
          edit_color = '#C6FF00';
          ring_anim_duration = 10000;
          ring_radius = 600;
      break;
      case "IssuesEvent":
        label_text = data.user.capitalize() + " " +
          data.action + " an issue in " + data.repo_name;
          edit_color = '#FFEB3B';
      break;
      case "IssueCommentEvent":
        label_text = data.user.capitalize() + " commented in " + data.repo_name;
        edit_color = '#FF5722';
      break;
    }
    var csize = size;
    var no_label = false;
    var type = data.type;

    var circle_id = 'd' + ((Math.random() * 100000) | 0);
    var abs_size = Math.abs(size);
    size = Math.max(Math.sqrt(abs_size) * scale_factor, 3);

    Math.seedrandom(data.message)
    var x = Math.random() * (width - size) + size;
    var y = Math.random() * (height - size) + size;

    var circle_group = svg_area.append('g')
        .attr('transform', 'translate(' + x + ', ' + y + ')')
        .attr('fill', edit_color)
        .style('opacity', starting_opacity)

    var ring = circle_group.append('circle');
    ring.attr({r: size, stroke: 'none'});
    ring.transition()
        .attr('r', size + ring_radius)
        .style('opacity', 0)
        .ease(Math.sqrt)
        .duration(ring_anim_duration)
        .remove();

    var circle_container = circle_group.append('a');
    circle_container.attr('xlink:href', 'https://github.com/' + data.repo_name);
    circle_container.attr('target', '_blank');
    circle_container.attr('fill', svg_text_color);

    var circle = circle_container.append('circle');
    circle.classed(type, true);
    circle.attr('r', size)
      .attr('fill', edit_color)
      .transition()
      .duration(max_life)
      .style('opacity', 0)
      .remove();

    circle_container.on('mouseover', function() {
      circle_container.append('text')
          .text(label_text)
          .classed('label', true)
          .attr('text-anchor', 'middle')
          .attr('font-size', '0.8em')
          .transition()
          .delay(1000)
          .style('opacity', 0)
          .duration(2000)
          .each(function() { no_label = true; })
          .remove();
    });

    var text = circle_container.append('text')
        .text(label_text)
        .classed('article-label', true)
        .attr('text-anchor', 'middle')
        .attr('font-size', '0.8em')
        .transition()
        .delay(2000)
        .style('opacity', 0)
        .duration(5000)
        .each(function() { no_label = true; })
        .remove();

  // Remove HTML of decayed events
  // Keep it less than 50
  if($('#area svg g').length > 50){
    $('#area svg g:lt(10)').remove();
  }
}
<|endoftext|>


<commit_before>'use strict';

var extend = require(__dirname + '/../../prototype.js').extend;

exports.JsonParser = extend({name: 'Json', humanName: 'JSON'}, {

  parse: function(log) {
    try {
      var parsed = JSON.parse(log);
    } catch (e) {
      if (e.toString.match(/Unexpected end of input/)) {
        return null; // Treat as general parse error.
      } else {
        throw e;
      }
    }
    return parsed;
  },

  buildTemplateContext: function(template, log) {
    if ('full' == template) {
      return log;
    }

    // Only use keys selected in config.js 'previewAttr' lists.
    if (log.previewAttr) {
      log.previewAttr = _.isArray(log.previewAttr) ? log.previewAttr : [log.previewAttr];

      var updated = {};
      _.each(log.previewAttr, function(name) {
        if (log[name]) {
          updated[name] = log[name];
        }
      });
      return updated;
    }
    return log;
  },

  buildPreviewText: function(parsed) {
    var preview = [];
    _.each(parsed, function(value, key) {
      preview.push(key + '=' + value);
    });
    return preview.join(', ');
  }
});
<commit_msg>Fix syntax.<commit_after>'use strict';

var extend = require(__dirname + '/../../prototype.js').extend;

exports.JsonParser = extend({name: 'Json', humanName: 'JSON'}, {

  parse: function(log) {
    try {
      var parsed = JSON.parse(log);
    } catch (e) {
      if (e.toString().match(/Unexpected end of input/)) {
        return null; // Treat as general parse error.
      } else {
        throw e;
      }
    }
    return parsed;
  },

  buildTemplateContext: function(template, log) {
    if ('full' == template) {
      return log;
    }

    // Only use keys selected in config.js 'previewAttr' lists.
    if (log.previewAttr) {
      log.previewAttr = _.isArray(log.previewAttr) ? log.previewAttr : [log.previewAttr];

      var updated = {};
      _.each(log.previewAttr, function(name) {
        if (log[name]) {
          updated[name] = log[name];
        }
      });
      return updated;
    }
    return log;
  },

  buildPreviewText: function(parsed) {
    var preview = [];
    _.each(parsed, function(value, key) {
      preview.push(key + '=' + value);
    });
    return preview.join(', ');
  }
});
<|endoftext|>


<commit_before>import { combineReducers } from 'redux';

import routes from './routes'

export default combineReducers({
    routes
});<commit_msg>update reducer index<commit_after>import { combineReducers } from 'redux';

import routes from './routes'
import login from './login'
import athlete from './athlete'

export default combineReducers({
    routes,
    login,
    athlete
});<|endoftext|>


<commit_before>wwt.app.factory(
  'AppState',

  function() {
    var api = {
      set: setKey,
      get: getKey,
      getAll: getAll
    };

    var data;

    function setKey(key, val) {
      try {
        if (val === null && data[key]) {
          delete data[key]
        } else {
          data[key] = val;
        }

        if (localStorage) {
          localStorage.setItem('appState', JSON.stringify(data));
        }
      } catch (er) {
        console.log('Error using localstorage. Is it turned off?');
      }
    }

    function getKey(k) {
      return data[k];
    }

    function getAll() {
      return data;
    }

    var initialState = {
      layerManagerHidden: true,
    };

    var init = function () {
      try {
        var storedString = localStorage.getItem('appState');
        var storedObj = JSON.parse(storedString);

        // This seems like the most robust way to make sure that we got a
        // dictlike (N.B., JSON.parse(null) => null).
        if (Object.prototype.toString.call(storedObj) != '[object Object]') {
          throw new Error('stored object not dict');
        }

        data = storedObj;
      } catch {
        data = initialState;
      }
    };

    init();
    return api;
  }
);
<commit_msg>Fixed uglify.js complaint<commit_after>wwt.app.factory(
  'AppState',

  function() {
    var api = {
      set: setKey,
      get: getKey,
      getAll: getAll
    };

    var data;

    function setKey(key, val) {
      try {
        if (val === null && data[key]) {
          delete data[key]
        } else {
          data[key] = val;
        }

        if (localStorage) {
          localStorage.setItem('appState', JSON.stringify(data));
        }
      } catch (er) {
        console.log('Error using localstorage. Is it turned off?');
      }
    }

    function getKey(k) {
      return data[k];
    }

    function getAll() {
      return data;
    }

    var initialState = {
      layerManagerHidden: true,
    };

    var init = function () {
      try {
        var storedString = localStorage.getItem('appState');
        var storedObj = JSON.parse(storedString);

        // This seems like the most robust way to make sure that we got a
        // dictlike (N.B., JSON.parse(null) => null).
        if (Object.prototype.toString.call(storedObj) != '[object Object]') {
          throw new Error('stored object not dict');
        }

        data = storedObj;
      } catch (e){
        data = initialState;
      }
    };

    init();
    return api;
  }
);
<|endoftext|>


<commit_before>// Copyright 2017-2019, University of Colorado Boulder

/**
 * Builds standalone JS deliverables (e.g. dot/kite/scenery)
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

'use strict';

// modules
const assert = require( 'assert' );
const fs = require( 'fs' );
const grunt = require( 'grunt' );
const minify = require( './minify' );
const requireBuild = require( './requireBuild' );

/**
 * Builds standalone JS deliverables (e.g. dot/kite/scenery)
 * @public
 *
 * @param {string} repo
 * @param {Object} minifyOptions
 * @returns {Promise}
 */
module.exports = async function( repo, minifyOptions ) {
  assert( typeof repo === 'string' );
  assert( typeof minifyOptions === 'object' );

  const packageObject = grunt.file.readJSON( `../${repo}/package.json` );

  const requireJS = await requireBuild( repo, `../${repo}/js/${repo}-config.js` );

  const includedSources = [
    '../assert/js/assert.js',
    '../tandem/js/PhetioIDUtils.js'
  ];
  if ( repo === 'scenery' ) {
    includedSources.push( '../sherpa/lib/himalaya-0.2.7.js' );
  }

  const includedJS = includedSources.map( file => fs.readFileSync( file, 'utf8' ) ).join( '\n' );

  // Checks if lodash exists
  const testLodash = '  if ( !window.hasOwnProperty( \'_\' ) ) {\n' +
                     '    throw new Error( \'Underscore/Lodash not found: _\' );\n' +
                     '  }\n';
  // Checks if jQuery exists
  const testJQuery = '  if ( !window.hasOwnProperty( \'$\' ) ) {\n' +
                     '    throw new Error( \'jQuery not found: $\' );\n' +
                     '  }\n';

  let fullSource = includedJS + '\n' + requireJS;
  if ( packageObject.phet.requiresJQuery ) {
    fullSource = testJQuery + fullSource;
  }
  if ( packageObject.phet.requiresLodash ) {
    fullSource = testLodash + fullSource;
  }

  // include globals assignment
  fullSource += Object.keys( packageObject.phet.assignGlobals ).sort().map( function( global ) {
    // For each key=>value in packageObject.phet.assignGlobals, we want to set window.key = require( 'value' ), to initialize our globals
    return `\n  window.${global} = require( '${packageObject.phet.assignGlobals[ global ]}' );`;
  } ).join( '' );

  if ( packageObject.phet.finalizeJS ) {
    fullSource += packageObject.phet.finalizeJS;
  }

  // Wrap with an IIFE
  fullSource = `(function() {\n${fullSource}\n}());`;

  fullSource = minify( fullSource, minifyOptions );

  return fullSource;
};
<commit_msg>support phet global in buildStandalone https://github.com/phetsims/utterance-queue/issues/6 https://github.com/phetsims/scenery/issues/966<commit_after>// Copyright 2017-2019, University of Colorado Boulder

/**
 * Builds standalone JS deliverables (e.g. dot/kite/scenery)
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

'use strict';

// modules
const assert = require( 'assert' );
const fs = require( 'fs' );
const grunt = require( 'grunt' );
const minify = require( './minify' );
const requireBuild = require( './requireBuild' );

/**
 * Builds standalone JS deliverables (e.g. dot/kite/scenery)
 * @public
 *
 * @param {string} repo
 * @param {Object} minifyOptions
 * @returns {Promise}
 */
module.exports = async function( repo, minifyOptions ) {
  assert( typeof repo === 'string' );
  assert( typeof minifyOptions === 'object' );

  const packageObject = grunt.file.readJSON( `../${repo}/package.json` );

  const requireJS = await requireBuild( repo, `../${repo}/js/${repo}-config.js` );

  const includedSources = [
    '../assert/js/assert.js',
    '../tandem/js/PhetioIDUtils.js'
  ];
  if ( repo === 'scenery' ) {
    includedSources.push( '../sherpa/lib/himalaya-0.2.7.js' );
  }

  const includedJS = includedSources.map( file => fs.readFileSync( file, 'utf8' ) ).join( '\n' );

  // Checks if lodash exists
  const testLodash = '  if ( !window.hasOwnProperty( \'_\' ) ) {\n' +
                     '    throw new Error( \'Underscore/Lodash not found: _\' );\n' +
                     '  }\n';
  // Checks if jQuery exists
  const testJQuery = '  if ( !window.hasOwnProperty( \'$\' ) ) {\n' +
                     '    throw new Error( \'jQuery not found: $\' );\n' +
                     '  }\n';

  let fullSource = includedJS + '\n' + requireJS;
  if ( packageObject.phet.requiresJQuery ) {
    fullSource = testJQuery + fullSource;
  }
  if ( packageObject.phet.requiresLodash ) {
    fullSource = testLodash + fullSource;
  }

  // include globals assignment
  if ( packageObject.phet.assignGlobals ) {
    fullSource += '\n window.phet = window.phet || {};\n';
    fullSource += Object.keys( packageObject.phet.assignGlobals ).sort().map( global => {

      // For each key=>value in packageObject.phet.assignGlobals, we want to set window.key = require( 'value' ), to initialize our globals
      return `\n  window.${global} = require( '${packageObject.phet.assignGlobals[ global ]}' );`;
    } ).join( '' );
  }

  if ( packageObject.phet.finalizeJS ) {
    fullSource += packageObject.phet.finalizeJS;
  }

  // Wrap with an IIFE
  fullSource = `(function() {\n${fullSource}\n}());`;

  fullSource = minify( fullSource, minifyOptions );

  return fullSource;
};
<|endoftext|>