<commit_before>goog.provide('gmf.QueryManager');

goog.require('gmf');
goog.require('gmf.Themes');
goog.require('ngeo.Query');

/**
 * The QueryManager service, uses the
 * c2cgeoportal's themes to configure ngeo's query service with each layer
 * found.
 *
 * Used UI metadata:
 *
 *  * identifierAttributeField: Field used to identify the feature (like a title).
 *  * queryLayers: The alternate layers used to do the query.
 *  * wmsLayers: The layers used to do the query, used if queryLayers is not provided.
 *  * wmsUrl: An alternate wmsUrl used to do the query (essential to query WMTS layer).
 *
 * @constructor
 * @param {ngeo.Query} ngeoQuery The ngeo Query service.
 * @param {gmf.Themes} gmfThemes The gmf Themes service.
 * @param {string} gmfWmsUrl URL to the wms service to use by default.
 * @ngInject
 * @ngdoc service
 * @ngname gmfThemes
 */
gmf.QueryManager = function(ngeoQuery, gmfThemes, gmfWmsUrl) {

  /**
   * @type {ngeo.Query}
   * @private
   */
  this.ngeoQuery_ = ngeoQuery;

  /**
   * @type {gmf.Themes}
   * @private
   */
  this.gmfThemes_ = gmfThemes;

  /**
   * @type {string}
   * @private
   */
  this.gmfWmsUrl_ = gmfWmsUrl;

  /**
   * @type {Array.<ngeox.QuerySource>}
   * @private
   */
  this.sources_ = [];

  /**
   * @type {Object.<number|string, ngeox.QuerySource>}
   * @private
   */
  this.cache_ = {};

  this.gmfThemes_.getThemesObject().then(function(themes) {
    // create sources for each themes
    for (var i = 0, len = themes.length; i < len; i++) {
      this.createSources_(themes[i]);
    }
    // then add them to the query service
    this.ngeoQuery_.addSources(this.sources_);
  }.bind(this));
};

/**
 * Create and add a source for the query service from the GMF theme node if
 * it has no children, otherwise create the sources for each child node if
 * it has any.
 * @param {GmfThemesNode} node Theme layer node.
 * @private
 */
gmf.QueryManager.prototype.createSources_ = function(node) {
  var meta = node.metadata;
  var children = node.children;
  var id = node.id;
  var identifierAttributeField = meta['identifierAttributeField'];
  var layers = meta['wmsLayers'] || meta['queryLayers'] || node.layers;
  var name = node.name;
  var url = meta['wmsUrl'] || node.url || this.gmfWmsUrl_;
  var validateLayerParams = false;

  if (children) {
    for (var i = 0, len = children.length; i < len; i++) {
      this.createSources_(children[i]);
    }
  } else {
    if (!this.cache_[id]) {

      // Some nodes have child layers, i.e. a list of layer names that are
      // part of a group. The name of the group itself can't be used 'as-is'
      // as an identifier of the layers for this source. For example, a
      // group named 'osm' might result in returning 'restaurant' features.
      // This override makes sure that those layer names are used instead of
      // the original one.
      if (node.childLayers && node.childLayers.length) {
        var childLayerNames = [];
        node.childLayers.forEach(function(childLayer) {
          childLayerNames.push(childLayer.name);
        }, this);
        layers = childLayerNames.join(',');
      } else if (node.type === 'WMS') {
        validateLayerParams = true;
      }

      var source = {
        'id': id,
        'identifierAttributeField': identifierAttributeField,
        'label': name,
        'params': {'LAYERS': layers},
        'url': url,
        'validateLayerParams': validateLayerParams
      };
      this.cache_[id] = source;
      this.sources_.push(source);
    }
  }
};

gmf.module.service('gmfQueryManager', gmf.QueryManager);
<commit_msg>Don't create query sources for WMTS layers without wmsUrl<commit_after>goog.provide('gmf.QueryManager');

goog.require('gmf');
goog.require('gmf.Themes');
goog.require('ngeo.Query');

/**
 * The QueryManager service, uses the
 * c2cgeoportal's themes to configure ngeo's query service with each layer
 * found.
 *
 * Used UI metadata:
 *
 *  * identifierAttributeField: Field used to identify the feature (like a title).
 *  * queryLayers: The alternate layers used to do the query.
 *  * wmsLayers: The layers used to do the query, used if queryLayers is not provided.
 *  * wmsUrl: An alternate wmsUrl used to do the query (essential to query WMTS layer).
 *
 * @constructor
 * @param {ngeo.Query} ngeoQuery The ngeo Query service.
 * @param {gmf.Themes} gmfThemes The gmf Themes service.
 * @param {string} gmfWmsUrl URL to the wms service to use by default.
 * @ngInject
 * @ngdoc service
 * @ngname gmfThemes
 */
gmf.QueryManager = function(ngeoQuery, gmfThemes, gmfWmsUrl) {

  /**
   * @type {ngeo.Query}
   * @private
   */
  this.ngeoQuery_ = ngeoQuery;

  /**
   * @type {gmf.Themes}
   * @private
   */
  this.gmfThemes_ = gmfThemes;

  /**
   * @type {string}
   * @private
   */
  this.gmfWmsUrl_ = gmfWmsUrl;

  /**
   * @type {Array.<ngeox.QuerySource>}
   * @private
   */
  this.sources_ = [];

  /**
   * @type {Object.<number|string, ngeox.QuerySource>}
   * @private
   */
  this.cache_ = {};

  this.gmfThemes_.getThemesObject().then(function(themes) {
    // create sources for each themes
    for (var i = 0, len = themes.length; i < len; i++) {
      this.createSources_(themes[i]);
    }
    // then add them to the query service
    this.ngeoQuery_.addSources(this.sources_);
  }.bind(this));
};

/**
 * Create and add a source for the query service from the GMF theme node if
 * it has no children, otherwise create the sources for each child node if
 * it has any.
 * @param {GmfThemesNode} node Theme layer node.
 * @private
 */
gmf.QueryManager.prototype.createSources_ = function(node) {
  var meta = node.metadata;
  var children = node.children;
  var id = node.id;
  var identifierAttributeField = meta['identifierAttributeField'];
  var layers = meta['wmsLayers'] || meta['queryLayers'] || node.layers;
  var name = node.name;
  var url = meta['wmsUrl'] || node.url || this.gmfWmsUrl_;
  var validateLayerParams = false;

  // don't create sources for WMTS layers without wmsUrl, they are not queryable.
  if (node.type === 'WMTS' && !meta['wmsUrl']) {
    return;
  }

  if (children) {
    for (var i = 0, len = children.length; i < len; i++) {
      this.createSources_(children[i]);
    }
  } else {
    if (!this.cache_[id]) {

      // Some nodes have child layers, i.e. a list of layer names that are
      // part of a group. The name of the group itself can't be used 'as-is'
      // as an identifier of the layers for this source. For example, a
      // group named 'osm' might result in returning 'restaurant' features.
      // This override makes sure that those layer names are used instead of
      // the original one.
      if (node.childLayers && node.childLayers.length) {
        var childLayerNames = [];
        node.childLayers.forEach(function(childLayer) {
          childLayerNames.push(childLayer.name);
        }, this);
        layers = childLayerNames.join(',');
      } else if (node.type === 'WMS') {
        validateLayerParams = true;
      }

      var source = {
        'id': id,
        'identifierAttributeField': identifierAttributeField,
        'label': name,
        'params': {'LAYERS': layers},
        'url': url,
        'validateLayerParams': validateLayerParams
      };
      this.cache_[id] = source;
      this.sources_.push(source);
    }
  }
};

gmf.module.service('gmfQueryManager', gmf.QueryManager);
<|endoftext|>


