(function($) {
  var isReady = false;
	var options = {
	  touch: {
	    pan: 'xy',
	    scale: 'xy',
	    autoWidth: true,
	    autoHeight: true
	  }
	};

	function init(plot) {
		var isPanning = false;
		var isZooming = false;
		var lastTouchPosition = { x: -1, y: -1 };
		var lastTouchDistance = 0;
		var relativeOffset = { x: 0, y: 0};
		var relativeScale = 1.0;
		var scaleOrigin = { x: 50, y: 50 };
		
		function pan(delta) {
			var placeholder = plot.getPlaceholder();
			var options = plot.getOptions();
			
			relativeOffset.x -= delta.x;
			relativeOffset.y -= delta.y;
			
			switch (options.touch.pan.toLowerCase()) {
			  case 'x':
			    placeholder.children('div.flot-touch-container').css('-webkit-transform', 'translateX(' + relativeOffset.x + 'px)');
			    break;
			  case 'y':
			    placeholder.children('div.flot-touch-container').css('-webkit-transform', 'translateY(' + relativeOffset.y + 'px)');
			    break;
			  default:
			    placeholder.children('div.flot-touch-container').css('-webkit-transform', 'translate(' + relativeOffset.x + 'px,' + relativeOffset.y + 'px)');
			    break;
			}
		}
		
		function scale(delta) {
			var placeholder = plot.getPlaceholder();
			var options = plot.getOptions();
			var container = placeholder.children('div.flot-touch-container');
			
			relativeScale *= 1 + (delta / 100);
			
			switch (options.touch.scale.toLowerCase()) {
			  case 'x':
			    container.css('-webkit-transform', 'scaleX(' + relativeScale + ')');
			    break;
			  case 'y':
			    container.css('-webkit-transform', 'scaleY(' + relativeScale + ')');
			    break;
			  default:
			    container.css('-webkit-transform', 'scale(' + relativeScale + ')');
			    break;
			}
		}

		function processOptions(plot, options) {	    
		  var placeholder = plot.getPlaceholder();
    
		  if (options.touch.autoWidth) {
		    placeholder.css('width', '100%');
		  }
		  
		  if (options.touch.autoHeight) {
  		  var placeholderParent = placeholder.parent();
  		  var height = 0;
		    
  		  placeholderParent.siblings().each(function() {
  		    height -= $(this).outerHeight();
  		  });
		    
  		  height -= parseInt(placeholderParent.css('padding-top'), 10);
        height -= parseInt(placeholderParent.css('padding-bottom'), 10);
		    height += window.innerHeight;
		    
  		  placeholder.css('height', (height <= 0) ? 100 : height + 'px');
  		}
		}

		function bindEvents(plot, eventHolder) {
			var placeholder = plot.getPlaceholder();
			var container = $('<div class="flot-touch-container" style="background:#fff;"/>');
			
			placeholder.css({
			  'background': '#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////mpqaPjL2kgAAABdJREFUeNpiYIQCBhgYIIEBth4mABBgAEUQAIEfdL0YAAAAAElFTkSuQmCC) repeat',
			  'overflow': 'hidden'
			}).children('canvas').wrapAll(container);
			
			placeholder.bind('touchstart', function(evt) {
				var touches = evt.originalEvent.touches;
				var container = placeholder.children('div.flot-touch-container');
				
				if (touches.length === 1) {
					isPanning = true;
					lastTouchPosition = {
						x: touches[0].pageX,
						y: touches[0].pageY
					};
					lastTouchDistance = 0;
				}
				
				else if (touches.length === 2) {
					isZooming = true;
					lastTouchPosition = {
						x: (touches[0].pageX + touches[1].pageX) / 2,
						y: (touches[0].pageY + touches[1].pageY) / 2
					};
					lastTouchDistance = Math.sqrt(Math.pow(touches[1].pageX - touches[0].pageX, 2) + Math.pow(touches[1].pageY - touches[0].pageY, 2));
				}
				
				var offset = placeholder.offset();
				var rect = {
				  x: offset.left,
				  y: offset.top,
				  width: placeholder.width(),
				  height: placeholder.height()
				};
				
				var normalizedTouchPosition = {
				  x: lastTouchPosition.x,
				  y: lastTouchPosition.y
				};
				
				if (normalizedTouchPosition.x < rect.x) {
				  normalizedTouchPosition.x = rect.x;
				} else if (normalizedTouchPosition.x > rect.x + rect.width) {
				  normalizedTouchPosition.x = rect.x + rect.width;
				}

				if (normalizedTouchPosition.y < rect.y) {
				  normalizedTouchPosition.y = rect.y;
				} else if (normalizedTouchPosition.y > rect.y + rect.height) {
				  normalizedTouchPosition.y = rect.y + rect.height;
				}
				
				scaleOrigin = {
  			  x: Math.round((normalizedTouchPosition.x / rect.width) * 100),
  			  y: Math.round((normalizedTouchPosition.y / rect.height) * 100)
  			};

  			container.css('-webkit-transform-origin', scaleOrigin.x + '% ' + scaleOrigin.y + '%');
				
				// Return false to prevent touch scrolling.
				return false;
			});
			
			placeholder.bind('touchmove', function(evt) {
				var touches = evt.originalEvent.touches;
				var position, distance, delta;
				
				if (isPanning && touches.length === 1) {
					position = {
						x: touches[0].pageX,
						y: touches[0].pageY
					};
					delta = {
						x: lastTouchPosition.x - position.x,
						y: lastTouchPosition.y - position.y
					};
					
					// Transform via the delta
					pan(delta);
					
					lastTouchPosition = position;
					lastTouchDistance = 0;
				}
				
				else if (isZooming && touches.length === 2) {
					distance = Math.sqrt(Math.pow(touches[1].pageX - touches[0].pageX, 2) + Math.pow(touches[1].pageY - touches[0].pageY, 2));
					position = {
						x: (touches[0].pageX + touches[1].pageX) / 2,
						y: (touches[0].pageY + touches[1].pageY) / 2
					};
					delta = distance - lastTouchDistance;
					
					// Scale via the delta
					scale(delta);
					
					lastTouchPosition = position;
					lastTouchDistance = distance;
				}
			});
			
			placeholder.bind('touchend', function(evt) {
  			var placeholder = plot.getPlaceholder();
  			var options = plot.getOptions();
			  var container = placeholder.children('div.flot-touch-container');
  			
  			// Apply the pan.
  			if (relativeOffset.x !== 0 || relativeOffset.y !== 0) {
  			  $.each(plot.getAxes(), function(index, axis) {
  			    if (axis.direction === options.touch.pan.toLowerCase() || options.touch.pan.toLowerCase() == 'xy') {
              var min = axis.c2p(axis.p2c(axis.min) - relativeOffset[axis.direction]);
              var max = axis.c2p(axis.p2c(axis.max) - relativeOffset[axis.direction]);
              
              axis.options.min = min;
              axis.options.max = max;
  			    }
  			  });
  			}
  			
  			// Apply the scale.
  			if (relativeScale !== 1.0) {
  			  var width = plot.width();
  			  var height = plot.height();
  			  var scaleOriginPixel = {
  			    x: Math.round((scaleOrigin.x / 100) * width),
  			    y: Math.round((scaleOrigin.y / 100) * height)
  			  };
  			  var range = {
        	  x: {
        	    min: scaleOriginPixel.x - (scaleOrigin.x / 100) * width / relativeScale,
        	    max: scaleOriginPixel.x + (1 - (scaleOrigin.x / 100)) * width / relativeScale
        	  },
        	  y: {
        	    min: scaleOriginPixel.y - (scaleOrigin.y / 100) * height / relativeScale,
        	    max: scaleOriginPixel.y + (1 - (scaleOrigin.y / 100)) * height / relativeScale
        	  }
        	};

  			  $.each(plot.getAxes(), function(index, axis) {
  			    if (axis.direction === options.touch.scale.toLowerCase() || options.touch.scale.toLowerCase() == 'xy') {
          	  var min = axis.c2p(range[axis.direction].min);
          	  var max = axis.c2p(range[axis.direction].max);

          	  if (min > max) {
          	    var temp = min;
          	    min = max;
          	    max = temp;
          	  }

          	  axis.options.min = min;
          	  axis.options.max = max;
  			    }
  			  });
			  }
			  
  			plot.setupGrid();
      	plot.draw();
			  
				isPanning = false;
				isZooming = false;
				lastTouchPosition = { x: -1, y: -1 };
				lastTouchDistance = 0;
				relativeOffset = { x: 0, y: 0 };
				relativeScale = 1.0;
				scaleOrigin = { x: 50, y: 50 };
				
				container.css({
				  '-webkit-transform': 'translate(' + relativeOffset.x + 'px,' + relativeOffset.y + 'px) scale(' + relativeScale + ')',
				  '-webkit-transform-origin': scaleOrigin.x + '% ' + scaleOrigin.y + '%'
				});
			});
		}
		
		function processDatapoints(plot, series, datapoints) {
		  if (window.devicePixelRatio) {
  			var placeholder = plot.getPlaceholder();
			  
			  placeholder.children('canvas').each(function(index, canvas) {
    		  var context  = canvas.getContext('2d');
    		  var width = $(canvas).attr('width');
    		  var height = $(canvas).attr('height');

    		  $(canvas).attr('width', width * window.devicePixelRatio);
    		  $(canvas).attr('height', height * window.devicePixelRatio);
    		  $(canvas).css('width', width + 'px');
    		  $(canvas).css('height', height + 'px');

    		  context.scale(window.devicePixelRatio, window.devicePixelRatio);
			  });
  		}
		}

		function shutdown(plot, eventHolder) {
			var placeholder = plot.getPlaceholder();
			
			placeholder.unbind('touchstart').unbind('touchmove').unbind('touchend');
		}

		plot.hooks.processOptions.push(processOptions);
		plot.hooks.bindEvents.push(bindEvents);
		plot.hooks.processDatapoints.push(processDatapoints);
		plot.hooks.shutdown.push(shutdown);

    if (!isReady) {
      $(document).bind('ready orientationchange', function(evt) {
			  window.scrollTo(0, 1);
			  
			  setTimeout(function() {
			    $.plot(placeholder, plot.getData(), plot.getOptions());
			  }, 50);
			});
  		
  		isReady = true;
  	}
	}

	$.plot.plugins.push({
		init: init,
		options: options,
		name: 'touch',
		version: '1.0'
	});
})(jQuery);