<commit_before>import { Component, createElement } from 'react';
import hoistStatics from 'hoist-non-react-statics';
import isPlainObject from '../utils/isPlainObject';
import shallowEqual from '../utils/shallowEqual';
import pubSubShape from '../utils/pubSubShape';

const defaultRetriveProps = () => ({});
const defaultMapPublishToProps = publish => ({ publish });

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

function wrapPublishMethods(mapPublishToProps) {
  return publish => Object.keys(mapPublishToProps)
  .filter(key => typeof mapPublishToProps[key] === 'function')
  .reduce((acc, key) => {
    acc[key] = (...args) => mapPublishToProps[key](publish, ...args);
    return acc;
  }, {});
}

function cleanEmptyKeys(obj = {}) {
  Object.keys(obj)
  .filter(key => obj[key] === undefined || obj[key] === null)
  .forEach(key => delete obj[key]);
  return obj;
}

export default function createPubSubConnector(mapSubscriptionsToProps, mapPublishToProps, options = {}) {
  const shouldMapSubscriptions = Boolean(mapSubscriptionsToProps);
  const finalMapPublishToProps = isPlainObject(mapPublishToProps) ?
    wrapPublishMethods(mapPublishToProps) :
    mapPublishToProps || defaultMapPublishToProps;
  const shouldUpdatePublishProps = finalMapPublishToProps.length > 1;
  const { withRef = false } = options || {};

  let mappedSubscriptions = {};
  function registerMappedSubscriptions(pubSub, subscriptionsMap = {}, updateSubscriptionProps, getProps) {
    const { add } = pubSub;

    // TODO: should be necessary to support hot reload
    const mappedSubscriptionsKeys = Object.keys(mappedSubscriptions);
    if (mappedSubscriptionsKeys.length) {
      mappedSubscriptionsKeys.forEach(key => mappedSubscriptions[key].unsubscribe());
    }

    if (!Object.keys(subscriptionsMap).length) {
      return;
    }

    const validMappedSubscriptions = Object.keys(subscriptionsMap)
    .every(key => (typeof subscriptionsMap[key] === 'function' || typeof subscriptionsMap[key] === 'string'));

    if (!validMappedSubscriptions) {
      throw new Error(
        `Every mapped Subscription of "createPubSubConnector" must be a function`
        + `returning the value to be passed as prop to the decorated component.`
      );
    }

    const updateMappedSubscriptions = (key, transformerOrAlias) => {
      let callback;
      if (typeof transformerOrAlias === 'function') {
        const shouldUpdateSubscriptionProps = transformerOrAlias.length > 1;
        callback = (retrieveProps = defaultRetriveProps, silent = false) =>
        (...args) => {
          // store received values
          mappedSubscriptions[key].lastResult = args;
          mappedSubscriptions[key].shouldUpdateSubscriptionProps = shouldUpdateSubscriptionProps;

          // transform values
          const newValues = transformerOrAlias(args, retrieveProps());

          if (!isPlainObject(newValues)) {
            throw new Error(
              `Transformer functions for mapped subscriptions must return a plain object, `
              + `instead received %s.`
            );
          }

          if (!Object.keys(newValues).length) {
            throw new Error(
              `Transformer functions for mapped subscriptions must return an object`
              + `with at least one property.`
            );
          }

          updateSubscriptionProps(newValues, silent);
          return newValues;
        };
      } else {
        callback = result => updateSubscriptionProps({ [transformerOrAlias]: result });
      }

      return callback;
    };

    mappedSubscriptions = Object.keys(subscriptionsMap)
    .reduce((acc, key) => {
      const refresh = updateMappedSubscriptions(key, subscriptionsMap[key]);
      let callback = refresh;
      if (typeof subscriptionsMap[key] === 'function') {
        callback = refresh(getProps);
      }
      acc[key] = {
        key,
        refresh,
        unsubscribe: add(key, callback),
      };
      return acc;
    }, {});
  }

  function refreshMappedSubscriptions(subscriptionsMap = {}, getProps) {
    const mappedSubscriptionsKeys = Object.keys(mappedSubscriptions);
    if (!mappedSubscriptionsKeys.length) {
      return {};
    }

    const silent = true;

    return Object.keys(subscriptionsMap)
    .reduce((acc, key) => {
      if (
        typeof subscriptionsMap[key] === 'function'
          && mappedSubscriptions[key] !== undefined
      ) {
        const {
          refresh,
          lastResult,
          shouldUpdateSubscriptionProps,
        } = mappedSubscriptions[key];

        if (shouldUpdateSubscriptionProps) {
          const res = refresh(getProps, silent)(...lastResult);
          Object.assign(acc, res);
        }
      }
      return acc;
    }, {});
  }

  function computePublishProps(pubSub, props) {
    const { publish } = pubSub;
    const publishProps = shouldUpdatePublishProps ?
      finalMapPublishToProps(publish, props) :
      finalMapPublishToProps(publish);

    if (!isPlainObject(publishProps)) {
      throw new Error(
        `'mapPublishToProps' must return an object.`
        + `Instead received ${publishProps}`
      );
    }
    return publishProps;
  }

  function computeMappedSubscriptionProps(mappedSubscriptionProps = {}, props = {}) {
    const nextSubscriptionsProps = refreshMappedSubscriptions(mapSubscriptionsToProps, () => props);
    const merged = Object.assign({}, mappedSubscriptionProps, nextSubscriptionsProps);
    return cleanEmptyKeys(merged);
  }

  return function wrapComponent(Composed) {
    class PubSubConnector extends Component {
      constructor(props, context) {
        super(props, context);
        this.pubSubCore = props.pubSubCore || context.pubSubCore;

        if (!this.pubSubCore) {
          throw new Error(
            `Could not find "pubSubCore" in either the context or `
            + `props of "${this.constructor.displayName}". `
            + `Either wrap the root component in a <PubSubProvider>, `
            + `or explicitly pass "pubSubCore" as a prop to "${this.constructor.displayName}".`
          );
        }

        this.pubSub = this.pubSubCore.register(this);

        if (shouldMapSubscriptions) {
          registerMappedSubscriptions(
            this.pubSub,
            mapSubscriptionsToProps,
            (...args) => this.updateSingleMappedSubscription(...args),
            () => this.props
          );
        }

        this.publishProps = computePublishProps(this.pubSub, props);
        this.mappedSubscriptionProps = computeMappedSubscriptionProps({}, props);
        this.state = {};
      }

      shouldComponentUpdate(nextProps, nextState) {
        const stateChanged = !shallowEqual(nextState, this.state);
        const propsChanged = !shallowEqual(nextProps, this.props);
        let publishPropsChanged = false;
        let mappedSubscriptionsPropsChanged = false;

        if (propsChanged && shouldUpdatePublishProps) {
          publishPropsChanged = this.updatePublishProps(nextProps);
        }

        if (propsChanged && shouldMapSubscriptions) {
          mappedSubscriptionsPropsChanged = this.updateMappedSubscriptions(nextProps);
        }

        return propsChanged || stateChanged || publishPropsChanged || mappedSubscriptionsPropsChanged;
      }

      componentWillUnmount() {
        mappedSubscriptions = {};
        this.pubSub.unsubscribe();
      }

      getWrappedInstance() {
        if (!withRef) {
          throw new Error(
            `To access the wrapped instance, you need to specify explicitly`
            + `{ withRef: true } in the options passed to the createPubSubConnector() call.`
          );
        }
        return this.refs.wrappedInstance;
      }

      updateSingleMappedSubscription(updatedSubscription, silent = false) {
        if (!silent) {
          Object.assign(this.mappedSubscriptionProps, updatedSubscription);
          cleanEmptyKeys(this.mappedSubscriptionProps);
          const lastSubscription = this.state.lastSubscription ? this.state.lastSubscription + 1 : 1;
          this.setState({ lastSubscription });
        }
      }

      updateMappedSubscriptions(props = this.props) {
        const nextSubscriptionProps = computeMappedSubscriptionProps(this.mappedSubscriptionProps, props);
        if (shallowEqual(nextSubscriptionProps, this.mappedSubscriptionProps)) {
          return false;
        }

        this.mappedSubscriptionProps = nextSubscriptionProps;
        return true;
      }

      updatePublishProps(props = this.props) {
        const nextPublishProps = computePublishProps(this.pubSub, props);
        if (shallowEqual(nextPublishProps, this.publishProps)) {
          return false;
        }

        this.publishProps = nextPublishProps;
        return true;
      }

      hasSubscriptions() {
        return Object.keys(mappedSubscriptions).length > 0;
      }

      render() {
        const { pubSub } = this;
        const ref = withRef ? 'wrappedInstance' : null;
        return createElement(
          Composed,
          Object.assign(
            { pubSub, ref },
            this.props,
            this.mappedSubscriptionProps,
            this.publishProps
          )
        );
      }
    }

    PubSubConnector.contextTypes = {
      pubSubCore: pubSubShape,
    };

    PubSubConnector.propTypes = {
      pubSubCore: pubSubShape,
    };

    PubSubConnector.displayName = `PubSubConnector(${getDisplayName(Composed)})`;
    PubSubConnector.WrappedComponent = Composed;

    return hoistStatics(PubSubConnector, Composed);
  };
}
<commit_msg>Added condition to updateSingleMappedSubscription() to reduce unnecessary renders on createPubSubConnector module<commit_after>import { Component, createElement } from 'react';
import hoistStatics from 'hoist-non-react-statics';
import isPlainObject from '../utils/isPlainObject';
import shallowEqual from '../utils/shallowEqual';
import pubSubShape from '../utils/pubSubShape';

const defaultRetriveProps = () => ({});
const defaultMapPublishToProps = publish => ({ publish });

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

function wrapPublishMethods(mapPublishToProps) {
  return publish => Object.keys(mapPublishToProps)
  .filter(key => typeof mapPublishToProps[key] === 'function')
  .reduce((acc, key) => {
    acc[key] = (...args) => mapPublishToProps[key](publish, ...args);
    return acc;
  }, {});
}

function cleanEmptyKeys(obj = {}) {
  Object.keys(obj)
  .filter(key => obj[key] === undefined || obj[key] === null)
  .forEach(key => delete obj[key]);
  return obj;
}