<commit_before>define(
  'ephox.dragster.api.Dragger',

  [
    'ephox.dragster.detect.Blocker',
    'ephox.dragster.detect.Movement',
    'ephox.porkbun.Event',
    'ephox.porkbun.Events',
    'ephox.sugar.api.DomEvent',
    'ephox.sugar.api.Insert',
    'ephox.sugar.api.Remove',
    'global!Array'
  ],

  function (Blocker, Movement, Event, Events, DomEvent, Insert, Remove, Array) {

    var transform = function (mutation, options) {
      var settings = options !== undefined ? options : {};
      var active = false;

      var events = Events.create({
        start: Event([]),
        stop: Event([])
      });

      var blocker = Blocker(settings);
      var movement = Movement();

      var drop = function () {
        Remove.remove(blocker.element());
        if (movement.isOn()) {
          movement.off();
          events.trigger.stop();
        }
      };

      var go = function (parent) {
        Insert.append(parent, blocker.element());
        movement.on();
        events.trigger.start();
      };

      var mouseup = function (event, ui) {
        drop();
      };

      var mousemove = function (event, ui) {
        movement.onEvent(event);
      };

      movement.events.move.bind(function (event) {
        mutation.mutate(event.x(), event.y());
      });

      var on = function () {
        active = true;
      };

      var off = function () {
        active = false;
        // acivate some events here?
      };

      var runIfActive = function (f) {
        return function () {
          var args = Array.prototype.slice.call(arguments, 0);
          if (active) {
            return f.apply(null, args);
          }
        };
      };

      var mup = DomEvent.bind(blocker.element(), 'mouseup', runIfActive(mouseup));
      var mmove = DomEvent.bind(blocker.element(), 'mousemove', runIfActive(mousemove));
      var mout = DomEvent.bind(blocker.element(), 'mouseout', runIfActive(drop));

      var destroy = function () {
        blocker.destroy();
        mup.unbind();
        mmove.unbind();
        mout.unbind();
      };

      return {
        go: go,
        on: on,
        off: off,
        destroy: destroy,
        events: events.registry
      };
    };

    return {
      transform: transform
    };
  }
);
<commit_msg>mousedow for blocker<commit_after>define(
  'ephox.dragster.api.Dragger',

  [
    'ephox.dragster.detect.Blocker',
    'ephox.dragster.detect.Movement',
    'ephox.porkbun.Event',
    'ephox.porkbun.Events',
    'ephox.sugar.api.DomEvent',
    'ephox.sugar.api.Insert',
    'ephox.sugar.api.Remove',
    'global!Array'
  ],

  function (Blocker, Movement, Event, Events, DomEvent, Insert, Remove, Array) {

    var transform = function (mutation, options) {
      var settings = options !== undefined ? options : {};
      var active = false;

      var events = Events.create({
        start: Event([]),
        stop: Event([])
      });

      var blocker = Blocker(settings);
      var movement = Movement();

      var drop = function () {
        Remove.remove(blocker.element());
        if (movement.isOn()) {
          movement.off();
          events.trigger.stop();
        }
      };

      var go = function (parent) {
        Insert.append(parent, blocker.element());
        movement.on();
        events.trigger.start();
      };

      var mouseup = function (event, ui) {
        drop();
      };

      var mousemove = function (event, ui) {
        movement.onEvent(event);
      };

      movement.events.move.bind(function (event) {
        mutation.mutate(event.x(), event.y());
      });

      var on = function () {
        active = true;
      };

      var off = function () {
        active = false;
        // acivate some events here?
      };

      var runIfActive = function (f) {
        return function () {
          var args = Array.prototype.slice.call(arguments, 0);
          if (active) {
            return f.apply(null, args);
          }
        };
      };

      // ASSUMPTION: runIfActive is not needed for mousedown. This is pretty much a safety measure for
      // inconsistent situations so that we don't block input.
      var mdown = DomEvent.bind(blocker.element(), 'mousedown', drop);
      var mup = DomEvent.bind(blocker.element(), 'mouseup', runIfActive(mouseup));
      var mmove = DomEvent.bind(blocker.element(), 'mousemove', runIfActive(mousemove));
      var mout = DomEvent.bind(blocker.element(), 'mouseout', runIfActive(drop));

      var destroy = function () {
        blocker.destroy();
        mup.unbind();
        mmove.unbind();
        mout.unbind();
        mdown.unbind();
      };

      return {
        go: go,
        on: on,
        off: off,
        destroy: destroy,
        events: events.registry
      };
    };

    return {
      transform: transform
    };
  }
);
<|endoftext|>


<commit_before>import { SAGA_LOCATION } from '@redux-saga/symbols'

function formatLocation(fileName, lineNumber) {
  return `${fileName}?${lineNumber}`
}

export function getLocation(instrumented) {
  return instrumented[SAGA_LOCATION]
}

function effectLocationAsString(effect) {
  const location = getLocation(effect)
  if (location) {
    const { code, fileName, lineNumber } = location
    const source = `${code}  ${formatLocation(fileName, lineNumber)}`
    return source
  }
  return ''
}

function sagaLocationAsString(sagaMeta) {
  const { name, location } = sagaMeta
  if (location) {
    return `${name}  ${formatLocation(location.fileName, location.lineNumber)}`
  }
  return name
}

const flatMap = (arr, getter = f => f) => arr.reduce((acc, i) => [...acc, ...getter(i)], [])

function cancelledTasksAsString(sagaStack) {
  const cancelledTasks = flatMap(sagaStack, i => i.cancelledTasks)
  if (!cancelledTasks.length) {
    return ''
  }
  return ['Tasks cancelled due to error:', ...cancelledTasks].join('\n')
}
/**
    @param {saga, effect}[] sagaStack
    @returns {string}

    @example
    The above error occurred in task errorInPutSaga {pathToFile}
    when executing effect put({type: 'REDUCER_ACTION_ERROR_IN_PUT'}) {pathToFile}
        created by fetchSaga {pathToFile}
        created by rootSaga {pathToFile}
*/
export function sagaStackToString(sagaStack) {
  const [firstSaga, ...otherSagas] = sagaStack
  const crashedEffectLocation = firstSaga.effect ? effectLocationAsString(firstSaga.effect) : null
  const errorMessage = `The above error occurred in task ${sagaLocationAsString(firstSaga.meta)}${
    crashedEffectLocation ? ` \n when executing effect ${crashedEffectLocation}` : ''
  }`

  return [
    errorMessage,
    ...otherSagas.map(s => `    created by ${sagaLocationAsString(s.meta)}`),
    cancelledTasksAsString(sagaStack),
  ].join('\n')
}

export function addSagaStack(errorObject, errorStack) {
  if (typeof errorObject === 'object') {
    if (typeof errorObject.sagaStack === 'undefined') {
      // property is used as a stack of descriptors for failed sagas
      // after formatting to string it will be re-written
      // to pass sagaStack as a string in user land
      Object.defineProperty(errorObject, 'sagaStack', {
        value: [],
        writable: true,
        enumerable: false,
      })
    }

    errorObject.sagaStack.push(errorStack)
  }
}
<commit_msg>Refactored flatMap implementation<commit_after>import { SAGA_LOCATION } from '@redux-saga/symbols'

function formatLocation(fileName, lineNumber) {
  return `${fileName}?${lineNumber}`
}

export function getLocation(instrumented) {
  return instrumented[SAGA_LOCATION]
}

function effectLocationAsString(effect) {
  const location = getLocation(effect)
  if (location) {
    const { code, fileName, lineNumber } = location
    const source = `${code}  ${formatLocation(fileName, lineNumber)}`
    return source
  }
  return ''
}

function sagaLocationAsString(sagaMeta) {
  const { name, location } = sagaMeta
  if (location) {
    return `${name}  ${formatLocation(location.fileName, location.lineNumber)}`
  }
  return name
}

const flatMap = (mapper, arr) => [].concat(...arr.map(mapper))

function cancelledTasksAsString(sagaStack) {
  const cancelledTasks = flatMap(i => i.cancelledTasks, sagaStack)
  if (!cancelledTasks.length) {
    return ''
  }
  return ['Tasks cancelled due to error:', ...cancelledTasks].join('\n')
}
/**
    @param {saga, effect}[] sagaStack
    @returns {string}

    @example
    The above error occurred in task errorInPutSaga {pathToFile}
    when executing effect put({type: 'REDUCER_ACTION_ERROR_IN_PUT'}) {pathToFile}
        created by fetchSaga {pathToFile}
        created by rootSaga {pathToFile}
*/
export function sagaStackToString(sagaStack) {
  const [firstSaga, ...otherSagas] = sagaStack
  const crashedEffectLocation = firstSaga.effect ? effectLocationAsString(firstSaga.effect) : null
  const errorMessage = `The above error occurred in task ${sagaLocationAsString(firstSaga.meta)}${
    crashedEffectLocation ? ` \n when executing effect ${crashedEffectLocation}` : ''
  }`

  return [
    errorMessage,
    ...otherSagas.map(s => `    created by ${sagaLocationAsString(s.meta)}`),
    cancelledTasksAsString(sagaStack),
  ].join('\n')
}

export function addSagaStack(errorObject, errorStack) {
  if (typeof errorObject === 'object') {
    if (typeof errorObject.sagaStack === 'undefined') {
      // property is used as a stack of descriptors for failed sagas
      // after formatting to string it will be re-written
      // to pass sagaStack as a string in user land
      Object.defineProperty(errorObject, 'sagaStack', {
        value: [],
        writable: true,
        enumerable: false,
      })
    }

    errorObject.sagaStack.push(errorStack)
  }
}
<|endoftext|>


<commit_before>var pathToRegexp = require('path-to-regexp');
var util         = require('util');

/**
 * Comparison of the path with a mask.
 *
 * @param  {String} path Inspected path, for example: "j:test-test:test:m:1:"
 * @param  {String} rule Rule-mask to check, for example: "j:*:m:*:"
 * @param  {Object} keys
 * @param  {Boolean} sensitive
 * @param  {Boolean} strict
 */
