<commit_before>
/**
 * Module dependencies.
 */

var interpolate = require('interpolate'),
    Emitter = require('emitter'),
    classes = require('classes'),
    Upload = require('upload'),
    events = require('events'),
    domify = require('domify'),
    type = require('type')

var template = require('./template')

var cdpload = module.exports = function (el, options) {
  if (!(this instanceof cdpload)) return new cdpload(el, options)
  Emitter.call(this)

  if(type(options) !== 'object')
    options = {}

  if(type(options.accept) === 'string')
    options.accept = [options.accept]

  if(type(options.accept) !== 'array')
    options.accept = []

  if(!options.maxSize)
    options.maxSize = Infinity

  this.ignored = {}
  this.maxSize = options.maxSize
  this.multiple = options.multiple || true
  this.accept = options.accept.filter(function (accept) {
    return (type(accept) === 'string') && accept.match(/(image|video|audio|text|\*)\/(\*|.*?)/)
  }).map(function (accept) {
    return accept.match(/(image|video|audio|text|\*)\/(\*|.*?)/)
  }).map(function (accept) {
    return {type: accept[0], format: accept[1]}
  })

  if(!this.accept.length)
    this.accept = [{type: '*', format: '*'}]

  this.el = el
  this.el.appendChild(domify(interpolate(template, this.multiple ? 'multiple' : '')))
  this.input = this.el.querySelector('.cdpload')

  this.classes = classes(el)
  this.events = events(el, this)

  this.events.bind('drop')
  this.events.bind('dragenter')
  this.events.bind('dragleave')
  this.events.bind('dragover')
  this.events.bind('click')
  this.events.bind('change .cdpload')
}

Emitter(cdpload.prototype)

cdpload.prototype.ignore = function(name){
  this.ignored[name] = true
}

cdpload.prototype.ignoring = function(name){
  return !!this.ignored[name]
}

cdpload.prototype.unbind = function () {
  this.events.unbind()
}

cdpload.prototype.ondragenter = function () {
  this.classes.add('over')
}

cdpload.prototype.ondragover = function (ev) {
  ev.preventDefault()
}

cdpload.prototype.ondragleave = function () {
  this.classes.remove('over')
}

cdpload.prototype.onchange = function () {
  this.upload(this.input.files);
}

cdpload.prototype.onclick = function (ev) {
  ev.stopPropagation()
  ev.preventDefault()

  this.input.click()
}

cdpload.prototype.ondrop = function (ev) {
  if(!ev.dataTransfer.files.length) return

  ev.stopPropagation()
  ev.preventDefault()

  this.classes.remove('over')
  var files = this.multiple ? [ev.dataTransfer.files[0]] : ev.dataTransfer.files
  Array.prototype.filter.call(files, this.filter, this).forEach(this.upload, this)
  this.emit('drop', ev)
}

cdpload.prototype.filter = function (file) {
  if(file.size > this.maxSize) {
    this.emit('maxSizeExceed', file)
    return false
  }

  var accepted = this.accept.some(function (accept) {
    var mime = file.type.split('/')
    var format = mime[1]
    var type = mime[0]

    if(accept.type === '*') return true
    if(accept.type === type) return true
    if(accept.format === '*') return true
    if(accept.format === format) return true
    return false
  })

  if(!accepted)
    this.emit('typeNotAllowed', file)

  return accepted
}

cdpload.prototype.upload = function(file){
  this.emit('upload', new Upload(file))
}<commit_msg>fix type parsing<commit_after>/**
 * Module dependencies.
 */

var interpolate = require('interpolate'),
    Emitter = require('emitter'),
    classes = require('classes'),
    Upload = require('upload'),
    events = require('events'),
    domify = require('domify'),
    type = require('type')

var template = require('./template')

var cdpload = module.exports = function (el, options) {
  if (!(this instanceof cdpload)) return new cdpload(el, options)
  Emitter.call(this)

  if(type(options) !== 'object')
    options = {}

  if(type(options.accept) === 'string')
    options.accept = [options.accept]

  if(type(options.accept) !== 'array')
    options.accept = []

  if(!options.maxSize)
    options.maxSize = Infinity

  this.ignored = {}
  this.maxSize = options.maxSize
  this.multiple = options.multiple || true
  this.accept = options.accept.filter(function (accept) {
    return (type(accept) === 'string') && accept.match(/(image|video|audio|text|\*)\/(\*|.*?)/)
  }).map(function (accept) {
    return accept.match(/(image|video|audio|text|\*)\/(\*|.*?)/)
  }).map(function (accept) {
    return {type: accept[1], format: accept[2]}
  })

  if(!this.accept.length)
    this.accept = [{type: '*', format: '*'}]

  this.el = el
  this.el.appendChild(domify(interpolate(template, this.multiple ? 'multiple' : '')))
  this.input = this.el.querySelector('.cdpload')

  this.classes = classes(el)
  this.events = events(el, this)

  this.events.bind('drop')
  this.events.bind('dragenter')
  this.events.bind('dragleave')
  this.events.bind('dragover')
  this.events.bind('click')
  this.events.bind('change .cdpload')
}

Emitter(cdpload.prototype)

cdpload.prototype.ignore = function(name){
  this.ignored[name] = true
}

cdpload.prototype.ignoring = function(name){
  return !!this.ignored[name]
}

cdpload.prototype.unbind = function () {
  this.events.unbind()
}

cdpload.prototype.ondragenter = function () {
  this.classes.add('over')
}

cdpload.prototype.ondragover = function (ev) {
  ev.preventDefault()
}

cdpload.prototype.ondragleave = function () {
  this.classes.remove('over')
}

cdpload.prototype.onchange = function () {
  this.upload(this.input.files);
}

cdpload.prototype.onclick = function (ev) {
  ev.stopPropagation()
  ev.preventDefault()

  this.input.click()
}

cdpload.prototype.ondrop = function (ev) {
  if(!ev.dataTransfer.files.length) return

  ev.stopPropagation()
  ev.preventDefault()

  this.classes.remove('over')
  var files = this.multiple ? [ev.dataTransfer.files[0]] : ev.dataTransfer.files
  Array.prototype.filter.call(files, this.filter, this).forEach(this.upload, this)
  this.emit('drop', ev)
}

cdpload.prototype.filter = function (file) {
  if(file.size > this.maxSize) {
    this.emit('maxSizeExceed', file)
    return false
  }

  var accepted = this.accept.some(function (accept) {
    var mime = file.type.split('/')
    var format = mime[1]
    var type = mime[0]

    if(accept.type === '*') return true
    if(accept.type === type) return true
    if(accept.format === '*') return true
    if(accept.format === format) return true
    return false
  })

  if(!accepted)
    this.emit('typeNotAllowed', file)

  return accepted
}

cdpload.prototype.upload = function(file){
  this.emit('upload', new Upload(file))
}
<|endoftext|>


<commit_before>import { ExtrudeBufferGeometry } from 'three';
import { boolean, number, shapes } from '../validators';
import VglGeometry from '../core/vgl-geometry';
import { parseShape } from '../parsers';

/**
 * A component for creating extruded geometry from a path shape,
 * corresponding [THREE.ExtrudeGeometry](https://threejs.org/docs/index.html#api/geometries/ExtrudeGeometry).
 *
 * Properties of [VglGeometry](../core/vgl-geometry) are also available as mixin.
 */

export default {
  mixins: [VglGeometry],
  props: {
    /** The Shape (or an Array of Shape)
     * Accepts an array of points to internally create the shape.
     * Points can either be String ("12 2"), Number ([12, 2]) or THREE.Vector2.
     *
     * For an array of shapes, please pass directly an Array of THREE.Shape
     */
    shapes,
    /** int. Number of points on the curves */
    curveSegments: number,
    /** int. Number of points used for subdividing segments
     * along the depth of the extruded spline
     */
    steps: number,
    /** float. Depth to extrude the shape */
    depth: number,
    /** Apply beveling to the shape */
    bevelEnabled: boolean,
    /** float. How deep into the original shape the bevel goes */
    bevelThickness: number,
    /** float. Distance from the shape outline that the bevel extends */
    bevelSize: number,
    /** float. Distance from the shape outline that the bevel starts */
    bevelOffset: number,
    /** int. Number of bevel layers */
    bevelSegments: number,
    /** THREE.Curve. A 3D spline path along which the shape should be extruded */
    extrudePath: Object,
    /**  Object that provides UV generator functions */
    UVGenerator: Object,
  },
  computed: {
    options() {
      const {
        curveSegments,
        steps,
        depth,
        bevelEnabled,
        bevelThickness,
        bevelSize,
        bevelOffset,
        bevelSegments,
        extrudePath,
        UVGenerator,
      } = this;
      return {
        ...(curveSegments != null && { curveSegments: parseInt(curveSegments, 10) }),
        ...(steps != null && { steps: parseInt(steps, 10) }),
        ...(depth != null && { depth: parseFloat(depth) }),
        ...(bevelEnabled != null && { bevelEnabled }),
        ...(bevelThickness != null && { bevelThickness: parseFloat(bevelThickness) }),
        ...(bevelSize != null && { bevelSize: parseFloat(bevelSize) }),
        ...(bevelOffset != null && { bevelOffset: parseFloat(bevelOffset) }),
        ...(bevelSegments != null && { bevelSegments: parseInt(bevelSegments, 10) }),
        ...(extrudePath != null && { extrudePath }),
        ...(UVGenerator != null && { UVGenerator }),
      };
    },
    inst() {
      return new ExtrudeBufferGeometry(
        parseShape(this.shapes),
        this.options,
      );
    },
  },
};
<commit_msg>refactor: shapes prop to be an array of shape names<commit_after>import { ExtrudeBufferGeometry } from 'three';
import { boolean, number, string } from '../validators';
import VglGeometry from '../core/vgl-geometry';
import { parseArray } from '../parsers';

/**
 * A component for creating extruded geometry from a path shape,
 * corresponding [THREE.ExtrudeGeometry](https://threejs.org/docs/index.html#api/geometries/ExtrudeGeometry).
 *
 * Properties of [VglGeometry](../core/vgl-geometry) are also available as mixin.
 */