export default function createPubSubConnector(mapSubscriptionsToProps, mapPublishToProps, options = {}) {
  const shouldMapSubscriptions = Boolean(mapSubscriptionsToProps);
  const finalMapPublishToProps = isPlainObject(mapPublishToProps) ?
    wrapPublishMethods(mapPublishToProps) :
    mapPublishToProps || defaultMapPublishToProps;
  const shouldUpdatePublishProps = finalMapPublishToProps.length > 1;
  const { withRef = false } = options || {};

  let mappedSubscriptions = {};
  function registerMappedSubscriptions(pubSub, subscriptionsMap = {}, updateSubscriptionProps, getProps) {
    const { add } = pubSub;

    // TODO: should be necessary to support hot reload
    const mappedSubscriptionsKeys = Object.keys(mappedSubscriptions);
    if (mappedSubscriptionsKeys.length) {
      mappedSubscriptionsKeys.forEach(key => mappedSubscriptions[key].unsubscribe());
    }

    if (!Object.keys(subscriptionsMap).length) {
      return;
    }

    const validMappedSubscriptions = Object.keys(subscriptionsMap)
    .every(key => (typeof subscriptionsMap[key] === 'function' || typeof subscriptionsMap[key] === 'string'));

    if (!validMappedSubscriptions) {
      throw new Error(
        `Every mapped Subscription of "createPubSubConnector" must be a function`
        + `returning the value to be passed as prop to the decorated component.`
      );
    }

    const updateMappedSubscriptions = (key, transformerOrAlias) => {
      let callback;
      if (typeof transformerOrAlias === 'function') {
        const shouldUpdateSubscriptionProps = transformerOrAlias.length > 1;
        callback = (retrieveProps = defaultRetriveProps, silent = false) =>
        (...args) => {
          // store received values
          mappedSubscriptions[key].lastResult = args;
          mappedSubscriptions[key].shouldUpdateSubscriptionProps = shouldUpdateSubscriptionProps;

          // transform values
          const newValues = transformerOrAlias(args, retrieveProps());

          if (!isPlainObject(newValues)) {
            throw new Error(
              `Transformer functions for mapped subscriptions must return a plain object, `
              + `instead received %s.`
            );
          }

          if (!Object.keys(newValues).length) {
            throw new Error(
              `Transformer functions for mapped subscriptions must return an object`
              + `with at least one property.`
            );
          }

          updateSubscriptionProps(newValues, silent);
          return newValues;
        };
      } else {
        callback = result => updateSubscriptionProps({ [transformerOrAlias]: result });
      }

      return callback;
    };

    mappedSubscriptions = Object.keys(subscriptionsMap)
    .reduce((acc, key) => {
      const refresh = updateMappedSubscriptions(key, subscriptionsMap[key]);
      let callback = refresh;
      if (typeof subscriptionsMap[key] === 'function') {
        callback = refresh(getProps);
      }
      acc[key] = {
        key,
        refresh,
        unsubscribe: add(key, callback),
      };
      return acc;
    }, {});
  }

  function refreshMappedSubscriptions(subscriptionsMap = {}, getProps) {
    const mappedSubscriptionsKeys = Object.keys(mappedSubscriptions);
    if (!mappedSubscriptionsKeys.length) {
      return {};
    }

    const silent = true;

    return Object.keys(subscriptionsMap)
    .reduce((acc, key) => {
      if (
        typeof subscriptionsMap[key] === 'function'
          && mappedSubscriptions[key] !== undefined
      ) {
        const {
          refresh,
          lastResult,
          shouldUpdateSubscriptionProps,
        } = mappedSubscriptions[key];

        if (shouldUpdateSubscriptionProps) {
          const res = refresh(getProps, silent)(...lastResult);
          Object.assign(acc, res);
        }
      }
      return acc;
    }, {});
  }

  function computePublishProps(pubSub, props) {
    const { publish } = pubSub;
    const publishProps = shouldUpdatePublishProps ?
      finalMapPublishToProps(publish, props) :
      finalMapPublishToProps(publish);

    if (!isPlainObject(publishProps)) {
      throw new Error(
        `'mapPublishToProps' must return an object.`
        + `Instead received ${publishProps}`
      );
    }
    return publishProps;
  }

  function computeMappedSubscriptionProps(mappedSubscriptionProps = {}, props = {}) {
    const nextSubscriptionsProps = refreshMappedSubscriptions(mapSubscriptionsToProps, () => props);
    const merged = Object.assign({}, mappedSubscriptionProps, nextSubscriptionsProps);
    return cleanEmptyKeys(merged);
  }

  return function wrapComponent(Composed) {
    class PubSubConnector extends Component {
      constructor(props, context) {
        super(props, context);
        this.pubSubCore = props.pubSubCore || context.pubSubCore;

        if (!this.pubSubCore) {
          throw new Error(
            `Could not find "pubSubCore" in either the context or `
            + `props of "${this.constructor.displayName}". `
            + `Either wrap the root component in a <PubSubProvider>, `
            + `or explicitly pass "pubSubCore" as a prop to "${this.constructor.displayName}".`
          );
        }

        this.pubSub = this.pubSubCore.register(this);

        if (shouldMapSubscriptions) {
          registerMappedSubscriptions(
            this.pubSub,
            mapSubscriptionsToProps,
            (...args) => this.updateSingleMappedSubscription(...args),
            () => this.props
          );
        }

        this.publishProps = computePublishProps(this.pubSub, props);
        this.mappedSubscriptionProps = computeMappedSubscriptionProps({}, props);
        this.state = {};
      }

      shouldComponentUpdate(nextProps, nextState) {
        const stateChanged = !shallowEqual(nextState, this.state);
        const propsChanged = !shallowEqual(nextProps, this.props);
        let publishPropsChanged = false;
        let mappedSubscriptionsPropsChanged = false;

        if (propsChanged && shouldUpdatePublishProps) {
          publishPropsChanged = this.updatePublishProps(nextProps);
        }

        if (propsChanged && shouldMapSubscriptions) {
          mappedSubscriptionsPropsChanged = this.updateMappedSubscriptions(nextProps);
        }

        return propsChanged || stateChanged || publishPropsChanged || mappedSubscriptionsPropsChanged;
      }

      componentWillUnmount() {
        mappedSubscriptions = {};
        this.pubSub.unsubscribe();
      }

      getWrappedInstance() {
        if (!withRef) {
          throw new Error(
            `To access the wrapped instance, you need to specify explicitly`
            + ` { withRef: true } in the options passed to the createPubSubConnector() call.`
          );
        }
        return this.refs.wrappedInstance;
      }

      updateSingleMappedSubscription(updatedSubscription, silent = false) {
        if (!silent && updatedSubscription) {
          Object.assign(this.mappedSubscriptionProps, updatedSubscription);
          cleanEmptyKeys(this.mappedSubscriptionProps);
          const lastSubscription = this.state.lastSubscription ? this.state.lastSubscription + 1 : 1;
          this.setState({ lastSubscription });
        }
      }

      updateMappedSubscriptions(props = this.props) {
        const nextSubscriptionProps = computeMappedSubscriptionProps(this.mappedSubscriptionProps, props);
        if (shallowEqual(nextSubscriptionProps, this.mappedSubscriptionProps)) {
          return false;
        }

        this.mappedSubscriptionProps = nextSubscriptionProps;
        return true;
      }

      updatePublishProps(props = this.props) {
        const nextPublishProps = computePublishProps(this.pubSub, props);
        if (shallowEqual(nextPublishProps, this.publishProps)) {
          return false;
        }

        this.publishProps = nextPublishProps;
        return true;
      }

      hasSubscriptions() {
        return Object.keys(mappedSubscriptions).length > 0;
      }

      render() {
        const { pubSub } = this;
        const ref = withRef ? 'wrappedInstance' : null;
        return createElement(
          Composed,
          Object.assign(
            { pubSub, ref },
            this.props,
            this.mappedSubscriptionProps,
            this.publishProps
          )
        );
      }
    }

    PubSubConnector.contextTypes = {
      pubSubCore: pubSubShape,
    };

    PubSubConnector.propTypes = {
      pubSubCore: pubSubShape,
    };

    PubSubConnector.displayName = `PubSubConnector(${getDisplayName(Composed)})`;
    PubSubConnector.WrappedComponent = Composed;

    return hoistStatics(PubSubConnector, Composed);
  };
}
<|endoftext|>


<commit_before>module.exports = '2018-09-06';
<commit_msg>bump version<commit_after>module.exports = '2018-10-04';
<|endoftext|>


<commit_before>// dependencies
//
import React from 'react';
import uniqueId from 'lodash/uniqueId';

class RadioButtonGroup extends React.Component {
    constructor(props) {
        super(props);

        this.id = uniqueId('form-radiobuttongroup-');

        this.permittedValues = this.props.options.map((opt) => `${opt.value}`);

        this.state = {
            value: this.props.options[0].value,
        };
    }

    isPermittedValue(value) {
        return this.permittedValues.findIndex((val) => val === `${value}`) !== -1;
    }

    render() {
        const { label, options } = this.props;

        return (

                {label ? ({label}) : null}
                {options.map((opt) => (

                            <input type="radio" name={this.id} value={opt.value} checked={this.state.value === opt.value} />
                            {opt.name}

                ))}

        );
    }
}

export default RadioButtonGroup;
<commit_msg>updated value initialization in ctor<commit_after>// dependencies
//
import React from 'react';
import uniqueId from 'lodash/uniqueId';

class RadioButtonGroup extends React.Component {
    constructor(props) {
        super(props);

        this.id = uniqueId('form-radiobuttongroup-');

        this.permittedValues = this.props.options.map((opt) => `${opt.value}`);

        this.state = {
            value: this.isPermittedValue(this.props.value)
                ? `${this.props.value}`
                : `${this.props.options[0].value}`,
        };
    }

    isPermittedValue(value) {
        return this.permittedValues.findIndex((val) => val === `${value}`) !== -1;
    }

    render() {
        const { label, options } = this.props;

        return (

                {label ? ({label}) : null}
                {options.map((opt) => (

                            <input type="radio" name={this.id} value={opt.value} checked={this.state.value === opt.value} />
                            {opt.name}

                ))}

        );
    }
}

export default RadioButtonGroup;
<|endoftext|>


<commit_before>"use strict";
exports.osmbc_version = "1.6.4";

<commit_msg>VErsion<commit_after>"use strict";
exports.osmbc_version = "1.6.4a";

<|endoftext|>


<commit_before>if (enyo && enyo.version) {
	enyo.version.canvas = "2.3.0-pre.12";
}
<commit_msg>Update version string to 2.3.0-rc.1<commit_after>if (enyo && enyo.version) {
	enyo.version.canvas = "2.3.0-rc.1";
}
<|endoftext|>


<commit_before>// Copyright 2002-2015, University of Colorado Boulder

/**
 * This grunt task checks out the shas for a project, as specified in a dependencies.json file in its top level.
 */

'use strict';