function pathTest(path, rule, keys, sensitive, strict){
	keys = keys || [];

	if (util.isRegExp(rule)) {

		var match = rule.exec(path);
		if (!match) return false;

		if (match.length > 1) {
			match.splice(0,1);
		}
		if (Array.isArray(keys)) {
			var m = match;
			match = {length: 0};
			keys.forEach(function(el, i) {
				if (m[i]) {
					match[el] = m[i];
					match.length++;
				}
			});
		} else {
			var ret = {length: match.length};
			match.forEach(function(item, i) { ret[i] = item; });
			return ret;
		}
		return match;
	} else if (typeof rule === "string"){

		if (! (/[\*\?\+\:]/gi).test(rule)) {
			if (path === rule) {
				return {"0": path, length: 1};
			} else {
				return false;
			}
		}
		if (rule === '' || rule === '*') {
			return  {"0": path, length: 1};
		}

		keys = [];
		var i = 0;
		return pathTest(path, pathToRegexp(rule.replace(/(\*|\?|\:[a-zA-Z0-9_]*)/gi, function(str) {
			if (str.indexOf(':') != -1) {
				keys.push(str.substring(1));
				return str;
			} else {
				i++;
				keys.push(i - 1);
				return ':' + (i - 1);
			}
		}), [], {
			sensitive: (typeof sensitive != 'undefined') ? sensitive : false,
			strict: (typeof strict != 'undefined') ? strict : true,
			end: false
		}), keys);

	}
	return false;
}

module.exports = pathTest;
<commit_msg>Fixed unsensitive routing<commit_after>var pathToRegexp = require('path-to-regexp');
var util         = require('util');

/**
 * Comparison of the path with a mask.
 *
 * @param  {String} path Inspected path, for example: "j:test-test:test:m:1:"
 * @param  {String} rule Rule-mask to check, for example: "j:*:m:*:"
 * @param  {Object} keys
 * @param  {Boolean} sensitive
 * @param  {Boolean} strict
 */
function pathTest(path, rule, keys, sensitive, strict){
	keys = keys || [];

	if (util.isRegExp(rule)) {

		var match = rule.exec(path);
		if (!match) return false;

		if (match.length > 1) {
			match.splice(0,1);
		}
		if (Array.isArray(keys)) {
			var m = match;
			match = {length: 0};
			keys.forEach(function(el, i) {
				if (m[i]) {
					match[el] = m[i];
					match.length++;
				}
			});
		} else {
			var ret = {length: match.length};
			match.forEach(function(item, i) { ret[i] = item; });
			return ret;
		}
		return match;
	} else if (typeof rule === "string"){

		if (! (/[\*\?\+\:]/gi).test(rule)) {
			if (sensitive && path === rule) {
				return {"0": path, length: 1};
			} else if (!sensitive && path.toLowerCase() === rule.toLowerCase()) {
				return {"0": path, length: 1};
			} else {
				return false;
			}
		}
		if (rule === '' || rule === '*') {
			return  {"0": path, length: 1};
		}

		keys = [];
		var i = 0;
		return pathTest(path, pathToRegexp(rule.replace(/(\*|\?|\:[a-zA-Z0-9_]*)/gi, function(str) {
			if (str.indexOf(':') != -1) {
				keys.push(str.substring(1));
				return str;
			} else {
				i++;
				keys.push(i - 1);
				return ':' + (i - 1);
			}
		}), [], {
			sensitive: (typeof sensitive != 'undefined') ? sensitive : false,
			strict: (typeof strict != 'undefined') ? strict : true,
			end: false
		}), keys);

	}
	return false;
}

module.exports = pathTest;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var contra = require('contra');
var mongoose = require('mongoose');
var ObjectId = mongoose.Schema.Types.ObjectId;
var Subscriber = require('../../../../models/Subscriber');
var cryptoService = require('../../../../services/crypto');

function unfold (req, res, done) {
  var id = _.first(req.params.hash, 24).join('');
  var email = _.rest(req.params.hash, 24).join('');

  contra.waterfall([findExisting, validateThenRemove], done);

  function findExisting (next) {
    Subscriber.findOne({ _id: id }, next);
  }

  function validateThenRemove (subscriber, next) {
    if (!subscriber) {
      next(); return;
    }
    if (cryptoService.md5(subscriber.email) !== email) {
      next(); return;
    }
    next(null, subscriber.email);
  }
}

module.exports = unfold;
<commit_msg>fix because lodash upgrade<commit_after>'use strict';

var _ = require('lodash');
var contra = require('contra');
var mongoose = require('mongoose');
var ObjectId = mongoose.Schema.Types.ObjectId;
var Subscriber = require('../../../../models/Subscriber');
var cryptoService = require('../../../../services/crypto');

function unfold (req, res, done) {
  var hash = req.params.hash;
  var id = hash.substr(0, 24);
  var email = hash.substr(24);

  contra.waterfall([findExisting, validateThenRemove], done);

  function findExisting (next) {
    Subscriber.findOne({ _id: id }, next);
  }

  function validateThenRemove (subscriber, next) {
    if (!subscriber) {
      next(); return;
    }
    if (cryptoService.md5(subscriber.email) !== email) {
      next(); return;
    }
    next(null, subscriber.email);
  }
}

module.exports = unfold;
<|endoftext|>


<commit_before>import { NoErrorsPlugin } from 'webpack'
import path from 'path'

export default {
  name: 'base',
  configure({ action, projectPath, neatoPath, plugins = [] }) {
    const projectSourcePath = path.join(projectPath, 'src')

    // Use a much faster cheap-module-eval-source-map setup when possible
    // see: http://webpack.github.io/docs/configuration.html#devtool
    const devtool = 'source-map'

    return {
      context: projectSourcePath,

      devtool,

      plugins: [new NoErrorsPlugin(), ...plugins],

      resolve: {
        extensions: [''],

        root: [
          path.join(projectPath, '/node_modules'),
          projectSourcePath,

          // Neato node_modules is required in the path to be able
          // to load the `webpack-hot-middleware`
          path.join(neatoPath, '/node_modules')
        ]
      },
      resolveLoader: {
        // Should first try to resolve loaders nested within Neato.
        // This fixes an issue in NPM v2 where webpack incorrectly
        // thinks that the package `eslint` is the `eslint-loader`
        modulesDirectories: [
          path.join(neatoPath, '/node_modules'),
          path.join(projectPath, '/node_modules')
        ]
      }
    }
  }
}
<commit_msg>Fix resolves<commit_after>import { NoErrorsPlugin } from 'webpack'
import path from 'path'

export default {
  name: 'base',
  configure({ action, projectPath, neatoPath, plugins = [] }) {
    const projectSourcePath = path.join(projectPath, 'src')

    // Use a much faster cheap-module-eval-source-map setup when possible
    // see: http://webpack.github.io/docs/configuration.html#devtool
    const devtool = 'source-map'

    return {
      context: projectSourcePath,

      devtool,

      plugins: [new NoErrorsPlugin(), ...plugins],

      resolve: {
        modules: [
          projectSourcePath,
          path.join(projectPath, '/node_modules'),
          // Neato node_modules is required in the path to be able
          // to load the `webpack-hot-middleware`
          path.join(neatoPath, '/node_modules')
        ]
      },
      resolveLoader: {
        // Should first try to resolve loaders nested within Neato.
        // This fixes an issue in NPM v2 where webpack incorrectly
        // thinks that the package `eslint` is the `eslint-loader`
        modulesDirectories: [
          path.join(neatoPath, '/node_modules'),
          path.join(projectPath, '/node_modules')
        ]
      }
    }
  }
}
<|endoftext|>


<commit_before>var async         = require('async');
var ripple        = require('ripple-lib');
var rpparser      = require('./rpparser');
var validator     = require('validator');
var remoteConnect = require('./remoteConnect');
var bignum        = require('bignumber.js');
var equal         = require('deep-equal');

function getPathFind(remote, params, callback) {

  async.waterfall([

    function(async_callback) {
      remoteConnect.ensureConnected(remote, async_callback);
    },

    function(connected, async_callback) {
      parseParams(params, async_callback);
    },

    function(pathfind_params, async_callback) {
      remote.requestRipplePathFind(pathfind_params, function(err, pathfind_res){
        if (err) {
          async_callback(err);
          return;
        }

        // Add values that are in the res from requestPathFind but not requestRipplePathFind
        pathfind_res.source_account = pathfind_params.src_account;
        pathfind_res.destination_amount = pathfind_params.dst_amount;

        async_callback(null, pathfind_res);
      });
    },

    function(pathfind_res, async_callback) {
      if (typeof pathfind_res.destination_amount === 'string') {
        addDirectXrpPath(remote, pathfind_res, async_callback);
      } else {
        async_callback(null, pathfind_res);
      }
    }

  ], function(err, pathfind_res){
    if (err) {
      callback(err);
      return;
    }

    var payments = pathsetToPayments(pathfind_res);

    if (payments.length === 0) {

        // Check if dst_amount.currency is accepted by the dst_address
        if (pathfind_res.destination_currencies.indexOf(params.dst_amount.currency) === -1) {
          callback({
            error: 'No paths found',
            message: 'The dst_address does not accept ' + params.dst_amount.currency + ', they only accept: ' + res.destination_currencies.join(', ')
          });
        } else {
          callback({
            error: 'No paths found',
            message: 'Please ensure that the src_address has sufficient funds to exectue the payment. If it does there may be insufficient liquidity in the network to execute this payment right now'
          });
        }

      } else {
        callback(null, payments);
      }
  });
}