export default {
  mixins: [VglGeometry],
  props: {
    /** The Shape names */
    shapes: string,
    /** int. Number of points on the curves */
    curveSegments: number,
    /** int. Number of points used for subdividing segments
     * along the depth of the extruded spline
     */
    steps: number,
    /** float. Depth to extrude the shape */
    depth: number,
    /** Apply beveling to the shape */
    bevelEnabled: boolean,
    /** float. How deep into the original shape the bevel goes */
    bevelThickness: number,
    /** float. Distance from the shape outline that the bevel extends */
    bevelSize: number,
    /** float. Distance from the shape outline that the bevel starts */
    bevelOffset: number,
    /** int. Number of bevel layers */
    bevelSegments: number,
    /** THREE.Curve. A 3D spline path along which the shape should be extruded */
    extrudePath: Object,
    /**  Object that provides UV generator functions */
    uvGenerator: Object,
  },
  data: () => ({
    shapeNames: [],
  }),
  computed: {
    options() {
      const {
        curveSegments,
        steps,
        depth,
        bevelEnabled,
        bevelThickness,
        bevelSize,
        bevelOffset,
        bevelSegments,
        extrudePath,
        uvGenerator,
      } = this;
      return {
        ...(curveSegments != null && { curveSegments: parseInt(curveSegments, 10) }),
        ...(steps != null && { steps: parseInt(steps, 10) }),
        ...(depth != null && { depth: parseFloat(depth) }),
        ...(bevelEnabled != null && { bevelEnabled }),
        ...(bevelThickness != null && { bevelThickness: parseFloat(bevelThickness) }),
        ...(bevelSize != null && { bevelSize: parseFloat(bevelSize) }),
        ...(bevelOffset != null && { bevelOffset: parseFloat(bevelOffset) }),
        ...(bevelSegments != null && { bevelSegments: parseInt(bevelSegments, 10) }),
        ...(extrudePath != null && { extrudePath }),
        ...(uvGenerator != null && { UVGenerator: uvGenerator }),
      };
    },
    inst() {
      const shapes = this.shapeNames.map(([name]) => this.vglNamespace.curves.get(name));
      return new ExtrudeBufferGeometry(shapes, this.options);
    },
  },
  methods: {
    /** Update the array of Shape names and Shape UUIDs and emit an event. */
    setShapeNames() {
      const names = parseArray(this.shapes);
      this.shapeNames = names.map((name) => [name, this.vglNamespace.curves.get(name).uuid]);
      this.update();
    },
  },
  beforeDestroy() {
    if (this.shapes !== undefined) {
      const names = parseArray(this.shapes);
      names.forEach((name) => this.vglNamespace.curves.unlisten(name, this.setShapeUuids));
    }
  },
  watch: {
    shapes: {
      handler(shapes, oldShapes) {
        const oldNames = oldShapes === undefined ? [] : parseArray(oldShapes);
        const names = shapes === undefined ? [] : parseArray(shapes);
        oldNames.forEach((name) => {
          if (!names.includes(name)) this.vglNamespace.curves.unlisten(name, this.setShapeNames);
        });
        names.forEach((name) => {
          if (!oldNames.includes(name)) this.vglNamespace.curves.listen(name, this.setShapeNames);
        });
        if (shapes !== undefined) this.setShapeNames();
      },
      immediate: true,
    },
  },
};
<|endoftext|>


<commit_before>

if (!trackpadMouseWheelTool) {

	/**
	 * Creates a new GoJs Tool that enables the trackpad to be used for omnidirectional
	 * panning.  Currently, the standardMouseWheel tool evaluates wheelDelta behavior
	 * that indicates north/south intentions.  This tool looks at the wheelDeltaX values
	 * and directs the panning in the intended (combined) X/Y direction.
	 *
	 * It also gives meta key the same status as ctrl when altering the mousewheel behavior
	 * to zoom instead of scroll (depending on the setting of diagram.toolManager.mouseWheelBehavior)
	 *
	 * overrides/extends diagram.toolManager.standardMouseWheel
	 *
	 * @link http://www.gojs.net/latest/api/symbols/Tool.html#standardMouseWheel
	 * @type {function}
	 * @author greg.herrington@gmail.com
	 */
	var trackpadMouseWheelTool = (function (go, $log, debug) {

		if (!go && !go.ToolManager) throw "GoJS not detected";

		$log = $log || console;

		return function trackpadMouseWheel() {

			$log.error(" > trackpadMouseWheel > ");

			var diagram = this.diagram;
			if (diagram === null) return;

			// instead of this you will want to look at e.event.wheelDelta, e.event.wheelDeltaX, and e.event.wheelDeltaY, and decide what direction to scroll.
			var
				e = diagram.lastInput,
				delta = e.delta,
				wheelDelta = e.event.wheelDelta,
				wheelDeltaX = e.event.wheelDeltaX,
				wheelDeltaY = e.event.wheelDeltaY
			;

			if (debug) {
				$log.error("__________________________________");
				$log.error("    > diagram.allowVerticalScroll > ", diagram.allowVerticalScroll);
				$log.error("    > diagram.allowVerticalScroll > ", diagram.allowHorizontalScroll);
				$log.error("    > delta > ", delta);
				$log.error("    > wheelDelta > ", wheelDelta);
				$log.error("    > wheelDeltaX > ", wheelDeltaX);
				$log.error("    > wheelDeltaY > ", wheelDeltaY);
				$log.error("    > e.shift > ", e.shift);
				$log.error("    > e.control  > ", e.control);
				$log.error("    > e.meta  > ", e.meta);
				$log.error("    > e.alt   > ", e.alt);
			}

			if (delta === 0) return;

			if (!diagram.documentBounds.isReal()) return;

			var cmd = diagram.commandHandler;
			var wheelBehavior = diagram.toolManager.mouseWheelBehavior;

			if (((wheelBehavior === go.ToolManager.WheelZoom && !(e.control || e.meta)) ||
			     (wheelBehavior === go.ToolManager.WheelScroll && (e.control || e.meta))) &&
			     (delta > 0 ? cmd.canIncreaseZoom() : cmd.canDecreaseZoom())) {

				debug && $log.error(" > zoom mode");

				// zoom in or out at current mouse point
				var oldzoom = diagram.zoomPoint;
				diagram.zoomPoint = e.viewPoint;
				if (delta > 0)
					cmd.increaseZoom();
				else
					cmd.decreaseZoom();
				diagram.zoomPoint = oldzoom;
				e.bubbles = false;

			} else if ((wheelBehavior === go.ToolManager.WheelZoom && (e.control || e.meta)) ||
			           (wheelBehavior === go.ToolManager.WheelScroll && !(e.control || e.meta))) {

				var
					oldpos = diagram.position.copy(),
					cY     = diagram.scrollVerticalLineChange,
					cX     = diagram.scrollHorizontalLineChange
				;

				/*______________________________________
				 |
				 | new omnidirectional panning
				 |   (logic based on wheelDeltaX/Y)
				 |______________________________________
				 |
				 |
				 */

				if (typeof wheelDeltaX != 'undefined' && typeof wheelDeltaY != 'undefined') {

					// pretty much everything but IE (handles mousewheel, but uses a separate wheel event to expose deltaX/Y )

					debug && $log.error(" >  omni-panning mode ...");

					// no need to test for shift modifier here as we're explicitly handling directional changes via swipe direction
					if (diagram.allowVerticalScroll) {

						var totalY = Math.abs(wheelDeltaY);

						// scroll up or down
						totalY = (totalY / 40) * cY;

						debug && $log.error("     >>>> scrolling Y-axis "+(wheelDeltaY > 0 ? "UP" : "DOWN")+" by:", totalY);

						if (wheelDeltaY > 0) diagram.scroll('pixel', 'up', totalY);
						else diagram.scroll('pixel', 'down', totalY);

					}

					if (diagram.allowHorizontalScroll) {

						var totalX = Math.abs(wheelDeltaX);

						// scroll left or right
						totalX = (totalX / 40) * cX;

						debug && $log.error("     >>>> scrolling X-axis "+(wheelDeltaX > 0 ? "LEFT" : "RIGHT")+" by:", totalX);

						if (wheelDeltaX > 0) diagram.scroll('pixel', 'left', totalX);
						else diagram.scroll('pixel', 'right', totalX);
					}

				} else { // default to standard behavior

					// for now, IE will fall back to this

					/*_______________________________________
					 |
					 | default GoJS scroll behavior
					 |  (no browser support for wheelDeltaX/Y
					 |_______________________________________
					 |
					 |
					 */

					debug && $log.error(" > reverting to standard GoJS tool behavior..")

					var total = (delta > 0) ? delta : -delta;
					// 48 pixels is normal (3 lines, 16*3)
					// 120 is the normal delta

					if (!e.shift && diagram.allowVerticalScroll) {

						// scroll up or down
						var c = diagram.scrollVerticalLineChange;
						total = (total / 40) * c;
						if (delta > 0) diagram.scroll('pixel', 'up', total);
						else diagram.scroll('pixel', 'down', total);

					} else if (e.shift && diagram.allowHorizontalScroll) {

						// scroll left or right
						var c = diagram.scrollHorizontalLineChange;
						total = (total / 40) * c;
						if (delta > 0) diagram.scroll('pixel', 'left', total);
						else diagram.scroll('pixel', 'right', total);
					}

				} // endif

				if (!diagram.position.equals(oldpos)) {
					e.bubbles = false;
				}

			} // endif

		};

	})(go, console, false);

}

<commit_msg>updated logging.<commit_after>