const assert = require( 'assert' );
const child_process = require( 'child_process' );
const grunt = require( 'grunt' );

/**
 * NOTE: This is somewhat vestigial, kept to ensure some build-server behavior for now.
 * TODO(chipper1.0): remove this when possible (when all chipper 1.0 usage is not required, since all sims are chipper 2.0+)
 *
 * @param {string} repositoryName name field from package.json
 * @param {boolean} toMaster whether master should be used, or dependencies.json shas should be used
 * @param {boolean} buildServer whether this build is initiated by the build server
 */
module.exports = function( repositoryName, toMaster, buildServer ) {

  const dependencies = grunt.file.readJSON( ( buildServer ) ? '../perennial/js/build-server/tmp/dependencies.json' : '../' + repositoryName + '/dependencies.json' );
  const done = grunt.task.current.async();
  let numToCheckOut = 0;
  let numCheckedOut = 0;
  for ( const property in dependencies ) {
    if ( property !== 'comment' && property !== repositoryName ) {
      numToCheckOut++;
    }
  }

  for ( const property of dependencies ) {
    if ( property !== 'comment' && property !== repositoryName && dependencies.hasOwnProperty( property ) ) {
      assert( typeof( dependencies[ property ].branch !== 'undefined' ) && typeof( dependencies[ property ].sha !== 'undefined' ) );

      grunt.log.writeln( 'Checking out dependency ' + property + ': ' + dependencies[ property ].branch + '@' + dependencies[ property ].sha );

      //To execute something from a different directory:
      //cp.exec('foocommand', { cwd: 'path/to/dir/' }, callback);
      //http://stackoverflow.com/questions/14026967/calling-child-process-exec-in-node-as-though-it-was-executed-in-a-specific-folde
      const command = 'git checkout ' + ( toMaster ? 'master' : dependencies[ property ].sha );
      child_process.exec( command, { cwd: '../' + property }, function( error1, stdout1, stderr1 ) {
        assert( !error1, 'error in ' + command + ' for repo ' + property );
        grunt.log.writeln( 'Finished checkout.' );
        grunt.log.writeln( stdout1 );
        grunt.log.writeln( stderr1 );
        numCheckedOut = numCheckedOut + 1;
        if ( numToCheckOut === numCheckedOut ) {
          done();
        }
      } );
    }
  }
};<commit_msg>TODO https://github.com/phetsims/perennial/issues/169<commit_after>// Copyright 2002-2015, University of Colorado Boulder

/**
 * This grunt task checks out the shas for a project, as specified in a dependencies.json file in its top level.
 */

'use strict';

const assert = require( 'assert' );
const child_process = require( 'child_process' );
const grunt = require( 'grunt' );

/**
 * NOTE: This is somewhat vestigial, kept to ensure some build-server behavior for now.
 * TODO(chipper1.0) https://github.com/phetsims/perennial/issues/169 remove this when possible (when all chipper 1.0 usage is not required, since all sims are chipper 2.0+)
 *
 * @param {string} repositoryName name field from package.json
 * @param {boolean} toMaster whether master should be used, or dependencies.json shas should be used
 * @param {boolean} buildServer whether this build is initiated by the build server
 */
module.exports = function( repositoryName, toMaster, buildServer ) {

  const dependencies = grunt.file.readJSON( ( buildServer ) ? '../perennial/js/build-server/tmp/dependencies.json' : '../' + repositoryName + '/dependencies.json' );
  const done = grunt.task.current.async();
  let numToCheckOut = 0;
  let numCheckedOut = 0;
  for ( const property in dependencies ) {
    if ( property !== 'comment' && property !== repositoryName ) {
      numToCheckOut++;
    }
  }

  for ( const property of dependencies ) {
    if ( property !== 'comment' && property !== repositoryName && dependencies.hasOwnProperty( property ) ) {
      assert( typeof( dependencies[ property ].branch !== 'undefined' ) && typeof( dependencies[ property ].sha !== 'undefined' ) );

      grunt.log.writeln( 'Checking out dependency ' + property + ': ' + dependencies[ property ].branch + '@' + dependencies[ property ].sha );

      //To execute something from a different directory:
      //cp.exec('foocommand', { cwd: 'path/to/dir/' }, callback);
      //http://stackoverflow.com/questions/14026967/calling-child-process-exec-in-node-as-though-it-was-executed-in-a-specific-folde
      const command = 'git checkout ' + ( toMaster ? 'master' : dependencies[ property ].sha );
      child_process.exec( command, { cwd: '../' + property }, function( error1, stdout1, stderr1 ) {
        assert( !error1, 'error in ' + command + ' for repo ' + property );
        grunt.log.writeln( 'Finished checkout.' );
        grunt.log.writeln( stdout1 );
        grunt.log.writeln( stderr1 );
        numCheckedOut = numCheckedOut + 1;
        if ( numToCheckOut === numCheckedOut ) {
          done();
        }
      } );
    }
  }
};<|endoftext|>


<commit_before>import { getWindow, getDocument } from 'ssr-window';
import $ from '../../utils/dom';
import { now, nextTick, bindModuleMethods } from '../../utils/utils';