function parseParams(params, callback) {

  if (!rpparser.isRippleAddress(params.src_address)) {
    callback(new TypeError('Invalid parameter: src_address. Must be a valid Ripple address'));
    return;
  }

  if (!rpparser.isRippleAddress(params.dst_address)) {
    callback(new TypeError('Invalid parameter: dst_address. Must be a valid Ripple address'));
    return;
  }

  if (!rpparser.isValidAmount(params.dst_amount)) {
    callback(new TypeError('Invalid parameter: dst_amount. Must be an object of the form { value: \'1\', currency: \'XRP\', issuer: \' }'));
    return;
  }

  var pathfindParams = {
    src_account: params.src_address,
    dst_account: params.dst_address,
    dst_amount: (params.dst_amount.currency === 'XRP' ?
      rpparser.xrpToDrops(params.dst_amount.value) :
      params.dst_amount)
  };

  if (typeof pathfindParams.dst_amount === 'object' && !pathfindParams.dst_amount.issuer) {
    pathfindParams.dst_amount.issuer = pathfindParams.dst_account;
  }

  callback(null, pathfindParams);

}

/**
 *  Since ripple_path_find does not return XRP to XRP paths,
 *  add the direct XRP "path", if applicable
 */
function addDirectXrpPath(remote, pathfind_res, callback) {

  // Check if dst_address accepts XRP
  if (pathfind_res.destination_currencies.indexOf('XRP') === -1) {
    callback(null, pathfind_res);
    return;
  }

  // Check src_address balance
  remote.requestAccountInfo(pathfind_res.source_account, function(err, res){
    if (err) {
      callback(new Error('Cannot get account info for src_address. ' + err));
      return;
    }

    if (!res || !res.account_data || !res.account_data.Balance) {
      callback(new Error('Internal Error. Malformed account info : ' + JSON.stringify(res)));
      return;
    }

    // Add XRP "path" only if the src_address has enough money to execute the payment
    if (bignum(res.account_data.Balance).greaterThan(pathfind_res.destination_amount)) {
      pathfind_res.alternatives.unshift({
        paths_canonical: [],
        paths_computed: [],
        source_amount: pathfind_res.destination_amount
      });
    }

    callback(null, pathfind_res);

  });

}

/**
 *  Since ripple_path_find does not return XRP to XRP paths,
 *  add the direct IOU "path", if applicable
 */
// function addDirectIouPath(remote, pathfind_res, callback) {

//   // Check that destination accepts currency
//   if (pathfind_res.destination_currencies.indexOf(pathfind_res.destination_amount.currency) === -1) {
//     callback(null, pathfind_res);
//     return;
//   }

//   // Check if there is a trustline between the sender and receiver for the destination currency
//   var account_request = remote.requestAccountLines({
//     account: pathfind_res.source_account,
//     peer: pathfind_res.destination_account,
//     ledger: 'validated'
//   }, function(err, res){
//     if (err) {
//       callback(new Error('Cannot get account lines. ' + err));
//       return;
//     }

//     // Find account line that matches destination currency
//     var currency_line;
//     res.lines.forEach(function(account_line){
//       if (account_line.currency === pathfind_res.destination_amount.currency) {
//         currency_line = account_line;
//       }
//     });

//     // If there is a direct trustline with enough room, add direct IOU path
//     if (currency_line) {

//       var max_send_amount = bignum(currency_line.limit_peer).plus(currency_line.balance);

//       if (max_send_amount.greaterThanOrEqualTo(pathfind_res.destination_amount.value)) {
//         pathfind_res.alternatives.unshift({
//           paths_canonical: [],
//           paths_computed: [],
//           source_amount: pathfind_res.destination_amount
//         });
//       }

//     }

//     callback(null, pathfind_res);
//   });
// }

function pathsetToPayments(pathset) {
  var payments = [];

  pathset.alternatives.forEach(function(alternative){

    var payment = {
      src_address: pathset.source_account,
      src_tag: '',
      src_amount: (typeof alternative.source_amount === 'string' ?
      {
        value: rpparser.dropsToXrp(alternative.source_amount),
        currency: 'XRP',
        issuer: ''
      } :
      {
        value: alternative.source_amount.value,
        currency: alternative.source_amount.currency,
        issuer: (alternative.source_amount.issuer === pathset.source_account ?
          '' :
          alternative.source_amount.issuer)
      }),
      src_slippage: '0',
      dst_address: pathset.destination_account,
      dst_tag: '',
      dst_amount: (typeof pathset.destination_amount === 'string' ?
        {
          value: rpparser.dropsToXrp(pathset.destination_amount),
          currency: 'XRP',
          issuer: ''
        } :
        {
          value: pathset.destination_amount.value,
          currency: pathset.destination_amount.currency,
          issuer: (pathset.destination_amount.issuer === pathset.destination_account ?
            '' :
            pathset.destination_amount.issuer)
        }),
      dst_slippage: '0',
      invoice_id: '',
      paths: JSON.stringify(alternative.paths_computed),
      flag_partial_payment: false,
      flag_no_direct_ripple: false
    };

    // Set slippage if src_amount is the same as dst_amount
    if (equal(payment.src_amount, payment.dst_amount)) {
      payment.src_slippage = bignum(payment.src_amount.value).times(0.01).toString();
    }

    payments.push(payment);

  });

  return payments;

}

module.exports.getPathFind = getPathFind;
<commit_msg>[CHORE] Refactoring using async<commit_after>var async         = require('async');
var ripple        = require('ripple-lib');
var rpparser      = require('./rpparser');
var validator     = require('validator');
var remoteConnect = require('./remoteConnect');
var bignum        = require('bignumber.js');
var equal         = require('deep-equal');

function getPathFind(remote, params, callback) {

  var steps = [

    function(async_callback) {
      remoteConnect.ensureConnected(remote, async_callback);
    },

    function(connected, async_callback) {
      parseParams(params, async_callback);
    },

    function(pathfind_params, async_callback) {
      remote.requestRipplePathFind(pathfind_params, function(err, pathfind_res){
        if (err) {
          async_callback(err);
          return;
        }

        // Add values that are in the res from requestPathFind but not requestRipplePathFind
        pathfind_res.source_account = pathfind_params.src_account;
        pathfind_res.destination_amount = pathfind_params.dst_amount;

        async_callback(null, pathfind_res);
      });
    },

    function(pathfind_res, async_callback) {
      if (typeof pathfind_res.destination_amount === 'string') {
        addDirectXrpPath(remote, pathfind_res, async_callback);
      } else {
        async_callback(null, pathfind_res);
      }
    },

    function(pathfind_res, async_callback) {
      var payments = pathsetToPayments(pathfind_res);

      if (payments.length === 0) {

        // Check if dst_amount.currency is accepted by the dst_address
        if (pathfind_res.destination_currencies.indexOf(params.dst_amount.currency) === -1) {
          async_callback({
            error: 'No paths found',
            message: 'The dst_address does not accept ' + params.dst_amount.currency + ', they only accept: ' + res.destination_currencies.join(', ')
          });
        } else {
          async_callback({
            error: 'No paths found',
            message: 'Please ensure that the src_address has sufficient funds to exectue the payment. If it does there may be insufficient liquidity in the network to execute this payment right now'
          });
        }

      } else {
        async_callback(null, payments);
      }
    }

  ];

  async.waterfall(steps, callback);
}

function parseParams(params, callback) {

  if (!rpparser.isRippleAddress(params.src_address)) {
    callback(new TypeError('Invalid parameter: src_address. Must be a valid Ripple address'));
    return;
  }

  if (!rpparser.isRippleAddress(params.dst_address)) {
    callback(new TypeError('Invalid parameter: dst_address. Must be a valid Ripple address'));
    return;
  }

  if (!rpparser.isValidAmount(params.dst_amount)) {
    callback(new TypeError('Invalid parameter: dst_amount. Must be an object of the form { value: \'1\', currency: \'XRP\', issuer: \' }'));
    return;
  }

  var pathfindParams = {
    src_account: params.src_address,
    dst_account: params.dst_address,
    dst_amount: (params.dst_amount.currency === 'XRP' ?
      rpparser.xrpToDrops(params.dst_amount.value) :
      params.dst_amount)
  };

  if (typeof pathfindParams.dst_amount === 'object' && !pathfindParams.dst_amount.issuer) {
    pathfindParams.dst_amount.issuer = pathfindParams.dst_account;
  }

  callback(null, pathfindParams);

}

/**
 *  Since ripple_path_find does not return XRP to XRP paths,
 *  add the direct XRP "path", if applicable
 */
function addDirectXrpPath(remote, pathfind_res, callback) {

  // Check if dst_address accepts XRP
  if (pathfind_res.destination_currencies.indexOf('XRP') === -1) {
    callback(null, pathfind_res);
    return;
  }

  // Check src_address balance
  remote.requestAccountInfo(pathfind_res.source_account, function(err, res){
    if (err) {
      callback(new Error('Cannot get account info for src_address. ' + err));
      return;
    }

    if (!res || !res.account_data || !res.account_data.Balance) {
      callback(new Error('Internal Error. Malformed account info : ' + JSON.stringify(res)));
      return;
    }

    // Add XRP "path" only if the src_address has enough money to execute the payment
    if (bignum(res.account_data.Balance).greaterThan(pathfind_res.destination_amount)) {
      pathfind_res.alternatives.unshift({
        paths_canonical: [],
        paths_computed: [],
        source_amount: pathfind_res.destination_amount
      });
    }

    callback(null, pathfind_res);

  });

}

