text: var PropTypes = require('prop-types');
  var React = require('react');
  
  function getPinchProps (touches) {
  	return {
  		touches: Array.prototype.map.call(touches, function copyTouch (touch) {
  			return { identifier: touch.identifier, pageX: touch.pageX, pageY: touch.pageY };
  		}),
  		center: {x: (touches[0].pageX + touches[1].pageX) / 2, y: (touches[0].pageY + touches[1].pageY) / 2 },
  		angle: Math.atan((touches[1].pageY - touches[0].pageY) / (touches[1].pageX - touches[0].pageX)) * 180 / Math.PI,
  		distance: Math.sqrt(Math.pow(Math.abs(touches[1].pageX - touches[0].pageX), 2) + Math.pow(Math.abs(touches[1].pageY - touches[0].pageY), 2))
  	};
  }
  
  var Mixin = {
  	propTypes: {
  		onPinchStart: PropTypes.func,          // fires when a pinch gesture is started
  		onPinchMove: PropTypes.func,           // fires on every touch-move when a pinch action is active
  		onPinchEnd: PropTypes.func             // fires when a pinch action ends
  	},
  
  	onPinchStart: function (event) {
  		// in case the two touches didn't start exactly at the same time
  		if (this._initialTouch) {
  			this.endTouch();
  		}
  		var touches = event.touches;
  		this._initialPinch = getPinchProps(touches);
  		this._initialPinch = Object.assign(this._initialPinch, {
  			displacement: { x: 0, y: 0 },
  			displacementVelocity: { x: 0, y: 0 },
  			rotation: 0,
  			rotationVelocity: 0,
  			zoom: 1,
  			zoomVelocity: 0,
  			time: Date.now()
  		});
  		this._lastPinch = this._initialPinch;
  		this.props.onPinchStart && this.props.onPinchStart(this._initialPinch, event);
  	},
  
  	onPinchMove: function (event) {
  		if (this._initialTouch) {
  			this.endTouch();
  		}
  		var touches = event.touches;
  		if (touches.length !== 2) {
  			return this.onPinchEnd(event); // bail out before disaster
  		}
  
  		var currentPinch =
  			touches[0].identifier === this._initialPinch.touches[0].identifier && touches[1].identifier === this._initialPinch.touches[1].identifier ?
  				getPinchProps(touches) // the touches are in the correct order
  			: touches[1].identifier === this._initialPinch.touches[0].identifier && touches[0].identifier === this._initialPinch.touches[1].identifier ?
  				getPinchProps(touches.reverse()) // the touches have somehow changed order
  				: getPinchProps(touches); // something is wrong, but we still have two touch-points, so we try not to fail
  
  		currentPinch.displacement = {
  			x: currentPinch.center.x - this._initialPinch.center.x,
  			y: currentPinch.center.y - this._initialPinch.center.y
  		};
  
  		currentPinch.time = Date.now();
  		var timeSinceLastPinch = currentPinch.time - this._lastPinch.time;
  
  		currentPinch.displacementVelocity = {
  			x: (currentPinch.displacement.x - this._lastPinch.displacement.x) / timeSinceLastPinch,
  			y: (currentPinch.displacement.y - this._lastPinch.displacement.y) / timeSinceLastPinch
  		};
  
  		currentPinch.rotation = currentPinch.angle - this._initialPinch.angle;
  		currentPinch.rotationVelocity = currentPinch.rotation - this._lastPinch.rotation / timeSinceLastPinch;
  
  		currentPinch.zoom = currentPinch.distance / this._initialPinch.distance;
  		currentPinch.zoomVelocity = (currentPinch.zoom - this._lastPinch.zoom) / timeSinceLastPinch;
  
  		this.props.onPinchMove && this.props.onPinchMove(currentPinch, event);
  
  		this._lastPinch = currentPinch;
  	},
  
  	onPinchEnd: function (event) {
  		// TODO use helper to order touches by identifier and use actual values on touchEnd.
  		var currentPinch = Object.assign({}, this._lastPinch);
  		currentPinch.time = Date.now();
  
  		if (currentPinch.time - this._lastPinch.time > 16) {
  			currentPinch.displacementVelocity = 0;
  			currentPinch.rotationVelocity = 0;
  			currentPinch.zoomVelocity = 0;
  		}
  
  		this.props.onPinchEnd && this.props.onPinchEnd(currentPinch, event);
  
  		this._initialPinch = this._lastPinch = null;
  
  		// If one finger is still on screen, it should start a new touch event for swiping etc
  		// But it should never fire an onTap or onPress event.
  		// Since there is no support swipes yet, this should be disregarded for now
  		// if (event.touches.length === 1) {
  		// 	this.onTouchStart(event);
  		// }
  	}
  };
  
  module.exports = Mixin;
  