function isEventSupported() {
  const document = getDocument();
  const eventName = 'onwheel';
  let isSupported = eventName in document;

  if (!isSupported) {
    const element = document.createElement('div');
    element.setAttribute(eventName, 'return;');
    isSupported = typeof element[eventName] === 'function';
  }

  if (
    !isSupported &&
    document.implementation &&
    document.implementation.hasFeature &&
    // always returns true in newer browsers as per the standard.
    // @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
    document.implementation.hasFeature('', '') !== true
  ) {
    // This is the only way to test support for the `wheel` event in IE9+.
    isSupported = document.implementation.hasFeature('Events.wheel', '3.0');
  }

  return isSupported;
}
const Mousewheel = {
  lastScrollTime: now(),
  lastEventBeforeSnap: undefined,
  recentWheelEvents: [],
  event() {
    const window = getWindow();
    if (window.navigator.userAgent.indexOf('firefox') > -1) return 'DOMMouseScroll';
    return isEventSupported() ? 'wheel' : 'mousewheel';
  },
  normalize(e) {
    // Reasonable defaults
    const PIXEL_STEP = 10;
    const LINE_HEIGHT = 40;
    const PAGE_HEIGHT = 800;

    let sX = 0;
    let sY = 0; // spinX, spinY
    let pX = 0;
    let pY = 0; // pixelX, pixelY

    // Legacy
    if ('detail' in e) {
      sY = e.detail;
    }
    if ('wheelDelta' in e) {
      sY = -e.wheelDelta / 120;
    }
    if ('wheelDeltaY' in e) {
      sY = -e.wheelDeltaY / 120;
    }
    if ('wheelDeltaX' in e) {
      sX = -e.wheelDeltaX / 120;
    }

    // side scrolling on FF with DOMMouseScroll
    if ('axis' in e && e.axis === e.HORIZONTAL_AXIS) {
      sX = sY;
      sY = 0;
    }

    pX = sX * PIXEL_STEP;
    pY = sY * PIXEL_STEP;

    if ('deltaY' in e) {
      pY = e.deltaY;
    }
    if ('deltaX' in e) {
      pX = e.deltaX;
    }

    if (e.shiftKey && !pX) {
      // if user scrolls with shift he wants horizontal scroll
      pX = pY;
      pY = 0;
    }

    if ((pX || pY) && e.deltaMode) {
      if (e.deltaMode === 1) {
        // delta in LINE units
        pX *= LINE_HEIGHT;
        pY *= LINE_HEIGHT;
      } else {
        // delta in PAGE units
        pX *= PAGE_HEIGHT;
        pY *= PAGE_HEIGHT;
      }
    }

    // Fall-back if spin cannot be determined
    if (pX && !sX) {
      sX = pX < 1 ? -1 : 1;
    }
    if (pY && !sY) {
      sY = pY < 1 ? -1 : 1;
    }

    return {
      spinX: sX,
      spinY: sY,
      pixelX: pX,
      pixelY: pY,
    };
  },
  handleMouseEnter() {
    const swiper = this;
    swiper.mouseEntered = true;
  },
  handleMouseLeave() {
    const swiper = this;
    swiper.mouseEntered = false;
  },
  handle(event) {
    let e = event;
    const swiper = this;
    const params = swiper.params.mousewheel;

    if (swiper.params.cssMode) {
      e.preventDefault();
    }

    let target = swiper.$el;
    if (swiper.params.mousewheel.eventsTarget !== 'container') {
      target = $(swiper.params.mousewheel.eventsTarget);
    }
    if (!swiper.mouseEntered && !target[0].contains(e.target) && !params.releaseOnEdges)
      return true;

    if (e.originalEvent) e = e.originalEvent; // jquery fix
    let delta = 0;
    const rtlFactor = swiper.rtlTranslate ? -1 : 1;

    const data = Mousewheel.normalize(e);

    if (params.forceToAxis) {
      if (swiper.isHorizontal()) {
        if (Math.abs(data.pixelX) > Math.abs(data.pixelY)) delta = -data.pixelX * rtlFactor;
        else return true;
      } else if (Math.abs(data.pixelY) > Math.abs(data.pixelX)) delta = -data.pixelY;
      else return true;
    } else {
      delta =
        Math.abs(data.pixelX) > Math.abs(data.pixelY) ? -data.pixelX * rtlFactor : -data.pixelY;
    }

    if (delta === 0) return true;

    if (params.invert) delta = -delta;

    if (!swiper.params.freeMode) {
      // Register the new event in a variable which stores the relevant data
      const newEvent = {
        time: now(),
        delta: Math.abs(delta),
        direction: Math.sign(delta),
        raw: event,
      };

      // Keep the most recent events
      const recentWheelEvents = swiper.mousewheel.recentWheelEvents;
      if (recentWheelEvents.length >= 2) {
        recentWheelEvents.shift(); // only store the last N events
      }
      const prevEvent = recentWheelEvents.length
        ? recentWheelEvents[recentWheelEvents.length - 1]
        : undefined;
      recentWheelEvents.push(newEvent);

      // If there is at least one previous recorded event:
      //   If direction has changed or
      //   if the scroll is quicker than the previous one:
      //     Animate the slider.
      // Else (this is the first time the wheel is moved):
      //     Animate the slider.
      if (prevEvent) {
        if (
          newEvent.direction !== prevEvent.direction ||
          newEvent.delta > prevEvent.delta ||
          newEvent.time > prevEvent.time + 150
        ) {
          swiper.mousewheel.animateSlider(newEvent);
        }
      } else {
        swiper.mousewheel.animateSlider(newEvent);
      }

      // If it's time to release the scroll:
      //   Return now so you don't hit the preventDefault.
      if (swiper.mousewheel.releaseScroll(newEvent)) {
        return true;
      }
    } else {
      // Freemode or scrollContainer:

      // If we recently snapped after a momentum scroll, then ignore wheel events
      // to give time for the deceleration to finish. Stop ignoring after 500 msecs
      // or if it's a new scroll (larger delta or inverse sign as last event before
      // an end-of-momentum snap).
      const newEvent = {
        time: now(),
        delta: Math.abs(delta),
        direction: Math.sign(delta),
      };
      const { lastEventBeforeSnap } = swiper.mousewheel;
      const ignoreWheelEvents =
        lastEventBeforeSnap &&
        newEvent.time < lastEventBeforeSnap.time + 500 &&
        newEvent.delta <= lastEventBeforeSnap.delta &&
        newEvent.direction === lastEventBeforeSnap.direction;
      if (!ignoreWheelEvents) {
        swiper.mousewheel.lastEventBeforeSnap = undefined;

        if (swiper.params.loop) {
          swiper.loopFix();
        }
        let position = swiper.getTranslate() + delta * params.sensitivity;
        const wasBeginning = swiper.isBeginning;
        const wasEnd = swiper.isEnd;

        if (position >= swiper.minTranslate()) position = swiper.minTranslate();
        if (position <= swiper.maxTranslate()) position = swiper.maxTranslate();

        swiper.setTransition(0);
        swiper.setTranslate(position);
        swiper.updateProgress();
        swiper.updateActiveIndex();
        swiper.updateSlidesClasses();

        if ((!wasBeginning && swiper.isBeginning) || (!wasEnd && swiper.isEnd)) {
          swiper.updateSlidesClasses();
        }

        if (swiper.params.freeModeSticky) {
          // When wheel scrolling starts with sticky (aka snap) enabled, then detect
          // the end of a momentum scroll by storing recent (N=15?) wheel events.
          // 1. do all N events have decreasing or same (absolute value) delta?
          // 2. did all N events arrive in the last M (M=500?) msecs?
          // 3. does the earliest event have an (absolute value) delta that's
          //    at least P (P=1?) larger than the most recent event's delta?
          // 4. does the latest event have a delta that's smaller than Q (Q=6?) pixels?
          // If 1-4 are "yes" then we're near the end of a momuntum scroll deceleration.
          // Snap immediately and ignore remaining wheel events in this scroll.
          // See comment above for "remaining wheel events in this scroll" determination.
          // If 1-4 aren't satisfied, then wait to snap until 500ms after the last event.
          clearTimeout(swiper.mousewheel.timeout);
          swiper.mousewheel.timeout = undefined;
          const recentWheelEvents = swiper.mousewheel.recentWheelEvents;
          if (recentWheelEvents.length >= 15) {
            recentWheelEvents.shift(); // only store the last N events
          }
          const prevEvent = recentWheelEvents.length
            ? recentWheelEvents[recentWheelEvents.length - 1]
            : undefined;
          const firstEvent = recentWheelEvents[0];
          recentWheelEvents.push(newEvent);
          if (
            prevEvent &&
            (newEvent.delta > prevEvent.delta || newEvent.direction !== prevEvent.direction)
          ) {
            // Increasing or reverse-sign delta means the user started scrolling again. Clear the wheel event log.
            recentWheelEvents.splice(0);
          } else if (
            recentWheelEvents.length >= 15 &&
            newEvent.time - firstEvent.time < 500 &&
            firstEvent.delta - newEvent.delta >= 1 &&
            newEvent.delta <= 6
          ) {
            // We're at the end of the deceleration of a momentum scroll, so there's no need
            // to wait for more events. Snap ASAP on the next tick.
            // Also, because there's some remaining momentum we'll bias the snap in the
            // direction of the ongoing scroll because it's better UX for the scroll to snap
            // in the same direction as the scroll instead of reversing to snap.  Therefore,
            // if it's already scrolled more than 20% in the current direction, keep going.
            const snapToThreshold = delta > 0 ? 0.8 : 0.2;
            swiper.mousewheel.lastEventBeforeSnap = newEvent;
            recentWheelEvents.splice(0);
            swiper.mousewheel.timeout = nextTick(() => {
              swiper.slideToClosest(swiper.params.speed, true, undefined, snapToThreshold);
            }, 0); // no delay; move on next tick
          }
          if (!swiper.mousewheel.timeout) {
            // if we get here, then we haven't detected the end of a momentum scroll, so
            // we'll consider a scroll "complete" when there haven't been any wheel events
            // for 500ms.
            swiper.mousewheel.timeout = nextTick(() => {
              const snapToThreshold = 0.5;
              swiper.mousewheel.lastEventBeforeSnap = newEvent;
              recentWheelEvents.splice(0);
              swiper.slideToClosest(swiper.params.speed, true, undefined, snapToThreshold);
            }, 500);
          }
        }

        // Emit event
        if (!ignoreWheelEvents) swiper.emit('scroll', e);

        // Stop autoplay
        if (swiper.params.autoplay && swiper.params.autoplayDisableOnInteraction)
          swiper.autoplay.stop();
        // Return page scroll on edge positions
        if (position === swiper.minTranslate() || position === swiper.maxTranslate()) return true;
      }
    }

    if (e.preventDefault) e.preventDefault();
    else e.returnValue = false;
    return false;
  },
  animateSlider(newEvent) {
    const swiper = this;
    const window = getWindow();

    if (
      this.params.mousewheel.preventSwipeThreshold &&
      newEvent.delta < this.params.mousewheel.preventSwipeThreshold
    ) {
      // Prevent swipe if delta of wheel scroll is below threshold
      return false;
    }

    // If the movement is NOT big enough and
    // if the last time the user scrolled was too close to the current one (avoid continuously triggering the slider):
    //   Don't go any further (avoid insignificant scroll movement).
    if (newEvent.delta >= 6 && now() - swiper.mousewheel.lastScrollTime < 60) {
      // Return false as a default
      return true;
    }
    // If user is scrolling towards the end:
    //   If the slider hasn't hit the latest slide or
    //   if the slider is a loop and
    //   if the slider isn't moving right now:
    //     Go to next slide and
    //     emit a scroll event.
    // Else (the user is scrolling towards the beginning) and
    // if the slider hasn't hit the first slide or
    // if the slider is a loop and
    // if the slider isn't moving right now:
    //   Go to prev slide and
    //   emit a scroll event.
    if (newEvent.direction < 0) {
      if ((!swiper.isEnd || swiper.params.loop) && !swiper.animating) {
        swiper.slideNext();
        swiper.emit('scroll', newEvent.raw);
      }
    } else if ((!swiper.isBeginning || swiper.params.loop) && !swiper.animating) {
      swiper.slidePrev();
      swiper.emit('scroll', newEvent.raw);
    }
    // If you got here is because an animation has been triggered so store the current time
    swiper.mousewheel.lastScrollTime = new window.Date().getTime();
    // Return false as a default
    return false;
  },
  releaseScroll(newEvent) {
    const swiper = this;
    const params = swiper.params.mousewheel;
    if (newEvent.direction < 0) {
      if (swiper.isEnd && !swiper.params.loop && params.releaseOnEdges) {
        // Return true to animate scroll on edges
        return true;
      }
    } else if (swiper.isBeginning && !swiper.params.loop && params.releaseOnEdges) {
      // Return true to animate scroll on edges
      return true;
    }
    return false;
  },
  enable() {
    const swiper = this;
    const event = Mousewheel.event();
    if (swiper.params.cssMode) {
      swiper.wrapperEl.removeEventListener(event, swiper.mousewheel.handle);
      return true;
    }
    if (!event) return false;
    if (swiper.mousewheel.enabled) return false;
    let target = swiper.$el;
    if (swiper.params.mousewheel.eventsTarget !== 'container') {
      target = $(swiper.params.mousewheel.eventsTarget);
    }
    target.on('mouseenter', swiper.mousewheel.handleMouseEnter);
    target.on('mouseleave', swiper.mousewheel.handleMouseLeave);
    target.on(event, swiper.mousewheel.handle);
    swiper.mousewheel.enabled = true;
    return true;
  },
  disable() {
    const swiper = this;
    const event = Mousewheel.event();
    if (swiper.params.cssMode) {
      swiper.wrapperEl.addEventListener(event, swiper.mousewheel.handle);
      return true;
    }
    if (!event) return false;
    if (!swiper.mousewheel.enabled) return false;
    let target = swiper.$el;
    if (swiper.params.mousewheel.eventsTarget !== 'container') {
      target = $(swiper.params.mousewheel.eventsTarget);
    }
    target.off(event, swiper.mousewheel.handle);
    swiper.mousewheel.enabled = false;
    return true;
  },
};

export default {
  name: 'mousewheel',
  params: {
    mousewheel: {
      enabled: false,
      releaseOnEdges: false,
      invert: false,
      forceToAxis: false,
      sensitivity: 1,
      eventsTarget: 'container',
      preventSwipeThreshold: null,
    },
  },
  create() {
    const swiper = this;
    bindModuleMethods(swiper, {
      mousewheel: {
        enabled: false,
        lastScrollTime: now(),
        lastEventBeforeSnap: undefined,
        recentWheelEvents: [],

        enable: Mousewheel.enable,
        disable: Mousewheel.disable,
        handle: Mousewheel.handle,
        handleMouseEnter: Mousewheel.handleMouseEnter,
        handleMouseLeave: Mousewheel.handleMouseLeave,
        animateSlider: Mousewheel.animateSlider,
        releaseScroll: Mousewheel.releaseScroll,
      },
    });
  },
  on: {
    init(swiper) {
      if (!swiper.params.mousewheel.enabled && swiper.params.cssMode) {
        swiper.mousewheel.disable();
      }
      if (swiper.params.mousewheel.enabled) swiper.mousewheel.enable();
    },
    destroy(swiper) {
      if (swiper.params.cssMode) {
        swiper.mousewheel.enable();
      }
      if (swiper.mousewheel.enabled) swiper.mousewheel.disable();
    },
  },
};
<commit_msg>Add threshold param for allowed time between wheel scrolls<commit_after>import { getWindow, getDocument } from 'ssr-window';
import $ from '../../utils/dom';
import { now, nextTick, bindModuleMethods } from '../../utils/utils';