if (!trackpadMouseWheelTool) {

	/**
	 * Creates a new GoJs Tool that enables the trackpad to be used for omnidirectional
	 * panning.  Currently, the standardMouseWheel tool evaluates wheelDelta behavior
	 * that indicates north/south intentions.  This tool looks at the wheelDeltaX values
	 * and directs the panning in the intended (combined) X/Y direction.
	 *
	 * It also gives meta key the same status as ctrl when altering the mousewheel behavior
	 * to zoom instead of scroll (depending on the setting of diagram.toolManager.mouseWheelBehavior)
	 *
	 * overrides/extends diagram.toolManager.standardMouseWheel
	 *
	 * @link http://www.gojs.net/latest/api/symbols/Tool.html#standardMouseWheel
	 * @type {function}
	 * @author greg.herrington@gmail.com
	 */
	var trackpadMouseWheelTool = (function (go, $log, debug) {

		if (!go && !go.ToolManager) throw "GoJS not detected";

		$log = $log || console;

		return function trackpadMouseWheel() {

			debug && $log.error(" > trackpadMouseWheel > ");

			var diagram = this.diagram;
			if (diagram === null) return;

			// instead of this you will want to look at e.event.wheelDelta, e.event.wheelDeltaX, and e.event.wheelDeltaY, and decide what direction to scroll.
			var
				e = diagram.lastInput,
				delta = e.delta,
				wheelDelta = e.event.wheelDelta,
				wheelDeltaX = e.event.wheelDeltaX,
				wheelDeltaY = e.event.wheelDeltaY
			;

			if (debug) {
				$log.error("__________________________________");
				$log.error("    > diagram.allowVerticalScroll > ", diagram.allowVerticalScroll);
				$log.error("    > diagram.allowVerticalScroll > ", diagram.allowHorizontalScroll);
				$log.error("    > delta > ", delta);
				$log.error("    > wheelDelta > ", wheelDelta);
				$log.error("    > wheelDeltaX > ", wheelDeltaX);
				$log.error("    > wheelDeltaY > ", wheelDeltaY);
				$log.error("    > e.shift > ", e.shift);
				$log.error("    > e.control  > ", e.control);
				$log.error("    > e.meta  > ", e.meta);
				$log.error("    > e.alt   > ", e.alt);
			}

			if (delta === 0) return;

			if (!diagram.documentBounds.isReal()) return;

			var cmd = diagram.commandHandler;
			var wheelBehavior = diagram.toolManager.mouseWheelBehavior;

			if (((wheelBehavior === go.ToolManager.WheelZoom && !(e.control || e.meta)) ||
			     (wheelBehavior === go.ToolManager.WheelScroll && (e.control || e.meta))) &&
			     (delta > 0 ? cmd.canIncreaseZoom() : cmd.canDecreaseZoom())) {

				debug && $log.error(" > zoom mode");

				// zoom in or out at current mouse point
				var oldzoom = diagram.zoomPoint;
				diagram.zoomPoint = e.viewPoint;
				if (delta > 0)
					cmd.increaseZoom();
				else
					cmd.decreaseZoom();
				diagram.zoomPoint = oldzoom;
				e.bubbles = false;

			} else if ((wheelBehavior === go.ToolManager.WheelZoom && (e.control || e.meta)) ||
			           (wheelBehavior === go.ToolManager.WheelScroll && !(e.control || e.meta))) {

				var
					oldpos = diagram.position.copy(),
					cY     = diagram.scrollVerticalLineChange,
					cX     = diagram.scrollHorizontalLineChange
				;

				/*______________________________________
				 |
				 | new omnidirectional panning
				 |   (logic based on wheelDeltaX/Y)
				 |______________________________________
				 |
				 |
				 */

				if (typeof wheelDeltaX != 'undefined' && typeof wheelDeltaY != 'undefined') {

					// pretty much everything but IE (handles mousewheel, but uses a separate wheel event to expose deltaX/Y )

					debug && $log.error(" >  omni-panning mode ...");

					// no need to test for shift modifier here as we're explicitly handling directional changes via swipe direction
					if (diagram.allowVerticalScroll) {

						var totalY = Math.abs(wheelDeltaY);

						// scroll up or down
						totalY = (totalY / 40) * cY;

						debug && $log.error("     >>>> scrolling Y-axis "+(wheelDeltaY > 0 ? "UP" : "DOWN")+" by:", totalY);

						if (wheelDeltaY > 0) diagram.scroll('pixel', 'up', totalY);
						else diagram.scroll('pixel', 'down', totalY);

					}

					if (diagram.allowHorizontalScroll) {

						var totalX = Math.abs(wheelDeltaX);

						// scroll left or right
						totalX = (totalX / 40) * cX;

						debug && $log.error("     >>>> scrolling X-axis "+(wheelDeltaX > 0 ? "LEFT" : "RIGHT")+" by:", totalX);

						if (wheelDeltaX > 0) diagram.scroll('pixel', 'left', totalX);
						else diagram.scroll('pixel', 'right', totalX);
					}

				} else { // default to standard behavior

					// for now, IE will fall back to this

					/*_______________________________________
					 |
					 | default GoJS scroll behavior
					 |  (no browser support for wheelDeltaX/Y
					 |_______________________________________
					 |
					 |
					 */

					debug && $log.error(" > reverting to standard GoJS tool behavior..")

					var total = (delta > 0) ? delta : -delta;
					// 48 pixels is normal (3 lines, 16*3)
					// 120 is the normal delta

					if (!e.shift && diagram.allowVerticalScroll) {

						// scroll up or down
						var c = diagram.scrollVerticalLineChange;
						total = (total / 40) * c;
						if (delta > 0) diagram.scroll('pixel', 'up', total);
						else diagram.scroll('pixel', 'down', total);

					} else if (e.shift && diagram.allowHorizontalScroll) {

						// scroll left or right
						var c = diagram.scrollHorizontalLineChange;
						total = (total / 40) * c;
						if (delta > 0) diagram.scroll('pixel', 'left', total);
						else diagram.scroll('pixel', 'right', total);
					}

				} // endif

				if (!diagram.position.equals(oldpos)) {
					e.bubbles = false;
				}

			} // endif

		};

	})(go, console, false);

}

<|endoftext|>


<commit_before>define(

function () {
	'use strict';

	/**
	 * A Particle influence modifies particles in some way over time
	 */
	function ParticleInfluence (settings) {
		settings = settings || {};

		/**
		 * Function for preparing to apply this particle influence. Useful for expensive operations that should only need computing once per frame
		 * @type {Function}
		 */
		// Was: function (particleEntity, emitter)
		this.prepare = settings.prepare ? settings.prepare : function () {
		};

		/**
		 * Function for applying this particle influence
		 * @type {Function}
		 */
		// Was: function (tpf, particle, index)
		this.apply = settings.apply ? settings.apply : function () {
		};

		/**
		 * Specifies whether this influence should be applied to particles. Prepare is called regardless
		 * @type {boolean}
		 */
		this.enabled = settings.enabled !== undefined ? settings.enabled === true : true;
	}

	return ParticleInfluence;
});<commit_msg>More docs.<commit_after>define(

function () {
	'use strict';

	/**
	 * A Particle influence modifies particles in some way over time.
	 * @param {object} [settings]
	 * @param {Function} [settings.prepare] prepare({@link Entity} particleEntity, {@link ParticleEmitter} emitter).
	 * @param {Function} [settings.apply] apply(number tpf, {@link Particle} particle, number particleIndex).
	 * @param {boolean} [settings.enabled=true]
	 */
	function ParticleInfluence (settings) {
		settings = settings || {};

		/**
		 * Function for preparing to apply this particle influence. Useful for expensive operations that should only need computing once per frame.
		 * @type {Function}
		 * @param {Entity} [particleEntity] The Entity on which the ParticleComponent is attached to.
		 * @param {ParticleEmitter} [emitter] The ParticleEmitter on which this ParticleInfluence is attached to.
		 */
		this.prepare = settings.prepare ? settings.prepare : function () {};

		/**
		 * Function for applying this particle influence
		 * @type {Function}
		 * @param {number} [tpf] Time since last frame.
		 * @param {Particle} [particle] The Particle object this function is applyed with.
		 * @param {number} [index] The particle's index in the ParticleComponents.particles array.
		 */
		this.apply = settings.apply ? settings.apply : function () {};

		/**
		 * Specifies whether this influence should be applied to particles. Prepare is called regardless
		 * @type {boolean}
		 */
		this.enabled = settings.enabled !== undefined ? settings.enabled === true : true;
	}

	return ParticleInfluence;
});<|endoftext|>


<commit_before>'use strict';

const mwUtil = require('../lib/mwUtil');
const HyperSwitch = require('hyperswitch');
const URI = HyperSwitch.URI;

const spec = HyperSwitch.utils.loadSpec(`${__dirname}/related.yaml`);

class Related {
    constructor(options) {
        this._options = options;
    }

    _relatedPageRedirect(hyper, domain, title) {
        const apiURI = new URI(`https://${domain}/w/api.php`);

        return hyper.get({
            uri: apiURI,
            headers: {
                'content-type': 'application/json'
            },
            query: {
                action: 'query',
                format: 'json',
                titles: title,
                redirects: 1,
                converttitles: 1
            }
        }).then((res) => {
            return res.body;
        }).catch((e) => {
            hyper.logger.log('error/related', {
                message: 'Failed to fetch related pages',
                error: e
            });
            throw e;
        });
    }

    getPages(hyper, req) {
        const rp = req.params;

        return this._relatedPageRedirect(hyper, rp.domain, rp.title).then((res) => {

            // Return the right title to redirect if it has another language option
            if (res && res.query) {
                if (res.query.converted) {
                    rp.title = res.query.converted[0].to;
                } else if (res.query.redirects) {
                    rp.title = res.query.redirects[0].to;
                }
            }

            const rh = Object.assign({}, req.headers);
            // we don't store /page/related no need for cache-control
            if (rh['cache-control']) {
                delete rh['cache-control'];
            }

            return hyper.post({
                uri: new URI([rp.domain, 'sys', 'action', 'query']),
                body: {
                    format: 'json',
                    generator: 'search',
                    gsrsearch: `morelike:${rp.title}`,
                    gsrnamespace: 0,
                    gsrwhat: 'text',
                    gsrinfo: '',
                    gsrprop: 'redirecttitle',
                    gsrlimit: 20
                }
            })
            .then((res) => {
                delete res.body.next;

                // Step 1: Normalize and convert titles to use $merge
                res.body.items.forEach((item) => {
                    // We can avoid using the full-blown title normalisation here because
                    // the titles come from MW API and they're already normalised except
                    // they use spaces instead of underscores.
                    item.$merge = [ new URI([rp.domain, 'v1', 'page',
                        'summary', item.title.replace(/ /g, '_')]) ];
                    delete item.title;
                });

                // Rename `items` to `pages`
                res.body.pages = res.body.items;
                delete res.body.items;

                // Step 2: Hydrate response as always.
                return mwUtil.hydrateResponse(res, (uri) => {
                    return mwUtil.fetchSummary(hyper, uri, rh).then((result) => {
                        if (!result) {
                            return result;
                        }

                        // Assign content-language and vary header to parent response
                        // based on one of summary responses
                        if (res && res.headers && !res.headers['content-language'] &&
                        result['content-language']) {
                            res.headers['content-language'] = result['content-language'];
                            mwUtil.addVaryHeader(res, 'accept-language');
                        }
                        return result.summary;
                    });
                });
            });
        });
    }
}

module.exports = (options) => {
    const relatedModule = new Related(options);

    return {
        spec,
        operations: {
            getRelatedPages: relatedModule.getPages.bind(relatedModule)
        }
    };
};
<commit_msg>Refactor _relatedPageRedirect()<commit_after>'use strict';

const mwUtil = require('../lib/mwUtil');
const HyperSwitch = require('hyperswitch');
const URI = HyperSwitch.URI;

const spec = HyperSwitch.utils.loadSpec(`${__dirname}/related.yaml`);

class Related {
    constructor(options) {
        this._options = options;
    }

    _relatedPageRedirect(hyper, domain, title) {
        const apiURI = new URI([domain, 'sys', 'action', 'rawquery']);

        return hyper.get({
            uri: apiURI,
            headers: {
                'content-type': 'application/json'
            },
            body: {
                action: 'query',
                format: 'json',
                titles: title,
                redirects: 1,
                converttitles: 1
            }
        }).then((res) => {
            return res.body;
        }).catch((e) => {
            hyper.logger.log('error/related', {
                message: 'Failed to fetch related pages',
                error: e
            });
            throw e;
        });
    }