/**
 *  Since ripple_path_find does not return XRP to XRP paths,
 *  add the direct IOU "path", if applicable
 */
// function addDirectIouPath(remote, pathfind_res, callback) {

//   // Check that destination accepts currency
//   if (pathfind_res.destination_currencies.indexOf(pathfind_res.destination_amount.currency) === -1) {
//     callback(null, pathfind_res);
//     return;
//   }

//   // Check if there is a trustline between the sender and receiver for the destination currency
//   var account_request = remote.requestAccountLines({
//     account: pathfind_res.source_account,
//     peer: pathfind_res.destination_account,
//     ledger: 'validated'
//   }, function(err, res){
//     if (err) {
//       callback(new Error('Cannot get account lines. ' + err));
//       return;
//     }

//     // Find account line that matches destination currency
//     var currency_line;
//     res.lines.forEach(function(account_line){
//       if (account_line.currency === pathfind_res.destination_amount.currency) {
//         currency_line = account_line;
//       }
//     });

//     // If there is a direct trustline with enough room, add direct IOU path
//     if (currency_line) {

//       var max_send_amount = bignum(currency_line.limit_peer).plus(currency_line.balance);

//       if (max_send_amount.greaterThanOrEqualTo(pathfind_res.destination_amount.value)) {
//         pathfind_res.alternatives.unshift({
//           paths_canonical: [],
//           paths_computed: [],
//           source_amount: pathfind_res.destination_amount
//         });
//       }

//     }

//     callback(null, pathfind_res);
//   });
// }

function pathsetToPayments(pathset) {
  var payments = [];

  pathset.alternatives.forEach(function(alternative){

    var payment = {
      src_address: pathset.source_account,
      src_tag: '',
      src_amount: (typeof alternative.source_amount === 'string' ?
      {
        value: rpparser.dropsToXrp(alternative.source_amount),
        currency: 'XRP',
        issuer: ''
      } :
      {
        value: alternative.source_amount.value,
        currency: alternative.source_amount.currency,
        issuer: (alternative.source_amount.issuer === pathset.source_account ?
          '' :
          alternative.source_amount.issuer)
      }),
      src_slippage: '0',
      dst_address: pathset.destination_account,
      dst_tag: '',
      dst_amount: (typeof pathset.destination_amount === 'string' ?
        {
          value: rpparser.dropsToXrp(pathset.destination_amount),
          currency: 'XRP',
          issuer: ''
        } :
        {
          value: pathset.destination_amount.value,
          currency: pathset.destination_amount.currency,
          issuer: (pathset.destination_amount.issuer === pathset.destination_account ?
            '' :
            pathset.destination_amount.issuer)
        }),
      dst_slippage: '0',
      invoice_id: '',
      paths: JSON.stringify(alternative.paths_computed),
      flag_partial_payment: false,
      flag_no_direct_ripple: false
    };

    // Set slippage if src_amount is the same as dst_amount
    if (equal(payment.src_amount, payment.dst_amount)) {
      payment.src_slippage = bignum(payment.src_amount.value).times(0.01).toString();
    }

    payments.push(payment);

  });

  return payments;

}

module.exports.getPathFind = getPathFind;
<|endoftext|>


<commit_before>// @flow
import * as React from "react";

import { richestMimetype, transforms, displayOrder } from "@nteract/transforms";

type Props = {
  expanded: boolean,
  displayOrder: Array<string>,
  transforms: Object,
  bundle: Object,
  metadata: Object,
  theme: string,
  models?: Object
};

type ErrorProps = {
  children?: React.Node
};

type State = {
  error?: Error | boolean,
  info?: Object
};

class ErrorBoundary extends React.Component<ErrorProps, State> {
  constructor(props: ErrorProps) {
    super(props);
    this.state = {
      error: false,
      info: {}
    };
  }

  componentDidCatch(error: Error, info: Object) {
    this.setState({ error, info });
  }

  render() {
    if (this.state.error) {
      return (

 Error: {this.state.error.toString()}

 {this.state.info ? this.state.info.toString() : ""}

            {this.state.info ? this.state.info.componentStack.toString() : ""}

      );
    }
    return this.props.children;
  }
}

export default class RichestMime extends React.Component<Props> {
  static defaultProps = {
    transforms,
    displayOrder,
    theme: "light",
    metadata: {},
    bundle: {},
    models: {}
  };

  shouldComponentUpdate(nextProps: Props): boolean {
    // eslint-disable-line class-methods-use-this
    if (
      nextProps &&
      nextProps.theme &&
      this.props &&
      nextProps.theme !== this.props.theme
    ) {
      return true;
    }
    // return false;
    return true;
  }

  render(): ?React$Element<any> | null {
    const mimetype = richestMimetype(
      this.props.bundle,
      this.props.displayOrder,
      this.props.transforms
    );

    if (!mimetype) {
      // If no mimetype is supported, don't return a component
      return null;
    }

    const Transform = this.props.transforms[mimetype];
    const data = this.props.bundle[mimetype];
    const metadata = this.props.metadata[mimetype];
    return (
      <ErrorBoundary>
        <Transform
          expanded={this.props.expanded}
          data={data}
          metadata={metadata}
          theme={this.props.theme}
          models={this.props.models}
        />
      </ErrorBoundary>
    );
  }
}
<commit_msg>improve formatting for error + traceback<commit_after>// @flow
import React from "react";

import { richestMimetype, transforms, displayOrder } from "@nteract/transforms";

type Props = {
  expanded: boolean,
  displayOrder: Array<string>,
  transforms: Object,
  bundle: Object,
  metadata: Object,
  theme: string,
  models?: Object
};

type ErrorProps = {
  children?: React$Node
};

type ErrorInfo = {
  componentStack: string
};

type State = {
  error: ?Error,
  info: ?ErrorInfo
};

type FallbackProps = {
  componentStack: string,
  error: Error
};

const Fallback = ({ componentStack, error }: FallbackProps) => (

 Error: {error.toString()}

      stack trace

{componentStack}

);

class ErrorBoundary extends React.Component<ErrorProps, State> {
  constructor(props: ErrorProps) {
    super(props);
    this.state = {
      error: null,
      info: null
    };
  }

  componentDidCatch(error: Error, info: ErrorInfo) {
    this.setState({ error, info });
  }

  render() {
    if (this.state.error) {
      return (
        <Fallback
          componentStack={this.state.info ? this.state.info.componentStack : ""}
          error={this.state.error}
        />
      );
    }
    return this.props.children;
  }
}

export default class RichestMime extends React.Component<Props> {
  static defaultProps = {
    transforms,
    displayOrder,
    theme: "light",
    metadata: {},
    bundle: {},
    models: {}
  };

  shouldComponentUpdate(nextProps: Props): boolean {
    // eslint-disable-line class-methods-use-this
    if (
      nextProps &&
      nextProps.theme &&
      this.props &&
      nextProps.theme !== this.props.theme
    ) {
      return true;
    }
    // return false;
    return true;
  }

  render(): ?React$Element<any> | null {
    const mimetype = richestMimetype(
      this.props.bundle,
      this.props.displayOrder,
      this.props.transforms
    );

    if (!mimetype) {
      // If no mimetype is supported, don't return a component
      return null;
    }

    const Transform = this.props.transforms[mimetype];
    const data = this.props.bundle[mimetype];
    const metadata = this.props.metadata[mimetype];
    return (
      <ErrorBoundary>
        <Transform
          expanded={this.props.expanded}
          data={data}
          metadata={metadata}
          theme={this.props.theme}
          models={this.props.models}
        />
      </ErrorBoundary>
    );
  }
}
<|endoftext|>


<commit_before>'use strict';
/*
 * This file was autogenerated
 * Any changes made here will be overriten!
 */

exports.EMOJI_PICKER_PATH = '.popouts';

exports.SETTINGS_CLASSES = 4840;
exports.SWITCH_CLASSES = 4537;
exports.FLEX_CHILD_CLASSES = 1106;
exports.FLEX_CLASSES = 1107;
exports.HEADER_CLASSES = 1209;
exports.SWITCH_ITEM_CLASSES = 1749;
exports.DIVIDER_ITEM_CLASSES = 4538;
exports.LABEL_ITEM_CLASSES = 4536;
exports.FONT_SIZE_CLASSES = 1163;
exports.CARD_CLASSES = 1174;
exports.EMOJI_BUTTON_CLASS = 5210;
exports.LOCAL_STORAGE_MODULE = 828;
exports.EMOJI_STORAGE_MODULE = 1310;
exports.SERVERS_STORAGE_MODULE = 45;
exports.SERVERS_PERMISSIONS_MODULE = 215;
exports.TRANSLATION_MODULE = 18;
exports.CUSTOM_EMOJI_STORAGE_MODULE = 1323;
exports.REACTION_EMOJI_CONVERTER = 1430;
<commit_msg>Updated module numbers<commit_after>'use strict';
/*
 * This file was autogenerated
 * Any changes made here will be overriten!
 */