function isEventSupported() {
  const document = getDocument();
  const eventName = 'onwheel';
  let isSupported = eventName in document;

  if (!isSupported) {
    const element = document.createElement('div');
    element.setAttribute(eventName, 'return;');
    isSupported = typeof element[eventName] === 'function';
  }

  if (
    !isSupported &&
    document.implementation &&
    document.implementation.hasFeature &&
    // always returns true in newer browsers as per the standard.
    // @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature
    document.implementation.hasFeature('', '') !== true
  ) {
    // This is the only way to test support for the `wheel` event in IE9+.
    isSupported = document.implementation.hasFeature('Events.wheel', '3.0');
  }

  return isSupported;
}
const Mousewheel = {
  lastScrollTime: now(),
  lastEventBeforeSnap: undefined,
  recentWheelEvents: [],
  event() {
    const window = getWindow();
    if (window.navigator.userAgent.indexOf('firefox') > -1) return 'DOMMouseScroll';
    return isEventSupported() ? 'wheel' : 'mousewheel';
  },
  normalize(e) {
    // Reasonable defaults
    const PIXEL_STEP = 10;
    const LINE_HEIGHT = 40;
    const PAGE_HEIGHT = 800;

    let sX = 0;
    let sY = 0; // spinX, spinY
    let pX = 0;
    let pY = 0; // pixelX, pixelY

    // Legacy
    if ('detail' in e) {
      sY = e.detail;
    }
    if ('wheelDelta' in e) {
      sY = -e.wheelDelta / 120;
    }
    if ('wheelDeltaY' in e) {
      sY = -e.wheelDeltaY / 120;
    }
    if ('wheelDeltaX' in e) {
      sX = -e.wheelDeltaX / 120;
    }

    // side scrolling on FF with DOMMouseScroll
    if ('axis' in e && e.axis === e.HORIZONTAL_AXIS) {
      sX = sY;
      sY = 0;
    }

    pX = sX * PIXEL_STEP;
    pY = sY * PIXEL_STEP;

    if ('deltaY' in e) {
      pY = e.deltaY;
    }
    if ('deltaX' in e) {
      pX = e.deltaX;
    }

    if (e.shiftKey && !pX) {
      // if user scrolls with shift he wants horizontal scroll
      pX = pY;
      pY = 0;
    }

    if ((pX || pY) && e.deltaMode) {
      if (e.deltaMode === 1) {
        // delta in LINE units
        pX *= LINE_HEIGHT;
        pY *= LINE_HEIGHT;
      } else {
        // delta in PAGE units
        pX *= PAGE_HEIGHT;
        pY *= PAGE_HEIGHT;
      }
    }

    // Fall-back if spin cannot be determined
    if (pX && !sX) {
      sX = pX < 1 ? -1 : 1;
    }
    if (pY && !sY) {
      sY = pY < 1 ? -1 : 1;
    }

    return {
      spinX: sX,
      spinY: sY,
      pixelX: pX,
      pixelY: pY,
    };
  },
  handleMouseEnter() {
    const swiper = this;
    swiper.mouseEntered = true;
  },
  handleMouseLeave() {
    const swiper = this;
    swiper.mouseEntered = false;
  },
  handle(event) {
    let e = event;
    const swiper = this;
    const params = swiper.params.mousewheel;

    if (swiper.params.cssMode) {
      e.preventDefault();
    }

    let target = swiper.$el;
    if (swiper.params.mousewheel.eventsTarget !== 'container') {
      target = $(swiper.params.mousewheel.eventsTarget);
    }
    if (!swiper.mouseEntered && !target[0].contains(e.target) && !params.releaseOnEdges)
      return true;

    if (e.originalEvent) e = e.originalEvent; // jquery fix
    let delta = 0;
    const rtlFactor = swiper.rtlTranslate ? -1 : 1;

    const data = Mousewheel.normalize(e);

    if (params.forceToAxis) {
      if (swiper.isHorizontal()) {
        if (Math.abs(data.pixelX) > Math.abs(data.pixelY)) delta = -data.pixelX * rtlFactor;
        else return true;
      } else if (Math.abs(data.pixelY) > Math.abs(data.pixelX)) delta = -data.pixelY;
      else return true;
    } else {
      delta =
        Math.abs(data.pixelX) > Math.abs(data.pixelY) ? -data.pixelX * rtlFactor : -data.pixelY;
    }

    if (delta === 0) return true;

    if (params.invert) delta = -delta;

    if (!swiper.params.freeMode) {
      // Register the new event in a variable which stores the relevant data
      const newEvent = {
        time: now(),
        delta: Math.abs(delta),
        direction: Math.sign(delta),
        raw: event,
      };

      // Keep the most recent events
      const recentWheelEvents = swiper.mousewheel.recentWheelEvents;
      if (recentWheelEvents.length >= 2) {
        recentWheelEvents.shift(); // only store the last N events
      }
      const prevEvent = recentWheelEvents.length
        ? recentWheelEvents[recentWheelEvents.length - 1]
        : undefined;
      recentWheelEvents.push(newEvent);

      // If there is at least one previous recorded event:
      //   If direction has changed or
      //   if the scroll is quicker than the previous one:
      //     Animate the slider.
      // Else (this is the first time the wheel is moved):
      //     Animate the slider.
      if (prevEvent) {
        if (
          newEvent.direction !== prevEvent.direction ||
          newEvent.delta > prevEvent.delta ||
          newEvent.time > prevEvent.time + 150
        ) {
          swiper.mousewheel.animateSlider(newEvent);
        }
      } else {
        swiper.mousewheel.animateSlider(newEvent);
      }

      // If it's time to release the scroll:
      //   Return now so you don't hit the preventDefault.
      if (swiper.mousewheel.releaseScroll(newEvent)) {
        return true;
      }
    } else {
      // Freemode or scrollContainer:

      // If we recently snapped after a momentum scroll, then ignore wheel events
      // to give time for the deceleration to finish. Stop ignoring after 500 msecs
      // or if it's a new scroll (larger delta or inverse sign as last event before
      // an end-of-momentum snap).
      const newEvent = {
        time: now(),
        delta: Math.abs(delta),
        direction: Math.sign(delta),
      };
      const { lastEventBeforeSnap } = swiper.mousewheel;
      const ignoreWheelEvents =
        lastEventBeforeSnap &&
        newEvent.time < lastEventBeforeSnap.time + 500 &&
        newEvent.delta <= lastEventBeforeSnap.delta &&
        newEvent.direction === lastEventBeforeSnap.direction;
      if (!ignoreWheelEvents) {
        swiper.mousewheel.lastEventBeforeSnap = undefined;

        if (swiper.params.loop) {
          swiper.loopFix();
        }
        let position = swiper.getTranslate() + delta * params.sensitivity;
        const wasBeginning = swiper.isBeginning;
        const wasEnd = swiper.isEnd;

        if (position >= swiper.minTranslate()) position = swiper.minTranslate();
        if (position <= swiper.maxTranslate()) position = swiper.maxTranslate();

        swiper.setTransition(0);
        swiper.setTranslate(position);
        swiper.updateProgress();
        swiper.updateActiveIndex();
        swiper.updateSlidesClasses();

        if ((!wasBeginning && swiper.isBeginning) || (!wasEnd && swiper.isEnd)) {
          swiper.updateSlidesClasses();
        }

        if (swiper.params.freeModeSticky) {
          // When wheel scrolling starts with sticky (aka snap) enabled, then detect
          // the end of a momentum scroll by storing recent (N=15?) wheel events.
          // 1. do all N events have decreasing or same (absolute value) delta?
          // 2. did all N events arrive in the last M (M=500?) msecs?
          // 3. does the earliest event have an (absolute value) delta that's
          //    at least P (P=1?) larger than the most recent event's delta?
          // 4. does the latest event have a delta that's smaller than Q (Q=6?) pixels?
          // If 1-4 are "yes" then we're near the end of a momuntum scroll deceleration.
          // Snap immediately and ignore remaining wheel events in this scroll.
          // See comment above for "remaining wheel events in this scroll" determination.
          // If 1-4 aren't satisfied, then wait to snap until 500ms after the last event.
          clearTimeout(swiper.mousewheel.timeout);
          swiper.mousewheel.timeout = undefined;
          const recentWheelEvents = swiper.mousewheel.recentWheelEvents;
          if (recentWheelEvents.length >= 15) {
            recentWheelEvents.shift(); // only store the last N events
          }
          const prevEvent = recentWheelEvents.length
            ? recentWheelEvents[recentWheelEvents.length - 1]
            : undefined;
          const firstEvent = recentWheelEvents[0];
          recentWheelEvents.push(newEvent);
          if (
            prevEvent &&
            (newEvent.delta > prevEvent.delta || newEvent.direction !== prevEvent.direction)
          ) {
            // Increasing or reverse-sign delta means the user started scrolling again. Clear the wheel event log.
            recentWheelEvents.splice(0);
          } else if (
            recentWheelEvents.length >= 15 &&
            newEvent.time - firstEvent.time < 500 &&
            firstEvent.delta - newEvent.delta >= 1 &&
            newEvent.delta <= 6
          ) {
            // We're at the end of the deceleration of a momentum scroll, so there's no need
            // to wait for more events. Snap ASAP on the next tick.
            // Also, because there's some remaining momentum we'll bias the snap in the
            // direction of the ongoing scroll because it's better UX for the scroll to snap
            // in the same direction as the scroll instead of reversing to snap.  Therefore,
            // if it's already scrolled more than 20% in the current direction, keep going.
            const snapToThreshold = delta > 0 ? 0.8 : 0.2;
            swiper.mousewheel.lastEventBeforeSnap = newEvent;
            recentWheelEvents.splice(0);
            swiper.mousewheel.timeout = nextTick(() => {
              swiper.slideToClosest(swiper.params.speed, true, undefined, snapToThreshold);
            }, 0); // no delay; move on next tick
          }
          if (!swiper.mousewheel.timeout) {
            // if we get here, then we haven't detected the end of a momentum scroll, so
            // we'll consider a scroll "complete" when there haven't been any wheel events
            // for 500ms.
            swiper.mousewheel.timeout = nextTick(() => {
              const snapToThreshold = 0.5;
              swiper.mousewheel.lastEventBeforeSnap = newEvent;
              recentWheelEvents.splice(0);
              swiper.slideToClosest(swiper.params.speed, true, undefined, snapToThreshold);
            }, 500);
          }
        }

        // Emit event
        if (!ignoreWheelEvents) swiper.emit('scroll', e);

        // Stop autoplay
        if (swiper.params.autoplay && swiper.params.autoplayDisableOnInteraction)
          swiper.autoplay.stop();
        // Return page scroll on edge positions
        if (position === swiper.minTranslate() || position === swiper.maxTranslate()) return true;
      }
    }

    if (e.preventDefault) e.preventDefault();
    else e.returnValue = false;
    return false;
  },
  animateSlider(newEvent) {
    const swiper = this;
    const window = getWindow();

    if (
      this.params.mousewheel.preventSwipeThresholdDelta &&
      newEvent.delta < this.params.mousewheel.preventSwipeThresholdDelta
    ) {
      // Prevent if delta of wheel scroll delta is below configured threshold
      return false;
    }

    if (
      this.params.mousewheel.preventSwipeThresholdTime &&
      now() - swiper.mousewheel.lastScrollTime < this.params.mousewheel.preventSwipeThresholdTime
    ) {
      // Prevent if time between scrolls is below configured threshold
      return false;
    }

    // If the movement is NOT big enough and
    // if the last time the user scrolled was too close to the current one (avoid continuously triggering the slider):
    //   Don't go any further (avoid insignificant scroll movement).
    if (newEvent.delta >= 6 && now() - swiper.mousewheel.lastScrollTime < 60) {
      // Return false as a default
      return true;
    }
    // If user is scrolling towards the end:
    //   If the slider hasn't hit the latest slide or
    //   if the slider is a loop and
    //   if the slider isn't moving right now:
    //     Go to next slide and
    //     emit a scroll event.
    // Else (the user is scrolling towards the beginning) and
    // if the slider hasn't hit the first slide or
    // if the slider is a loop and
    // if the slider isn't moving right now:
    //   Go to prev slide and
    //   emit a scroll event.
    if (newEvent.direction < 0) {
      if ((!swiper.isEnd || swiper.params.loop) && !swiper.animating) {
        swiper.slideNext();
        swiper.emit('scroll', newEvent.raw);
      }
    } else if ((!swiper.isBeginning || swiper.params.loop) && !swiper.animating) {
      swiper.slidePrev();
      swiper.emit('scroll', newEvent.raw);
    }
    // If you got here is because an animation has been triggered so store the current time
    swiper.mousewheel.lastScrollTime = new window.Date().getTime();
    // Return false as a default
    return false;
  },
  releaseScroll(newEvent) {
    const swiper = this;
    const params = swiper.params.mousewheel;
    if (newEvent.direction < 0) {
      if (swiper.isEnd && !swiper.params.loop && params.releaseOnEdges) {
        // Return true to animate scroll on edges
        return true;
      }
    } else if (swiper.isBeginning && !swiper.params.loop && params.releaseOnEdges) {
      // Return true to animate scroll on edges
      return true;
    }
    return false;
  },
  enable() {
    const swiper = this;
    const event = Mousewheel.event();
    if (swiper.params.cssMode) {
      swiper.wrapperEl.removeEventListener(event, swiper.mousewheel.handle);
      return true;
    }
    if (!event) return false;
    if (swiper.mousewheel.enabled) return false;
    let target = swiper.$el;
    if (swiper.params.mousewheel.eventsTarget !== 'container') {
      target = $(swiper.params.mousewheel.eventsTarget);
    }
    target.on('mouseenter', swiper.mousewheel.handleMouseEnter);
    target.on('mouseleave', swiper.mousewheel.handleMouseLeave);
    target.on(event, swiper.mousewheel.handle);
    swiper.mousewheel.enabled = true;
    return true;
  },
  disable() {
    const swiper = this;
    const event = Mousewheel.event();
    if (swiper.params.cssMode) {
      swiper.wrapperEl.addEventListener(event, swiper.mousewheel.handle);
      return true;
    }
    if (!event) return false;
    if (!swiper.mousewheel.enabled) return false;
    let target = swiper.$el;
    if (swiper.params.mousewheel.eventsTarget !== 'container') {
      target = $(swiper.params.mousewheel.eventsTarget);
    }
    target.off(event, swiper.mousewheel.handle);
    swiper.mousewheel.enabled = false;
    return true;
  },
};

