text: /*
   * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
   * http://paperjs.org/
   *
   * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
   * http://scratchdisk.com/ & http://jonathanpuckey.com/
   *
   * Distributed under the MIT license. See LICENSE file for details.
   *
   * All rights reserved.
   */
  
  /**
   * @name Tool
   *
   * @class The Tool object refers to a script that the user can interact with
   * by using the mouse and keyboard and can be accessed through the global
   * {@code tool} variable. All its properties are also available in the paper
   * scope.
   *
   * The global {@code tool} variable only exists in scripts that contain mouse
   * handler functions ({@link #onMouseMove}, {@link #onMouseDown},
   * {@link #onMouseDrag}, {@link #onMouseUp}) or a keyboard handler
   * function ({@link #onKeyDown}, {@link #onKeyUp}).
   *
   * @classexample
   * var path;
   *
   * // Only execute onMouseDrag when the mouse
   * // has moved at least 10 points:
   * tool.distanceThreshold = 10;
   *
   * function onMouseDown(event) {
   *     // Create a new path every time the mouse is clicked
   *     path = new Path();
   *     path.add(event.point);
   *     path.strokeColor = 'black';
   * }
   *
   * function onMouseDrag(event) {
   *     // Add a point to the path every time the mouse is dragged
   *     path.add(event.point);
   * }
   */
  var Tool = PaperScopeItem.extend(/** @lends Tool# */{
  	_class: 'Tool',
  	_list: 'tools',
  	_reference: 'tool',
  	_events: [ 'onActivate', 'onDeactivate', 'onEditOptions',
  			'onMouseDown', 'onMouseUp', 'onMouseDrag', 'onMouseMove',
  			'onKeyDown', 'onKeyUp' ],
  
  	// DOCS: rewrite Tool constructor explanation
  	initialize: function Tool(props) {
  		PaperScopeItem.call(this);
  		this._firstMove = true;
  		this._count = 0;
  		this._downCount = 0;
  		this._set(props);
  	},
  
  	/**
  	 * Activates this tool, meaning {@link PaperScope#tool} will
  	 * point to it and it will be the one that recieves mouse events.
  	 *
  	 * @name Tool#activate
  	 * @function
  	 */
  
  	/**
  	 * Removes this tool from the {@link PaperScope#tools} list.
  	 *
  	 * @name Tool#remove
  	 * @function
  	 */
  
  	/**
  	 * The minimum distance the mouse has to drag before firing the onMouseDrag
  	 * event, since the last onMouseDrag event.
  	 *
  	 * @type Number
  	 * @bean
  	 */
  	getMinDistance: function() {
  		return this._minDistance;
  	},
  
  	setMinDistance: function(minDistance) {
  		this._minDistance = minDistance;
  		if (this._minDistance != null && this._maxDistance != null
  				&& this._minDistance > this._maxDistance) {
  			this._maxDistance = this._minDistance;
  		}
  	},
  
  	/**
  	 * The maximum distance the mouse has to drag before firing the onMouseDrag
  	 * event, since the last onMouseDrag event.
  	 *
  	 * @type Number
  	 * @bean
  	 */
  	getMaxDistance: function() {
  		return this._maxDistance;
  	},
  
  	setMaxDistance: function(maxDistance) {
  		this._maxDistance = maxDistance;
  		if (this._minDistance != null && this._maxDistance != null
  				&& this._maxDistance < this._minDistance) {
  			this._minDistance = maxDistance;
  		}
  	},
  
  	// DOCS: document Tool#fixedDistance
  	/**
  	 * @type Number
  	 * @bean
  	 */
  	getFixedDistance: function() {
  		return this._minDistance == this._maxDistance
  			? this._minDistance : null;
  	},
  
  	setFixedDistance: function(distance) {
  		this._minDistance = distance;
  		this._maxDistance = distance;
  	},
  
  	/**
  	 * {@grouptitle Mouse Event Handlers}
  	 *
  	 * The function to be called when the mouse button is pushed down. The
  	 * function receives a {@link ToolEvent} object which contains information
  	 * about the mouse event.
  	 *
  	 * @name Tool#onMouseDown
  	 * @property
  	 * @type Function
  	 *
  	 * @example {@paperscript}
  	 * // Creating circle shaped paths where the user presses the mouse button:
  	 * function onMouseDown(event) {
  	 *     // Create a new circle shaped path with a radius of 10
  	 *     // at the position of the mouse (event.point):
  	 *     var path = new Path.Circle({
  	 *         center: event.point,
  	 *         radius: 10,
  	 *         fillColor: 'black'
  	 *     });
  	 * }
  	 */
  
  	/**
  	 * The function to be called when the mouse position changes while the mouse
  	 * is being dragged. The function receives a {@link ToolEvent} object which
  	 * contains information about the mouse event.
  	 *
  	 * @name Tool#onMouseDrag
  	 * @property
  	 * @type Function
  	 *
  	 * @example {@paperscript}
  	 * // Draw a line by adding a segment to a path on every mouse drag event:
  	 *
  	 * // Create an empty path:
  	 * var path = new Path({
  	 *     strokeColor: 'black'
  	 * });
  	 *
  	 * function onMouseDrag(event) {
  	 *     // Add a segment to the path at the position of the mouse:
  	 *     path.add(event.point);
  	 * }
  	 */
  
  	/**
  	 * The function to be called the mouse moves within the project view. The
  	 * function receives a {@link ToolEvent} object which contains information
  	 * about the mouse event.
  	 *
  	 * @name Tool#onMouseMove
  	 * @property
  	 * @type Function
  	 *
  	 * @example {@paperscript}
  	 * // Moving a path to the position of the mouse:
  	 *
  	 * // Create a circle shaped path with a radius of 10 at {x: 0, y: 0}:
  	 * var path = new Path.Circle({
  	 *     center: [0, 0],
  	 *     radius: 10,
  	 *     fillColor: 'black'
  	 * });
  	 *
  	 * function onMouseMove(event) {
  	 *     // Whenever the user moves the mouse, move the path
  	 *     // to that position:
  	 *     path.position = event.point;
  	 * }
  	 */
  
  	/**
  	 * The function to be called when the mouse button is released. The function
  	 * receives a {@link ToolEvent} object which contains information about the
  	 * mouse event.
  	 *
  	 * @name Tool#onMouseUp
  	 * @property
  	 * @type Function
  	 *
  	 * @example {@paperscript}
  	 * // Creating circle shaped paths where the user releases the mouse:
  	 * function onMouseUp(event) {
  	 *     // Create a new circle shaped path with a radius of 10
  	 *     // at the position of the mouse (event.point):
  	 *     var path = new Path.Circle({
  	 *         center: event.point,
  	 *         radius: 10,
  	 *         fillColor: 'black'
  	 *     });
  	 * }
  	 */
  
  	/**
  	 * {@grouptitle Keyboard Event Handlers}
  	 *
  	 * The function to be called when the user presses a key on the keyboard.
  	 * The function receives a {@link KeyEvent} object which contains
  	 * information about the keyboard event.
  	 * If the function returns {@code false}, the keyboard event will be
  	 * prevented from bubbling up. This can be used for example to stop the
  	 * window from scrolling, when you need the user to interact with arrow
  	 * keys.
  	 *
  	 * @name Tool#onKeyDown
  	 * @property
  	 * @type Function
  	 *
  	 * @example {@paperscript}
  	 * // Scaling a path whenever the user presses the space bar:
  	 *
  	 * // Create a circle shaped path:
  	 *     var path = new Path.Circle({
  	 *         center: new Point(50, 50),
  	 *         radius: 30,
  	 *         fillColor: 'red'
  	 *     });
  	 *
  	 * function onKeyDown(event) {
  	 *     if (event.key == 'space') {
  	 *         // Scale the path by 110%:
  	 *         path.scale(1.1);
  	 *
  	 *         // Prevent the key event from bubbling
  	 *         return false;
  	 *     }
  	 * }
  	 */
  
  	/**
  	 * The function to be called when the user releases a key on the keyboard.
  	 * The function receives a {@link KeyEvent} object which contains
  	 * information about the keyboard event.
  	 * If the function returns {@code false}, the keyboard event will be
  	 * prevented from bubbling up. This can be used for example to stop the
  	 * window from scrolling, when you need the user to interact with arrow
  	 * keys.
  	 *
  	 * @name Tool#onKeyUp
  	 * @property
  	 * @type Function
  	 *
  	 * @example
  	 * function onKeyUp(event) {
  	 *     if (event.key == 'space') {
  	 *         console.log('The spacebar was released!');
  	 *     }
  	 * }
  	 */
  
  	_updateEvent: function(type, point, minDistance, maxDistance, start,
  			needsChange, matchMaxDistance) {
  		if (!start) {
  			if (minDistance != null || maxDistance != null) {
  				var minDist = minDistance != null ? minDistance : 0,
  					vector = point.subtract(this._point),
  					distance = vector.getLength();
  				if (distance < minDist)
  					return false;
  				// Produce a new point on the way to point if point is further
  				// away than maxDistance
  				var maxDist = maxDistance != null ? maxDistance : 0;
  				if (maxDist != 0) {
  					if (distance > maxDist) {
  						point = this._point.add(vector.normalize(maxDist));
  					} else if (matchMaxDistance) {
  						return false;
  					}
  				}
  			}
  			if (needsChange && point.equals(this._point))
  				return false;
  		}
  		// Make sure mousemove events have lastPoint set even for the first move
  		// so event.delta is always defined for them.
  		// TODO: Decide whether mousedown also should always have delta set.
  		this._lastPoint = start && type == 'mousemove' ? point : this._point;
  		this._point = point;
  		switch (type) {
  		case 'mousedown':
  			this._lastPoint = this._downPoint;
  			this._downPoint = this._point;
  			this._downCount++;
  			break;
  		case 'mouseup':
  			// Mouse up events return the down point for last point, so delta is
  			// spanning over the whole drag.
  			this._lastPoint = this._downPoint;
  			break;
  		}
  		this._count = start ? 0 : this._count + 1;
  		return true;
  	},
  
  	_fireEvent: function(type, event) {
  		// Handle items marked in removeOn*() calls first,.
  		var sets = paper.project._removeSets;
  		if (sets) {
  			// Always clear the drag set on mouseup
  			if (type === 'mouseup')
  				sets.mousedrag = null;
  			var set = sets[type];
  			if (set) {
  				for (var id in set) {
  					var item = set[id];
  					// If we remove this item, we also need to erase it from all
  					// other sets.
  					for (var key in sets) {
  						var other = sets[key];
  						if (other && other != set)
  							delete other[item._id];
  					}
  					item.remove();
  				}
  				sets[type] = null;
  			}
  		}
  		return this.responds(type)
  				&& this.fire(type, new ToolEvent(this, type, event));
  	},
  
  	_handleEvent: function(type, point, event) {
  		// Update global reference to this scope.
  		paper = this._scope;
  		// Now handle event callbacks
  		var called = false;
  		switch (type) {
  		case 'mousedown':
  			this._updateEvent(type, point, null, null, true, false, false);
  			called = this._fireEvent(type, event);
  			break;
  		case 'mousedrag':
  			// In order for idleInterval drag events to work, we need to not
  			// check the first call for a change of position. Subsequent calls
  			// required by min/maxDistance functionality will require it,
  			// otherwise this might loop endlessly.
  			var needsChange = false,
  			// If the mouse is moving faster than maxDistance, do not produce
  			// events for what is left after the first event is generated in
  			// case it is shorter than maxDistance, as this would produce weird
  			// results. matchMaxDistance controls this.
  				matchMaxDistance = false;
  			while (this._updateEvent(type, point, this.minDistance,
  					this.maxDistance, false, needsChange, matchMaxDistance)) {
  				called = this._fireEvent(type, event) || called;
  				needsChange = true;
  				matchMaxDistance = true;
  			}
  			break;
  		case 'mouseup':
  			// If the last mouse drag happened in a different place, call mouse
  			// drag first, then mouse up.
  			if (!point.equals(this._point)
  					&& this._updateEvent('mousedrag', point, this.minDistance,
  							this.maxDistance, false, false, false)) {
  				called = this._fireEvent('mousedrag', event);
  			}
  			this._updateEvent(type, point, null, this.maxDistance, false,
  					false, false);
  			called = this._fireEvent(type, event) || called;
  			// Start with new values for 'mousemove'
  			this._updateEvent(type, point, null, null, true, false, false);
  			this._firstMove = true;
  			break;
  		case 'mousemove':
  			while (this._updateEvent(type, point, this.minDistance,
  					this.maxDistance, this._firstMove, true, false)) {
  				called = this._fireEvent(type, event) || called;
  				this._firstMove = false;
  			}
  			break;
  		}
  		// Prevent default if mouse event was handled.
  		// !!! commented out because it broke text selection functionality
  		/*if (called)
  		 event.preventDefault();*/
  		return called;
  	}
  	/**
  	 * {@grouptitle Event Handling}
  	 *
  	 * Attach an event handler to the tool.
  	 *
  	 * @name Tool#attach
  	 * @alias Tool#on
  	 * @function
  	 * @param {String('mousedown', 'mouseup', 'mousedrag', 'mousemove',
  	 * 'keydown', 'keyup')} type the event type
  	 * @param {Function} function The function to be called when the event
  	 * occurs
  	 */
  	/**
  	 * Attach one or more event handlers to the tool.
  	 *
  	 * @name Tool#attach
  	 * @alias Tool#on
  	 * @function
  	 * @param {Object} param an object literal containing one or more of the
  	 * following properties: {@code mousedown, mouseup, mousedrag, mousemove,
  	 * keydown, keyup}.
  	 */
  
  	/**
  	 * Detach an event handler from the tool.
  	 *
  	 * @name Tool#detach
  	 * @alias Tool#off
  	 * @function
  	 * @param {String('mousedown', 'mouseup', 'mousedrag', 'mousemove',
  	 * 'keydown', 'keyup')} type the event type
  	 * @param {Function} function The function to be detached
  	 */
  	/**
  	 * Detach one or more event handlers from the tool.
  	 *
  	 * @name Tool#detach
  	 * @alias Tool#off
  	 * @function
  	 * @param {Object} param an object literal containing one or more of the
  	 * following properties: {@code mousedown, mouseup, mousedrag, mousemove,
  	 * keydown, keyup}
  	 */
  
  	/**
  	 * Fire an event on the tool.
  	 *
  	 * @name Tool#fire
  	 * @alias Tool#trigger
  	 * @function
  	 * @param {String('mousedown', 'mouseup', 'mousedrag', 'mousemove',
  	 * 'keydown', 'keyup')} type the event type
  	 * @param {Object} event an object literal containing properties describing
  	 * the event.
  	 */
  
  	/**
  	 * Check if the tool has one or more event handlers of the specified type.
  	 *
  	 * @name Tool#responds
  	 * @function
  	 * @param {String('mousedown', 'mouseup', 'mousedrag', 'mousemove',
  	 * 'keydown', 'keyup')} type the event type
  	 * @return {Boolean} {@true if the tool has one or more event handlers of
  	 * the specified type}
  	 */
  });
  