exports.EMOJI_PICKER_PATH = '.popouts';

exports.SETTINGS_CLASSES = undefined;
exports.SWITCH_CLASSES = 4536;
exports.FLEX_CHILD_CLASSES = 1107;
exports.FLEX_CLASSES = 1108;
exports.HEADER_CLASSES = 1210;
exports.SWITCH_ITEM_CLASSES = 1747;
exports.DIVIDER_ITEM_CLASSES = 4537;
exports.LABEL_ITEM_CLASSES = 4535;
exports.FONT_SIZE_CLASSES = 1164;
exports.CARD_CLASSES = 1175;
exports.EMOJI_BUTTON_CLASS = 5212;
exports.LOCAL_STORAGE_MODULE = 829;
exports.EMOJI_STORAGE_MODULE = 1311;
exports.SERVERS_STORAGE_MODULE = 45;
exports.SERVERS_PERMISSIONS_MODULE = 215;
exports.TRANSLATION_MODULE = 18;
exports.CUSTOM_EMOJI_STORAGE_MODULE = 1324;
exports.REACTION_EMOJI_CONVERTER = 1430;
<|endoftext|>


<commit_before>'use strict';

/**
 * Admit One
 *
 */

import Auth from 'ember-tiny-auth';
import Authenticator from 'ember-admit-one/authenticator';
import Authorizer from 'ember-admit-one/authorizer';
import setup from 'ember-admit-one/setup';

var AdmitOne = {};

// expose everything in tiny-auth to admit-one users. we'll end up overwriting
// some of the pieces in here, but they'll serve the same purposes, and
// advanced uses should directly access tiny-auth.
Ember.$.extend(AdmitOne, Auth);

AdmitOne.Authenticator = Authenticator;
AdmitOne.Authorizer = Authorizer;
AdmitOne.setup = setup;

export default AdmitOne;
<commit_msg>Comment no longer really needed.<commit_after>'use strict';

import Auth from 'ember-tiny-auth';
import Authenticator from 'ember-admit-one/authenticator';
import Authorizer from 'ember-admit-one/authorizer';
import setup from 'ember-admit-one/setup';

var AdmitOne = {};

// expose everything in tiny-auth to admit-one users. we'll end up overwriting
// some of the pieces in here, but they'll serve the same purposes, and
// advanced uses should directly access tiny-auth.
Ember.$.extend(AdmitOne, Auth);

AdmitOne.Authenticator = Authenticator;
AdmitOne.Authorizer = Authorizer;
AdmitOne.setup = setup;

export default AdmitOne;
<|endoftext|>


<commit_before>/**
 * Generic selection of options.
 *
 * Items can contain any rendering, and are uniquely identified by a has of thier data. Any widget
 * that provides options, from which the user must choose one, should be built on this class.
 *
 * Use together with OO.ui.OptionWidget.
 *
 * @class
 * @extends OO.ui.Widget
 * @mixins OO.ui.GroupElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {OO.ui.OptionWidget[]} [items] Options to add
 */
OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
	// Config intialization
	config = config || {};

	// Parent constructor
	OO.ui.SelectWidget.super.call( this, config );

	// Mixin constructors
	OO.ui.GroupWidget.call( this, $.extend( {}, config, { $group: this.$element } ) );

	// Properties
	this.pressed = false;
	this.selecting = null;
	this.hashes = {};
	this.onMouseUpHandler = OO.ui.bind( this.onMouseUp, this );
	this.onMouseMoveHandler = OO.ui.bind( this.onMouseMove, this );

	// Events
	this.$element.on( {
		mousedown: OO.ui.bind( this.onMouseDown, this ),
		mouseover: OO.ui.bind( this.onMouseOver, this ),
		mouseleave: OO.ui.bind( this.onMouseLeave, this )
	} );

	// Initialization
	this.$element.addClass( 'oo-ui-selectWidget oo-ui-selectWidget-depressed' );
	if ( $.isArray( config.items ) ) {
		this.addItems( config.items );
	}
};

/* Setup */

OO.inheritClass( OO.ui.SelectWidget, OO.ui.Widget );

// Need to mixin base class as well
OO.mixinClass( OO.ui.SelectWidget, OO.ui.GroupElement );
OO.mixinClass( OO.ui.SelectWidget, OO.ui.GroupWidget );

/* Events */

/**
 * @event highlight
 * @param {OO.ui.OptionWidget|null} item Highlighted item
 */

/**
 * @event press
 * @param {OO.ui.OptionWidget|null} item Pressed item
 */

/**
 * @event select
 * @param {OO.ui.OptionWidget|null} item Selected item
 */

/**
 * @event choose
 * @param {OO.ui.OptionWidget|null} item Chosen item
 */

/**
 * @event add
 * @param {OO.ui.OptionWidget[]} items Added items
 * @param {number} index Index items were added at
 */

/**
 * @event remove
 * @param {OO.ui.OptionWidget[]} items Removed items
 */

/* Methods */

/**
 * Handle mouse down events.
 *
 * @private
 * @param {jQuery.Event} e Mouse down event
 */
OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
	var item;

	if ( !this.isDisabled() && e.which === 1 ) {
		this.togglePressed( true );
		item = this.getTargetItem( e );
		if ( item && item.isSelectable() ) {
			this.pressItem( item );
			this.selecting = item;
			this.getElementDocument().addEventListener(
				'mouseup',
				this.onMouseUpHandler,
				true
			);
			this.getElementDocument().addEventListener(
				'mousemove',
				this.onMouseMoveHandler,
				true
			);
		}
	}
	return false;
};

/**
 * Handle mouse up events.
 *
 * @private
 * @param {jQuery.Event} e Mouse up event
 */
OO.ui.SelectWidget.prototype.onMouseUp = function ( e ) {
	var item;

	this.togglePressed( false );
	if ( !this.selecting ) {
		item = this.getTargetItem( e );
		if ( item && item.isSelectable() ) {
			this.selecting = item;
		}
	}
	if ( !this.isDisabled() && e.which === 1 && this.selecting ) {
		this.pressItem( null );
		this.chooseItem( this.selecting );
		this.selecting = null;
	}

	this.getElementDocument().removeEventListener(
		'mouseup',
		this.onMouseUpHandler,
		true
	);
	this.getElementDocument().removeEventListener(
		'mousemove',
		this.onMouseMoveHandler,
		true
	);

	return false;
};

/**
 * Handle mouse move events.
 *
 * @private
 * @param {jQuery.Event} e Mouse move event
 */
OO.ui.SelectWidget.prototype.onMouseMove = function ( e ) {
	var item;

	if ( !this.isDisabled() && this.pressed ) {
		item = this.getTargetItem( e );
		if ( item && item !== this.selecting && item.isSelectable() ) {
			this.pressItem( item );
			this.selecting = item;
		}
	}
	return false;
};

/**
 * Handle mouse over events.
 *
 * @private
 * @param {jQuery.Event} e Mouse over event
 */
OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
	var item;

	if ( !this.isDisabled() ) {
		item = this.getTargetItem( e );
		this.highlightItem( item && item.isHighlightable() ? item : null );
	}
	return false;
};

/**
 * Handle mouse leave events.
 *
 * @private
 * @param {jQuery.Event} e Mouse over event
 */
OO.ui.SelectWidget.prototype.onMouseLeave = function () {
	if ( !this.isDisabled() ) {
		this.highlightItem( null );
	}
	return false;
};

/**
 * Get the closest item to a jQuery.Event.
 *
 * @private
 * @param {jQuery.Event} e
 * @return {OO.ui.OptionWidget|null} Outline item widget, `null` if none was found
 */
OO.ui.SelectWidget.prototype.getTargetItem = function ( e ) {
	var $item = this.$( e.target ).closest( '.oo-ui-optionWidget' );
	if ( $item.length ) {
		return $item.data( 'oo-ui-optionWidget' );
	}
	return null;
};

/**
 * Get selected item.
 *
 * @return {OO.ui.OptionWidget|null} Selected item, `null` if no item is selected
 */
OO.ui.SelectWidget.prototype.getSelectedItem = function () {
	var i, len;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		if ( this.items[i].isSelected() ) {
			return this.items[i];
		}
	}
	return null;
};

/**
 * Get highlighted item.
 *
 * @return {OO.ui.OptionWidget|null} Highlighted item, `null` if no item is highlighted
 */
OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
	var i, len;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		if ( this.items[i].isHighlighted() ) {
			return this.items[i];
		}
	}
	return null;
};

/**
 * Get an existing item with equivilant data.
 *
 * @param {Object} data Item data to search for
 * @return {OO.ui.OptionWidget|null} Item with equivilent value, `null` if none exists
 */
OO.ui.SelectWidget.prototype.getItemFromData = function ( data ) {
	var hash = OO.getHash( data );

	if ( hash in this.hashes ) {
		return this.hashes[hash];
	}

	return null;
};

/**
 * Toggle pressed state.
 *
 * @param {boolean} pressed An option is being pressed
 */