    getPages(hyper, req) {
        const rp = req.params;

        return this._relatedPageRedirect(hyper, rp.domain, rp.title).then((res) => {

            // Return the right title to redirect if it has another language option
            if (res && res.query) {
                if (res.query.converted) {
                    rp.title = res.query.converted[0].to;
                } else if (res.query.redirects) {
                    rp.title = res.query.redirects[0].to;
                }
            }

            const rh = Object.assign({}, req.headers);
            // we don't store /page/related no need for cache-control
            if (rh['cache-control']) {
                delete rh['cache-control'];
            }

            return hyper.post({
                uri: new URI([rp.domain, 'sys', 'action', 'query']),
                body: {
                    format: 'json',
                    generator: 'search',
                    gsrsearch: `morelike:${rp.title}`,
                    gsrnamespace: 0,
                    gsrwhat: 'text',
                    gsrinfo: '',
                    gsrprop: 'redirecttitle',
                    gsrlimit: 20
                }
            })
            .then((res) => {
                delete res.body.next;

                // Step 1: Normalize and convert titles to use $merge
                res.body.items.forEach((item) => {
                    // We can avoid using the full-blown title normalisation here because
                    // the titles come from MW API and they're already normalised except
                    // they use spaces instead of underscores.
                    item.$merge = [ new URI([rp.domain, 'v1', 'page',
                        'summary', item.title.replace(/ /g, '_')]) ];
                    delete item.title;
                });

                // Rename `items` to `pages`
                res.body.pages = res.body.items;
                delete res.body.items;

                // Step 2: Hydrate response as always.
                return mwUtil.hydrateResponse(res, (uri) => {
                    return mwUtil.fetchSummary(hyper, uri, rh).then((result) => {
                        if (!result) {
                            return result;
                        }

                        // Assign content-language and vary header to parent response
                        // based on one of summary responses
                        if (res && res.headers && !res.headers['content-language'] &&
                        result['content-language']) {
                            res.headers['content-language'] = result['content-language'];
                            mwUtil.addVaryHeader(res, 'accept-language');
                        }
                        return result.summary;
                    });
                });
            });
        });
    }
}

module.exports = (options) => {
    const relatedModule = new Related(options);

    return {
        spec,
        operations: {
            getRelatedPages: relatedModule.getPages.bind(relatedModule)
        }
    };
};
<|endoftext|>


<commit_before>/* eslint-disable no-constant-condition */
import React from 'react'
import get from 'lodash/get'
import { camelizeKeys } from 'humps'
import { actionChannel, call, fork, put, select, take } from 'redux-saga/effects'
import * as ACTION_TYPES from '../constants/action_types'
import { selectIsLoggedIn, selectRefreshToken } from '../selectors/authentication'
import { selectLastNotificationCheck } from '../selectors/gui'
import { selectPathname } from '../selectors/routing'
import { refreshAuthenticationToken } from '../actions/authentication'
import { pauseRequester, unpauseRequester } from '../actions/api'
import { fetchCredentials, getClientCredentials, sagaFetch } from './api'
import { openAlert } from '../actions/modals'
import Dialog from '../components/dialogs/Dialog'

export const requestTypes = [
  ACTION_TYPES.AUTHENTICATION.FORGOT_PASSWORD,
  ACTION_TYPES.AUTHENTICATION.LOGOUT,
  ACTION_TYPES.AUTHENTICATION.REFRESH,
  ACTION_TYPES.AUTHENTICATION.USER,
  ACTION_TYPES.COMMENT.CREATE,
  ACTION_TYPES.COMMENT.DELETE,
  ACTION_TYPES.COMMENT.EDITABLE,
  ACTION_TYPES.COMMENT.FLAG,
  ACTION_TYPES.COMMENT.UPDATE,
  ACTION_TYPES.EDITOR.EMOJI_COMPLETER,
  ACTION_TYPES.EDITOR.LOAD_REPLY_ALL,
  ACTION_TYPES.EDITOR.POST_PREVIEW,
  ACTION_TYPES.EDITOR.USER_COMPLETER,
  ACTION_TYPES.HEAD,
  ACTION_TYPES.INVITATIONS.GET_EMAIL,
  ACTION_TYPES.INVITATIONS.INVITE,
  ACTION_TYPES.LOAD_NEXT_CONTENT,
  ACTION_TYPES.LOAD_STREAM,
  ACTION_TYPES.POST.COMMENT,
  ACTION_TYPES.POST.CREATE,
  ACTION_TYPES.POST.DELETE,
  ACTION_TYPES.POST.DETAIL,
  ACTION_TYPES.POST.EDITABLE,
  ACTION_TYPES.POST.FLAG,
  ACTION_TYPES.POST.LOVE,
  ACTION_TYPES.POST.UPDATE,
  ACTION_TYPES.POST_FORM,
  ACTION_TYPES.POST_JSON,
  ACTION_TYPES.POST.WATCH,
  ACTION_TYPES.PROFILE.AVAILABILITY,
  ACTION_TYPES.PROFILE.DELETE,
  ACTION_TYPES.PROFILE.EXPORT,
  ACTION_TYPES.PROFILE.FOLLOW_CATEGORIES,
  ACTION_TYPES.PROFILE.LOAD,
  ACTION_TYPES.PROFILE.REGISTER_FOR_GCM,
  ACTION_TYPES.PROFILE.REQUEST_INVITE,
  ACTION_TYPES.PROFILE.SAVE,
  ACTION_TYPES.PROFILE.SIGNUP,
  ACTION_TYPES.PROFILE.UNREGISTER_FOR_GCM,
  ACTION_TYPES.PROMOTIONS.AUTHENTICATION,
  ACTION_TYPES.PROMOTIONS.LOGGED_IN,
  ACTION_TYPES.PROMOTIONS.LOGGED_OUT,
  ACTION_TYPES.RELATIONSHIPS.UPDATE,
  ACTION_TYPES.USER.COLLAB_WITH,
  ACTION_TYPES.USER.DETAIL,
  ACTION_TYPES.USER.FLAG,
  ACTION_TYPES.USER.HIRE_ME,
]

let requesterIsPaused = false

const runningFetches = {}
function updateRunningFetches(serverResponse) {
  if (!serverResponse) { return }
  const serverResponseUrl = serverResponse.url && serverResponse.url.length
        ? serverResponse.url
        : serverResponse.headers.get('X-Request-Url')

  if (runningFetches[serverResponseUrl]) {
    delete runningFetches[serverResponseUrl]
  } else {
    Object.keys(runningFetches).forEach((key) => {
      delete runningFetches[key]
    })
  }
}

function parseLink(linksHeader) {
  if (!linksHeader) { return {} }
  const result = {}
  const entries = linksHeader.split(',')
  // compile regular expressions ahead of time for efficiency
  const relsRegExp = /\brel="?([^"]+)"?\s*;?/
  const keysRegExp = /(\b[0-9a-z\.-]+\b)/g
  const sourceRegExp = /^<(.*)>/
  for (let entry of entries) {
    entry = entry.trim()
    const rels = relsRegExp.exec(entry)
    if (rels) {
      const keys = rels[1].match(keysRegExp)
      const source = sourceRegExp.exec(entry)[1]
      for (const key of keys) {
        result[key] = source
      }
    }
  }
  return result
}

const defaultHeaders = {
  Accept: 'application/json',
  'Content-Type': 'application/json',
}

function getAuthToken(accessToken) {
  return {
    ...defaultHeaders,
    Authorization: `Bearer ${accessToken}`,
  }
}

function getHeaders(accessToken) {
  return getAuthToken(accessToken)
}

function getHeadHeader(accessToken, lastCheck) {
  return {
    ...getAuthToken(accessToken),
    'If-Modified-Since': lastCheck,
  }
}

export function extractJSON(serverResponse) {
  return serverResponse ? serverResponse.json() : serverResponse
}

export function* handleRequestError(error, action) {
  const { meta, payload, type } = action
  const FAILURE = `${type}_FAILURE`
  function* fireFailureAction() {
    if (meta && meta.failureAction) {
      if (typeof meta.failureAction === 'function') {
        yield call(meta.failureAction)
      } else {
        yield put(meta.failureAction)
      }
    }
  }

  if (error.response) {
    payload.serverStatus = error.response.status
    if (error.response.status === 401) {
      const isLoggedIn = yield select(selectIsLoggedIn)
      const refreshToken = yield select(selectRefreshToken)
      if (type !== ACTION_TYPES.AUTHENTICATION.LOGOUT &&
          type !== ACTION_TYPES.AUTHENTICATION.REFRESH &&
          isLoggedIn) {
        // If we just have an expired session,
        // trigger a refresh then put the request back on the queue
        yield put(refreshAuthenticationToken(refreshToken))
        yield put(action)
        return true
      }
    }

    if (error.response.status === 420) {
      yield put(openAlert(
        <Dialog
          title="Take a breath. You're doing Ello way too fast. A few more seconds. That's better."
          body="More Ello? Refresh the page to continue."
        />
      ))
      yield put({ error, meta, payload, type: FAILURE })
      return true
    }

    const contentType = error.response.headers.get('content-type')
    if (contentType && contentType.indexOf('application/json') > -1) {
      const errorJson = yield call(extractJSON, error.response)
      payload.response = camelizeKeys(errorJson)
      yield put({ error, meta, payload, type: FAILURE })
      yield call(fireFailureAction)
    }
  } else {
    if (/Failed to fetch/.test(error)) {
      payload.serverStatus = 404
    }
    yield put({ error, meta, payload, type: FAILURE })
    yield call(fireFailureAction)
  }
  return false
}

export function* performRequest(action) {
  const {
    type,
    meta,
    payload: { endpoint, method, body },
  } = action
  let { payload } = action

  const pathname = yield select(selectPathname)
  payload = {
    ...payload,
    pathname,
  }

  const options = {
    method: method || 'GET',
    credentials: 'include',
  }

  if (options.method !== 'GET' && options.method !== 'HEAD') {
    options.body = body || null
    if (options.body && typeof options.body !== 'string') {
      options.body = JSON.stringify(options.body)
    }
  }

  const REQUEST = `${type}_REQUEST`
  const SUCCESS = `${type}_SUCCESS`

  let tokenJSON = null
  if (action.type === ACTION_TYPES.AUTHENTICATION.REFRESH) {
    tokenJSON = yield call(getClientCredentials)
  } else {
    tokenJSON = yield call(fetchCredentials)
  }
  const accessToken = get(tokenJSON, 'token.access_token')

  yield put({ type: REQUEST, payload, meta })

  function* fireSuccessAction() {
    if (meta && meta.successAction) {
      if (typeof meta.successAction === 'function') {
        yield call(meta.successAction)
      } else {
        yield put(meta.successAction)
      }
    }
  }

  switch (method) {
    case 'HEAD':
      options.headers = getHeadHeader(
        accessToken,
        yield select(selectLastNotificationCheck)
      )
      break
    default:
      options.headers = getHeaders(accessToken)
      break
  }

  let response

  try {
    response = yield call(sagaFetch, endpoint.path, options)
  } catch (error) {
    updateRunningFetches(error.response)
    yield fork(handleRequestError, error, action)
    return false
  }

  const { json, serverResponse } = response

  updateRunningFetches(serverResponse)

  payload.serverStatus = serverResponse.status
  if (serverResponse.status === 200 || serverResponse.status === 201) {
    payload.response = camelizeKeys(json)

    if (endpoint.pagingPath && payload.response[meta.mappingType].id) {
      payload.pagination = get(
        payload,
        [
          'serverResponse',
          meta.mappingType,
          'links',
          endpoint.pagingPath,
          'pagination',
        ]
      )
    } else {
      const linkPagination = parseLink(serverResponse.headers.get('Link'))
      linkPagination.totalCount = parseInt(serverResponse.headers.get('X-TotalCount'), 10)
      linkPagination.totalPages = parseInt(serverResponse.headers.get('X-Total-Pages'), 10)
      linkPagination.totalPagesRemaining = parseInt(
        serverResponse.headers.get('X-Total-Pages-Remaining'), 10
      )
      payload.pagination = linkPagination
    }
  }
  yield put({ meta, payload, type: SUCCESS })
  yield call(fireSuccessAction)
  return true
}

