<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Segment = this.Segment = Base.extend({
	beans: true,

	initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
		if (arguments.length == 0) {
			this._point = SegmentPoint.create(this, 0, 0);
		} else if (arguments.length == 1) {
			// TODO: If beans are not activated, this won't copy from n existing
			// segment. OK?
			if (arg0.point) {
				this._point = SegmentPoint.create(this, arg0.point);
				this._handleIn = SegmentPoint.create(this, arg0.handleIn);
				this._handleOut = SegmentPoint.create(this, arg0.handleOut);
			} else {
				this._point = SegmentPoint.create(this, arg0);
			}
		} else if (arguments.length < 6) {
			if (arguments.length == 2 && !arg1.x) {
				this._point = SegmentPoint.create(this, arg0, arg1);
			} else {
				this._point = SegmentPoint.create(this, arg0);
				// Doesn't matter if these arguments exist, it creates 0, 0
				// points otherwise
				this._handleIn = SegmentPoint.create(this, arg1);
				this._handleOut = SegmentPoint.create(this, arg2);
			}
		} else if (arguments.length == 6) {
			this._point = SegmentPoint.create(this, arg0, arg1);
			this._handleIn = SegmentPoint.create(this, arg2, arg3);
			this._handleOut = SegmentPoint.create(this, arg4, arg5);
		}
		if (!this._handleIn)
			this._handleIn = SegmentPoint.create(this, 0, 0);
		if (!this._handleOut)
			this._handleOut = SegmentPoint.create(this, 0, 0);
	},

	_changed: function(point) {
		if (this._path) {
			// Delegate changes to affected curves if they exist
			if (this._path._curves) {
				var curve = this.getCurve(), other;
				if (curve) {
					curve._changed();
					// Get the other affected curve, which is the previous one
					// for _point or _handleIn changing when this segment is
					// _segment1 of the curve, for all other cases it's the next
					// (e.g. _handleOut or this segment == _segment2)
					if (other = (curve[point == this._point
						|| point == this._handleIn && curve._segment1 == this
							? 'getPrevious' : 'getNext']())) {
						other._changed();
					}
				}
			}
			this._path._changed();
		}
	},

	getPoint: function() {
		return this._point;
	},

	setPoint: function(point) {
		point = Point.read(arguments);
		// Do not replace the internal object but update it instead, so
		// references to it are kept alive.
		this._point.set(point.x, point.y);
	},

	getHandleIn: function() {
		return this._handleIn;
	},

	setHandleIn: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleIn.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	getHandleInIfSet: function() {
		return this._handleIn._x == 0 && this._handleIn._y == 0
			? null : this._handleIn;
	},

	getHandleOut: function() {
		return this._handleOut;
	},

	setHandleOut: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleOut.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	getHandleOutIfSet: function() {
		return this._handleOut._x == 0 && this._handleOut._y == 0
			? null : this._handleOut;
	},

	getPath: function() {
		return this._path;
	},

	getIndex: function() {
		return this._index;
	},

	getCurve: function() {
		if (this._path) {
			var index = this._index;
			// The last segment of an open path belongs to the last curve
			if (!this._path._closed && index == this._path._segments.length - 1)
				index--;
			return this._path.getCurves()[index] || null;
		}
		return null;
	},

	getNext: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index + 1]
				|| this._path._closed && segments[0]) || null;
	},

	getPrevious: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index - 1]
				|| this._path._closed && segments[segments.length - 1]) || null;
	},

	_isSelected: function(point) {
		var state = this._selectionState;
		return point == this._point ? !!(state & SelectionState.POINT)
			: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
			: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
			: false;
	},

	_setSelected: function(point, selected) {
		if (!this._path)
			return;
		var selected = !!selected, // convert to boolean
			state = this._selectionState,
			wasSelected = !!state,
			// For performance reasons use array indices to access the various
			// selection states: 0 = point, 1 = handleIn, 2 = handleOut
			selection = [
				!!(state & SelectionState.POINT),
				!!(state & SelectionState.HANDLE_IN),
				!!(state & SelectionState.HANDLE_OUT)
			];
		if (point == this._point) {
			if (selected) {
				// We're selecting point, deselect the handles
				selection[1] = selection[2] = false;
			} else {
				var previous = this.getPrevious(),
					next = this.getNext();
				// When deselecting a point, the handles get selected instead
				// depending on the selection state of their neighbors.
				selection[1] = previous && (previous._point.isSelected()
						|| previous._handleOut.isSelected());
				selection[2] = next && (next._point.isSelected()
						|| next._handleIn.isSelected());
			}
			selection[0] = selected;
		} else {
			var index = point == this._handleIn ? 1 : 2;
			if (selection[index] != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					selection[0] = false;
				selection[index] = selected;
			}
		}
		this._selectionState = (selection[0] ? SelectionState.POINT : 0)
				| (selection[1] ? SelectionState.HANDLE_IN : 0)
				| (selection[2] ? SelectionState.HANDLE_OUT : 0);
		// If the selection state of the segment has changed, we need to let
		// it's path know and possibly add or remove it from
		// document._selectedItems
		if (wasSelected != !!this._selectionState) {
			var path = this._path,
				count = path._selectedSegmentCount
						+= this._selectionState ? 1 : -1;
			if (count <= 1)
				path._document._selectItem(path, count == 1);
		}
	},

	isSelected: function() {
		return this._isSelected(this._point);
	},

	setSelected: function(selected) {
		this._setSelected(this._point, selected);
	},

	reverse: function() {
		return new Segment(this._point, this._handleOut, this._handleIn);
	},

	clone: function() {
		return new Segment(this);
	},

	remove: function() {
		return this._path ? !!this._path.removeSegment(this._index) : false;
	},

	toString: function() {
		var parts = [ 'point: ' + this._point ];
		if (!this._handleIn.isZero())
			parts.push('handleIn: ' + this._handleIn);
		if (!this._handleOut.isZero())
			parts.push('handleOut: ' + this._handleOut);
		return '{ ' + parts.join(', ') + ' }';
	},

	_transformCoordinates: function(matrix, coords, change) {
		// Use matrix.transform version() that takes arrays of multiple
		// points for largely improved performance, as no calls to
		// Point.read() and Point constructors are necessary.
		var point = this._point,
			// If a matrix is defined, only transform handles if they are set.
			// This saves some computation time. If no matrix is set, always
			// use the real handles, as we just want to receive a filled
			// coords array for getBounds().
			handleIn =  matrix && this.getHandleInIfSet() || this._handleIn,
			handleOut = matrix && this.getHandleOutIfSet() || this._handleOut,
			x = point._x,
			y = point._y;
		coords[0] = x;
		coords[1] = y;
		var index = 2;
		// We need to convert handles to absolute coordinates in order
		// to transform them.
		if (handleIn) {
			coords[index++] = handleIn._x + x;
			coords[index++] = handleIn._y + y;
		}
		if (handleOut) {
			coords[index++] = handleOut._x + x;
			coords[index++] = handleOut._y + y;
		}
		if (matrix) {
			matrix.transform(coords, 0, coords, 0, index / 2);
			x = coords[0];
			y = coords[1];
			if (change) {
				// If change is true, we need to set the new values back
				point._x = x;
				point._y = y;
				index  = 2;
				if (handleIn) {
					handleIn._x = coords[index++] - x;
					handleIn._y = coords[index++] - y;
				}
				if (handleOut) {
					handleOut._x = coords[index++] - x;
					handleOut._y = coords[index++] - y;
				}
			} else {
				// We want to receive the results in coords, so make sure
				// handleIn and out are defined too, even if they're 0
				if (!handleIn) {
					coords[index++] = x;
					coords[index++] = y;
				}
				if (!handleOut) {
					coords[index++] = x;
					coords[index++] = y;
				}
			}
		}
	}
});
<commit_msg>Shorten #_transformCoordinates() a bit.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Segment = this.Segment = Base.extend({
	beans: true,

	initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
		if (arguments.length == 0) {
			this._point = SegmentPoint.create(this, 0, 0);
		} else if (arguments.length == 1) {
			// TODO: If beans are not activated, this won't copy from n existing
			// segment. OK?
			if (arg0.point) {
				this._point = SegmentPoint.create(this, arg0.point);
				this._handleIn = SegmentPoint.create(this, arg0.handleIn);
				this._handleOut = SegmentPoint.create(this, arg0.handleOut);
			} else {
				this._point = SegmentPoint.create(this, arg0);
			}
		} else if (arguments.length < 6) {
			if (arguments.length == 2 && !arg1.x) {
				this._point = SegmentPoint.create(this, arg0, arg1);
			} else {
				this._point = SegmentPoint.create(this, arg0);
				// Doesn't matter if these arguments exist, it creates 0, 0
				// points otherwise
				this._handleIn = SegmentPoint.create(this, arg1);
				this._handleOut = SegmentPoint.create(this, arg2);
			}
		} else if (arguments.length == 6) {
			this._point = SegmentPoint.create(this, arg0, arg1);
			this._handleIn = SegmentPoint.create(this, arg2, arg3);
			this._handleOut = SegmentPoint.create(this, arg4, arg5);
		}
		if (!this._handleIn)
			this._handleIn = SegmentPoint.create(this, 0, 0);
		if (!this._handleOut)
			this._handleOut = SegmentPoint.create(this, 0, 0);
	},

	_changed: function(point) {
		if (this._path) {
			// Delegate changes to affected curves if they exist
			if (this._path._curves) {
				var curve = this.getCurve(), other;
				if (curve) {
					curve._changed();
					// Get the other affected curve, which is the previous one
					// for _point or _handleIn changing when this segment is
					// _segment1 of the curve, for all other cases it's the next
					// (e.g. _handleOut or this segment == _segment2)
					if (other = (curve[point == this._point
						|| point == this._handleIn && curve._segment1 == this
							? 'getPrevious' : 'getNext']())) {
						other._changed();
					}
				}
			}
			this._path._changed();
		}
	},

	getPoint: function() {
		return this._point;
	},

	setPoint: function(point) {
		point = Point.read(arguments);
		// Do not replace the internal object but update it instead, so
		// references to it are kept alive.
		this._point.set(point.x, point.y);
	},

	getHandleIn: function() {
		return this._handleIn;
	},

	setHandleIn: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleIn.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	getHandleInIfSet: function() {
		return this._handleIn._x == 0 && this._handleIn._y == 0
			? null : this._handleIn;
	},

	getHandleOut: function() {
		return this._handleOut;
	},

	setHandleOut: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleOut.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	getHandleOutIfSet: function() {
		return this._handleOut._x == 0 && this._handleOut._y == 0
			? null : this._handleOut;
	},

	getPath: function() {
		return this._path;
	},

	getIndex: function() {
		return this._index;
	},

	getCurve: function() {
		if (this._path) {
			var index = this._index;
			// The last segment of an open path belongs to the last curve
			if (!this._path._closed && index == this._path._segments.length - 1)
				index--;
			return this._path.getCurves()[index] || null;
		}
		return null;
	},

	getNext: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index + 1]
				|| this._path._closed && segments[0]) || null;
	},

	getPrevious: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index - 1]
				|| this._path._closed && segments[segments.length - 1]) || null;
	},

	_isSelected: function(point) {
		var state = this._selectionState;
		return point == this._point ? !!(state & SelectionState.POINT)
			: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
			: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
			: false;
	},

	_setSelected: function(point, selected) {
		if (!this._path)
			return;
		var selected = !!selected, // convert to boolean
			state = this._selectionState,
			wasSelected = !!state,
			// For performance reasons use array indices to access the various
			// selection states: 0 = point, 1 = handleIn, 2 = handleOut
			selection = [
				!!(state & SelectionState.POINT),
				!!(state & SelectionState.HANDLE_IN),
				!!(state & SelectionState.HANDLE_OUT)
			];
		if (point == this._point) {
			if (selected) {
				// We're selecting point, deselect the handles
				selection[1] = selection[2] = false;
			} else {
				var previous = this.getPrevious(),
					next = this.getNext();
				// When deselecting a point, the handles get selected instead
				// depending on the selection state of their neighbors.
				selection[1] = previous && (previous._point.isSelected()
						|| previous._handleOut.isSelected());
				selection[2] = next && (next._point.isSelected()
						|| next._handleIn.isSelected());
			}
			selection[0] = selected;
		} else {
			var index = point == this._handleIn ? 1 : 2;
			if (selection[index] != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					selection[0] = false;
				selection[index] = selected;
			}
		}
		this._selectionState = (selection[0] ? SelectionState.POINT : 0)
				| (selection[1] ? SelectionState.HANDLE_IN : 0)
				| (selection[2] ? SelectionState.HANDLE_OUT : 0);
		// If the selection state of the segment has changed, we need to let
		// it's path know and possibly add or remove it from
		// document._selectedItems
		if (wasSelected != !!this._selectionState) {
			var path = this._path,
				count = path._selectedSegmentCount
						+= this._selectionState ? 1 : -1;
			if (count <= 1)
				path._document._selectItem(path, count == 1);
		}
	},

	isSelected: function() {
		return this._isSelected(this._point);
	},

	setSelected: function(selected) {
		this._setSelected(this._point, selected);
	},

	reverse: function() {
		return new Segment(this._point, this._handleOut, this._handleIn);
	},

	clone: function() {
		return new Segment(this);
	},

	remove: function() {
		return this._path ? !!this._path.removeSegment(this._index) : false;
	},

	toString: function() {
		var parts = [ 'point: ' + this._point ];
		if (!this._handleIn.isZero())
			parts.push('handleIn: ' + this._handleIn);
		if (!this._handleOut.isZero())
			parts.push('handleOut: ' + this._handleOut);
		return '{ ' + parts.join(', ') + ' }';
	},

	_transformCoordinates: function(matrix, coords, change) {
		// Use matrix.transform version() that takes arrays of multiple
		// points for largely improved performance, as no calls to
		// Point.read() and Point constructors are necessary.
		var point = this._point,
			// If a matrix is defined, only transform handles if they are set.
			// This saves some computation time. If no matrix is set, always
			// use the real handles, as we just want to receive a filled
			// coords array for getBounds().
			handleIn =  matrix && this.getHandleInIfSet() || this._handleIn,
			handleOut = matrix && this.getHandleOutIfSet() || this._handleOut,
			x = point._x,
			y = point._y,
			i = 2;
		coords[0] = x;
		coords[1] = y;
		// We need to convert handles to absolute coordinates in order
		// to transform them.
		if (handleIn) {
			coords[i++] = handleIn._x + x;
			coords[i++] = handleIn._y + y;
		}
		if (handleOut) {
			coords[i++] = handleOut._x + x;
			coords[i++] = handleOut._y + y;
		}
		if (matrix) {
			matrix.transform(coords, 0, coords, 0, i / 2);
			x = coords[0];
			y = coords[1];
			if (change) {
				// If change is true, we need to set the new values back
				point._x = x;
				point._y = y;
				i  = 2;
				if (handleIn) {
					handleIn._x = coords[i++] - x;
					handleIn._y = coords[i++] - y;
				}
				if (handleOut) {
					handleOut._x = coords[i++] - x;
					handleOut._y = coords[i++] - y;
				}
			} else {
				// We want to receive the results in coords, so make sure
				// handleIn and out are defined too, even if they're 0
				if (!handleIn) {
					coords[i++] = x;
					coords[i++] = y;
				}
				if (!handleOut) {
					coords[i++] = x;
					coords[i++] = y;
				}
			}
		}
	}
});
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name Segment
 *
 * @class The Segment object represents the points of a path through which its
 * {@link Curve} objects pass. The segments of a path can be accessed through
 * its {@link Path#segments} array.
 *
 * Each segment consists of an anchor point ({@link Segment#point}) and
 * optionaly an incoming and an outgoing handle ({@link Segment#handleIn} and
 * {@link Segment#handleOut}), describing the tangents of the two {@link Curve}
 * objects that are connected by this segment.
 */
var Segment = this.Segment = Base.extend(/** @lends Segment# */{
	/**
	 * Creates a new Segment object.
	 *
	 * @param {Point} [point={x: 0, y: 0}] the anchor point of the segment
	 * @param {Point} [handleIn={x: 0, y: 0}] the handle point relative to the
	 *        anchor point of the segment that describes the in tangent of the
	 *        segment.
	 * @param {Point} [handleOut={x: 0, y: 0}] the handle point relative to the
	 *        anchor point of the segment that describes the out tangent of the
	 *        segment.
	 *
	 * @example {@paperscript}
	 * var handleIn = new Point(-80, -100);
	 * var handleOut = new Point(80, 100);
	 *
	 * var firstPoint = new Point(100, 50);
	 * var firstSegment = new Segment(firstPoint, null, handleOut);
	 *
	 * var secondPoint = new Point(300, 50);
	 * var secondSegment = new Segment(secondPoint, handleIn, null);
	 *
	 * var path = new Path(firstSegment, secondSegment);
	 * path.strokeColor = 'black';
	 */
	initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
		var count = arguments.length,
			createPoint = SegmentPoint.create,
			point, handleIn, handleOut;
		if (count == 0) {
			// Nothing
		} else if (count == 1) {
			// TODO: If beans are not activated, this won't copy from existing
			// segments. OK?
			if (arg0.point) {
				point = arg0.point;
				handleIn = arg0.handleIn;
				handleOut = arg0.handleOut;
			} else {
				point = arg0;
			}
		} else if (count < 6) {
			if (count == 2 && arg1.x === undefined) {
				point = [ arg0, arg1 ];
			} else {
				point = arg0;
				// Doesn't matter if these arguments exist, SegmentPointcreate
				// produces creates points with (0, 0) otherwise
				handleIn = arg1;
				handleOut = arg2;
			}
		} else if (count == 6) {
			point = [ arg0, arg1 ];
			handleIn = [ arg2, arg3 ];
			handleOut = [ arg4, arg5 ];
		}
		createPoint(this, '_point', point);
		createPoint(this, '_handleIn', handleIn);
		createPoint(this, '_handleOut', handleOut);
	},

	_changed: function(point) {
		if (!this._path)
			return;
		// Delegate changes to affected curves if they exist
		var curve = this._path._curves && this.getCurve(), other;
		if (curve) {
			curve._changed();
			// Get the other affected curve, which is the previous one for
			// _point or _handleIn changing when this segment is _segment1 of
			// the curve, for all other cases it's the next (e.g. _handleOut
			// when this segment is _segment2)
			if (other = (curve[point == this._point
					|| point == this._handleIn && curve._segment1 == this
					? 'getPrevious' : 'getNext']())) {
				other._changed();
			}
		}
		this._path._changed(Change.GEOMETRY);
	},

	/**
	 * The anchor point of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getPoint: function() {
		return this._point;
	},

	setPoint: function(point) {
		point = Point.read(arguments);
		// Do not replace the internal object but update it instead, so
		// references to it are kept alive.
		this._point.set(point.x, point.y);
	},

	/**
	 * The handle point relative to the anchor point of the segment that
	 * describes the in tangent of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getHandleIn: function() {
		return this._handleIn;
	},

	setHandleIn: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleIn.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	/**
	 * The handle point relative to the anchor point of the segment that
	 * describes the out tangent of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getHandleOut: function() {
		return this._handleOut;
	},

	setHandleOut: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleOut.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	_isSelected: function(point) {
		var state = this._selectionState;
		return point == this._point ? !!(state & SelectionState.POINT)
			: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
			: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
			: false;
	},

	_setSelected: function(point, selected) {
		var path = this._path,
			selected = !!selected, // convert to boolean
			state = this._selectionState || 0,
			// For performance reasons use array indices to access the various
			// selection states: 0 = point, 1 = handleIn, 2 = handleOut
			selection = [
				!!(state & SelectionState.POINT),
				!!(state & SelectionState.HANDLE_IN),
				!!(state & SelectionState.HANDLE_OUT)
			];
		if (point == this._point) {
			if (selected) {
				// We're selecting point, deselect the handles
				selection[1] = selection[2] = false;
			} else {
				var previous = this.getPrevious(),
					next = this.getNext();
				// When deselecting a point, the handles get selected instead
				// depending on the selection state of their neighbors.
				selection[1] = previous && (previous._point.isSelected()
						|| previous._handleOut.isSelected());
				selection[2] = next && (next._point.isSelected()
						|| next._handleIn.isSelected());
			}
			selection[0] = selected;
		} else {
			var index = point == this._handleIn ? 1 : 2;
			if (selection[index] != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					selection[0] = false;
				selection[index] = selected;
				// Let path know that we changed something and the view
				// should be redrawn
				path._changed(Change.ATTRIBUTE);
			}
		}
		this._selectionState = (selection[0] ? SelectionState.POINT : 0)
				| (selection[1] ? SelectionState.HANDLE_IN : 0)
				| (selection[2] ? SelectionState.HANDLE_OUT : 0);
		// If the selection state of the segment has changed, we need to let
		// it's path know and possibly add or remove it from
		// project._selectedItems
		if (path && state != this._selectionState)
			path._updateSelection(this, state, this._selectionState);
	},

	/**
	 * Specifies whether the {@link #point} of the segment is selected.
	 * @type Boolean
	 * @bean
	 */
	isSelected: function() {
		return this._isSelected(this._point);
	},

	setSelected: function(selected) {
		this._setSelected(this._point, selected);
	},

	/**
	 * {@grouptitle Hierarchy}
	 *
	 * The index of the segment in the {@link Path#segments} array that the
	 * segment belongs to.
	 *
	 * @type Number
	 * @bean
	 */
	getIndex: function() {
		return this._index !== undefined ? this._index : null;
	},

	/**
	 * The path that the segment belongs to.
	 *
	 * @type Path
	 * @bean
	 */
	getPath: function() {
		return this._path || null;
	},

	/**
	 * The curve that the segment belongs to.
	 *
	 * @type Curve
	 * @bean
	 */
	getCurve: function() {
		if (this._path) {
			var index = this._index;
			// The last segment of an open path belongs to the last curve
			if (!this._path._closed && index == this._path._segments.length - 1)
				index--;
			return this._path.getCurves()[index] || null;
		}
		return null;
	},

	/**
	 * {@grouptitle Sibling Segments}
	 *
	 * The next segment in the {@link Path#segments} array that the segment
	 * belongs to. If the segments belongs to a closed path, the first segment
	 * is returned for the last segment of the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getNext: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index + 1]
				|| this._path._closed && segments[0]) || null;
	},

	/**
	 * The previous segment in the {@link Path#segments} array that the
	 * segment belongs to. If the segments belongs to a closed path, the last
	 * segment is returned for the first segment of the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getPrevious: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index - 1]
				|| this._path._closed && segments[segments.length - 1]) || null;
	},

	/**
	 * Returns the reversed the segment, without modifying the segment itself.
	 * @return {Segment} the reversed segment
	 */
	reverse: function() {
		return new Segment(this._point, this._handleOut, this._handleIn);
	},

	/**
	 * Removes the segment from the path that it belongs to.
	 */
	remove: function() {
		return this._path ? !!this._path.removeSegment(this._index) : false;
	},

	/**
	 * @return {String} A string representation of the segment.
	 */
	toString: function() {
		var parts = [ 'point: ' + this._point ];
		if (!this._handleIn.isZero())
			parts.push('handleIn: ' + this._handleIn);
		if (!this._handleOut.isZero())
			parts.push('handleOut: ' + this._handleOut);
		return '{ ' + parts.join(', ') + ' }';
	},

	_transformCoordinates: function(matrix, coords, change) {
		// Use matrix.transform version() that takes arrays of multiple
		// points for largely improved performance, as no calls to
		// Point.read() and Point constructors are necessary.
		var point = this._point,
			// If change is true, only transform handles if they are set, as
			// _transformCoordinates is called only to change the segment, no
			// to receive the coords.
			// This saves some computation time. If change is false, always
			// use the real handles, as we just want to receive a filled
			// coords array for getBounds().
			handleIn =  !change || !this._handleIn.isZero()
					? this._handleIn : null,
			handleOut = !change || !this._handleOut.isZero()
					? this._handleOut : null,
			x = point._x,
			y = point._y,
			i = 2;
		coords[0] = x;
		coords[1] = y;
		// We need to convert handles to absolute coordinates in order
		// to transform them.
		if (handleIn) {
			coords[i++] = handleIn._x + x;
			coords[i++] = handleIn._y + y;
		}
		if (handleOut) {
			coords[i++] = handleOut._x + x;
			coords[i++] = handleOut._y + y;
		}
		// If no matrix was previded, this was just called to get the coords and
		// we are done now.
		if (!matrix)
			return;
		matrix._transformCoordinates(coords, 0, coords, 0, i / 2);
		x = coords[0];
		y = coords[1];
		if (change) {
			// If change is true, we need to set the new values back
			point._x = x;
			point._y = y;
			i  = 2;
			if (handleIn) {
				handleIn._x = coords[i++] - x;
				handleIn._y = coords[i++] - y;
			}
			if (handleOut) {
				handleOut._x = coords[i++] - x;
				handleOut._y = coords[i++] - y;
			}
		} else {
			// We want to receive the results in coords, so make sure
			// handleIn and out are defined too, even if they're 0
			if (!handleIn) {
				coords[i++] = x;
				coords[i++] = y;
			}
			if (!handleOut) {
				coords[i++] = x;
				coords[i++] = y;
			}
		}
	}
});
<commit_msg>Define Segment#equals().<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name Segment
 *
 * @class The Segment object represents the points of a path through which its
 * {@link Curve} objects pass. The segments of a path can be accessed through
 * its {@link Path#segments} array.
 *
 * Each segment consists of an anchor point ({@link Segment#point}) and
 * optionaly an incoming and an outgoing handle ({@link Segment#handleIn} and
 * {@link Segment#handleOut}), describing the tangents of the two {@link Curve}
 * objects that are connected by this segment.
 */
var Segment = this.Segment = Base.extend(/** @lends Segment# */{
	/**
	 * Creates a new Segment object.
	 *
	 * @param {Point} [point={x: 0, y: 0}] the anchor point of the segment
	 * @param {Point} [handleIn={x: 0, y: 0}] the handle point relative to the
	 *        anchor point of the segment that describes the in tangent of the
	 *        segment.
	 * @param {Point} [handleOut={x: 0, y: 0}] the handle point relative to the
	 *        anchor point of the segment that describes the out tangent of the
	 *        segment.
	 *
	 * @example {@paperscript}
	 * var handleIn = new Point(-80, -100);
	 * var handleOut = new Point(80, 100);
	 *
	 * var firstPoint = new Point(100, 50);
	 * var firstSegment = new Segment(firstPoint, null, handleOut);
	 *
	 * var secondPoint = new Point(300, 50);
	 * var secondSegment = new Segment(secondPoint, handleIn, null);
	 *
	 * var path = new Path(firstSegment, secondSegment);
	 * path.strokeColor = 'black';
	 */
	initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
		var count = arguments.length,
			createPoint = SegmentPoint.create,
			point, handleIn, handleOut;
		if (count == 0) {
			// Nothing
		} else if (count == 1) {
			// TODO: If beans are not activated, this won't copy from existing
			// segments. OK?
			if (arg0.point) {
				point = arg0.point;
				handleIn = arg0.handleIn;
				handleOut = arg0.handleOut;
			} else {
				point = arg0;
			}
		} else if (count < 6) {
			if (count == 2 && arg1.x === undefined) {
				point = [ arg0, arg1 ];
			} else {
				point = arg0;
				// Doesn't matter if these arguments exist, SegmentPointcreate
				// produces creates points with (0, 0) otherwise
				handleIn = arg1;
				handleOut = arg2;
			}
		} else if (count == 6) {
			point = [ arg0, arg1 ];
			handleIn = [ arg2, arg3 ];
			handleOut = [ arg4, arg5 ];
		}
		createPoint(this, '_point', point);
		createPoint(this, '_handleIn', handleIn);
		createPoint(this, '_handleOut', handleOut);
	},

	_changed: function(point) {
		if (!this._path)
			return;
		// Delegate changes to affected curves if they exist
		var curve = this._path._curves && this.getCurve(), other;
		if (curve) {
			curve._changed();
			// Get the other affected curve, which is the previous one for
			// _point or _handleIn changing when this segment is _segment1 of
			// the curve, for all other cases it's the next (e.g. _handleOut
			// when this segment is _segment2)
			if (other = (curve[point == this._point
					|| point == this._handleIn && curve._segment1 == this
					? 'getPrevious' : 'getNext']())) {
				other._changed();
			}
		}
		this._path._changed(Change.GEOMETRY);
	},

	/**
	 * The anchor point of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getPoint: function() {
		return this._point;
	},

	setPoint: function(point) {
		point = Point.read(arguments);
		// Do not replace the internal object but update it instead, so
		// references to it are kept alive.
		this._point.set(point.x, point.y);
	},

	/**
	 * The handle point relative to the anchor point of the segment that
	 * describes the in tangent of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getHandleIn: function() {
		return this._handleIn;
	},

	setHandleIn: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleIn.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	/**
	 * The handle point relative to the anchor point of the segment that
	 * describes the out tangent of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getHandleOut: function() {
		return this._handleOut;
	},

	setHandleOut: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleOut.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	_isSelected: function(point) {
		var state = this._selectionState;
		return point == this._point ? !!(state & SelectionState.POINT)
			: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
			: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
			: false;
	},

	_setSelected: function(point, selected) {
		var path = this._path,
			selected = !!selected, // convert to boolean
			state = this._selectionState || 0,
			// For performance reasons use array indices to access the various
			// selection states: 0 = point, 1 = handleIn, 2 = handleOut
			selection = [
				!!(state & SelectionState.POINT),
				!!(state & SelectionState.HANDLE_IN),
				!!(state & SelectionState.HANDLE_OUT)
			];
		if (point == this._point) {
			if (selected) {
				// We're selecting point, deselect the handles
				selection[1] = selection[2] = false;
			} else {
				var previous = this.getPrevious(),
					next = this.getNext();
				// When deselecting a point, the handles get selected instead
				// depending on the selection state of their neighbors.
				selection[1] = previous && (previous._point.isSelected()
						|| previous._handleOut.isSelected());
				selection[2] = next && (next._point.isSelected()
						|| next._handleIn.isSelected());
			}
			selection[0] = selected;
		} else {
			var index = point == this._handleIn ? 1 : 2;
			if (selection[index] != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					selection[0] = false;
				selection[index] = selected;
				// Let path know that we changed something and the view
				// should be redrawn
				path._changed(Change.ATTRIBUTE);
			}
		}
		this._selectionState = (selection[0] ? SelectionState.POINT : 0)
				| (selection[1] ? SelectionState.HANDLE_IN : 0)
				| (selection[2] ? SelectionState.HANDLE_OUT : 0);
		// If the selection state of the segment has changed, we need to let
		// it's path know and possibly add or remove it from
		// project._selectedItems
		if (path && state != this._selectionState)
			path._updateSelection(this, state, this._selectionState);
	},

	/**
	 * Specifies whether the {@link #point} of the segment is selected.
	 * @type Boolean
	 * @bean
	 */
	isSelected: function() {
		return this._isSelected(this._point);
	},

	setSelected: function(selected) {
		this._setSelected(this._point, selected);
	},

	/**
	 * {@grouptitle Hierarchy}
	 *
	 * The index of the segment in the {@link Path#segments} array that the
	 * segment belongs to.
	 *
	 * @type Number
	 * @bean
	 */
	getIndex: function() {
		return this._index !== undefined ? this._index : null;
	},

	/**
	 * The path that the segment belongs to.
	 *
	 * @type Path
	 * @bean
	 */
	getPath: function() {
		return this._path || null;
	},

	/**
	 * The curve that the segment belongs to.
	 *
	 * @type Curve
	 * @bean
	 */
	getCurve: function() {
		if (this._path) {
			var index = this._index;
			// The last segment of an open path belongs to the last curve
			if (!this._path._closed && index == this._path._segments.length - 1)
				index--;
			return this._path.getCurves()[index] || null;
		}
		return null;
	},

	/**
	 * {@grouptitle Sibling Segments}
	 *
	 * The next segment in the {@link Path#segments} array that the segment
	 * belongs to. If the segments belongs to a closed path, the first segment
	 * is returned for the last segment of the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getNext: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index + 1]
				|| this._path._closed && segments[0]) || null;
	},

	/**
	 * The previous segment in the {@link Path#segments} array that the
	 * segment belongs to. If the segments belongs to a closed path, the last
	 * segment is returned for the first segment of the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getPrevious: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index - 1]
				|| this._path._closed && segments[segments.length - 1]) || null;
	},

	/**
	 * Returns the reversed the segment, without modifying the segment itself.
	 * @return {Segment} the reversed segment
	 */
	reverse: function() {
		return new Segment(this._point, this._handleOut, this._handleIn);
	},

	/**
	 * Removes the segment from the path that it belongs to.
	 */
	remove: function() {
		return this._path ? !!this._path.removeSegment(this._index) : false;
	},

	equals: function(segment) {
		return segment == this || segment
				&& this._point.equals(segment._point)
				&& this._handleIn.equals(segment._handleIn)
				&& this._handleOut.equals(segment._handleOut);
	},

	/**
	 * @return {String} A string representation of the segment.
	 */
	toString: function() {
		var parts = [ 'point: ' + this._point ];
		if (!this._handleIn.isZero())
			parts.push('handleIn: ' + this._handleIn);
		if (!this._handleOut.isZero())
			parts.push('handleOut: ' + this._handleOut);
		return '{ ' + parts.join(', ') + ' }';
	},

	_transformCoordinates: function(matrix, coords, change) {
		// Use matrix.transform version() that takes arrays of multiple
		// points for largely improved performance, as no calls to
		// Point.read() and Point constructors are necessary.
		var point = this._point,
			// If change is true, only transform handles if they are set, as
			// _transformCoordinates is called only to change the segment, no
			// to receive the coords.
			// This saves some computation time. If change is false, always
			// use the real handles, as we just want to receive a filled
			// coords array for getBounds().
			handleIn =  !change || !this._handleIn.isZero()
					? this._handleIn : null,
			handleOut = !change || !this._handleOut.isZero()
					? this._handleOut : null,
			x = point._x,
			y = point._y,
			i = 2;
		coords[0] = x;
		coords[1] = y;
		// We need to convert handles to absolute coordinates in order
		// to transform them.
		if (handleIn) {
			coords[i++] = handleIn._x + x;
			coords[i++] = handleIn._y + y;
		}
		if (handleOut) {
			coords[i++] = handleOut._x + x;
			coords[i++] = handleOut._y + y;
		}
		// If no matrix was previded, this was just called to get the coords and
		// we are done now.
		if (!matrix)
			return;
		matrix._transformCoordinates(coords, 0, coords, 0, i / 2);
		x = coords[0];
		y = coords[1];
		if (change) {
			// If change is true, we need to set the new values back
			point._x = x;
			point._y = y;
			i  = 2;
			if (handleIn) {
				handleIn._x = coords[i++] - x;
				handleIn._y = coords[i++] - y;
			}
			if (handleOut) {
				handleOut._x = coords[i++] - x;
				handleOut._y = coords[i++] - y;
			}
		} else {
			// We want to receive the results in coords, so make sure
			// handleIn and out are defined too, even if they're 0
			if (!handleIn) {
				coords[i++] = x;
				coords[i++] = y;
			}
			if (!handleOut) {
				coords[i++] = x;
				coords[i++] = y;
			}
		}
	}
});
<|endoftext|>


<commit_before>/* @flow */
"use strict";

const kp = require("keypather")();

import prefix from "prefix-property";
import h from "yasmf-h";

import scrollContainer from "$WIDGETS/scrollContainer";
import textContainer from "$WIDGETS/textContainer";
import listItemActions from "$WIDGETS/listItemActions";
import View from "$LIB/View";
import GCS from "$LIB/grandCentralStation";
import L from "$APP/localization/localization";

import lemmaList from "./lemmaList";
import lemmaActions from "./lemmaActions";

import {settings} from "$MODELS/Settings";

const prefixedJSTransition = prefix("transition"),
      prefixedCSSTransform = prefix.css("transform"),
      prefixedJSTransform  = prefix("transform");

//region private properties
//-----------------------------------------------------------------------------
const _panningItem = Symbol("_panningItem"),
      _panningStartX = Symbol("panningStartX"),
      _panningActionWidth = Symbol("_panningActionWidth"),
      _filter = Symbol("_filter"),
      _filteredItems = Symbol("_filteredItems"),
      _page = Symbol("_page"),
      _actionsElement = Symbol("_actionsElement");
//-----------------------------------------------------------------------------
//endregion

//region private methods
//-----------------------------------------------------------------------------
/**
 * Translates the element using CSS 2D transforms, optionally using animation.
 *
 * @param {Node} el - element to translate
 * @param {Number} [v=0] - pixel position to translate the elemnt to
 * @param {Boolean} [withAnimation=false] - If true, uses 100ms animation
 * @return {void}
 */
function translateElement(el: Node, v: number = 0, withAnimation: boolean = false): void {
    if (withAnimation) {
        el.style[prefixedJSTransition] = `${prefixedCSSTransform} 0.1s linear`;
        setTimeout(() => {
            el.style[prefixedJSTransition] = "";
        }, 100);
    }
    if (v!==0) {
        el.style[prefixedJSTransform] = `translateX(${v}px)`;
    } else {
        el.style[prefixedJSTransform] = "";
    }
}

/**
 * Return the internal contents and actions items in a list item
 *
 * @param {Node} listItem
 * @return {Array<Node>} array of nodes
 */
function getInternalItems(listItem: Node): Array<Node> {
    return ['y-list-contents', 'y-list-actions'].map(s => listItem.querySelector(`[is='${s}']`));
}

/**
 * Return the width of an element
 *
 * @param {Node} el
 * @return {number}
 */
function getWidth(el: Node): number {
    return el ? parseInt(window.getComputedStyle(el).getPropertyValue("width"),10) : 0;
}

/**
 * Closes a currently panned item.
 *
 * @this SearchView
 */
function closePannedItem(): void {
    if (this[_panningItem]) {
        translateElement(this[_panningItem], 0, true);
        this[_panningItem] = null;
        this[_panningStartX] = undefined;
        this[_panningActionWidth] = undefined;
    }
}

/**
 * Starts panning a list item; closes any open panned items.
 *
 * @this SearchView
 * @param {Node} listItem
 * @param {Event} evt
 */
function panItemStart(listItem: Node, evt: Event): void {
    listItem.appendChild(this[_actionsElement]);
    const [panItem, actionItem] = getInternalItems(listItem);
    if (panItem !== this[_panningItem] && this[_panningItem]) {
        closePannedItem.call(this);
    }
    this[_panningActionWidth] = getWidth(actionItem);
    this[_panningItem] = panItem;
    if (this[_panningStartX] === undefined) {
        this[_panningStartX] = evt.center.x;
    }
}

/**
 * Moves the currently panning item, based on the event.
 *
 * @this SearchView
 * @param {Node} listItem
 * @param {Event} evt
 */
function panItemMove(listItem: Node, evt: Event): void {
    const panItem = this[_panningItem];
    if (panItem) {
        let startX = this[_panningStartX],
            curX = evt.center.x,
            deltaX = curX - startX;
        translateElement(panItem, deltaX);
    }
}

/**
 * Stops panning an item. Decides to slide open or closed based on the
 * last touch position in the event.
 *
 * @this SearchView
 * @param {Node} listItem
 * @param {Event} evt
 */
function panItemEnd(listItem: Node, evt: Event): void {
    const panItem = this[_panningItem],
          actionWidth = this[_panningActionWidth];
    if (panItem) {
        let startX = this[_panningStartX],
            curX = evt.center.x,
            deltaX = curX - startX,
            targetX = (actionWidth + deltaX < actionWidth / 2) ? -actionWidth : 0;
        if (targetX === 0) {
            closePannedItem.call(this);
        } else {
            translateElement(panItem, targetX, true);
        }
    }
}
//-----------------------------------------------------------------------------
//endregion

//region SearchView
//-----------------------------------------------------------------------------
export default class SearchView extends View {
    constructor (options={}) {
        super(options);

        // initialize our private properties for item panning support
        this[_panningItem] = null;
        this[_panningStartX] = undefined;
        this[_panningActionWidth] = undefined;

        // indicate that we want all dictionary items
        this[_filter] = undefined;
        this[_filteredItems] = [];

        // page number is zero
        this[_page] = 0;

        // get one shared set of list actions
        this[_actionsElement] = listItemActions({contents: lemmaActions()});
    }

    get page() {
        return this[_page];
    }

    get filter() {
        return this[_filter];
    }
    set filter(f) {
        this[_filter] = f ? f.trim() : undefined;
        this.emit("filterChanged");
    }
    clearFilter() {
        this[_filter] = undefined;
        this.emit("filterChanged");
    }

    template() {
        let model = kp.get(this, "controller.model");
        let dictionaryItems = model ? ((this[_filter] ? this[_filteredItems] : []).slice((this.page * settings.pageSize),settings.pageSize)) : [];
        return  scrollContainer({contents: [lemmaList(dictionaryItems)].concat(textContainer({contents:h.el("p.search-info",
            this[_filter] ? (
                this[_filteredItems].length === 0 ? L.T("search:no-results") : (
                    this[_filteredItems].length > settings.pageSize ? L.T("search:too-many-results") : ""
                )
            ) : L.T("search:enter")
        )}))});
    }

    get TARGET_SELECTORS() {
        return [
            {selector: "tap:ul li > button", emit:"listItemTapped"},
            {selector: "panstart panmove panend:ul li", emit:"listItemPanned"},
            {selector: "tap:ul li div button", emit:"actionTapped"}
        ];
    }

    //region Event Handlers
    //-------------------------------------------------------------------------
    onListItemTapped(sender, notice, listItem/*, evt*/) {
        closePannedItem.call(this);
        GCS.emit("APP:DO:viewDefinition", listItem.value);
    }

    onListItemPanned(sender, notice, listItem, evt) {
        switch (evt.type) {
            case "panstart":
                panItemStart.call(this, listItem, evt);
                break;
            case "panmove":
                panItemMove.call(this, listItem, evt);
                break;
            case "panend":
                panItemEnd.call(this, listItem, evt);
                break;
        }
    }

    onActionTapped(sender, notice, action) {
        closePannedItem.call(this);
        GCS.emit(`APP:DO:${action.value}Definition`, action.parentElement.parentElement.firstChild.value);
    }

    onFilterChanged(_, notice, data) {
        this[_page] = 0;
        this.elementTree.scrollTop = 0; // scroll the page back to the top

        let model = kp.get(this, "controller.model");
        let filter = this[_filter];
        if (filter) {
            // look up some entries!
            model.asyncFilteredIndex(filter)
                .then(entries => {
                    this[_filteredItems] = entries;
                    this.render();
                });
        } else {
            this.render();
        }
    }

    //-------------------------------------------------------------------------
    //endregion
}
//-----------------------------------------------------------------------------
//endregion

//region Factory
//-----------------------------------------------------------------------------
export function createSearchView(options={}) {
    return new SearchView(options);
}
//-----------------------------------------------------------------------------
//endregion
<commit_msg>Need to get notes & favorite status on swipe<commit_after>/* @flow */
"use strict";

const kp = require("keypather")();

import prefix from "prefix-property";
import h from "yasmf-h";

import scrollContainer from "$WIDGETS/scrollContainer";
import textContainer from "$WIDGETS/textContainer";
import listItemActions from "$WIDGETS/listItemActions";
import View from "$LIB/View";
import GCS from "$LIB/grandCentralStation";
import L from "$APP/localization/localization";
import lemmaList from "./lemmaList";
import lemmaActions from "./lemmaActions";
import {getSettings} from "../models/settings";
import {getFavorites} from "../models/Favorites";
import {getNotes} from "../models/Notes";
let settings = getSettings();

const prefixedJSTransition = prefix("transition"),
      prefixedCSSTransform = prefix.css("transform"),
      prefixedJSTransform  = prefix("transform");

//region private properties
//-----------------------------------------------------------------------------
const _panningItem = Symbol("_panningItem"),
      _panningStartX = Symbol("panningStartX"),
      _panningActionWidth = Symbol("_panningActionWidth"),
      _filter = Symbol("_filter"),
      _filteredItems = Symbol("_filteredItems"),
      _page = Symbol("_page"),
      _actionsElement = Symbol("_actionsElement");
//-----------------------------------------------------------------------------
//endregion

//region private methods
//-----------------------------------------------------------------------------
/**
 * Translates the element using CSS 2D transforms, optionally using animation.
 *
 * @param {Node} el - element to translate
 * @param {Number} [v=0] - pixel position to translate the elemnt to
 * @param {Boolean} [withAnimation=false] - If true, uses 100ms animation
 * @return {void}
 */
function translateElement(el: Node, v: number = 0, withAnimation: boolean = false): void {
    if (withAnimation) {
        el.style[prefixedJSTransition] = `${prefixedCSSTransform} 0.1s linear`;
        setTimeout(() => {
            el.style[prefixedJSTransition] = "";
        }, 100);
    }
    if (v!==0) {
        el.style[prefixedJSTransform] = `translateX(${v}px)`;
    } else {
        el.style[prefixedJSTransform] = "";
    }
}

/**
 * Return the internal contents and actions items in a list item
 *
 * @param {Node} listItem
 * @return {Array<Node>} array of nodes
 */
function getInternalItems(listItem: Node): Array<Node> {
    return ['y-list-contents', 'y-list-actions'].map(s => listItem.querySelector(`[is='${s}']`));
}

/**
 * Return the width of an element
 *
 * @param {Node} el
 * @return {number}
 */
function getWidth(el: Node): number {
    return el ? parseInt(window.getComputedStyle(el).getPropertyValue("width"),10) : 0;
}

/**
 * Closes a currently panned item.
 *
 * @this SearchView
 */
function closePannedItem(): void {
    if (this[_panningItem]) {
        translateElement(this[_panningItem], 0, true);
        this[_panningItem] = null;
        this[_panningStartX] = undefined;
        this[_panningActionWidth] = undefined;
    }
}

/**
 * Starts panning a list item; closes any open panned items.
 *
 * @this SearchView
 * @param {Node} listItem
 * @param {Event} evt
 */
function panItemStart(listItem: Node, evt: Event): void {
    listItem.appendChild(this[_actionsElement]);
    const [panItem, actionItem] = getInternalItems(listItem);
    if (panItem !== this[_panningItem] && this[_panningItem]) {
        closePannedItem.call(this);
    }
    this[_panningActionWidth] = getWidth(actionItem);
    this[_panningItem] = panItem;
    if (this[_panningStartX] === undefined) {
        this[_panningStartX] = evt.center.x;
    }
    let lemma = panItem.value;
    getFavorites().isWordAFavorite(lemma).then((fav) => {
        let actions = lemmaActions({isFavorite: fav});
        let favIcon = this[_actionsElement].querySelector(".fav-icon");
        favIcon.title = actions[0].title;
        favIcon.setAttribute("data-fav", actions[0].getAttribute("data-fav"));
    }).then(() => getNotes().doesWordHaveANote(lemma)).then((note) => {
        let actions = lemmaActions({hasNote: note});
        let noteIcon = this[_actionsElement].querySelector(".note-icon");
        noteIcon.setAttribute("data-note", actions[0].getAttribute("data-note"));
    });
}

/**
 * Moves the currently panning item, based on the event.
 *
 * @this SearchView
 * @param {Node} listItem
 * @param {Event} evt
 */
function panItemMove(listItem: Node, evt: Event): void {
    const panItem = this[_panningItem];
    if (panItem) {
        let startX = this[_panningStartX],
            curX = evt.center.x,
            deltaX = curX - startX;
        translateElement(panItem, deltaX);
    }
}

/**
 * Stops panning an item. Decides to slide open or closed based on the
 * last touch position in the event.
 *
 * @this SearchView
 * @param {Node} listItem
 * @param {Event} evt
 */
function panItemEnd(listItem: Node, evt: Event): void {
    const panItem = this[_panningItem],
          actionWidth = this[_panningActionWidth];
    if (panItem) {
        let startX = this[_panningStartX],
            curX = evt.center.x,
            deltaX = curX - startX,
            targetX = (actionWidth + deltaX < actionWidth / 2) ? -actionWidth : 0;
        if (targetX === 0) {
            closePannedItem.call(this);
        } else {
            translateElement(panItem, targetX, true);
        }
    }
}
//-----------------------------------------------------------------------------
//endregion

//region SearchView
//-----------------------------------------------------------------------------
export default class SearchView extends View {
    constructor (options={}) {
        super(options);

        // initialize our private properties for item panning support
        this[_panningItem] = null;
        this[_panningStartX] = undefined;
        this[_panningActionWidth] = undefined;

        // indicate that we want all dictionary items
        this[_filter] = undefined;
        this[_filteredItems] = [];

        // page number is zero
        this[_page] = 0;

        // get one shared set of list actions
        this[_actionsElement] = listItemActions({contents: lemmaActions()});
    }

    get page() {
        return this[_page];
    }

    get filter() {
        return this[_filter];
    }
    set filter(f) {
        this[_filter] = f ? f.trim() : undefined;
        this.emit("filterChanged");
    }
    clearFilter() {
        this[_filter] = undefined;
        this.emit("filterChanged");
    }

    template() {
        let model = kp.get(this, "controller.model");
        let dictionaryItems = model ? ((this[_filter] ? this[_filteredItems] : []).slice((this.page * settings.pageSize),settings.pageSize)) : [];
        return  scrollContainer({contents: [lemmaList(dictionaryItems)].concat(textContainer({contents:h.el("p.search-info",
            this[_filter] ? (
                this[_filteredItems].length === 0 ? L.T("search:no-results") : (
                    this[_filteredItems].length > settings.pageSize ? L.T("search:too-many-results") : ""
                )
            ) : L.T("search:enter")
        )}))});
    }

    get TARGET_SELECTORS() {
        return [
            {selector: "tap:ul li > button", emit:"listItemTapped"},
            {selector: "panstart panmove panend:ul li", emit:"listItemPanned"},
            {selector: "tap:ul li div button", emit:"actionTapped"}
        ];
    }

    //region Event Handlers
    //-------------------------------------------------------------------------
    onListItemTapped(sender, notice, listItem/*, evt*/) {
        closePannedItem.call(this);
        GCS.emit("APP:DO:viewDefinition", listItem.value);
    }

    onListItemPanned(sender, notice, listItem, evt) {
        switch (evt.type) {
            case "panstart":
                panItemStart.call(this, listItem, evt);
                break;
            case "panmove":
                panItemMove.call(this, listItem, evt);
                break;
            case "panend":
                panItemEnd.call(this, listItem, evt);
                break;
        }
    }

    onActionTapped(sender, notice, action) {
        closePannedItem.call(this);
        GCS.emit(`APP:DO:${action.value}Definition`, action.parentElement.parentElement.firstChild.value);
    }

    onFilterChanged(_, notice, data) {
        this[_page] = 0;
        this.elementTree.scrollTop = 0; // scroll the page back to the top

        let model = kp.get(this, "controller.model");
        let filter = this[_filter];
        if (filter) {
            // look up some entries!
            model.asyncFilteredIndex(filter)
                .then(entries => {
                    this[_filteredItems] = entries;
                    this.render();
                });
        } else {
            this.render();
        }
    }

    //-------------------------------------------------------------------------
    //endregion
}
//-----------------------------------------------------------------------------
//endregion

//region Factory
//-----------------------------------------------------------------------------
export function createSearchView(options={}) {
    return new SearchView(options);
}
//-----------------------------------------------------------------------------
//endregion
<|endoftext|>


<commit_before>var quotes = [
  'If something is too hard, either you are not doing it right or it is not worth doing',
  'Communication is key in any relationship',
  'Think about how the other person feels',
  'The opportunity for success is there - you just need to acknowledge its presence and grasp it',
  'You are only as good as you think you are',
];

function question() {
  var randomNumber = Math.floor(Math.random() * (quotes.length));
  document.getElementById("output").innerHTML= quotes[randomNumber];
}
<commit_msg>deleted this<commit_after><|endoftext|>


<commit_before>define(['underscore'], function(_) {

    var BASE_MAP_SIZE = 200;

    var module = {};

    var exp = {},
        map = {},
        bestStrategy = {};

    // In lieu of a mature class system, an inheritance mechanism for expressions
    exp.create = function(ctor) {
        ctor.prototype = new exp.base();

        return function() {
            var result = Object.create(ctor.prototype);
            ctor.apply(result, arguments);
            return result;
        };
    };

    // The base expression from which all others are derived
    exp.base = function() {
        this.getType = function() { throw 'Must override'; }
        this.isAutoParenthesis = function() { return false; };
    };

    // A unit expression, either 0 or 1, created by coercing
    // an array literal between Array, Number and Boolean types.
    // All expression trees are comprised of at least one unit expression.
    exp.unit = exp.create(function(val) {
        this.getType = function() { return 'unit'; }
        this.build = function(opts) {
            opts = opts || {};
            if(val === 0) return '+[]';
            if(val === 1) return opts.coerce ? '+!+[]' : '!+[]';
            throw 'Invalid Unit';
        };
    });

    var ADD = '+', SUBTRACT = '-', MULTIPLY = '*', DIVIDE = '/';

    // A binary expression which supports addition, subtraction, multiplication and division.
    // Most of the power of the efficiency of the number system is derived from these binary expressions.
    exp.binary = exp.create(function(left, op, right) {
        if(typeof left === 'number') {
            left = exp.map(left);
        }
        if(typeof right === 'number') {
            right = exp.map(right);
        }

        this.getType = function() { return 'binary'; }

        this.build = function(opts) {
            opts = opts || {};

            var isAddition = (op === ADD);

            // Only the left operand needs to be explicitly coerced to a number,
            // and only addition (as it can be confused with concatenation).
            var l = left.build( { coerce: opts.coerce && isAddition } ),
                r = right.build( { coerce: false } );

            // Addition can be safely performed without bracketing sub-expressions,
            // but not when the right hand side is a concat expression, which can cause
            // issues with both coercion and syntactic correctness
            if(isAddition && right.getType() !== 'concat') {
                return l + op + r;
            } else {
                // Some sub-expressions will become bracketed 'for free', so we don't need to double up.
                // Some sub-expressions will only be bracketed 'for free' depending on which side of the binary
                // expression they are found.
                l = left.isAutoParenthesis(true) ? l : ( '(' + l + ')' );
                r = right.isAutoParenthesis(false) ? r : ( '(' + r + ')' );
                return l + op + r;
            }
        };
    });

    // The concat expression abuses coercion between Arrays and Strings, and Strings to numbers.
    // It is a little known fact that single element arrays can be coerced to strings (e.g., ['a'] + 'b' == 'ab').
    // We can use this to form numbers one decimal digit at a time (e.g., [1]+[0]+[0] == '100')
    // It is an excellent general case, capable of representing any number, provided that it has at least two digits.
    // It is also an efficient representation in many cases, although it's complexity does scale linearly with
    // the number of decimal digits in the number.
    exp.concat = exp.create(function(val) {
        this.isAutoParenthesis = function(isLeft) { return isLeft; };
        this.getType = function() { return 'concat'; }
        this.build = function(opts) {
            opts = opts || {};

            var digits = String(val).split(''),
                parts = [],
                result,
                i;

            for(i = 0; i < digits.length; i++) {
                parts.push( '[' + exp.map( Number(digits[i]) ).build({ coerce: true }) + ']' );
            }

            result = '(' + parts.join('+') + ')';
            if(opts.coerce) {
                result = '+' + result;
            }

            return result;
        };
    });

    exp.map = exp.create(function(val) {
        var get = function() {
            if(map[val] === undefined) {
                exp.chooseBest(val);
            }

            return map[val];
        };

        this.isAutoParenthesis = function(isLeft) { return get().isAutoParenthesis(isLeft); };
        this.getType = function() { return get().getType(); }

        this.build = function(opts) {
            opts = opts || {};
            return get().build(opts);
        };
    });

    exp.chooseBest = function(value, options) {

        var defaults = {
            maxPasses: 5,
            skipLarger: false,
            missingFilter: function(v) { return v <= 10000; }
        };

        options = _.extend({}, defaults, options);

        var missingMapping = [];

        var pass = function(val) {

            var winner = null, smallest = Number.POSITIVE_INFINITY, count = 0;
            var trySolution = function(name, expr) {
                var built = expr.build({ coerce: true }),
                    len = built.length;

                if(len < smallest) {
                    smallest = len;
                    winner = { name: name, expr: expr };
                }

                count++;
            };

            trySolution('concat', exp.concat(val));

            var isCandidate = function(n) {
                n = +n;
                if(n < 0 || Math.floor(n) !== Math.ceil(n)) {
                    return false;
                }

                return true;
            };

            var expand = function(val) {
                var existing = map[val];
                if(existing) {
                    return existing;
                }

                missingMapping.push(val);
                return exp.concat(val);
            }

            var i, a, b;

            var max = Math.min(Math.ceil(val / 2), BASE_MAP_SIZE);
            for(i = 1; i <= max; i++) {
                a = i;
                // val = a + b
                b = val - a;
                if(isCandidate(b)) {
                    // Sometimes the coercion is more efficient by swapping the operands
                    trySolution(val + '=' + a + '+' + b, exp.binary(expand(a), ADD, expand(b)));
                    trySolution(val + '=' + b + '+' + a, exp.binary(expand(b), ADD, expand(a)));
                }
            }

            max = Math.ceil(Math.sqrt(val));
            for(i = 1; i <= max; i++) {
                a = i;
                // val = a * b
                b = val / a;
                if(isCandidate(b)) {
                    trySolution(val + '=' + a + '*' + b, exp.binary(expand(a), MULTIPLY, expand(b)));
                }
            }

            if(!options.skipLarger) {
                max = val + BASE_MAP_SIZE;
                for(i = val + 1; i <= max; i++) {
                    a = i;
                    // val = a - b
                    b = -(val - a);
                    if(isCandidate(b)) {
                        trySolution(val + '=' + a + '-' + b, exp.binary(expand(a), SUBTRACT, expand(b)));
                    }
                }

                max = BASE_MAP_SIZE;
                for(i = 2; i <= max; i++) {
                    b = i;
                    // val = a / b
                    a = val * b;
                    if(isCandidate(b)) {
                        trySolution(val + '=' + a + '/' + b, exp.binary(expand(a), DIVIDE, expand(b)));
                    }
                }
            }

            bestStrategy[val] = winner.name;
            return winner.expr;
        };

        for(var i = 0; i < options.maxPasses; i++) {
            map[value] = pass(value);
            missingMapping = _.uniq(_.filter(missingMapping, options.missingFilter));

            if(missingMapping.length === 0) {
                break;
            }

            var temp;
            missingMapping = [];
            _.each(temp, function(v) {
                map[v] = pass(v);
            });
        }

        return { missing: missingMapping };
    };

    var isInitialised = false;

    var initialiseMap = function() {
        if(isInitialised) { return };
        isInitialised = true;

        map[0] = exp.unit(0);
        map[1] = exp.unit(1);
        map[2] = exp.binary(1, ADD, 1);
        map[3] = exp.binary(1, ADD, 2);
        map[4] = exp.binary(2, ADD, 2);
        map[5] = exp.binary(10, DIVIDE, 2);
        map[6] = exp.binary(3, MULTIPLY, 2);
        map[7] = exp.binary(10, SUBTRACT, 3);
        map[8] = exp.binary(10, SUBTRACT, 2);
        map[9] = exp.binary(10, SUBTRACT, 1);
        map[10] = exp.concat(10);

        // Start by filling out the base map in a strictly incrementing fashion,
        // ignoring any candidates involving larger numbers.
        for(var i = 11; i <= BASE_MAP_SIZE; i++) {
            exp.chooseBest(i, { maxPasses: 1, skipLarger: true });
        }

        // Perform a second pass, this time considering larger candidates
        for(var i = 11; i <= BASE_MAP_SIZE; i++) {
            exp.chooseBest(i, { maxPasses: 1 });
        }

    };

    module.getSymbolic = function(val) {
        initialiseMap();

        var symbolic = exp.map(val).build( { coerce: true } ),
            validator = new Function('return (' + symbolic + ');');

        if(validator() !== val) {
            throw 'Symbolic representation did not match expected value.';
        }

        console.log(bestStrategy[val] || 'N/A');

        return symbolic;
    };

    module.test = function() {
        initialiseMap();

        var pad = function(direction, input, width) {
            input = String(input);
            while(input.length < width) {
                input = (direction === 'left') ? (' ' + input) : (input + ' ');
            }
            return input;
        };
        var padLeft = _.partial(pad, 'left');
        var padRight = _.partial(pad, 'right');

        var total = 0;
        for(var i = 0; i <= BASE_MAP_SIZE; i++) {
            var symbolic = exp.map(i).build( { coerce: true } );
            var validator = new Function('return (' + symbolic + ');');
            var evaluated = validator();
            total += symbolic.length;

            var strategy = (bestStrategy[i] || 'N/A');

            var info = padLeft(i, 3) + '=' + padRight(evaluated, 3) + ' ' + padLeft(symbolic.length, 2) + 'chars. ' + padRight(strategy, 11) + ' ' + symbolic;

            if((i === evaluated)) {
                console.log(info);
            } else {
                console.log('FAIL ' + info);
            }
        }
        console.log('TOTAL: ' + total + ' chars.');
    };

    return module;
});<commit_msg>Improved performance around the number map.<commit_after>define(['underscore'], function(_) {

    var BASE_MAP_SIZE = 200;

    var module = {};

    var exp = {},
        map = {},
        bestStrategy = {};

    // In lieu of a mature class system, an inheritance mechanism for expressions
    exp.create = function(ctor) {
        ctor.prototype = new exp.base();

        return function() {
            var result = Object.create(ctor.prototype);
            ctor.apply(result, arguments);
            return result;
        };
    };

    // The base expression from which all others are derived
    exp.base = function() {
        this.getType = function() { throw 'Must override'; }
        this.build = function() { throw 'Must override'; }
        this.isAutoParenthesis = function() { return false; };
    };

    // A unit expression, either 0 or 1, created by coercing
    // an array literal between Array, Number and Boolean types.
    // All expression trees are comprised of at least one unit expression.
    exp.unit = exp.create(function(val) {
        this.getType = function() { return 'unit'; }
        this.build = function(opts) {
            opts = opts || {};
            if(val === 0) return '+[]';
            if(val === 1) return opts.coerce ? '+!+[]' : '!+[]';
            throw 'Invalid Unit';
        };
    });

    var ADD = '+', SUBTRACT = '-', MULTIPLY = '*', DIVIDE = '/';

    // A binary expression which supports addition, subtraction, multiplication and division.
    // Most of the power of the efficiency of the number system is derived from these binary expressions.
    exp.binary = exp.create(function(left, op, right) {

        this.getType = function() { return 'binary'; }

        this.build = function(opts) {
            opts = opts || {};

            var leftExpr = (typeof left === 'number') ? exp.map(left) : left,
                rightExpr = (typeof left === 'number') ? exp.map(right) : right;

            var isAddition = (op === ADD);

            // Only the left operand needs to be explicitly coerced to a number,
            // and only addition (as it can be confused with concatenation).
            var l = leftExpr.build( { coerce: opts.coerce && isAddition } ),
                r = rightExpr.build( { coerce: false } );

            // Addition can be safely performed without bracketing sub-expressions,
            // but not when the right hand side is a concat expression, which can cause
            // issues with both coercion and syntactic correctness
            if(isAddition && rightExpr.getType() !== 'concat') {
                return l + op + r;
            } else {
                // Some sub-expressions will become bracketed 'for free', so we don't need to double up.
                // Some sub-expressions will only be bracketed 'for free' depending on which side of the binary
                // expression they are found.
                l = leftExpr.isAutoParenthesis(true) ? l : ( '(' + l + ')' );
                r = rightExpr.isAutoParenthesis(false) ? r : ( '(' + r + ')' );
                return l + op + r;
            }
        };
    });

    // The concat expression abuses coercion between Arrays and Strings, and Strings to numbers.
    // It is a little known fact that single element arrays can be coerced to strings (e.g., ['a'] + 'b' == 'ab').
    // We can use this to form numbers one decimal digit at a time (e.g., [1]+[0]+[0] == '100')
    // It is an excellent general case, capable of representing any number, provided that it has at least two digits.
    // It is also an efficient representation in many cases, although it's complexity does scale linearly with
    // the number of decimal digits in the number.
    exp.concat = exp.create(function(val) {
        this.isAutoParenthesis = function(isLeft) { return isLeft; };
        this.getType = function() { return 'concat'; }
        this.build = function(opts) {
            opts = opts || {};

            var digits = String(val).split(''),
                parts = [],
                result,
                i;

            for(i = 0; i < digits.length; i++) {
                parts.push( '[' + exp.map( Number(digits[i]) ).build({ coerce: true }) + ']' );
            }

            result = '(' + parts.join('+') + ')';
            if(opts.coerce) {
                result = '+' + result;
            }

            return result;
        };
    });

    // An expression proxy. The expression will be lazily initialised once any method is called.
    exp.lazy = exp.create(function(val) {
        var memo = null;
        var init = function() {
            !map[val] && exp.chooseBest(val);
            memo = map[val];
        };

        this.isAutoParenthesis = function(isLeft) { init(); return memo.isAutoParenthesis(isLeft);  };
        this.getType = function() { init(); return memo.getType(); };
        this.build = function(opts) { init(); return memo.build(opts); };
    });

    // Get the expression for a value from the map, or return a lazily initialised proxy if it doesn't exist.
    exp.map = function(val) {
        return map[val] || exp.lazy(val);
    };

    exp.chooseBest = function(value, options) {

        var defaults = {
            maxPasses: 5,
            skipLarger: false,
            missingFilter: function(v) { return v <= 10000; }
        };

        options = _.extend({}, defaults, options);

        var missingMapping = [];

        var pass = function(val) {

            var winner = null, smallest = Number.POSITIVE_INFINITY, count = 0;
            var trySolution = function(name, expr) {
                var built = expr.build({ coerce: true }),
                    len = built.length;

                if(len < smallest) {
                    smallest = len;
                    winner = { name: name, expr: expr };
                }

                count++;
            };

            trySolution('concat', exp.concat(val));

            var isCandidate = function(n) {
                n = +n;
                if(n < 0 || Math.floor(n) !== Math.ceil(n)) {
                    return false;
                }

                return true;
            };

            var expand = function(val) {
                var existing = map[val];
                if(existing) {
                    return existing;
                }

                missingMapping.push(val);
                return exp.concat(val);
            }

            var i, a, b;

            var max = Math.min(Math.ceil(val / 2), BASE_MAP_SIZE);
            for(i = 1; i <= max; i++) {
                a = i;
                // val = a + b
                b = val - a;
                if(isCandidate(b)) {
                    // Sometimes the coercion is more efficient by swapping the operands
                    trySolution(val + '=' + a + '+' + b, exp.binary(expand(a), ADD, expand(b)));
                    trySolution(val + '=' + b + '+' + a, exp.binary(expand(b), ADD, expand(a)));
                }
            }

            max = Math.ceil(Math.sqrt(val));
            for(i = 1; i <= max; i++) {
                a = i;
                // val = a * b
                b = val / a;
                if(isCandidate(b)) {
                    trySolution(val + '=' + a + '*' + b, exp.binary(expand(a), MULTIPLY, expand(b)));
                }
            }

            if(!options.skipLarger) {
                max = val + BASE_MAP_SIZE;
                for(i = val + 1; i <= max; i++) {
                    a = i;
                    // val = a - b
                    b = -(val - a);
                    if(isCandidate(b)) {
                        trySolution(val + '=' + a + '-' + b, exp.binary(expand(a), SUBTRACT, expand(b)));
                    }
                }

                max = BASE_MAP_SIZE;
                for(i = 2; i <= max; i++) {
                    b = i;
                    // val = a / b
                    a = val * b;
                    if(isCandidate(b)) {
                        trySolution(val + '=' + a + '/' + b, exp.binary(expand(a), DIVIDE, expand(b)));
                    }
                }
            }

            bestStrategy[val] = winner.name;
            return winner.expr;
        };

        for(var i = 0; i < options.maxPasses; i++) {
            map[value] = pass(value);
            missingMapping = _.uniq(_.filter(missingMapping, options.missingFilter));

            if(missingMapping.length === 0) {
                break;
            }

            var temp;
            missingMapping = [];
            _.each(temp, function(v) {
                map[v] = pass(v);
            });
        }

        return { missing: missingMapping };
    };

    var isInitialised = false;

    var initialiseMap = function() {
        if(isInitialised) { return };
        isInitialised = true;

        map[0] = exp.unit(0);
        map[1] = exp.unit(1);
        map[2] = exp.binary(1, ADD, 1);
        map[3] = exp.binary(1, ADD, 2);
        map[4] = exp.binary(2, ADD, 2);
        map[5] = exp.binary(10, DIVIDE, 2);
        map[6] = exp.binary(3, MULTIPLY, 2);
        map[7] = exp.binary(10, SUBTRACT, 3);
        map[8] = exp.binary(10, SUBTRACT, 2);
        map[9] = exp.binary(10, SUBTRACT, 1);
        map[10] = exp.concat(10);

        // Start by filling out the base map in a strictly incrementing fashion,
        // ignoring any candidates involving larger numbers.
        for(var i = 11; i <= BASE_MAP_SIZE; i++) {
            exp.chooseBest(i, { maxPasses: 1, skipLarger: true });
        }

        // Perform a second pass, this time considering larger candidates
        for(var i = 11; i <= BASE_MAP_SIZE; i++) {
            exp.chooseBest(i, { maxPasses: 1 });
        }

    };

    module.getSymbolic = function(val) {
        initialiseMap();

        var symbolic = exp.map(val).build( { coerce: true } ),
            validator = new Function('return (' + symbolic + ');');

        if(validator() !== val) {
            throw 'Symbolic representation did not match expected value.';
        }

        console.log(bestStrategy[val] || 'N/A');

        return symbolic;
    };

    module.test = function() {
        initialiseMap();

        var pad = function(direction, input, width) {
            input = String(input);
            while(input.length < width) {
                input = (direction === 'left') ? (' ' + input) : (input + ' ');
            }
            return input;
        };
        var padLeft = _.partial(pad, 'left');
        var padRight = _.partial(pad, 'right');

        var total = 0;
        for(var i = 0; i <= BASE_MAP_SIZE; i++) {
            var symbolic = exp.map(i).build( { coerce: true } );
            var validator = new Function('return (' + symbolic + ');');
            var evaluated = validator();
            total += symbolic.length;

            var strategy = (bestStrategy[i] || 'N/A');

            var info = padLeft(i, 3) + '=' + padRight(evaluated, 3) + ' ' + padLeft(symbolic.length, 2) + 'chars. ' + padRight(strategy, 11) + ' ' + symbolic;

            if((i === evaluated)) {
                console.log(info);
            } else {
                console.log('FAIL ' + info);
            }
        }
        console.log('TOTAL: ' + total + ' chars.');
    };

    return module;
});<|endoftext|>


<commit_before>const checkForDuplicateIssue = require('./utils/check-for-duplicate-issue')
const handlerSetup = require('./utils/handler-setup')
const { assignFlow, endDiff } = require('./utils/helpers')
const parseDiff = require('./utils/parse-diff')
const reopenClosed = require('./utils/reopen-closed')
const { issue } = require('./templates')

module.exports = async context => {
  // Only trigger push handler on pushes to master
  if (context.payload.ref !== `refs/heads/${context.payload.repository.master_branch}`) {
    return
  }

  // Do not trigger on merge commits
  const commit = await context.github.gitdata.getCommit(context.repo({sha: context.payload.head_commit.id}))
  if (commit.data.parents.length > 1) return

  const [
    { regex, config, labels },
    { data: diff }
  ] = await Promise.all([
    handlerSetup(context),
    context.github.repos.getCommit(context.repo({
      sha: context.payload.head_commit.id,
      headers: { Accept: 'application/vnd.github.diff' }
    }))
  ])

  let match
  while ((match = regex.exec(endDiff(diff))) !== null) {
    const parsed = parseDiff({ match, context, config })

    if (parsed.filename === '.github/config.yml') continue

    // Prevent duplicates
    const existingIssue = await checkForDuplicateIssue(context, parsed.title)
    if (existingIssue) {
      context.log(`Duplicate issue found with title [${parsed.title}]`)
      await reopenClosed({ context, config, issue: existingIssue }, parsed)
      continue
    }

    let body = issue(context.repo({
      sha: parsed.sha,
      assignedToString: parsed.assignedToString,
      range: parsed.range,
      filename: parsed.filename,
      keyword: parsed.keyword,
      body: parsed.body
    }))

    const regEx = /\/?<br(?:\s\/)?>/g //Regular expression to match all occurences of '<br&gt'
    body = body.replace(regEx, '
')
    context.log(`Creating issue [${parsed.title}] in [${context.repo().owner}/${context.repo().repo}]`)
    await context.github.issues.create(context.repo({ title: parsed.title, body, labels, ...assignFlow(config, parsed.username) }))
  }
}
<commit_msg>Another Style Fix<commit_after>const checkForDuplicateIssue = require('./utils/check-for-duplicate-issue')
const handlerSetup = require('./utils/handler-setup')
const { assignFlow, endDiff } = require('./utils/helpers')
const parseDiff = require('./utils/parse-diff')
const reopenClosed = require('./utils/reopen-closed')
const { issue } = require('./templates')

module.exports = async context => {
  // Only trigger push handler on pushes to master
  if (context.payload.ref !== `refs/heads/${context.payload.repository.master_branch}`) {
    return
  }

  // Do not trigger on merge commits
  const commit = await context.github.gitdata.getCommit(context.repo({sha: context.payload.head_commit.id}))
  if (commit.data.parents.length > 1) return

  const [
    { regex, config, labels },
    { data: diff }
  ] = await Promise.all([
    handlerSetup(context),
    context.github.repos.getCommit(context.repo({
      sha: context.payload.head_commit.id,
      headers: { Accept: 'application/vnd.github.diff' }
    }))
  ])

  let match
  while ((match = regex.exec(endDiff(diff))) !== null) {
    const parsed = parseDiff({ match, context, config })

    if (parsed.filename === '.github/config.yml') continue

    // Prevent duplicates
    const existingIssue = await checkForDuplicateIssue(context, parsed.title)
    if (existingIssue) {
      context.log(`Duplicate issue found with title [${parsed.title}]`)
      await reopenClosed({ context, config, issue: existingIssue }, parsed)
      continue
    }

    let body = issue(context.repo({
      sha: parsed.sha,
      assignedToString: parsed.assignedToString,
      range: parsed.range,
      filename: parsed.filename,
      keyword: parsed.keyword,
      body: parsed.body
    }))

    const regEx = /\/?<br(?:\s\/)?>/g // Regular expression to match all occurences of '<br&gt'
    body = body.replace(regEx, '
')
    context.log(`Creating issue [${parsed.title}] in [${context.repo().owner}/${context.repo().repo}]`)
    await context.github.issues.create(context.repo({ title: parsed.title, body, labels, ...assignFlow(config, parsed.username) }))
  }
}
<|endoftext|>