OO.ui.SelectWidget.prototype.togglePressed = function ( pressed ) {
	if ( pressed === undefined ) {
		pressed = !this.pressed;
	}
	if ( pressed !== this.pressed ) {
		this.$element
			.toggleClass( 'oo-ui-selectWidget-pressed', pressed )
			.toggleClass( 'oo-ui-selectWidget-depressed', !pressed );
		this.pressed = pressed;
	}
};

/**
 * Highlight an item.
 *
 * Highlighting is mutually exclusive.
 *
 * @param {OO.ui.OptionWidget} [item] Item to highlight, omit to deselect all
 * @fires highlight
 * @chainable
 */
OO.ui.SelectWidget.prototype.highlightItem = function ( item ) {
	var i, len, highlighted,
		changed = false;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		highlighted = this.items[i] === item;
		if ( this.items[i].isHighlighted() !== highlighted ) {
			this.items[i].setHighlighted( highlighted );
			changed = true;
		}
	}
	if ( changed ) {
		this.emit( 'highlight', item );
	}

	return this;
};

/**
 * Select an item.
 *
 * @param {OO.ui.OptionWidget} [item] Item to select, omit to deselect all
 * @fires select
 * @chainable
 */
OO.ui.SelectWidget.prototype.selectItem = function ( item ) {
	var i, len, selected,
		changed = false;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		selected = this.items[i] === item;
		if ( this.items[i].isSelected() !== selected ) {
			this.items[i].setSelected( selected );
			changed = true;
		}
	}
	if ( changed ) {
		this.emit( 'select', item );
	}

	return this;
};

/**
 * Press an item.
 *
 * @param {OO.ui.OptionWidget} [item] Item to press, omit to depress all
 * @fires press
 * @chainable
 */
OO.ui.SelectWidget.prototype.pressItem = function ( item ) {
	var i, len, pressed,
		changed = false;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		pressed = this.items[i] === item;
		if ( this.items[i].isPressed() !== pressed ) {
			this.items[i].setPressed( pressed );
			changed = true;
		}
	}
	if ( changed ) {
		this.emit( 'press', item );
	}

	return this;
};

/**
 * Choose an item.
 *
 * Identical to #selectItem, but may vary in subclasses that want to take additional action when
 * an item is selected using the keyboard or mouse.
 *
 * @param {OO.ui.OptionWidget} item Item to choose
 * @fires choose
 * @chainable
 */
OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
	this.selectItem( item );
	this.emit( 'choose', item );

	return this;
};

/**
 * Get an item relative to another one.
 *
 * @param {OO.ui.OptionWidget} item Item to start at
 * @param {number} direction Direction to move in
 * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the menu
 */
OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction ) {
	var inc = direction > 0 ? 1 : -1,
		len = this.items.length,
		index = item instanceof OO.ui.OptionWidget ?
			$.inArray( item, this.items ) : ( inc > 0 ? -1 : 0 ),
		stopAt = Math.max( Math.min( index, len - 1 ), 0 ),
		i = inc > 0 ?
			// Default to 0 instead of -1, if nothing is selected let's start at the beginning
			Math.max( index, -1 ) :
			// Default to n-1 instead of -1, if nothing is selected let's start at the end
			Math.min( index, len );

	while ( len !== 0 ) {
		i = ( i + inc + len ) % len;
		item = this.items[i];
		if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
			return item;
		}
		// Stop iterating when we've looped all the way around
		if ( i === stopAt ) {
			break;
		}
	}
	return null;
};

/**
 * Get the next selectable item.
 *
 * @return {OO.ui.OptionWidget|null} Item, `null` if ther aren't any selectable items
 */
OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
	var i, len, item;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		item = this.items[i];
		if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
			return item;
		}
	}

	return null;
};

/**
 * Add items.
 *
 * When items are added with the same values as existing items, the existing items will be
 * automatically removed before the new items are added.
 *
 * @param {OO.ui.OptionWidget[]} items Items to add
 * @param {number} [index] Index to insert items after
 * @fires add
 * @chainable
 */
OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
	var i, len, item, hash,
		remove = [];

	for ( i = 0, len = items.length; i < len; i++ ) {
		item = items[i];
		hash = OO.getHash( item.getData() );
		if ( hash in this.hashes ) {
			// Remove item with same value
			remove.push( this.hashes[hash] );
		}
		this.hashes[hash] = item;
	}
	if ( remove.length ) {
		this.removeItems( remove );
	}

	// Mixin method
	OO.ui.GroupWidget.prototype.addItems.call( this, items, index );

	// Always provide an index, even if it was omitted
	this.emit( 'add', items, index === undefined ? this.items.length - items.length - 1 : index );

	return this;
};

/**
 * Remove items.
 *
 * Items will be detached, not removed, so they can be used later.
 *
 * @param {OO.ui.OptionWidget[]} items Items to remove
 * @fires remove
 * @chainable
 */
OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
	var i, len, item, hash;

	for ( i = 0, len = items.length; i < len; i++ ) {
		item = items[i];
		hash = OO.getHash( item.getData() );
		if ( hash in this.hashes ) {
			// Remove existing item
			delete this.hashes[hash];
		}
		if ( item.isSelected() ) {
			this.selectItem( null );
		}
	}

	// Mixin method
	OO.ui.GroupWidget.prototype.removeItems.call( this, items );

	this.emit( 'remove', items );

	return this;
};

/**
 * Clear all items.
 *
 * Items will be detached, not removed, so they can be used later.
 *
 * @fires remove
 * @chainable
 */
OO.ui.SelectWidget.prototype.clearItems = function () {
	var items = this.items.slice();

	// Clear all items
	this.hashes = {};
	// Mixin method
	OO.ui.GroupWidget.prototype.clearItems.call( this );
	this.selectItem( null );

	this.emit( 'remove', items );

	return this;
};
<commit_msg>SelectWidget: Fix typos in documentation<commit_after>/**
 * Generic selection of options.
 *
 * Items can contain any rendering, and are uniquely identified by a hash of their data. Any widget
 * that provides options, from which the user must choose one, should be built on this class.
 *
 * Use together with OO.ui.OptionWidget.
 *
 * @class
 * @extends OO.ui.Widget
 * @mixins OO.ui.GroupElement
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {OO.ui.OptionWidget[]} [items] Options to add
 */
OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
	// Config intialization
	config = config || {};

	// Parent constructor
	OO.ui.SelectWidget.super.call( this, config );

	// Mixin constructors
	OO.ui.GroupWidget.call( this, $.extend( {}, config, { $group: this.$element } ) );

	// Properties
	this.pressed = false;
	this.selecting = null;
	this.hashes = {};
	this.onMouseUpHandler = OO.ui.bind( this.onMouseUp, this );
	this.onMouseMoveHandler = OO.ui.bind( this.onMouseMove, this );

	// Events
	this.$element.on( {
		mousedown: OO.ui.bind( this.onMouseDown, this ),
		mouseover: OO.ui.bind( this.onMouseOver, this ),
		mouseleave: OO.ui.bind( this.onMouseLeave, this )
	} );

	// Initialization
	this.$element.addClass( 'oo-ui-selectWidget oo-ui-selectWidget-depressed' );
	if ( $.isArray( config.items ) ) {
		this.addItems( config.items );
	}
};

/* Setup */

OO.inheritClass( OO.ui.SelectWidget, OO.ui.Widget );

// Need to mixin base class as well
OO.mixinClass( OO.ui.SelectWidget, OO.ui.GroupElement );
OO.mixinClass( OO.ui.SelectWidget, OO.ui.GroupWidget );

/* Events */

/**
 * @event highlight
 * @param {OO.ui.OptionWidget|null} item Highlighted item
 */

/**
 * @event press
 * @param {OO.ui.OptionWidget|null} item Pressed item
 */

/**
 * @event select
 * @param {OO.ui.OptionWidget|null} item Selected item
 */

/**
 * @event choose
 * @param {OO.ui.OptionWidget|null} item Chosen item
 */

/**
 * @event add
 * @param {OO.ui.OptionWidget[]} items Added items
 * @param {number} index Index items were added at
 */

/**
 * @event remove
 * @param {OO.ui.OptionWidget[]} items Removed items
 */

/* Methods */

/**
 * Handle mouse down events.
 *
 * @private
 * @param {jQuery.Event} e Mouse down event
 */
OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
	var item;

	if ( !this.isDisabled() && e.which === 1 ) {
		this.togglePressed( true );
		item = this.getTargetItem( e );
		if ( item && item.isSelectable() ) {
			this.pressItem( item );
			this.selecting = item;
			this.getElementDocument().addEventListener(
				'mouseup',
				this.onMouseUpHandler,
				true
			);
			this.getElementDocument().addEventListener(
				'mousemove',
				this.onMouseMoveHandler,
				true
			);
		}
	}
	return false;
};

/**
 * Handle mouse up events.
 *
 * @private
 * @param {jQuery.Event} e Mouse up event
 */