function* waitForUnpause() {
  yield take(ACTION_TYPES.REQUESTER.UNPAUSE)
}

export function* handleRequest(requestChannel) {
  while (true) {
    const action = yield take(requestChannel)

    const { meta, payload: { endpoint } } = action

    if (!runningFetches[endpoint.path]) {
      if (requesterIsPaused) {
        yield call(waitForUnpause)
      }

      runningFetches[endpoint.path] = true

      if (get(meta, 'pauseRequester')) {
        yield put(pauseRequester())
        yield call(performRequest, action)
        yield put(unpauseRequester())
      } else {
        yield fork(performRequest, action)
      }
    }
  }
}

function* pauseMechanism() {
  while (yield take(ACTION_TYPES.REQUESTER.PAUSE)) {
    requesterIsPaused = true
    yield take(ACTION_TYPES.REQUESTER.UNPAUSE)
    requesterIsPaused = false
  }
}

export default function* requester() {
  const requestChannel = yield actionChannel(requestTypes)
  yield fork(pauseMechanism)
  yield fork(handleRequest, requestChannel)
  // yield fork(requestSaga, requestChannel)
}

export { runningFetches }

<commit_msg>Call localStorage.clear when in auth refresh loop<commit_after>/* eslint-disable no-constant-condition */
import React from 'react'
import get from 'lodash/get'
import { camelizeKeys } from 'humps'
import { actionChannel, call, fork, put, select, take } from 'redux-saga/effects'
import * as ACTION_TYPES from '../constants/action_types'
import { selectIsLoggedIn, selectRefreshToken } from '../selectors/authentication'
import { selectLastNotificationCheck } from '../selectors/gui'
import { selectPathname } from '../selectors/routing'
import { refreshAuthenticationToken } from '../actions/authentication'
import { pauseRequester, unpauseRequester } from '../actions/api'
import { fetchCredentials, getClientCredentials, sagaFetch } from './api'
import { openAlert } from '../actions/modals'
import Dialog from '../components/dialogs/Dialog'

export const requestTypes = [
  ACTION_TYPES.AUTHENTICATION.FORGOT_PASSWORD,
  ACTION_TYPES.AUTHENTICATION.LOGOUT,
  ACTION_TYPES.AUTHENTICATION.REFRESH,
  ACTION_TYPES.AUTHENTICATION.USER,
  ACTION_TYPES.COMMENT.CREATE,
  ACTION_TYPES.COMMENT.DELETE,
  ACTION_TYPES.COMMENT.EDITABLE,
  ACTION_TYPES.COMMENT.FLAG,
  ACTION_TYPES.COMMENT.UPDATE,
  ACTION_TYPES.EDITOR.EMOJI_COMPLETER,
  ACTION_TYPES.EDITOR.LOAD_REPLY_ALL,
  ACTION_TYPES.EDITOR.POST_PREVIEW,
  ACTION_TYPES.EDITOR.USER_COMPLETER,
  ACTION_TYPES.HEAD,
  ACTION_TYPES.INVITATIONS.GET_EMAIL,
  ACTION_TYPES.INVITATIONS.INVITE,
  ACTION_TYPES.LOAD_NEXT_CONTENT,
  ACTION_TYPES.LOAD_STREAM,
  ACTION_TYPES.POST.COMMENT,
  ACTION_TYPES.POST.CREATE,
  ACTION_TYPES.POST.DELETE,
  ACTION_TYPES.POST.DETAIL,
  ACTION_TYPES.POST.EDITABLE,
  ACTION_TYPES.POST.FLAG,
  ACTION_TYPES.POST.LOVE,
  ACTION_TYPES.POST.UPDATE,
  ACTION_TYPES.POST_FORM,
  ACTION_TYPES.POST_JSON,
  ACTION_TYPES.POST.WATCH,
  ACTION_TYPES.PROFILE.AVAILABILITY,
  ACTION_TYPES.PROFILE.DELETE,
  ACTION_TYPES.PROFILE.EXPORT,
  ACTION_TYPES.PROFILE.FOLLOW_CATEGORIES,
  ACTION_TYPES.PROFILE.LOAD,
  ACTION_TYPES.PROFILE.REGISTER_FOR_GCM,
  ACTION_TYPES.PROFILE.REQUEST_INVITE,
  ACTION_TYPES.PROFILE.SAVE,
  ACTION_TYPES.PROFILE.SIGNUP,
  ACTION_TYPES.PROFILE.UNREGISTER_FOR_GCM,
  ACTION_TYPES.PROMOTIONS.AUTHENTICATION,
  ACTION_TYPES.PROMOTIONS.LOGGED_IN,
  ACTION_TYPES.PROMOTIONS.LOGGED_OUT,
  ACTION_TYPES.RELATIONSHIPS.UPDATE,
  ACTION_TYPES.USER.COLLAB_WITH,
  ACTION_TYPES.USER.DETAIL,
  ACTION_TYPES.USER.FLAG,
  ACTION_TYPES.USER.HIRE_ME,
]

let requesterIsPaused = false
let breakRefreshCycle = false

const runningFetches = {}
function updateRunningFetches(serverResponse) {
  if (!serverResponse) { return }
  const serverResponseUrl = serverResponse.url && serverResponse.url.length
        ? serverResponse.url
        : serverResponse.headers.get('X-Request-Url')

  if (runningFetches[serverResponseUrl]) {
    delete runningFetches[serverResponseUrl]
  } else {
    Object.keys(runningFetches).forEach((key) => {
      delete runningFetches[key]
    })
  }
}

function parseLink(linksHeader) {
  if (!linksHeader) { return {} }
  const result = {}
  const entries = linksHeader.split(',')
  // compile regular expressions ahead of time for efficiency
  const relsRegExp = /\brel="?([^"]+)"?\s*;?/
  const keysRegExp = /(\b[0-9a-z\.-]+\b)/g
  const sourceRegExp = /^<(.*)>/
  for (let entry of entries) {
    entry = entry.trim()
    const rels = relsRegExp.exec(entry)
    if (rels) {
      const keys = rels[1].match(keysRegExp)
      const source = sourceRegExp.exec(entry)[1]
      for (const key of keys) {
        result[key] = source
      }
    }
  }
  return result
}

const defaultHeaders = {
  Accept: 'application/json',
  'Content-Type': 'application/json',
}

function getAuthToken(accessToken) {
  return {
    ...defaultHeaders,
    Authorization: `Bearer ${accessToken}`,
  }
}

function getHeaders(accessToken) {
  return getAuthToken(accessToken)
}

function getHeadHeader(accessToken, lastCheck) {
  return {
    ...getAuthToken(accessToken),
    'If-Modified-Since': lastCheck,
  }
}

export function extractJSON(serverResponse) {
  return serverResponse ? serverResponse.json() : serverResponse
}

export function* handleRequestError(error, action) {
  const { meta, payload, type } = action
  const FAILURE = `${type}_FAILURE`
  function* fireFailureAction() {
    if (meta && meta.failureAction) {
      if (typeof meta.failureAction === 'function') {
        yield call(meta.failureAction)
      } else {
        yield put(meta.failureAction)
      }
    }
  }

  if (error.response) {
    payload.serverStatus = error.response.status
    if (error.response.status === 401) {
      const isLoggedIn = yield select(selectIsLoggedIn)
      const refreshToken = yield select(selectRefreshToken)
      if (type !== ACTION_TYPES.AUTHENTICATION.LOGOUT &&
          type !== ACTION_TYPES.AUTHENTICATION.REFRESH &&
          isLoggedIn) {
        // If we just have an expired session,
        // trigger a refresh then put the request back on the queue
        yield put(refreshAuthenticationToken(refreshToken))
        yield put(action)
        return true
      } else if (type === ACTION_TYPES.AUTHENTICATION.REFRESH) {
        if (!breakRefreshCycle) {
          breakRefreshCycle = true
        } else {
          breakRefreshCycle = false
          localStorage.clear()
          yield put(refreshAuthenticationToken(refreshToken))
          yield put(action)
        }
        return true
      }
    }

    if (error.response.status === 420) {
      yield put(openAlert(
        <Dialog
          title="Take a breath. You're doing Ello way too fast. A few more seconds. That's better."
          body="More Ello? Refresh the page to continue."
        />
      ))
      yield put({ error, meta, payload, type: FAILURE })
      return true
    }

    const contentType = error.response.headers.get('content-type')
    if (contentType && contentType.indexOf('application/json') > -1) {
      const errorJson = yield call(extractJSON, error.response)
      payload.response = camelizeKeys(errorJson)
      yield put({ error, meta, payload, type: FAILURE })
      yield call(fireFailureAction)
    }
  } else {
    if (/Failed to fetch/.test(error)) {
      payload.serverStatus = 404
    }
    yield put({ error, meta, payload, type: FAILURE })
    yield call(fireFailureAction)
  }
  return false
}

export function* performRequest(action) {
  const {
    type,
    meta,
    payload: { endpoint, method, body },
  } = action
  let { payload } = action

  const pathname = yield select(selectPathname)
  payload = {
    ...payload,
    pathname,
  }

  const options = {
    method: method || 'GET',
    credentials: 'include',
  }

  if (options.method !== 'GET' && options.method !== 'HEAD') {
    options.body = body || null
    if (options.body && typeof options.body !== 'string') {
      options.body = JSON.stringify(options.body)
    }
  }

  const REQUEST = `${type}_REQUEST`
  const SUCCESS = `${type}_SUCCESS`

  let tokenJSON = null
  if (action.type === ACTION_TYPES.AUTHENTICATION.REFRESH) {
    tokenJSON = yield call(getClientCredentials)
  } else {
    tokenJSON = yield call(fetchCredentials)
  }
  const accessToken = get(tokenJSON, 'token.access_token')

  yield put({ type: REQUEST, payload, meta })

  function* fireSuccessAction() {
    if (meta && meta.successAction) {
      if (typeof meta.successAction === 'function') {
        yield call(meta.successAction)
      } else {
        yield put(meta.successAction)
      }
    }
  }

  switch (method) {
    case 'HEAD':
      options.headers = getHeadHeader(
        accessToken,
        yield select(selectLastNotificationCheck)
      )
      break
    default:
      options.headers = getHeaders(accessToken)
      break
  }

  let response

  try {
    response = yield call(sagaFetch, endpoint.path, options)
  } catch (error) {
    updateRunningFetches(error.response)
    yield fork(handleRequestError, error, action)
    return false
  }

  const { json, serverResponse } = response

  updateRunningFetches(serverResponse)

  payload.serverStatus = serverResponse.status
  if (serverResponse.status === 200 || serverResponse.status === 201) {
    payload.response = camelizeKeys(json)

    if (endpoint.pagingPath && payload.response[meta.mappingType].id) {
      payload.pagination = get(
        payload,
        [
          'serverResponse',
          meta.mappingType,
          'links',
          endpoint.pagingPath,
          'pagination',
        ]
      )
    } else {
      const linkPagination = parseLink(serverResponse.headers.get('Link'))
      linkPagination.totalCount = parseInt(serverResponse.headers.get('X-TotalCount'), 10)
      linkPagination.totalPages = parseInt(serverResponse.headers.get('X-Total-Pages'), 10)
      linkPagination.totalPagesRemaining = parseInt(
        serverResponse.headers.get('X-Total-Pages-Remaining'), 10
      )
      payload.pagination = linkPagination
    }
  }
  yield put({ meta, payload, type: SUCCESS })
  yield call(fireSuccessAction)
  return true
}

