text: (function($, window) {
  	var CLASS_ZOOM = $.className('zoom');
  	var CLASS_ZOOM_SCROLLER = $.className('zoom-scroller');
  
  	var SELECTOR_ZOOM = '.' + CLASS_ZOOM;
  	var SELECTOR_ZOOM_SCROLLER = '.' + CLASS_ZOOM_SCROLLER;
  
  	var EVENT_PINCH_START = 'pinchstart';
  	var EVENT_PINCH = 'pinch';
  	var EVENT_PINCH_END = 'pinchend';
  	if('ongesturestart' in window) {
  		EVENT_PINCH_START = 'gesturestart';
  		EVENT_PINCH = 'gesturechange';
  		EVENT_PINCH_END = 'gestureend';
  	}
  	$.Zoom = function(element, options) {
  		var zoom = this;
  
  		zoom.options = $.extend($.Zoom.defaults, options);
  
  		zoom.wrapper = zoom.element = element;
  		zoom.scroller = element.querySelector(SELECTOR_ZOOM_SCROLLER);
  		zoom.scrollerStyle = zoom.scroller && zoom.scroller.style;
  
  		zoom.zoomer = element.querySelector(SELECTOR_ZOOM);
  		zoom.zoomerStyle = zoom.zoomer && zoom.zoomer.style;
  
  		zoom.init = function() {
  			//自动启用
  			$.options.gestureConfig.pinch = true;
  			$.options.gestureConfig.doubletap = true;
  			zoom.initEvents();
  		};
  
  		zoom.initEvents = function(detach) {
  			var action = detach ? 'removeEventListener' : 'addEventListener';
  			var target = zoom.scroller;
  
  			target[action](EVENT_PINCH_START, zoom.onPinchstart);
  			target[action](EVENT_PINCH, zoom.onPinch);
  			target[action](EVENT_PINCH_END, zoom.onPinchend);
  
  			target[action]('touchstart', zoom.onTouchstart);
  			target[action]('touchmove', zoom.onTouchMove);
  			target[action]('touchcancel', zoom.onTouchEnd);
  			target[action]('touchend', zoom.onTouchEnd);
  
  			target[action]('drag', function(e) {
  				if(imageIsMoved || isGesturing) {
  					e.stopPropagation();
  				}
  			});
  			target[action]('doubletap', function(e) {
  				zoom.toggleZoom(e.detail.center);
  			});
  		};
  		zoom.transition = function(style, time) {
  			time = time || 0;
  			style['webkitTransitionDuration'] = time + 'ms';
  			return zoom;
  		};
  		zoom.translate = function(style, x, y) {
  			x = x || 0;
  			y = y || 0;
  			style['webkitTransform'] = 'translate3d(' + x + 'px,' + y + 'px,0px)';
  			return zoom;
  		};
  		zoom.scale = function(style, scale) {
  			scale = scale || 1;
  			style['webkitTransform'] = 'translate3d(0,0,0) scale(' + scale + ')';
  			return zoom;
  		};
  		zoom.scrollerTransition = function(time) {
  			return zoom.transition(zoom.scrollerStyle, time);
  		};
  		zoom.scrollerTransform = function(x, y) {
  			return zoom.translate(zoom.scrollerStyle, x, y);
  		};
  		zoom.zoomerTransition = function(time) {
  			return zoom.transition(zoom.zoomerStyle, time);
  		};
  		zoom.zoomerTransform = function(scale) {
  			return zoom.scale(zoom.zoomerStyle, scale);
  		};
  
  		// Gestures
  		var scale = 1,
  			currentScale = 1,
  			isScaling = false,
  			isGesturing = false;
  		zoom.onPinchstart = function(e) {
  			isGesturing = true;
  		};
  		zoom.onPinch = function(e) {
  			if(!isScaling) {
  				zoom.zoomerTransition(0);
  				isScaling = true;
  			}
  			scale = (e.detail ? e.detail.scale : e.scale) * currentScale;
  			if(scale > zoom.options.maxZoom) {
  				scale = zoom.options.maxZoom - 1 + Math.pow((scale - zoom.options.maxZoom + 1), 0.5);
  			}
  			if(scale < zoom.options.minZoom) {
  				scale = zoom.options.minZoom + 1 - Math.pow((zoom.options.minZoom - scale + 1), 0.5);
  			}
  			zoom.zoomerTransform(scale);
  		};
  		zoom.onPinchend = function(e) {
  			scale = Math.max(Math.min(scale, zoom.options.maxZoom), zoom.options.minZoom);
  			zoom.zoomerTransition(zoom.options.speed).zoomerTransform(scale);
  			currentScale = scale;
  			isScaling = false;
  		};
  		zoom.setZoom = function(newScale) {
  			scale = currentScale = newScale;
  			zoom.scrollerTransition(zoom.options.speed).scrollerTransform(0, 0);
  			zoom.zoomerTransition(zoom.options.speed).zoomerTransform(scale);
  		};
  		zoom.toggleZoom = function(position, speed) {
  			if(typeof position === 'number') {
  				speed = position;
  				position = undefined;
  			}
  			speed = typeof speed === 'undefined' ? zoom.options.speed : speed;
  			if(scale && scale !== 1) {
  				scale = currentScale = 1;
  				zoom.scrollerTransition(speed).scrollerTransform(0, 0);
  			} else {
  				scale = currentScale = zoom.options.maxZoom;
  				if(position) {
  					var offset = $.offset(zoom.zoomer);
  					var top = offset.top;
  					var left = offset.left;
  					var offsetX = (position.x - left) * scale;
  					var offsetY = (position.y - top) * scale;
  					this._cal();
  					if(offsetX >= imageMaxX && offsetX <= (imageMaxX + wrapperWidth)) { //center
  						offsetX = imageMaxX - offsetX + wrapperWidth / 2;
  					} else if(offsetX < imageMaxX) { //left
  						offsetX = imageMaxX - offsetX + wrapperWidth / 2;
  					} else if(offsetX > (imageMaxX + wrapperWidth)) { //right
  						offsetX = imageMaxX + wrapperWidth - offsetX - wrapperWidth / 2;
  					}
  					if(offsetY >= imageMaxY && offsetY <= (imageMaxY + wrapperHeight)) { //middle
  						offsetY = imageMaxY - offsetY + wrapperHeight / 2;
  					} else if(offsetY < imageMaxY) { //top
  						offsetY = imageMaxY - offsetY + wrapperHeight / 2;
  					} else if(offsetY > (imageMaxY + wrapperHeight)) { //bottom
  						offsetY = imageMaxY + wrapperHeight - offsetY - wrapperHeight / 2;
  					}
  					offsetX = Math.min(Math.max(offsetX, imageMinX), imageMaxX);
  					offsetY = Math.min(Math.max(offsetY, imageMinY), imageMaxY);
  					zoom.scrollerTransition(speed).scrollerTransform(offsetX, offsetY);
  				} else {
  					zoom.scrollerTransition(speed).scrollerTransform(0, 0);
  				}
  			}
  			zoom.zoomerTransition(speed).zoomerTransform(scale);
  		};
  
  		zoom._cal = function() {
  			wrapperWidth = zoom.wrapper.offsetWidth;
  			wrapperHeight = zoom.wrapper.offsetHeight;
  			imageWidth = zoom.zoomer.offsetWidth;
  			imageHeight = zoom.zoomer.offsetHeight;
  			var scaledWidth = imageWidth * scale;
  			var scaledHeight = imageHeight * scale;
  			imageMinX = Math.min((wrapperWidth / 2 - scaledWidth / 2), 0);
  			imageMaxX = -imageMinX;
  			imageMinY = Math.min((wrapperHeight / 2 - scaledHeight / 2), 0);
  			imageMaxY = -imageMinY;
  		};
  
  		var wrapperWidth, wrapperHeight, imageIsTouched, imageIsMoved, imageCurrentX, imageCurrentY, imageMinX, imageMinY, imageMaxX, imageMaxY, imageWidth, imageHeight, imageTouchesStart = {},
  			imageTouchesCurrent = {},
  			imageStartX, imageStartY, velocityPrevPositionX, velocityPrevTime, velocityX, velocityPrevPositionY, velocityY;
  
  		zoom.onTouchstart = function(e) {
  			e.preventDefault();
  			imageIsTouched = true;
  			imageTouchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
  			imageTouchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
  		};
  		zoom.onTouchMove = function(e) {
  			e.preventDefault();
  			if(!imageIsTouched) return;
  			if(!imageIsMoved) {
  				wrapperWidth = zoom.wrapper.offsetWidth;
  				wrapperHeight = zoom.wrapper.offsetHeight;
  				imageWidth = zoom.zoomer.offsetWidth;
  				imageHeight = zoom.zoomer.offsetHeight;
  				var translate = $.parseTranslateMatrix($.getStyles(zoom.scroller, 'webkitTransform'));
  				imageStartX = translate.x || 0;
  				imageStartY = translate.y || 0;
  				zoom.scrollerTransition(0);
  			}
  			var scaledWidth = imageWidth * scale;
  			var scaledHeight = imageHeight * scale;
  
  			if(scaledWidth < wrapperWidth && scaledHeight < wrapperHeight) return;
  
  			imageMinX = Math.min((wrapperWidth / 2 - scaledWidth / 2), 0);
  			imageMaxX = -imageMinX;
  			imageMinY = Math.min((wrapperHeight / 2 - scaledHeight / 2), 0);
  			imageMaxY = -imageMinY;
  
  			imageTouchesCurrent.x = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
  			imageTouchesCurrent.y = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
  
  			if(!imageIsMoved && !isScaling) {
  				//				if (Math.abs(imageTouchesCurrent.y - imageTouchesStart.y) < Math.abs(imageTouchesCurrent.x - imageTouchesStart.x)) {
  				//TODO 此处需要优化，当遇到长图，需要上下滚动时，下列判断会导致滚动不流畅
  				if(
  					(Math.floor(imageMinX) === Math.floor(imageStartX) && imageTouchesCurrent.x < imageTouchesStart.x) ||
  					(Math.floor(imageMaxX) === Math.floor(imageStartX) && imageTouchesCurrent.x > imageTouchesStart.x)
  				) {
  					imageIsTouched = false;
  					return;
  				}
  				//				}
  			}
  			imageIsMoved = true;
  			imageCurrentX = imageTouchesCurrent.x - imageTouchesStart.x + imageStartX;
  			imageCurrentY = imageTouchesCurrent.y - imageTouchesStart.y + imageStartY;
  
  			if(imageCurrentX < imageMinX) {
  				imageCurrentX = imageMinX + 1 - Math.pow((imageMinX - imageCurrentX + 1), 0.8);
  			}
  			if(imageCurrentX > imageMaxX) {
  				imageCurrentX = imageMaxX - 1 + Math.pow((imageCurrentX - imageMaxX + 1), 0.8);
  			}
  
  			if(imageCurrentY < imageMinY) {
  				imageCurrentY = imageMinY + 1 - Math.pow((imageMinY - imageCurrentY + 1), 0.8);
  			}
  			if(imageCurrentY > imageMaxY) {
  				imageCurrentY = imageMaxY - 1 + Math.pow((imageCurrentY - imageMaxY + 1), 0.8);
  			}
  
  			//Velocity
  			if(!velocityPrevPositionX) velocityPrevPositionX = imageTouchesCurrent.x;
  			if(!velocityPrevPositionY) velocityPrevPositionY = imageTouchesCurrent.y;
  			if(!velocityPrevTime) velocityPrevTime = $.now();
  			velocityX = (imageTouchesCurrent.x - velocityPrevPositionX) / ($.now() - velocityPrevTime) / 2;
  			velocityY = (imageTouchesCurrent.y - velocityPrevPositionY) / ($.now() - velocityPrevTime) / 2;
  			if(Math.abs(imageTouchesCurrent.x - velocityPrevPositionX) < 2) velocityX = 0;
  			if(Math.abs(imageTouchesCurrent.y - velocityPrevPositionY) < 2) velocityY = 0;
  			velocityPrevPositionX = imageTouchesCurrent.x;
  			velocityPrevPositionY = imageTouchesCurrent.y;
  			velocityPrevTime = $.now();
  
  			zoom.scrollerTransform(imageCurrentX, imageCurrentY);
  		};
  		zoom.onTouchEnd = function(e) {
  			if(!e.touches.length) {
  				isGesturing = false;
  			}
  			if(!imageIsTouched || !imageIsMoved) {
  				imageIsTouched = false;
  				imageIsMoved = false;
  				return;
  			}
  			imageIsTouched = false;
  			imageIsMoved = false;
  			var momentumDurationX = 300;
  			var momentumDurationY = 300;
  			var momentumDistanceX = velocityX * momentumDurationX;
  			var newPositionX = imageCurrentX + momentumDistanceX;
  			var momentumDistanceY = velocityY * momentumDurationY;
  			var newPositionY = imageCurrentY + momentumDistanceY;
  
  			if(velocityX !== 0) momentumDurationX = Math.abs((newPositionX - imageCurrentX) / velocityX);
  			if(velocityY !== 0) momentumDurationY = Math.abs((newPositionY - imageCurrentY) / velocityY);
  			var momentumDuration = Math.max(momentumDurationX, momentumDurationY);
  
  			imageCurrentX = newPositionX;
  			imageCurrentY = newPositionY;
  
  			var scaledWidth = imageWidth * scale;
  			var scaledHeight = imageHeight * scale;
  			imageMinX = Math.min((wrapperWidth / 2 - scaledWidth / 2), 0);
  			imageMaxX = -imageMinX;
  			imageMinY = Math.min((wrapperHeight / 2 - scaledHeight / 2), 0);
  			imageMaxY = -imageMinY;
  			imageCurrentX = Math.max(Math.min(imageCurrentX, imageMaxX), imageMinX);
  			imageCurrentY = Math.max(Math.min(imageCurrentY, imageMaxY), imageMinY);
  
  			zoom.scrollerTransition(momentumDuration).scrollerTransform(imageCurrentX, imageCurrentY);
  		};
  		zoom.destory = function() {
  			zoom.initEvents(true); //detach
  			delete $.data[zoom.wrapper.getAttribute('data-zoomer')];
  			zoom.wrapper.setAttribute('data-zoomer', '');
  		}
  		zoom.init();
  		return zoom;
  	};
  	$.Zoom.defaults = {
  		speed: 300,
  		maxZoom: 3,
  		minZoom: 1,
  	};
  	$.fn.zoom = function(options) {
  		var zoomApis = [];
  		this.each(function() {
  			var zoomApi = null;
  			var self = this;
  			var id = self.getAttribute('data-zoomer');
  			if(!id) {
  				id = ++$.uuid;
  				$.data[id] = zoomApi = new $.Zoom(self, options);
  				self.setAttribute('data-zoomer', id);
  			} else {
  				zoomApi = $.data[id];
  			}
  			zoomApis.push(zoomApi);
  		});
  		return zoomApis.length === 1 ? zoomApis[0] : zoomApis;
  	};
  })(mui, window);