export default {
  name: 'mousewheel',
  params: {
    mousewheel: {
      enabled: false,
      releaseOnEdges: false,
      invert: false,
      forceToAxis: false,
      sensitivity: 1,
      eventsTarget: 'container',
      preventSwipeThresholdDelta: null,
      preventSwipeThresholdTime: null,
    },
  },
  create() {
    const swiper = this;
    bindModuleMethods(swiper, {
      mousewheel: {
        enabled: false,
        lastScrollTime: now(),
        lastEventBeforeSnap: undefined,
        recentWheelEvents: [],

        enable: Mousewheel.enable,
        disable: Mousewheel.disable,
        handle: Mousewheel.handle,
        handleMouseEnter: Mousewheel.handleMouseEnter,
        handleMouseLeave: Mousewheel.handleMouseLeave,
        animateSlider: Mousewheel.animateSlider,
        releaseScroll: Mousewheel.releaseScroll,
      },
    });
  },
  on: {
    init(swiper) {
      if (!swiper.params.mousewheel.enabled && swiper.params.cssMode) {
        swiper.mousewheel.disable();
      }
      if (swiper.params.mousewheel.enabled) swiper.mousewheel.enable();
    },
    destroy(swiper) {
      if (swiper.params.cssMode) {
        swiper.mousewheel.enable();
      }
      if (swiper.mousewheel.enabled) swiper.mousewheel.disable();
    },
  },
};
<|endoftext|>


<commit_before>iD.behavior.Select = function(context) {
    function keydown() {
        if (d3.event && d3.event.shiftKey) {
            context.surface()
                .classed('behavior-multiselect', true);
        }
    }

    function keyup() {
        if (!d3.event || !d3.event.shiftKey) {
            context.surface()
                .classed('behavior-multiselect', false);
        }
    }

    function click() {
        var datum = d3.event.target.__data__;
        var lasso = d3.select('#surface .lasso').node();
        if (!(datum instanceof iD.Entity)) {
            if (!d3.event.shiftKey && !lasso)
                context.enter(iD.modes.Browse(context));

        } else if (!d3.event.shiftKey && !lasso) {
            // Avoid re-entering Select mode with same entity.
            if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
                context.enter(iD.modes.Select(context, [datum.id]));
            } else {
                context.mode().reselect();
            }
        } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
            var selectedIDs = _.without(context.selectedIDs(), datum.id);
            context.enter(selectedIDs.length ?
                iD.modes.Select(context, selectedIDs) :
                iD.modes.Browse(context));

        } else {
            context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
        }
    }

    var behavior = function(selection) {
        d3.select(window)
            .on('keydown.select', keydown)
            .on('keyup.select', keyup);

        selection.on('click.select', click);

        keydown();
    };

    behavior.off = function(selection) {
        d3.select(window)
            .on('keydown.select', null)
            .on('keyup.select', null);

        selection.on('click.select', null);

        keyup();
    };

    return behavior;
};
<commit_msg>Unsuppress menu when reselecting (fixes #2533)<commit_after>iD.behavior.Select = function(context) {
    function keydown() {
        if (d3.event && d3.event.shiftKey) {
            context.surface()
                .classed('behavior-multiselect', true);
        }
    }

    function keyup() {
        if (!d3.event || !d3.event.shiftKey) {
            context.surface()
                .classed('behavior-multiselect', false);
        }
    }

    function click() {
        var datum = d3.event.target.__data__;
        var lasso = d3.select('#surface .lasso').node();
        if (!(datum instanceof iD.Entity)) {
            if (!d3.event.shiftKey && !lasso)
                context.enter(iD.modes.Browse(context));

        } else if (!d3.event.shiftKey && !lasso) {
            // Avoid re-entering Select mode with same entity.
            if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
                context.enter(iD.modes.Select(context, [datum.id]));
            } else {
                context.mode().suppressMenu(false).reselect();
            }
        } else if (context.selectedIDs().indexOf(datum.id) >= 0) {
            var selectedIDs = _.without(context.selectedIDs(), datum.id);
            context.enter(selectedIDs.length ?
                iD.modes.Select(context, selectedIDs) :
                iD.modes.Browse(context));

        } else {
            context.enter(iD.modes.Select(context, context.selectedIDs().concat([datum.id])));
        }
    }

    var behavior = function(selection) {
        d3.select(window)
            .on('keydown.select', keydown)
            .on('keyup.select', keyup);

        selection.on('click.select', click);

        keydown();
    };

    behavior.off = function(selection) {
        d3.select(window)
            .on('keydown.select', null)
            .on('keyup.select', null);

        selection.on('click.select', null);

        keyup();
    };

    return behavior;
};
<|endoftext|>


<commit_before>Module.preRun = [
  function ()
  {
    FS.mkdir('/test');
    FS.mount(NODEFS, { root: '../../Test/test' }, '/test');

    FS.mkdir('/resources');
    FS.mount(NODEFS, { root: './resources' }, '/resources');

    //
    // Mock Implementations
    //
    global.navigator = {
      getGamepads: function() {
        return [];
      }
    }

    global.Module = {
      canvas: {
        style: {}
      }
    }
  }
]
<commit_msg>[Web][Test] fix mock<commit_after>Module.preRun = [
  function ()
  {
    FS.mkdir('/test');
    FS.mount(NODEFS, { root: '../../Test/test' }, '/test');

    FS.mkdir('/resources');
    FS.mount(NODEFS, { root: './resources' }, '/resources');

    //
    // Mock Implementations
    //
    global.navigator = {
      getGamepads: function() {
        return [];
      }
    }

    Module.canvas = {
      style: {}
    }
  }
]
<|endoftext|>


<commit_before>'use strict';

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

const Exchange = require ('./base/Exchange');

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