function* waitForUnpause() {
  yield take(ACTION_TYPES.REQUESTER.UNPAUSE)
}

export function* handleRequest(requestChannel) {
  while (true) {
    const action = yield take(requestChannel)

    const { meta, payload: { endpoint } } = action

    if (!runningFetches[endpoint.path]) {
      if (requesterIsPaused) {
        yield call(waitForUnpause)
      }

      runningFetches[endpoint.path] = true

      if (get(meta, 'pauseRequester')) {
        yield put(pauseRequester())
        yield call(performRequest, action)
        yield put(unpauseRequester())
      } else {
        yield fork(performRequest, action)
      }
    }
  }
}

function* pauseMechanism() {
  while (yield take(ACTION_TYPES.REQUESTER.PAUSE)) {
    requesterIsPaused = true
    yield take(ACTION_TYPES.REQUESTER.UNPAUSE)
    requesterIsPaused = false
  }
}

export default function* requester() {
  const requestChannel = yield actionChannel(requestTypes)
  yield fork(pauseMechanism)
  yield fork(handleRequest, requestChannel)
}

export { runningFetches }

<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import {Link} from 'react-router'

import studentActions from '../flux/student-actions'
import studentStore from '../flux/student-store'

import Button from '../components/button'
import CourseSearcher from './course-searcher'
import GraduationStatus from './graduation-status'
import Icon from '../components/icon'
import Toolbar from '../components/toolbar'
import Separator from '../components/separator'

import CourseRemovalBox from '../components/course-removal-box'

import './sidebar.scss'

export default class Sidebar extends Component {
	static propTypes = {
		allAreas: PropTypes.object, // Immutable.List
		baseSearchQuery: PropTypes.object,
		courses: PropTypes.object, // Immutable.List
		coursesLoaded: PropTypes.bool.isRequired,
		isSearching: PropTypes.bool.isRequired,
		student: PropTypes.object.isRequired,
		toggleSearchSidebar: PropTypes.func.isRequired,
	}

	render() {
		return (
			<aside className='sidebar'>
				<Toolbar className='student-buttons'>
					<Button className='sidebar-btn' title='Students'>
						<Link to='/'>
							<Icon name='ionicon-ios-people-outline' type='block' />
						</Link>
					</Button>
					<Button className='sidebar-btn'
						title='Search'
						onClick={this.props.toggleSearchSidebar}>
						<Icon name='ionicon-ios-search' type='block' />
					</Button>

					<Separator type='spacer' />

					<Button className='sidebar-btn'
						title='Undo'
						onClick={studentActions.undo}
						disabled={studentStore.history.size === 0}>
						<Icon name={`ionicon-ios-undo${studentStore.history.size === 0 ? '-outline' : ''}`} type='block' />
					</Button>
					<Button className='sidebar-btn'
						title='Redo'
						onClick={studentActions.redo}
						disabled={studentStore.future.size === 0}>
						<Icon name={`ionicon-ios-redo${studentStore.future.size === 0 ? '-outline' : ''}`} type='block' />
					</Button>

					<Separator type='spacer' />

					<Button className='sidebar-btn' title='Download'>
						<Link to='download' params={{id: this.props.student.id}}>
							<Icon name='ionicon-ios-download-outline' type='block' />
						</Link>
					</Button>
					<Button className='sidebar-btn'
						title='Revert to Demo'
						onClick={() => studentActions.resetStudentToDemo(this.props.student.id)}>
						<Icon name='ionicon-ios-reload' type='block' />
					</Button>
				</Toolbar>

				<CourseRemovalBox studentId={this.props.student.id} />

				{!this.props.isSearching
					? <GraduationStatus
						allAreas={this.props.allAreas}
						courses={this.props.courses}
						coursesLoaded={this.props.coursesLoaded}
						isHidden={this.props.isSearching}
						student={this.props.student}
					/>
					: <CourseSearcher
						isHidden={!this.props.isSearching}
						toggle={this.props.toggleSearchSidebar}
						student={this.props.student}
						baseSearchQuery={this.props.baseSearchQuery}
					/>}
			</aside>
		)
	}
}
<commit_msg>remove .sidebar-btn<commit_after>import React, {Component, PropTypes} from 'react'
import {Link} from 'react-router'

import studentActions from '../flux/student-actions'
import studentStore from '../flux/student-store'

import Button from '../components/button'
import CourseSearcher from './course-searcher'
import GraduationStatus from './graduation-status'
import Icon from '../components/icon'
import Toolbar from '../components/toolbar'
import Separator from '../components/separator'

import CourseRemovalBox from '../components/course-removal-box'

import './sidebar.scss'

export default class Sidebar extends Component {
	static propTypes = {
		allAreas: PropTypes.object, // Immutable.List
		baseSearchQuery: PropTypes.object,
		courses: PropTypes.object, // Immutable.List
		coursesLoaded: PropTypes.bool.isRequired,
		isSearching: PropTypes.bool.isRequired,
		student: PropTypes.object.isRequired,
		toggleSearchSidebar: PropTypes.func.isRequired,
	}

	render() {
		return (
			<aside className='sidebar'>
				<Toolbar className='student-buttons'>
					<Button className='sidebar-btn' title='Students'>
						<Link to='/'>
							<Icon name='ionicon-ios-people-outline' type='block' />
						</Link>
					</Button>
					<Button
						title='Search'
						onClick={this.props.toggleSearchSidebar}>
						<Icon name='ionicon-ios-search' type='block' />
					</Button>

					<Separator type='spacer' />

					<Button
						title='Undo'
						onClick={studentActions.undo}
						disabled={studentStore.history.size === 0}>
						<Icon name={`ionicon-ios-undo${studentStore.history.size === 0 ? '-outline' : ''}`} type='block' />
					</Button>
					<Button
						title='Redo'
						onClick={studentActions.redo}
						disabled={studentStore.future.size === 0}>
						<Icon name={`ionicon-ios-redo${studentStore.future.size === 0 ? '-outline' : ''}`} type='block' />
					</Button>

					<Separator type='spacer' />

					<Button title='Download'>
						<Link to='download' params={{id: this.props.student.id}}>
							<Icon name='ionicon-ios-download-outline' type='block' />
						</Link>
					</Button>
					<Button
						title='Revert to Demo'
						onClick={() => studentActions.resetStudentToDemo(this.props.student.id)}>
						<Icon name='ionicon-ios-reload' type='block' />
					</Button>
				</Toolbar>

				<CourseRemovalBox studentId={this.props.student.id} />

				{!this.props.isSearching
					? <GraduationStatus
						allAreas={this.props.allAreas}
						courses={this.props.courses}
						coursesLoaded={this.props.coursesLoaded}
						isHidden={this.props.isSearching}
						student={this.props.student}
					/>
					: <CourseSearcher
						isHidden={!this.props.isSearching}
						toggle={this.props.toggleSearchSidebar}
						student={this.props.student}
						baseSearchQuery={this.props.baseSearchQuery}
					/>}
			</aside>
		)
	}
}
<|endoftext|>


<commit_before>/* global $ */
var settings = document.getElementById("settings");
var clearMessageHistoryButton = document.getElementById("clearMessageHistory");
var loginButton = document.getElementById("login");
var accessToken = "";
var time = "" ;
var BASE_URL = "https://api.susi.ai";

settings.addEventListener("submit", saveOptions);
loginButton.addEventListener("submit", login);
clearMessageHistoryButton.addEventListener("click", clearMessageHistory);

document.addEventListener("DOMContentLoaded", persistSettings);

function login(event){
	event.preventDefault();
	var loginEndPoint = BASE_URL+"/aaa/login.json?type=access-token&login="
			+ encodeURIComponent(document.getElementById("username").value)
			+ "&password="
			+ encodeURIComponent(document.getElementById("password").value);
	$.ajax({
		url: loginEndPoint,
		dataType: "jsonp",
		jsonpCallback: "p",
		jsonp: "callback",
		crossDomain: true,
		success: function (response) {
			if(response.accepted){
				accessToken = response.access_token;
				time = response.valid_seconds;
				alert(response.message);
				applyUserSettings();
			}
			else {
				alert("Login Failed. Try Again");
			}
		},
		error: function ( jqXHR, textStatus, errorThrown) {
			var msg = "";
			var jsonValue =  jqXHR.status;
			if (jsonValue === 404) {
				msg = "Login Failed. Try Again";
			}
			else {
				msg = "Some error occurred. Try Again";
			}
			if (status === "timeout") {
				msg = "Please check your internet connection";
			}
			alert(msg);
		}
	});

}

function applyUserSettings(){
	var userSettings = BASE_URL+"/aaa/listUserSettings.json?access_token="+accessToken;
	$.ajax({
		url: userSettings,
		dataType: "jsonp",
		jsonpCallback: "p",
		jsonp: "callback",
		crossDomain: true,
		success: function (response) {
			//persist theme
			if(response.settings.theme){
				browser.storage.sync.set({
					theme: response.settings.theme
				});
			}

		}
	});

}

function clearMessageHistory(){
	var isConfirm = confirm("Are you sure? This cannot be undone.");
	if(isConfirm == true){
		//clears messages stored in browser
		browser.storage.sync.clear();
	}
}

function persistSettings(){
	var buffer = browser.storage.sync.get(null);
	buffer.then(function(res){

		if(res["theme"]){
			if(res["theme"]=="dark"){
				$("#theme").val("dark");
			}
			else{
				$("#theme").val("light");
			}
		}

	});
}

function saveOptions(e) {
	e.preventDefault();
	browser.storage.sync.set({
		theme: document.querySelector("#theme").value
	});

}
<commit_msg>Fix #73 - prevent the theme from getting cleared (#74)<commit_after>/* global $ */
var settings = document.getElementById("settings");
var clearMessageHistoryButton = document.getElementById("clearMessageHistory");
var loginButton = document.getElementById("login");
var accessToken = "";
var time = "" ;
var BASE_URL = "https://api.susi.ai";

settings.addEventListener("submit", saveOptions);
loginButton.addEventListener("submit", login);
clearMessageHistoryButton.addEventListener("click", clearMessageHistory);

document.addEventListener("DOMContentLoaded", persistSettings);