OO.ui.SelectWidget.prototype.onMouseUp = function ( e ) {
	var item;

	this.togglePressed( false );
	if ( !this.selecting ) {
		item = this.getTargetItem( e );
		if ( item && item.isSelectable() ) {
			this.selecting = item;
		}
	}
	if ( !this.isDisabled() && e.which === 1 && this.selecting ) {
		this.pressItem( null );
		this.chooseItem( this.selecting );
		this.selecting = null;
	}

	this.getElementDocument().removeEventListener(
		'mouseup',
		this.onMouseUpHandler,
		true
	);
	this.getElementDocument().removeEventListener(
		'mousemove',
		this.onMouseMoveHandler,
		true
	);

	return false;
};

/**
 * Handle mouse move events.
 *
 * @private
 * @param {jQuery.Event} e Mouse move event
 */
OO.ui.SelectWidget.prototype.onMouseMove = function ( e ) {
	var item;

	if ( !this.isDisabled() && this.pressed ) {
		item = this.getTargetItem( e );
		if ( item && item !== this.selecting && item.isSelectable() ) {
			this.pressItem( item );
			this.selecting = item;
		}
	}
	return false;
};

/**
 * Handle mouse over events.
 *
 * @private
 * @param {jQuery.Event} e Mouse over event
 */
OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
	var item;

	if ( !this.isDisabled() ) {
		item = this.getTargetItem( e );
		this.highlightItem( item && item.isHighlightable() ? item : null );
	}
	return false;
};

/**
 * Handle mouse leave events.
 *
 * @private
 * @param {jQuery.Event} e Mouse over event
 */
OO.ui.SelectWidget.prototype.onMouseLeave = function () {
	if ( !this.isDisabled() ) {
		this.highlightItem( null );
	}
	return false;
};

/**
 * Get the closest item to a jQuery.Event.
 *
 * @private
 * @param {jQuery.Event} e
 * @return {OO.ui.OptionWidget|null} Outline item widget, `null` if none was found
 */
OO.ui.SelectWidget.prototype.getTargetItem = function ( e ) {
	var $item = this.$( e.target ).closest( '.oo-ui-optionWidget' );
	if ( $item.length ) {
		return $item.data( 'oo-ui-optionWidget' );
	}
	return null;
};

/**
 * Get selected item.
 *
 * @return {OO.ui.OptionWidget|null} Selected item, `null` if no item is selected
 */
OO.ui.SelectWidget.prototype.getSelectedItem = function () {
	var i, len;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		if ( this.items[i].isSelected() ) {
			return this.items[i];
		}
	}
	return null;
};

/**
 * Get highlighted item.
 *
 * @return {OO.ui.OptionWidget|null} Highlighted item, `null` if no item is highlighted
 */
OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
	var i, len;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		if ( this.items[i].isHighlighted() ) {
			return this.items[i];
		}
	}
	return null;
};

/**
 * Get an existing item with equivilant data.
 *
 * @param {Object} data Item data to search for
 * @return {OO.ui.OptionWidget|null} Item with equivilent value, `null` if none exists
 */
OO.ui.SelectWidget.prototype.getItemFromData = function ( data ) {
	var hash = OO.getHash( data );

	if ( hash in this.hashes ) {
		return this.hashes[hash];
	}

	return null;
};

/**
 * Toggle pressed state.
 *
 * @param {boolean} pressed An option is being pressed
 */
OO.ui.SelectWidget.prototype.togglePressed = function ( pressed ) {
	if ( pressed === undefined ) {
		pressed = !this.pressed;
	}
	if ( pressed !== this.pressed ) {
		this.$element
			.toggleClass( 'oo-ui-selectWidget-pressed', pressed )
			.toggleClass( 'oo-ui-selectWidget-depressed', !pressed );
		this.pressed = pressed;
	}
};

/**
 * Highlight an item.
 *
 * Highlighting is mutually exclusive.
 *
 * @param {OO.ui.OptionWidget} [item] Item to highlight, omit to deselect all
 * @fires highlight
 * @chainable
 */
OO.ui.SelectWidget.prototype.highlightItem = function ( item ) {
	var i, len, highlighted,
		changed = false;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		highlighted = this.items[i] === item;
		if ( this.items[i].isHighlighted() !== highlighted ) {
			this.items[i].setHighlighted( highlighted );
			changed = true;
		}
	}
	if ( changed ) {
		this.emit( 'highlight', item );
	}

	return this;
};

/**
 * Select an item.
 *
 * @param {OO.ui.OptionWidget} [item] Item to select, omit to deselect all
 * @fires select
 * @chainable
 */
OO.ui.SelectWidget.prototype.selectItem = function ( item ) {
	var i, len, selected,
		changed = false;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		selected = this.items[i] === item;
		if ( this.items[i].isSelected() !== selected ) {
			this.items[i].setSelected( selected );
			changed = true;
		}
	}
	if ( changed ) {
		this.emit( 'select', item );
	}

	return this;
};

/**
 * Press an item.
 *
 * @param {OO.ui.OptionWidget} [item] Item to press, omit to depress all
 * @fires press
 * @chainable
 */
OO.ui.SelectWidget.prototype.pressItem = function ( item ) {
	var i, len, pressed,
		changed = false;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		pressed = this.items[i] === item;
		if ( this.items[i].isPressed() !== pressed ) {
			this.items[i].setPressed( pressed );
			changed = true;
		}
	}
	if ( changed ) {
		this.emit( 'press', item );
	}

	return this;
};

/**
 * Choose an item.
 *
 * Identical to #selectItem, but may vary in subclasses that want to take additional action when
 * an item is selected using the keyboard or mouse.
 *
 * @param {OO.ui.OptionWidget} item Item to choose
 * @fires choose
 * @chainable
 */
OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
	this.selectItem( item );
	this.emit( 'choose', item );

	return this;
};

/**
 * Get an item relative to another one.
 *
 * @param {OO.ui.OptionWidget} item Item to start at
 * @param {number} direction Direction to move in
 * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the menu
 */
OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction ) {
	var inc = direction > 0 ? 1 : -1,
		len = this.items.length,
		index = item instanceof OO.ui.OptionWidget ?
			$.inArray( item, this.items ) : ( inc > 0 ? -1 : 0 ),
		stopAt = Math.max( Math.min( index, len - 1 ), 0 ),
		i = inc > 0 ?
			// Default to 0 instead of -1, if nothing is selected let's start at the beginning
			Math.max( index, -1 ) :
			// Default to n-1 instead of -1, if nothing is selected let's start at the end
			Math.min( index, len );

	while ( len !== 0 ) {
		i = ( i + inc + len ) % len;
		item = this.items[i];
		if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
			return item;
		}
		// Stop iterating when we've looped all the way around
		if ( i === stopAt ) {
			break;
		}
	}
	return null;
};

/**
 * Get the next selectable item.
 *
 * @return {OO.ui.OptionWidget|null} Item, `null` if ther aren't any selectable items
 */
OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
	var i, len, item;

	for ( i = 0, len = this.items.length; i < len; i++ ) {
		item = this.items[i];
		if ( item instanceof OO.ui.OptionWidget && item.isSelectable() ) {
			return item;
		}
	}

	return null;
};

/**
 * Add items.
 *
 * When items are added with the same values as existing items, the existing items will be
 * automatically removed before the new items are added.
 *
 * @param {OO.ui.OptionWidget[]} items Items to add
 * @param {number} [index] Index to insert items after
 * @fires add
 * @chainable
 */
OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
	var i, len, item, hash,
		remove = [];

	for ( i = 0, len = items.length; i < len; i++ ) {
		item = items[i];
		hash = OO.getHash( item.getData() );
		if ( hash in this.hashes ) {
			// Remove item with same value
			remove.push( this.hashes[hash] );
		}
		this.hashes[hash] = item;
	}
	if ( remove.length ) {
		this.removeItems( remove );
	}

	// Mixin method
	OO.ui.GroupWidget.prototype.addItems.call( this, items, index );

	// Always provide an index, even if it was omitted
	this.emit( 'add', items, index === undefined ? this.items.length - items.length - 1 : index );

	return this;
};

/**
 * Remove items.
 *
 * Items will be detached, not removed, so they can be used later.
 *
 * @param {OO.ui.OptionWidget[]} items Items to remove
 * @fires remove
 * @chainable
 */
OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
	var i, len, item, hash;

	for ( i = 0, len = items.length; i < len; i++ ) {
		item = items[i];
		hash = OO.getHash( item.getData() );
		if ( hash in this.hashes ) {
			// Remove existing item
			delete this.hashes[hash];
		}
		if ( item.isSelected() ) {
			this.selectItem( null );
		}
	}

	// Mixin method
	OO.ui.GroupWidget.prototype.removeItems.call( this, items );

	this.emit( 'remove', items );

	return this;
};

/**
 * Clear all items.
 *
 * Items will be detached, not removed, so they can be used later.
 *
 * @fires remove
 * @chainable
 */
OO.ui.SelectWidget.prototype.clearItems = function () {
	var items = this.items.slice();

	// Clear all items
	this.hashes = {};
	// Mixin method
	OO.ui.GroupWidget.prototype.clearItems.call( this );
	this.selectItem( null );

	this.emit( 'remove', items );

	return this;
};
<|endoftext|>