module.exports = class independentreserve extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'independentreserve',
            'name': 'Independent Reserve',
            'countries': [ 'AU', 'NZ' ], // Australia, New Zealand
            'rateLimit': 1000,
            'has': {
                'CORS': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/87182090-1e9e9080-c2ec-11ea-8e49-563db9a38f37.jpg',
                'api': {
                    'public': 'https://api.independentreserve.com/Public',
                    'private': 'https://api.independentreserve.com/Private',
                },
                'www': 'https://www.independentreserve.com',
                'doc': 'https://www.independentreserve.com/API',
            },
            'api': {
                'public': {
                    'get': [
                        'GetValidPrimaryCurrencyCodes',
                        'GetValidSecondaryCurrencyCodes',
                        'GetValidLimitOrderTypes',
                        'GetValidMarketOrderTypes',
                        'GetValidOrderTypes',
                        'GetValidTransactionTypes',
                        'GetMarketSummary',
                        'GetOrderBook',
                        'GetAllOrders',
                        'GetTradeHistorySummary',
                        'GetRecentTrades',
                        'GetFxRates',
                    ],
                },
                'private': {
                    'post': [
                        'PlaceLimitOrder',
                        'PlaceMarketOrder',
                        'CancelOrder',
                        'GetOpenOrders',
                        'GetClosedOrders',
                        'GetClosedFilledOrders',
                        'GetOrderDetails',
                        'GetAccounts',
                        'GetTransactions',
                        'GetDigitalCurrencyDepositAddress',
                        'GetDigitalCurrencyDepositAddresses',
                        'SynchDigitalCurrencyDepositAddressWithBlockchain',
                        'WithdrawDigitalCurrency',
                        'RequestFiatWithdrawal',
                        'GetTrades',
                        'GetBrokerageFees',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'taker': 0.5 / 100,
                    'maker': 0.5 / 100,
                    'percentage': true,
                    'tierBased': false,
                },
            },
            'commonCurrencies': {
                'PLA': 'PlayChip',
            },
        });
    }

    async fetchMarkets (params = {}) {
        const baseCurrencies = await this.publicGetGetValidPrimaryCurrencyCodes (params);
        const quoteCurrencies = await this.publicGetGetValidSecondaryCurrencyCodes (params);
        const result = [];
        for (let i = 0; i < baseCurrencies.length; i++) {
            const baseId = baseCurrencies[i];
            const base = this.safeCurrencyCode (baseId);
            for (let j = 0; j < quoteCurrencies.length; j++) {
                const quoteId = quoteCurrencies[j];
                const quote = this.safeCurrencyCode (quoteId);
                const id = baseId + '/' + quoteId;
                const symbol = base + '/' + quote;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': id,
                    'active': undefined,
                    'precision': this.precision,
                    'limits': this.limits,
                });
            }
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const balances = await this.privatePostGetAccounts (params);
        const result = { 'info': balances };
        for (let i = 0; i < balances.length; i++) {
            const balance = balances[i];
            const currencyId = this.safeString (balance, 'CurrencyCode');
            const code = this.safeCurrencyCode (currencyId);
            const account = this.account ();
            account['free'] = this.safeFloat (balance, 'AvailableBalance');
            account['total'] = this.safeFloat (balance, 'TotalBalance');
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
        };
        const response = await this.publicGetGetOrderBook (this.extend (request, params));
        const timestamp = this.parse8601 (this.safeString (response, 'CreatedTimestampUtc'));
        return this.parseOrderBook (response, timestamp, 'BuyOrders', 'SellOrders', 'Price', 'Volume');
    }

    parseTicker (ticker, market = undefined) {
        const timestamp = this.parse8601 (this.safeString (ticker, 'CreatedTimestampUtc'));
        let symbol = undefined;
        if (market) {
            symbol = market['symbol'];
        }
        const last = this.safeFloat (ticker, 'LastPrice');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'DayHighestPrice'),
            'low': this.safeFloat (ticker, 'DayLowestPrice'),
            'bid': this.safeFloat (ticker, 'CurrentHighestBidPrice'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'CurrentLowestOfferPrice'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': this.safeFloat (ticker, 'DayAvgPrice'),
            'baseVolume': this.safeFloat (ticker, 'DayVolumeXbtInSecondaryCurrrency'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
        };
        const response = await this.publicGetGetMarketSummary (this.extend (request, params));
        return this.parseTicker (response, market);
    }

    parseOrder (order, market = undefined) {
        //
        //     {
        //         "OrderGuid": "c7347e4c-b865-4c94-8f74-d934d4b0b177",
        //         "CreatedTimestampUtc": "2014-09-23T12:39:34.3817763Z",
        //         "Type": "MarketBid",
        //         "VolumeOrdered": 5.0,
        //         "VolumeFilled": 5.0,
        //         "Price": null,
        //         "AvgPrice": 100.0,
        //         "ReservedAmount": 0.0,
        //         "Status": "Filled",
        //         "PrimaryCurrencyCode": "Xbt",
        //         "SecondaryCurrencyCode": "Usd"
        //     }
        //
        let symbol = undefined;
        const baseId = this.safeString (order, 'PrimaryCurrencyCode');
        const quoteId = this.safeString (order, 'PrimaryCurrencyCode');
        let base = undefined;
        let quote = undefined;
        if ((baseId !== undefined) && (quoteId !== undefined)) {
            base = this.safeCurrencyCode (baseId);
            quote = this.safeCurrencyCode (quoteId);
            symbol = base + '/' + quote;
        } else if (market !== undefined) {
            symbol = market['symbol'];
            base = market['base'];
            quote = market['quote'];
        }
        let orderType = this.safeValue (order, 'Type');
        if (orderType.indexOf ('Market') >= 0) {
            orderType = 'market';
        } else if (orderType.indexOf ('Limit') >= 0) {
            orderType = 'limit';
        }
        let side = undefined;
        if (orderType.indexOf ('Bid') >= 0) {
            side = 'buy';
        } else if (orderType.indexOf ('Offer') >= 0) {
            side = 'sell';
        }
        const timestamp = this.parse8601 (this.safeString (order, 'CreatedTimestampUtc'));
        let amount = this.safeFloat (order, 'VolumeOrdered');
        if (amount === undefined) {
            amount = this.safeFloat (order, 'Volume');
        }
        const filled = this.safeFloat (order, 'VolumeFilled');
        let remaining = undefined;
        const feeRate = this.safeFloat (order, 'FeePercent');
        let feeCost = undefined;
        if (amount !== undefined) {
            if (filled !== undefined) {
                remaining = amount - filled;
                if (feeRate !== undefined) {
                    feeCost = feeRate * filled;
                }
            }
        }
        const fee = {
            'rate': feeRate,
            'cost': feeCost,
            'currency': base,
        };
        const id = this.safeString (order, 'OrderGuid');
        const status = this.parseOrderStatus (this.safeString (order, 'Status'));
        const cost = this.safeFloat (order, 'Value');
        const average = this.safeFloat (order, 'AvgPrice');
        const price = this.safeFloat (order, 'Price', average);
        return {
            'info': order,
            'id': id,
            'clientOrderId': undefined,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'symbol': symbol,
            'type': orderType,
            'side': side,
            'price': price,
            'cost': cost,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
            'trades': undefined,
        };
    }

    parseOrderStatus (status) {
        const statuses = {
            'Open': 'open',
            'PartiallyFilled': 'open',
            'Filled': 'closed',
            'PartiallyFilledAndCancelled': 'canceled',
            'Cancelled': 'canceled',
            'PartiallyFilledAndExpired': 'canceled',
            'Expired': 'canceled',
        };
        return this.safeString (statuses, status, status);
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        const response = await this.privatePostGetOrderDetails (this.extend ({
            'orderGuid': id,
        }, params));
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market (symbol);
        }
        return this.parseOrder (response, market);
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = 50, params = {}) {
        await this.loadMarkets ();
        const pageIndex = this.safeInteger (params, 'pageIndex', 1);
        if (limit === undefined) {
            limit = 50;
        }
        const request = this.ordered ({
            'pageIndex': pageIndex,
            'pageSize': limit,
        });
        const response = await this.privatePostGetTrades (this.extend (request, params));
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market (symbol);
        }
        return this.parseTrades (response['Data'], market, since, limit);
    }

    parseTrade (trade, market = undefined) {
        const timestamp = this.parse8601 (trade['TradeTimestampUtc']);
        const id = this.safeString (trade, 'TradeGuid');
        const orderId = this.safeString (trade, 'OrderGuid');
        const price = this.safeFloat2 (trade, 'Price', 'SecondaryCurrencyTradePrice');
        const amount = this.safeFloat2 (trade, 'VolumeTraded', 'PrimaryCurrencyAmount');
        let cost = undefined;
        if (price !== undefined) {
            if (amount !== undefined) {
                cost = price * amount;
            }
        }
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        let side = this.safeString (trade, 'OrderType');
        if (side !== undefined) {
            if (side.indexOf ('Bid') >= 0) {
                side = 'buy';
            } else if (side.indexOf ('Offer') >= 0) {
                side = 'sell';
            }
        }
        return {
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': undefined,
            'side': side,
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
            'numberOfRecentTradesToRetrieve': 50, // max = 50
        };
        const response = await this.publicGetGetRecentTrades (this.extend (request, params));
        return this.parseTrades (response['Trades'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const capitalizedOrderType = this.capitalize (type);
        const method = 'privatePostPlace' + capitalizedOrderType + 'Order';
        let orderType = capitalizedOrderType;
        orderType += (side === 'sell') ? 'Offer' : 'Bid';
        const request = this.ordered ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
            'orderType': orderType,
        });
        if (type === 'limit') {
            request['price'] = price;
        }
        request['volume'] = amount;
        const response = await this[method] (this.extend (request, params));
        return {
            'info': response,
            'id': response['OrderGuid'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'orderGuid': id,
        };
        return await this.privatePostCancelOrder (this.extend (request, params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/' + path;
        if (api === 'public') {
            if (Object.keys (params).length) {
                url += '?' + this.urlencode (params);
            }
        } else {
            this.checkRequiredCredentials ();
            const nonce = this.nonce ();
            const auth = [
                url,
                'apiKey=' + this.apiKey,
                'nonce=' + nonce.toString (),
            ];
            const keys = Object.keys (params);
            for (let i = 0; i < keys.length; i++) {
                const key = keys[i];
                const value = params[key].toString ();
                auth.push (key + '=' + value);
            }
            const message = auth.join (',');
            const signature = this.hmac (this.encode (message), this.encode (this.secret));
            const query = this.ordered ({});
            query['apiKey'] = this.apiKey;
            query['nonce'] = nonce;
            query['signature'] = signature.toUpperCase ();
            for (let i = 0; i < keys.length; i++) {
                const key = keys[i];
                query[key] = params[key];
            }
            body = this.json (query);
            headers = { 'Content-Type': 'application/json' };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }
};
<commit_msg>independentreserve metainfo updates<commit_after>'use strict';

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

const Exchange = require ('./base/Exchange');

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

module.exports = class independentreserve extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'independentreserve',
            'name': 'Independent Reserve',
            'countries': [ 'AU', 'NZ' ], // Australia, New Zealand
            'rateLimit': 1000,
            'has': {
                'cancelOrder': true,
                'CORS': false,
                'createOrder': true,
                'fetchBalance': true,
                'fetchMarkets': true,
                'fetchMyTrades': true,
                'fetchOrder': true,
                'fetchOrderBook': true,
                'fetchTicker': true,
                'fetchTrades': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/87182090-1e9e9080-c2ec-11ea-8e49-563db9a38f37.jpg',
                'api': {
                    'public': 'https://api.independentreserve.com/Public',
                    'private': 'https://api.independentreserve.com/Private',
                },
                'www': 'https://www.independentreserve.com',
                'doc': 'https://www.independentreserve.com/API',
            },
            'api': {
                'public': {
                    'get': [
                        'GetValidPrimaryCurrencyCodes',
                        'GetValidSecondaryCurrencyCodes',
                        'GetValidLimitOrderTypes',
                        'GetValidMarketOrderTypes',
                        'GetValidOrderTypes',
                        'GetValidTransactionTypes',
                        'GetMarketSummary',
                        'GetOrderBook',
                        'GetAllOrders',
                        'GetTradeHistorySummary',
                        'GetRecentTrades',
                        'GetFxRates',
                    ],
                },
                'private': {
                    'post': [
                        'PlaceLimitOrder',
                        'PlaceMarketOrder',
                        'CancelOrder',
                        'GetOpenOrders',
                        'GetClosedOrders',
                        'GetClosedFilledOrders',
                        'GetOrderDetails',
                        'GetAccounts',
                        'GetTransactions',
                        'GetDigitalCurrencyDepositAddress',
                        'GetDigitalCurrencyDepositAddresses',
                        'SynchDigitalCurrencyDepositAddressWithBlockchain',
                        'WithdrawDigitalCurrency',
                        'RequestFiatWithdrawal',
                        'GetTrades',
                        'GetBrokerageFees',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'taker': 0.5 / 100,
                    'maker': 0.5 / 100,
                    'percentage': true,
                    'tierBased': false,
                },
            },
            'commonCurrencies': {
                'PLA': 'PlayChip',
            },
        });
    }

    async fetchMarkets (params = {}) {
        const baseCurrencies = await this.publicGetGetValidPrimaryCurrencyCodes (params);
        const quoteCurrencies = await this.publicGetGetValidSecondaryCurrencyCodes (params);
        const result = [];
        for (let i = 0; i < baseCurrencies.length; i++) {
            const baseId = baseCurrencies[i];
            const base = this.safeCurrencyCode (baseId);
            for (let j = 0; j < quoteCurrencies.length; j++) {
                const quoteId = quoteCurrencies[j];
                const quote = this.safeCurrencyCode (quoteId);
                const id = baseId + '/' + quoteId;
                const symbol = base + '/' + quote;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': id,
                    'active': undefined,
                    'precision': this.precision,
                    'limits': this.limits,
                });
            }
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const balances = await this.privatePostGetAccounts (params);
        const result = { 'info': balances };
        for (let i = 0; i < balances.length; i++) {
            const balance = balances[i];
            const currencyId = this.safeString (balance, 'CurrencyCode');
            const code = this.safeCurrencyCode (currencyId);
            const account = this.account ();
            account['free'] = this.safeFloat (balance, 'AvailableBalance');
            account['total'] = this.safeFloat (balance, 'TotalBalance');
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
        };
        const response = await this.publicGetGetOrderBook (this.extend (request, params));
        const timestamp = this.parse8601 (this.safeString (response, 'CreatedTimestampUtc'));
        return this.parseOrderBook (response, timestamp, 'BuyOrders', 'SellOrders', 'Price', 'Volume');
    }

    parseTicker (ticker, market = undefined) {
        const timestamp = this.parse8601 (this.safeString (ticker, 'CreatedTimestampUtc'));
        let symbol = undefined;
        if (market) {
            symbol = market['symbol'];
        }
        const last = this.safeFloat (ticker, 'LastPrice');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'DayHighestPrice'),
            'low': this.safeFloat (ticker, 'DayLowestPrice'),
            'bid': this.safeFloat (ticker, 'CurrentHighestBidPrice'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'CurrentLowestOfferPrice'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': this.safeFloat (ticker, 'DayAvgPrice'),
            'baseVolume': this.safeFloat (ticker, 'DayVolumeXbtInSecondaryCurrrency'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
        };
        const response = await this.publicGetGetMarketSummary (this.extend (request, params));
        return this.parseTicker (response, market);
    }

    parseOrder (order, market = undefined) {
        //
        //     {
        //         "OrderGuid": "c7347e4c-b865-4c94-8f74-d934d4b0b177",
        //         "CreatedTimestampUtc": "2014-09-23T12:39:34.3817763Z",
        //         "Type": "MarketBid",
        //         "VolumeOrdered": 5.0,
        //         "VolumeFilled": 5.0,
        //         "Price": null,
        //         "AvgPrice": 100.0,
        //         "ReservedAmount": 0.0,
        //         "Status": "Filled",
        //         "PrimaryCurrencyCode": "Xbt",
        //         "SecondaryCurrencyCode": "Usd"
        //     }
        //
        let symbol = undefined;
        const baseId = this.safeString (order, 'PrimaryCurrencyCode');
        const quoteId = this.safeString (order, 'PrimaryCurrencyCode');
        let base = undefined;
        let quote = undefined;
        if ((baseId !== undefined) && (quoteId !== undefined)) {
            base = this.safeCurrencyCode (baseId);
            quote = this.safeCurrencyCode (quoteId);
            symbol = base + '/' + quote;
        } else if (market !== undefined) {
            symbol = market['symbol'];
            base = market['base'];
            quote = market['quote'];
        }
        let orderType = this.safeValue (order, 'Type');
        if (orderType.indexOf ('Market') >= 0) {
            orderType = 'market';
        } else if (orderType.indexOf ('Limit') >= 0) {
            orderType = 'limit';
        }
        let side = undefined;
        if (orderType.indexOf ('Bid') >= 0) {
            side = 'buy';
        } else if (orderType.indexOf ('Offer') >= 0) {
            side = 'sell';
        }
        const timestamp = this.parse8601 (this.safeString (order, 'CreatedTimestampUtc'));
        let amount = this.safeFloat (order, 'VolumeOrdered');
        if (amount === undefined) {
            amount = this.safeFloat (order, 'Volume');
        }
        const filled = this.safeFloat (order, 'VolumeFilled');
        let remaining = undefined;
        const feeRate = this.safeFloat (order, 'FeePercent');
        let feeCost = undefined;
        if (amount !== undefined) {
            if (filled !== undefined) {
                remaining = amount - filled;
                if (feeRate !== undefined) {
                    feeCost = feeRate * filled;
                }
            }
        }
        const fee = {
            'rate': feeRate,
            'cost': feeCost,
            'currency': base,
        };
        const id = this.safeString (order, 'OrderGuid');
        const status = this.parseOrderStatus (this.safeString (order, 'Status'));
        const cost = this.safeFloat (order, 'Value');
        const average = this.safeFloat (order, 'AvgPrice');
        const price = this.safeFloat (order, 'Price', average);
        return {
            'info': order,
            'id': id,
            'clientOrderId': undefined,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'lastTradeTimestamp': undefined,
            'symbol': symbol,
            'type': orderType,
            'side': side,
            'price': price,
            'cost': cost,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
            'trades': undefined,
        };
    }

    parseOrderStatus (status) {
        const statuses = {
            'Open': 'open',
            'PartiallyFilled': 'open',
            'Filled': 'closed',
            'PartiallyFilledAndCancelled': 'canceled',
            'Cancelled': 'canceled',
            'PartiallyFilledAndExpired': 'canceled',
            'Expired': 'canceled',
        };
        return this.safeString (statuses, status, status);
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        const response = await this.privatePostGetOrderDetails (this.extend ({
            'orderGuid': id,
        }, params));
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market (symbol);
        }
        return this.parseOrder (response, market);
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = 50, params = {}) {
        await this.loadMarkets ();
        const pageIndex = this.safeInteger (params, 'pageIndex', 1);
        if (limit === undefined) {
            limit = 50;
        }
        const request = this.ordered ({
            'pageIndex': pageIndex,
            'pageSize': limit,
        });
        const response = await this.privatePostGetTrades (this.extend (request, params));
        let market = undefined;
        if (symbol !== undefined) {
            market = this.market (symbol);
        }
        return this.parseTrades (response['Data'], market, since, limit);
    }

    parseTrade (trade, market = undefined) {
        const timestamp = this.parse8601 (trade['TradeTimestampUtc']);
        const id = this.safeString (trade, 'TradeGuid');
        const orderId = this.safeString (trade, 'OrderGuid');
        const price = this.safeFloat2 (trade, 'Price', 'SecondaryCurrencyTradePrice');
        const amount = this.safeFloat2 (trade, 'VolumeTraded', 'PrimaryCurrencyAmount');
        let cost = undefined;
        if (price !== undefined) {
            if (amount !== undefined) {
                cost = price * amount;
            }
        }
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        let side = this.safeString (trade, 'OrderType');
        if (side !== undefined) {
            if (side.indexOf ('Bid') >= 0) {
                side = 'buy';
            } else if (side.indexOf ('Offer') >= 0) {
                side = 'sell';
            }
        }
        return {
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': undefined,
            'side': side,
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
            'numberOfRecentTradesToRetrieve': 50, // max = 50
        };
        const response = await this.publicGetGetRecentTrades (this.extend (request, params));
        return this.parseTrades (response['Trades'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const capitalizedOrderType = this.capitalize (type);
        const method = 'privatePostPlace' + capitalizedOrderType + 'Order';
        let orderType = capitalizedOrderType;
        orderType += (side === 'sell') ? 'Offer' : 'Bid';
        const request = this.ordered ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
            'orderType': orderType,
        });
        if (type === 'limit') {
            request['price'] = price;
        }
        request['volume'] = amount;
        const response = await this[method] (this.extend (request, params));
        return {
            'info': response,
            'id': response['OrderGuid'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'orderGuid': id,
        };
        return await this.privatePostCancelOrder (this.extend (request, params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/' + path;
        if (api === 'public') {
            if (Object.keys (params).length) {
                url += '?' + this.urlencode (params);
            }
        } else {
            this.checkRequiredCredentials ();
            const nonce = this.nonce ();
            const auth = [
                url,
                'apiKey=' + this.apiKey,
                'nonce=' + nonce.toString (),
            ];
            const keys = Object.keys (params);
            for (let i = 0; i < keys.length; i++) {
                const key = keys[i];
                const value = params[key].toString ();
                auth.push (key + '=' + value);
            }
            const message = auth.join (',');
            const signature = this.hmac (this.encode (message), this.encode (this.secret));
            const query = this.ordered ({});
            query['apiKey'] = this.apiKey;
            query['nonce'] = nonce;
            query['signature'] = signature.toUpperCase ();
            for (let i = 0; i < keys.length; i++) {
                const key = keys[i];
                query[key] = params[key];
            }
            body = this.json (query);
            headers = { 'Content-Type': 'application/json' };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }
};
<|endoftext|>