function login(event){
	event.preventDefault();
	var loginEndPoint = BASE_URL+"/aaa/login.json?type=access-token&login="
			+ encodeURIComponent(document.getElementById("username").value)
			+ "&password="
			+ encodeURIComponent(document.getElementById("password").value);
	$.ajax({
		url: loginEndPoint,
		dataType: "jsonp",
		jsonpCallback: "p",
		jsonp: "callback",
		crossDomain: true,
		success: function (response) {
			if(response.accepted){
				accessToken = response.access_token;
				time = response.valid_seconds;
				alert(response.message);
				applyUserSettings();
			}
			else {
				alert("Login Failed. Try Again");
			}
		},
		error: function ( jqXHR, textStatus, errorThrown) {
			var msg = "";
			var jsonValue =  jqXHR.status;
			if (jsonValue === 404) {
				msg = "Login Failed. Try Again";
			}
			else {
				msg = "Some error occurred. Try Again";
			}
			if (status === "timeout") {
				msg = "Please check your internet connection";
			}
			alert(msg);
		}
	});

}

function applyUserSettings(){
	var userSettings = BASE_URL+"/aaa/listUserSettings.json?access_token="+accessToken;
	$.ajax({
		url: userSettings,
		dataType: "jsonp",
		jsonpCallback: "p",
		jsonp: "callback",
		crossDomain: true,
		success: function (response) {
			//persist theme
			if(response.settings.theme){
				browser.storage.sync.set({
					theme: response.settings.theme
				});
			}

		}
	});

}

function clearMessageHistory(){
	var isConfirm = confirm("Are you sure? This cannot be undone.");
	if(isConfirm == true){
		//clears messages stored in browser
		browser.storage.sync.remove("messagesHistory");
		browser.storage.sync.remove("userMapObj");
	}
}

function persistSettings(){
	var buffer = browser.storage.sync.get(null);
	buffer.then(function(res){

		if(res["theme"]){
			if(res["theme"]=="dark"){
				$("#theme").val("dark");
			}
			else{
				$("#theme").val("light");
			}
		}

	});
}

function saveOptions(e) {
	e.preventDefault();
	browser.storage.sync.set({
		theme: document.querySelector("#theme").value
	});

}
<|endoftext|>


<commit_before>
import {binarySearch, insertAt, removeAt} from './utils'
// import isString from 'mout/lang/isString'
import isArray from 'mout/lang/isArray'
import merge from 'mout/object/merge'
import omit from 'mout/object/omit'
import clone from 'mout/lang/clone'

var BaseSecondaryIndex = {

  set: function (keyList, value) {
    if (!isArray(keyList)) {
      keyList = [keyList]
    }

    let key = keyList.shift()
    let pos = binarySearch(this.keys, key)

    if (keyList.length === 0) {
      if (pos.found) {
        let dataLocation = binarySearch(this.values[pos.index], value)
        if (!dataLocation.found) {
          insertAt(this.values[pos.index], dataLocation.index, value)
        }
      } else {
        insertAt(this.keys, pos.index, key)
        insertAt(this.values, pos.index, [value])
      }
    } else {
      if (pos.found) {
        this.values[pos.index].set(keyList, value)
      } else {
        insertAt(this.keys, pos.index, key)
        let newIndex = createIndex()
        newIndex.set(keyList, value)
        insertAt(this.values, pos.index, newIndex)
      }
    }
  },

  get: function (keyList) {
    if (!isArray(keyList)) {
      keyList = [keyList]
    }

    let key = keyList.shift()
    let pos = binarySearch(this.keys, key)

    if (keyList.length === 0) {
      if (pos.found) {
        if (this.values[pos.index].isIndex) {
          return this.values[pos.index].getAll()
        } else {
          return this.values[pos.index]
        }
      } else {
        return []
      }
    } else {
      if (pos.found) {
        return this.values[pos.index].get(keyList)
      } else {
        return []
      }
    }
  },

  getAll: function () {
    let results = []
    this.values.forEach(function (value) {
      if (value.isIndex) {
        results = results.concat(value.getAll())
      } else {
        results = results.concat(value)
      }
    })
    return results
  },

  query: function (query) {
    let leftKeys
    let rightKeys

    if (query['>']) {
      leftKeys = query['>']
      query.leftInclusive = false
    } else if (query['>=']) {
      leftKeys = query['>=']
      query.leftInclusive = true
    }

    if (query['<']) {
      rightKeys = query['<']
      query.rightInclusive = false
    } else if (query['<=']) {
      rightKeys = query['<=']
      query.rightInclusive = true
    }

    if (leftKeys.length !== rightKeys.length) {
      throw new Error('Key arrays must be same length')
    }

    return this.between(leftKeys, rightKeys, omit(query, ['>', '>=', '<', '<=']))
  },

  between: function (leftKeys, rightKeys, opts) {
    opts = merge({
      leftInclusive: true,
      rightInclusive: false,
      limit: undefined,
      offset: 0
    }, opts)

    let results = this._between(leftKeys, rightKeys, opts)

    if (opts.limit) {
      return results.slice(opts.offset, opts.limit + opts.offset)
    } else {
      return results.slice(opts.offset)
    }
  },

  _between: function (leftKeys, rightKeys, opts) {
    let results = []

    let leftKey = leftKeys.shift()
    let rightKey = rightKeys.shift()

    let pos

    if (leftKey) {
      pos = binarySearch(this.keys, leftKey)
    } else {
      pos = {
        found: false,
        index: 0
      }
    }

    if (leftKeys.length === 0) {
      if (pos.found && opts.leftInclusive === false) {
        pos.index += 1
      }

      for (let i = pos.index; i < this.keys.length; i += 1) {
        if (rightKey) {
          if (opts.rightInclusive) {
            if (this.keys[i] > rightKey) { break }
          } else {
            if (this.keys[i] >= rightKey) { break }
          }
        }

        if (this.values[i].isIndex) {
          results = results.concat(this.values[i].getAll())
        } else {
          results = results.concat(this.values[i])
        }

        if (opts.limit) {
          if (results.length >= (opts.limit + opts.offset)) {
            break
          }
        }
      }
    } else {
      for (let i = pos.index; i < this.keys.length; i += 1) {
        let currKey = this.keys[i]
        if (currKey > rightKey) { break }

        if (this.values[i].isIndex) {
          if (currKey === leftKey) {
            results = results.concat(this.values[i]._between(clone(leftKeys), rightKeys.map(function () { return null }), opts))
          } else if (currKey === rightKey) {
            results = results.concat(this.values[i]._between(leftKeys.map(function () { return null }), clone(rightKeys), opts))
          } else {
            results = results.concat(this.values[i].getAll())
          }
        } else {
          results = results.concat(this.values[i])
        }

        if (opts.limit) {
          if (results.length >= (opts.limit + opts.offset)) {
            break
          }
        }
      }
    }

    if (opts.limit) {
      return results.slice(0, opts.limit + opts.offset)
    } else {
      return results
    }
  },

  delete: function (keyList, value) {
    if (!isArray(keyList)) {
      keyList = [keyList]
    }

    let key = keyList.shift()
    let pos = binarySearch(this.keys, key)

    if (keyList.length === 0) {
      if (pos.found) {
        let dataLocation = binarySearch(this.values[pos.index], value)
        if (dataLocation.found) {
          removeAt(this.values[pos.index], dataLocation.index)
          if (this.values[pos.index].length === 0) {
            removeAt(this.keys, pos.index)
            removeAt(this.values, pos.index)
          }
        }
      }
    } else {
      if (pos.found) {
        this.values[pos.index].delete(keyList, value)
      }
    }
  },

  clear: function () {
    this.keys = []
    this.values = []
  }
}

var createIndex = function () {
  var index = Object.create(BaseSecondaryIndex)

  index.isIndex = true
  index.keys = []
  index.values = []

  return index
}

export default createIndex
<commit_msg>make configurable<commit_after>
import {binarySearch, insertAt, removeAt} from './utils'
// import isString from 'mout/lang/isString'
import isArray from 'mout/lang/isArray'
import merge from 'mout/object/merge'
import omit from 'mout/object/omit'
import clone from 'mout/lang/clone'
import Immutable from 'immutable'

var BaseSecondaryIndex = {

  set: function (keyList, value) {
    if (!isArray(keyList)) {
      keyList = [keyList]
    }

    let key = keyList.shift() || null
    let pos = binarySearch(this.keys, key)

    if (keyList.length === 0) {
      if (pos.found) {
        let dataLocation = binarySearch(this.values[pos.index], value)
        if (!dataLocation.found) {
          insertAt(this.values[pos.index], dataLocation.index, value)
        }
      } else {
        insertAt(this.keys, pos.index, key)
        insertAt(this.values, pos.index, [value])
      }
    } else {
      if (pos.found) {
        this.values[pos.index].set(keyList, value)
      } else {
        insertAt(this.keys, pos.index, key)
        let newIndex = createIndex()
        newIndex.set(keyList, value)
        insertAt(this.values, pos.index, newIndex)
      }
    }
  },

  get: function (keyList) {
    if (!isArray(keyList)) {
      keyList = [keyList]
    }

    let key = keyList.shift() || null
    let pos = binarySearch(this.keys, key)

    if (keyList.length === 0) {
      if (pos.found) {
        if (this.values[pos.index].isIndex) {
          return this.values[pos.index].getAll()
        } else {
          return this.values[pos.index]
        }
      } else {
        return []
      }
    } else {
      if (pos.found) {
        return this.values[pos.index].get(keyList)
      } else {
        return []
      }
    }
  },

  getAll: function () {
    let results = []
    this.values.forEach(function (value) {
      if (value.isIndex) {
        results = results.concat(value.getAll())
      } else {
        results = results.concat(value)
      }
    })
    return results
  },

  query: function (query) {
    let leftKeys
    let rightKeys

    if (query['>']) {
      leftKeys = query['>']
      query.leftInclusive = false
    } else if (query['>=']) {
      leftKeys = query['>=']
      query.leftInclusive = true
    }

    if (query['<']) {
      rightKeys = query['<']
      query.rightInclusive = false
    } else if (query['<=']) {
      rightKeys = query['<=']
      query.rightInclusive = true
    }

    if (leftKeys.length !== rightKeys.length) {
      throw new Error('Key arrays must be same length')
    }

    return this.between(leftKeys, rightKeys, omit(query, ['>', '>=', '<', '<=']))
  },

  between: function (leftKeys, rightKeys, opts) {
    opts = merge({
      leftInclusive: true,
      rightInclusive: false,
      limit: undefined,
      offset: 0
    }, opts)

    let results = this._between(leftKeys, rightKeys, opts)

    if (opts.limit) {
      return results.slice(opts.offset, opts.limit + opts.offset)
    } else {
      return results.slice(opts.offset)
    }
  },

  _between: function (leftKeys, rightKeys, opts) {
    let results = []

    let leftKey = leftKeys.shift()
    let rightKey = rightKeys.shift()

    let pos

    if (leftKey !== undefined) {
      pos = binarySearch(this.keys, leftKey)
    } else {
      pos = {
        found: false,
        index: 0
      }
    }

    if (leftKeys.length === 0) {
      if (pos.found && opts.leftInclusive === false) {
        pos.index += 1
      }

      for (let i = pos.index; i < this.keys.length; i += 1) {
        if (rightKey !== undefined) {
          if (opts.rightInclusive) {
            if (this.keys[i] > rightKey) { break }
          } else {
            if (this.keys[i] >= rightKey) { break }
          }
        }

        if (this.values[i].isIndex) {
          results = results.concat(this.values[i].getAll())
        } else {
          results = results.concat(this.values[i])
        }

        if (opts.limit) {
          if (results.length >= (opts.limit + opts.offset)) {
            break
          }
        }
      }
    } else {
      for (let i = pos.index; i < this.keys.length; i += 1) {
        let currKey = this.keys[i]
        if (currKey > rightKey) { break }

        if (this.values[i].isIndex) {
          if (currKey === leftKey) {
            results = results.concat(this.values[i]._between(clone(leftKeys), rightKeys.map(function () { return undefined }), opts))
          } else if (currKey === rightKey) {
            results = results.concat(this.values[i]._between(leftKeys.map(function () { return undefined }), clone(rightKeys), opts))
          } else {
            results = results.concat(this.values[i].getAll())
          }
        } else {
          results = results.concat(this.values[i])
        }

        if (opts.limit) {
          if (results.length >= (opts.limit + opts.offset)) {
            break
          }
        }
      }
    }

    if (opts.limit) {
      return results.slice(0, opts.limit + opts.offset)
    } else {
      return results
    }
  },

  remove: function (keyList, value) {
    if (!isArray(keyList)) {
      keyList = [keyList]
    }

    let key = keyList.shift()
    let pos = binarySearch(this.keys, key)

    if (keyList.length === 0) {
      if (pos.found) {
        let dataLocation = binarySearch(this.values[pos.index], value)
        if (dataLocation.found) {
          removeAt(this.values[pos.index], dataLocation.index)
          if (this.values[pos.index].length === 0) {
            removeAt(this.keys, pos.index)
            removeAt(this.values, pos.index)
          }
        }
      }
    } else {
      if (pos.found) {
        this.values[pos.index].delete(keyList, value)
      }
    }
  },

  clear: function () {
    this.keys = []
    this.values = []
  },

  insertRecord: function (data) {
    if (!Immutable.Map.isMap(data)) {
      throw new Error('data must be an Immutable Map')
    }

    let keyList = []
    this.fieldList.forEach(function (field) {
      let key = data.get(field, null)
      keyList.push(key)
    })

    this.set(keyList, data.get('id'))
  },

  removeRecord: function (data) {
    if (!Immutable.Map.isMap(data)) {
      throw new Error('data must be an Immutable Map')
    }

    let keyList = []
    this.fieldList.forEach(function (field) {
      let key = data.get(field, null)
      keyList.push(key)
    })

    this.remove(keyList, data.get('id'))
  },

  updateRecord: function (data) {
    this.removeRecord(data)
    this.insertRecord(data)
  }
}

var createIndex = function (fieldList = []) {
  var index = Object.create(BaseSecondaryIndex)

  if (!isArray(fieldList)) {
    throw new Error('fieldList must be an array.')
  }

  index.fieldList = fieldList
  index.isIndex = true
  index.keys = []
  index.values = []

  return index
}

export default createIndex
<|endoftext|>


<commit_before>import { parseString } from 'xml2js'
import fetch from 'node-fetch'
import mongoose, { Schema } from 'mongoose'

mongoose.Promise = global.Promise

export default function createFeed({ connection, url }) {
  const feedSchema = new Schema({
    title: String,
    pubDate: String,
    description: String,
    url: { type: String, index: true, unique: true, default: url },
    items: [{ pubDate: String, title: String, description: String, link: String, guid: String }],
  })

  feedSchema.statics = {
    pullFeed() {
      const Feed = this
      function getItemProps(item) {
        const { pubDate, title, description, link, guid } = item
        const [N, W] = item['georss:point'][0].split(' ')
        return {
          id: guid[0]._,
          pubDate: pubDate[0],
          title: title[0],
          description: description[0],
          link: link[0],
          coordinates: { N, W },
        }
      }

      function parseFeed(xml) {
        console.log('parsing feed')
        return new Promise((resolve, reject) => (
          parseString(xml, (error, result) => {
            if (error) {
              console.log(error)
              return reject(error)
            }

            const feed = result.rss.channel[0]
            return resolve({
              title: feed.title[0],
              pubDate: feed.pubDate[0],
              description: feed.description[0],
              items: feed.item.map(getItemProps),
            })
          })
        ))
      }

      function fetchFeed() {
        console.log('fetching feed')
        return fetch(url).then(res => res.text())
      }

      function saveFeed(data) {
        const { title, description, pubDate } = data
        console.log('saving feed', { title, description, pubDate })
        return Feed.findOneAndUpdate({ url }, data, { upsert: true }, () => Promise.resolve())
      }

      return fetchFeed()
        .then(parseFeed)
        .then(saveFeed)
        .catch(err => console.log(err))
    },

    listBuoys() {
      return this.findOne({ url: this().url })
        .exec()
        .then(feed => feed && feed.items)
    },
  }

  return connection.model('Feed', feedSchema)
}
<commit_msg>Parse data from description in xml<commit_after>import { parseString } from 'xml2js'
import fetch from 'node-fetch'
import mongoose, { Schema } from 'mongoose'

mongoose.Promise = global.Promise

export default function createFeed({ connection, url }) {
  const feedSchema = new Schema({
    title: String,
    pubDate: String,
    description: String,
    url: { type: String, index: true, unique: true, default: url },
    items: [{
      pubDate: String,
      title: String,
      description: String,
      link: String,
      guid: String,
      data: {
        pubDate: String,
        location: String,
        windDirection: String,
        windSpeed: String,
        windGust: String,
        significantWaveHeight: String,
        dominantWavePeriod: String,
        averagePeriod: String,
        meanWaveDirection: String,
        atmosphericPressure: String,
        pressureTendency: String,
        airTemperature: String,
        waterTemperature: String,
      },
    }],
  })

  feedSchema.statics = {
    pullFeed() {
      const Feed = this

      function descriptionToData(description) {
        const gimmeKey = mess => mess.replace('', '').replace(':', '')
        const gimmeVal = mess => mess.replace('
', '').trim()
        const camelize = text => `${text.charAt(0).toLowerCase()}${text.slice(1)}`.replace(/ /g, '')
        const decode = text => text.replace(/&#(\d+);/g, (_, d) => String.fromCharCode(d))
        return description
          .trim()
          .split('\n')
          .map(t => t.trim())
          .map(f => f.split(''))
          .map(p => ([gimmeKey(p[0]), gimmeVal(p[1])]))
          .reduce((acc, pair, i) => {
            const data = i ? { [camelize(pair[0])]: decode(pair[1]) } : { pubDate: pair[0] }
            return Object.assign({}, acc, data)
          }, {})
      }

      function getItemProps(item) {
        const { pubDate, title, description, link, guid } = item
        const [N, W] = item['georss:point'][0].split(' ')
        return {
          id: guid[0]._,
          pubDate: pubDate[0],
          title: title[0],
          data: descriptionToData(description[0]),
          link: link[0],
          coordinates: { N, W },
        }
      }

      function parseFeed(xml) {
        console.log('parsing feed')
        return new Promise((resolve, reject) => (
          parseString(xml, (error, result) => {
            if (error) {
              console.log(error)
              return reject(error)
            }

            const feed = result.rss.channel[0]
            return resolve({
              title: feed.title[0],
              pubDate: feed.pubDate[0],
              description: feed.description[0],
              items: feed.item.map(getItemProps),
            })
          })
        ))
      }

      function fetchFeed() {
        console.log('fetching feed')
        return fetch(url).then(res => res.text())
      }

      function saveFeed(data) {
        const { title, description, pubDate } = data
        console.log('saving feed', { title, description, pubDate })
        return Feed.findOneAndUpdate({ url }, data, { upsert: true }, () => Promise.resolve())
      }

      return fetchFeed()
        .then(parseFeed)
        .then(saveFeed)
        .catch(err => console.log(err))
    },

    listBuoys() {
      return this.findOne({ url: this().url })
        .exec()
        .then(feed => feed && feed.items)
    },
  }

  return connection.model('Feed', feedSchema)
}
<|endoftext|>


<commit_before>import crypto from 'crypto'

import Router from './router'

import { PullRequestEvent, PushEvent, PingEvent } from '../events'

export default class Github extends Router {
  constructor ({ token = '' }) {
    super(token, 'github')

    this.router.use('/:appName', (req, res, next) => {
      if (token) {
        const signature = req.headers['x-hub-signature']
        let hmac = crypto.createHmac('sha1', token)
        hmac.update(JSON.stringify(req.body))

        if (`sha1=${hmac.digest('hex')}` === signature) {
          next()
        } else {
          res.sendStatus(401)
        }
      } else {
        next()
      }
    })

    this.router.post('/:appName', (req, res) => {
      const event = req.headers['x-github-event']
      console.log(event, req.body.ref)
      if (event) {
        if (event === 'push' && req.body.ref && req.body.ref.split('/').length >= 3) {
          const branch = req.body.ref.split('/')[2]
          res.sendStatus(204)
          this.emit('push', new PushEvent(req.params.appName, '', branch))
        } else if (event === 'ping') {
          res.sendStatus(204)
          this.emit('push', new PingEvent(req.params.appName, ''))
        } else {
          res.sendStatus(400)
        }
      } else {
        res.sendStatus(400)
      }
    })

    this.router.post('*', (req, res) => {
      res.status(404).send('ko')
    })
  }
}
<commit_msg>add console.log to understand push<commit_after>import crypto from 'crypto'

import Router from './router'

import { PullRequestEvent, PushEvent, PingEvent } from '../events'

export default class Github extends Router {
  constructor ({ token = '' }) {
    super(token, 'github')

    this.router.use('/:appName', (req, res, next) => {
      if (token) {
        const signature = req.headers['x-hub-signature']
        let hmac = crypto.createHmac('sha1', token)
        hmac.update(JSON.stringify(req.body))

        if (`sha1=${hmac.digest('hex')}` === signature) {
          next()
        } else {
          res.sendStatus(401)
        }
      } else {
        next()
      }
    })

    this.router.post('/:appName', (req, res) => {
      const event = req.headers['x-github-event']
      console.log(typeof req.body)
      console.log(event, JSON.stringify(req.body, null, 4))
      if (event) {
        if (event === 'push' && req.body.ref && req.body.ref.split('/').length >= 3) {
          const branch = req.body.ref.split('/')[2]
          res.sendStatus(204)
          this.emit('push', new PushEvent(req.params.appName, '', branch))
        } else if (event === 'ping') {
          res.sendStatus(204)
          this.emit('push', new PingEvent(req.params.appName, ''))
        } else {
          res.sendStatus(400)
        }
      } else {
        res.sendStatus(400)
      }
    })

    this.router.post('*', (req, res) => {
      res.status(404).send('ko')
    })
  }
}
<|endoftext|>