/* Smooth Zoom Pan - jQuery Image Viewer Copyright (c) 2011-12 Ramesh Kumar http://codecanyon.net/user/VF Version: 1.6.7 RELEASE: 09 SEP 2011 UPDATE: 27 SEP 2012 Built using: jQuery version:1.7.1 http://jquery.com/ Modernizr version:2.5.3 http://www.modernizr.com/ MouseWheel version:3.0.6 http://brandonaaron.net/code/mousewheel/docs */ (function ($, window, document) { /***************************************************************************** Default settings: For detailed description of individual parameters, see the help document ******************************************************************************/ var defaults = { width: '', //Width of the view area [480, '480px', '100%'] height: '', //Height of the view area [480, '480px', '100%'] initial_ZOOM: '', //Initial zoom level to start with (in percentage) [100] initial_POSITION: '', //Initial location to be focused in pixel value [150,150 or 150 150] animation_SMOOTHNESS: 5.5, //Ease or smoothness of all movements [Any number from 0] animation_SPEED_ZOOM: 5.5, //Speed of zoom movements [Any number from 0] animation_SPEED_PAN: 5.5, //Speed of pan movements [Any number from 0] zoom_MAX: 800, //Maximum limit for zooming (in percentage) zoom_MIN: '', //Minimum limit for zooming (in percentage) zoom_SINGLE_STEP: false, //To reach maximum and minimum zoom levels in single click zoom_OUT_TO_FIT: true, //To allow image to be zoomed out with whitespace on sides zoom_BUTTONS_SHOW: true, //To enable/disable the + and - buttons pan_BUTTONS_SHOW: true, //To enable/disable the arrow and reset buttons pan_LIMIT_BOUNDARY: true, //To allow/restrict moving the image beyond boundaries pan_REVERSE: false, reset_ALIGN_TO: 'center center', //Image can be aligned to desired position on reset. Example: 'Top Left' reset_TO_ZOOM_MIN: true, //How it should behave if zoom_MIN value set and while clicking reset button, button_SIZE: 18, //Button width and height (in pixels) button_SIZE_TOUCH_DEVICE: 30, //Button width and height (in pixels) on touch devices button_COLOR: '#FFFFFF', //Button color in hexadecimal button_BG_COLOR: '#000000', //Button set's background color in hexadecimal button_BG_TRANSPARENCY: 55, //Background transparency (in percentage) button_AUTO_HIDE: false, //To hide the button set when mouse moved outside the view area button_AUTO_HIDE_DELAY: 1, //Auto hide delay time in seconds button_ALIGN: 'bottom right', //Button set can be aligned to any side or center button_MARGIN: 10, //Space between button set and view port's edge button_ROUND_CORNERS: true, //To enable disable roundness of button corner touch_DRAG: true, //Enable/disable the dragability of image (touch) mouse_DRAG: true, //Enable/disable the dragability of image (mouse) mouse_WHEEL: true, //Enable/disable mousewheel zoom mouse_WHEEL_CURSOR_POS: true, //Enable/disable position sensitive mousewheel zoom mouse_DOUBLE_CLICK: true, //Enable/disable zoom action with double click background_COLOR: '#FFFFFF', //Background colour of image container border_SIZE: 1, //Border size of view area border_COLOR: '#000000', //Border color of view area border_TRANSPARENCY: 10, //Border transparency of view area image_url: '', //Set url or image to be zoomed image_original_width: '', //Original width of main image image_original_height: '', //Original height of main image container: '', //Set container element of image (id of container) on_IMAGE_LOAD: '', //To Call external function immediatly after image loaded on_ZOOM_PAN_UPDATE: '', //To Call external function for each zoom, pan animation frame on_ZOOM_PAN_COMPLETE: '', //To Call external function whenever zoom, pan animation completes on_LANDMARK_STATE_CHANGE: '', //To Call external function whenever the zoom leval crosses global "data-show-at-zoom" value use_3D_Transform: true, //To enable / disable Hardware acceleration on webkit browsers responsive: false, //To enable / disable Responsive / fluid layout responsive_maintain_ratio: true, //To maintain view area width/height ratio or not max_WIDTH: '', //Maximum allowed width of view area (helpful when 'width' parameter set with % and need limit) max_HEIGHT: '' //Maximum allowed height of view area (helpful when 'height' parameter set with % and need limit) }; function Zoomer($elem, params) { var self = this; this.$elem = $elem; var op = $.extend({}, defaults, params); /********************************************************** Option values verified and formated if needed **********************************************************/ this.sW = op.width; this.sH = op.height; this.init_zoom = op.initial_ZOOM / 100; this.init_pos = op.initial_POSITION.replace(/,/g, ' ').replace(/\s{2,}/g, ' ').split(' '); this.zoom_max = op.zoom_MAX / 100; this.zoom_min = op.zoom_MIN / 100; this.zoom_single = checkBoolean(op.zoom_SINGLE_STEP); this.zoom_fit = checkBoolean(op.zoom_OUT_TO_FIT); this.zoom_speed = 1 + (((op.animation_SPEED === 0 || op.animation_SPEED ? op.animation_SPEED : op.animation_SPEED_ZOOM) + 1) / 20); this.zoom_show = checkBoolean(op.zoom_BUTTONS_SHOW); this.pan_speed_o = (op.animation_SPEED === 0 || op.animation_SPEED ? op.animation_SPEED : op.animation_SPEED_PAN); this.pan_show = checkBoolean(op.pan_BUTTONS_SHOW); this.pan_limit = checkBoolean(op.pan_LIMIT_BOUNDARY); this.pan_rev = checkBoolean(op.pan_REVERSE); this.reset_align = op.reset_ALIGN_TO.toLowerCase().split(' '); this.reset_to_zmin = checkBoolean(op.reset_TO_ZOOM_MIN); if (supportsTouch) { this.bu_size = parseInt(op.button_SIZE_TOUCH_DEVICE / 2) * 2; } else { this.bu_size = parseInt(op.button_SIZE / 2) * 2; } this.bu_color = op.button_COLOR; this.bu_bg = op.button_BG_COLOR; this.bu_bg_alpha = op.button_BG_TRANSPARENCY / 100; this.bu_icon = op.button_ICON_IMAGE; this.bu_auto = checkBoolean(op.button_AUTO_HIDE); this.bu_delay = op.button_AUTO_HIDE_DELAY * 1000; this.bu_align = op.button_ALIGN.toLowerCase().split(' '); this.bu_margin = op.button_MARGIN; this.bu_round = checkBoolean(op.button_ROUND_CORNERS); this.touch_drag = checkBoolean(op.touch_DRAG); this.mouse_drag = checkBoolean(op.mouse_DRAG); this.mouse_wheel = checkBoolean(op.mouse_WHEEL); this.mouse_wheel_cur = checkBoolean(op.mouse_WHEEL_CURSOR_POS); this.mouse_dbl_click = checkBoolean(op.mouse_DOUBLE_CLICK); this.ani_smooth = Math.max(1, (op.animation_SMOOTHNESS + 1) / 1.45); this.bg_color = op.background_COLOR; this.bord_size = op.border_SIZE; this.bord_color = op.border_COLOR; this.bord_alpha = op.border_TRANSPARENCY / 100; this.container = op.container; this.image_url = op.image_url; this.image_width = op.image_original_width; this.image_height = op.image_original_height; this.responsive = checkBoolean(op.responsive); this.maintain_ratio = checkBoolean(op.responsive_maintain_ratio); this.w_max = op.max_WIDTH; this.h_max = op.max_HEIGHT; this.onLOAD = op.on_IMAGE_LOAD; this.onUPDATE = op.on_ZOOM_PAN_UPDATE; this.onZOOM_PAN = op.on_ZOOM_PAN_COMPLETE; this.onLANDMARK = op.on_LANDMARK_STATE_CHANGE; /*********************************************************** Variables for inner operation. x, y, width, height and scale value of image ***********************************************************/ this._x; this._y; this._w; this._h; this._sc = 0; this.rA = 1; this.rF = 1; this.rR = 1; this.iW = 0; this.iH = 0; this.tX = 0; this.tY = 0; this.oX = 0; this.oY = 0; this.fX = 0; this.fY = 0; this.dX = 0; this.dY = 0; this.cX = 0; this.cY = 0; this.transOffX = 0; this.transOffY = 0; this.focusOffX = 0; this.focusOffY = 0; this.offX = 0; this.offY = 0; /*********************************************************** Flags that convey current states and events ***********************************************************/ this._playing = false; this._dragging = false; this._onfocus = false; this._moveCursor = false; this._wheel = false; this._recent = 'zoomOut'; this._pinching = false; this._landmark = false; this._rA; this._centx; this._centy; this._onButton = false; this._onHitArea = false; this.cFlag = { _zi: false, _zo: false, _ml: false, _mr: false, _mu: false, _md: false, _rs: false, _nd: false }; /*********************************************************** Elements and arrays that references elements ***********************************************************/ this.$holder; this.$hitArea; this.$controls; this.$loc_cont; this.map_coordinates = []; this.locations = []; this.buttons = []; this.border = []; /*********************************************************** miscellaneous ***********************************************************/ this.buttons_total = 7; this.cButtId = 0; this.pan_speed; this.auto_timer; this.ani_timer; this.ani_end; this.focusSpeed = this.reduction = .5; this.orig_style; this.mapAreas; this.icons; this.show_at_zoom; this.assetsLoaded = false; this.zStep = 0; this.sRed = 300; this.use3D = op.use_3D_Transform && supportsTrans3D; if (supportsTouch) { this.event_down = 'touchstart.sz'; this.event_up = 'touchend.sz'; this.event_move = 'touchmove.sz'; } else { this.event_down = 'mousedown.sz'; this.event_up = 'mouseup.sz'; this.event_move = 'mousemove.sz'; } //Case 1: Image specificed (possibly) through img tag: if (this.image_url == '') { this.$image = $elem; this.id = this.$image.attr('id'); //Case 2: Image url specificed through parameter: } else { var img = new Image(); if (this.image_width) { img.width = this.image_width; } if (this.image_height) { img.height = this.image_height; } img.src = this.image_url; this.$image = $(img).appendTo($elem); } //Prepare container div (Basically the element that masks image with overflow hidden) this.setContainer(); //Get button icon image's url var testOb; if (!this.bu_icon) { var regx = /url\(["']?([^'")]+)['"]?\)/; testOb = $('
'); this.$holder.append(testOb); this.bu_icon = testOb.css("background-image").replace(regx, '$1'); if (this.bu_icon == 'none') { this.bu_icon = 'zoom_assets/icons.png'; } testOb.remove(); } //Firefox feature checkup if (this.$image.css('-moz-transform') && prop_transform) { testOb = $('
'); this.$holder.append(testOb); this.fixMoz = testOb.position().left === 1 ? false : true; testOb.remove(); } else { this.fixMoz = false; } //Preload icons and main image. this.$image.hide(); this.imgList = [ { loaded: false, src: this.bu_icon || 'zoom_assets/icons.png' }, //Icon image { loaded: false, src: this.image_url == '' ? this.$image.attr('src') : this.image_url } // Main image ]; $.each(this.imgList, function (i) { var _img = new Image(); $(_img).bind('load', { id: i, self: self }, self.loadComplete) .bind('error', { id: i, self: self }, self.loadComplete); //Allow initiation even if image is not there :( _img.src = self.imgList[i].src; }); } Zoomer.prototype = { /*Preload the icon and main image *********************************************************************************************************************/ loadComplete: function (e) { var self = e.data.self, complete = true; self.imgList[e.data.id].loaded = true; for (var j = 0; j < self.imgList.length; j++) { if (!self.imgList[j].loaded) { complete = false; } } if (complete) { self.assetsLoaded = true; if (self.onLOAD !== '') { self.onLOAD(); } //Assets loaded, initiate plugin self.init(); } }, /*Initiate after assets loaded ***********************************************************************************************************************/ init: function () { var self = this, $image = self.$image, sW = self.sW, sH = self.sH, container = self.container, cBW, cBH, pan_show = self.pan_show, zoom_show = self.zoom_show, $controls = self.$controls, buttons = self.buttons, cFlag = self.cFlag, bu_align = self.bu_align, bu_margin = self.bu_margin, $holder = self.$holder; //Store the default image properties so that it can be reverted back when plugin needs to be destroyed self.orig_style = self.getStyle(); //IE 6 Image tool bar disabled $image.attr('galleryimg', 'no'); if (!navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad)/)) { $image.removeAttr('width'); $image.removeAttr('height'); } //In case parent element's display property set to 'none', we need to first set them 'block', measure the width and height and then set them back to 'none' var temp = $image, dispArray = []; for (var i = 0; i < 5; i++) { if (temp && temp[0].tagName !== 'BODY' && temp[0].tagName !== 'HTML') { if (temp.css('display') == 'none') { temp.css('display', 'block'); dispArray.push(temp); } temp = temp.parent(); } else { break; } } self.iW = $image.width(); self.iH = $image.height(); for (var i = 0; i < dispArray.length; i++) { dispArray[i].css('display', 'none'); } //Initially the image needs to be resized to fit container. To do so, first measure the scaledown ratio self.rF = self.rR = self.checkRatio(sW, sH, self.iW, self.iH, self.zoom_fit); //If NO Minimum zoom value set if (self.zoom_min == 0 || self.init_zoom != 0) { if (self.init_zoom != '') { self.rA = self._sc = self.init_zoom; } else { self.rA = self._sc = self.rF; } if (self.zoom_min != 0) { self.rF = self.zoom_min; } //If Minimum zoom value set } else { if (self.rF < self.zoom_min) { self.rF = self.zoom_min; if (self.reset_to_zmin) { self.rR = self.zoom_min } self.rA = self._sc = self.zoom_min; } else { self.rA = self._sc = self.rR; } } //Width and Height to be applied to the image self._w = self._sc * self.iW; self._h = self._sc * self.iH; //Resize the image and position it centered inside the wrapper if (self.init_pos == '') { self._x = self.tX = (sW - self._w) / 2; self._y = self.tY = (sH - self._h) / 2; } else { self._x = self.tX = (sW / 2) - parseInt(self.init_pos[0]) * self._sc; self._y = self.tY = (sH / 2) - parseInt(self.init_pos[1]) * self._sc; self.oX = (self.tX - ((sW - self._w) / 2)) / (self._w / sW); self.oY = (self.tY - ((sH - self._h) / 2)) / (self._h / sH); } if ((!self.pan_limit || self._moveCursor || self.init_zoom != self.rF) && self.mouse_drag) { $image.css('cursor', 'move'); self.$hitArea.css('cursor', 'move'); } if ($.browser.mozilla && supportsTrans3D) { $image.css('opacity', 0); } if (prop_transform) { self.$image.css(prop_origin, '0 0'); } if (self.use3D) { $image.css({ '-webkit-backface-visibility': 'hidden', '-webkit-perspective': 1000 }); } //Start displaying the image $image.css({ position: 'absolute', 'z-index': 2, left: '0px', top: '0px', '-webkit-box-shadow': '1px 1px rgba(0,0,0,0)' }) .hide() .fadeIn(500, function () { $holder.css('background-image', 'none'); if ($.browser.mozilla && supportsTrans3D) { $image.css('opacity', 1); } }); //Create Control buttons and events var self = self, bs = self.bu_size, iSize = 50, sDiff = 2, bSpace = 3, mSize = Math.ceil(self.bu_size / 4), iconOff = bs < 16 ? 50 : 0, bsDiff = bs - sDiff; //Show all buttons if (pan_show) { if (zoom_show) { cBW = parseInt(bs + (bs * .85) + (bsDiff * 3) + (bSpace * 2) + (mSize * 2)); } else { cBW = parseInt((bsDiff * 3) + (bSpace * 2) + (mSize * 2)); } cBH = parseInt((bsDiff * 3) + (bSpace * 2) + (mSize * 2)); //Show zoom buttons only } else { if (zoom_show) { cBW = parseInt(bs + mSize * 2); cBH = parseInt(bs * 2 + mSize * 3); cBW = parseInt(cBW / 2) * 2; cBH = parseInt(cBH / 2) * 2; } else { cBW = 0; cBH = 0; } } var pOff = (iSize - bs) / 2, resetCenterX = cBW - ((bs - (pan_show ? sDiff : 0)) * 2) - mSize - bSpace, resetCenterY = (cBH / 2) - ((bs - (pan_show ? sDiff : 0)) / 2); var hProp, vProp, hVal, vVal; //Align button set as per settings if (bu_align[0] == 'top') { vProp = 'top'; vVal = bu_margin; } else if (bu_align[0] == 'center') { vProp = 'top'; vVal = parseInt((sH - cBH) / 2); } else { vProp = 'bottom'; vVal = bu_margin; } if (bu_align[1] == 'right') { hProp = 'right'; hVal = bu_margin; } else if (bu_align[1] == 'center') { hProp = 'right'; hVal = parseInt((sW - cBW) / 2); } else { hProp = 'left'; hVal = bu_margin; } //Buttons Container $controls = $( '
\
\
\
' ); $holder.append($controls); var $controlsBg = $controls.find('.controlsBg'); //Make the corners rounded if (self.bu_round) { if (prop_radius) { $controlsBg .css(prop_radius, (iconOff > 0 ? 4 : 5) + 'px') .css('background-color', self.bu_bg); } else { self.roundBG( $controlsBg, 'cBg', cBW, cBH, iconOff > 0 ? 4 : 5, 375, self.bu_bg, self.bu_icon, 1, iconOff ? 50 : 0 ); } } else { $controlsBg.css('background-color', self.bu_bg); } $controlsBg.css('opacity', self.bu_bg_alpha); //Generating Button properties (7 buttons) buttons[0] = { _var: '_zi', l: mSize, t: pan_show ? (cBH - (bs * 2) - (bSpace * 2) + 2) / 2 : mSize, w: bs, h: bs, bx: -pOff, by: -pOff - iconOff }; buttons[1] = { _var: '_zo', l: mSize, t: pan_show ? ((cBH - (bs * 2) - (bSpace * 2) + 2) / 2) + bs + (bSpace * 2) - 2 : cBH - bs - mSize, w: bs, h: bs, bx: -iSize - pOff, by: -pOff - iconOff }; buttons[2] = { _var: self.pan_rev ? '_ml' : '_mr', l: resetCenterX - bsDiff - bSpace, t: resetCenterY, w: bsDiff, h: bsDiff, bx: -(sDiff / 2) - iSize * 2 - pOff, by: -(sDiff / 2) - pOff - iconOff }; buttons[3] = { _var: self.pan_rev ? '_mr' : '_ml', l: resetCenterX + bsDiff + bSpace, t: resetCenterY, w: bsDiff, h: bsDiff, bx: -(sDiff / 2) - iSize * 3 - pOff, by: -(sDiff / 2) - pOff - iconOff }; buttons[4] = { _var: self.pan_rev ? '_md' : '_mu', l: resetCenterX, t: resetCenterY + bsDiff + bSpace, w: bsDiff, h: bsDiff, bx: -(sDiff / 2) - iSize * 4 - pOff, by: -(sDiff / 2) - pOff - iconOff }; buttons[5] = { _var: self.pan_rev ? '_mu' : '_md', l: resetCenterX, t: resetCenterY - bsDiff - bSpace, w: bsDiff, h: bsDiff, bx: -(sDiff / 2) - iSize * 5 - pOff, by: -(sDiff / 2) - pOff - iconOff }; buttons[6] = { _var: '_rs', l: resetCenterX, t: resetCenterY, w: bsDiff, h: bsDiff, bx: -(sDiff / 2) - iSize * 6 - pOff, by: -(sDiff / 2) - pOff - iconOff }; for (var i = 0; i < 7; i++) { buttons[i].$ob = $( '
\
' ) .css('opacity', .7) .bind((supportsTouch ? "" : 'mouseover.sz mouseout.sz ') + self.event_down, { id: i }, function (e) { self._onfocus = false; $this = $(this); //Button over if (e.type == 'mouseover') { if ($this.css('opacity') > .5) { $this.css('opacity', 1); } //Button out } else if (e.type == 'mouseout') { if ($this.css('opacity') > .5) { $this.css('opacity', .7); } //Button press/down } else if (e.type == 'mousedown' || e.type == 'touchstart') { self.cButtId = e.data.id; self._onButton = true; self._wheel = false; //If NOT already down.. if ($this.css('opacity') > .5) { $this.css('opacity', 1); $holder.find('#' + buttons[self.cButtId]._var + 'norm').hide(); $holder.find('#' + buttons[self.cButtId]._var + 'over').show(); //CASE 1: If zoomIn pressed and single step zoom enabled if (self.cButtId <= 1 && self.zoom_single) { if (!cFlag[buttons[self.cButtId]._var]) { self.sRed = 300; cFlag[buttons[self.cButtId]._var] = true; } //CASE 2: If any button except RESET pressed } else if (self.cButtId < 6) { cFlag[buttons[self.cButtId]._var] = true; //CASE 3: RESET pressed } else { cFlag._rs = true; self.rA = self.rR; if (self.reset_align[0] == 'top') { self.fY = (self.sH / 2) * (self.rA / 2); } else if (self.reset_align[0] == 'bottom') { self.fY = -(self.sH / 2) * (self.rA / 2); } else { self.fY = 0; } if (self.reset_align[1] == 'left') { self.fX = (self.sW / 2) * (self.rA / 2); } else if (self.reset_align[1] == 'right') { self.fX = -(self.sW / 2) * (self.rA / 2); } else { self.fX = 0; } } self.focusOffX = self.focusOffY = 0; self.changeOffset(true, true); if (!self._playing) { self.Animate(); } } e.preventDefault(); e.stopPropagation(); } }); //Make 2 BGs for Button Normal and Over state //Button BG normal var tpm = $( '
\
' ); //Button BG hover var tpmo = $( '' ); //Add the button icons var cont = $( '
\
' ); buttons[i].$ob.append(tpm, tpmo, cont); $controls.append(buttons[i].$ob); //Apply corner radius if (self.bu_round) { if (prop_radius) { tpm.css(prop_radius, '2px'); tpmo.css(prop_radius, '2px'); } else { self.roundBG( tpm, buttons[i]._var + "norm", buttons[i].w, buttons[i].h, 2, 425, self.bu_color, self.bu_icon, i + 1, iconOff ? 50 : 0 ); self.roundBG( tpmo, buttons[i]._var + "over", buttons[i].w + 2, buttons[i].h + 2, 2, 425, self.bu_color, self.bu_icon, i + 1, iconOff ? 50 : 0 ); } } } //Add Events for mouse drag / touch swipe action $(document).bind(self.event_up + self.id, { self: self }, self.mouseUp); if ((self.mouse_drag && !supportsTouch) || (self.touch_drag && supportsTouch)) { self.$holder.bind(self.event_down, { self: self }, self.mouseDown); if (supportsTouch) { $(document).bind(self.event_move + self.id, { self: self }, self.mouseDrag); } } //Add Double click / Double tap zoom if (self.mouse_dbl_click) { var dClickedX, dClickedY, dbl_click_dir = 1; self.$holder.bind('dblclick.sz', function (e) { self.focusOffX = e.pageX - $holder.offset().left - (self.sW / 2); self.focusOffY = e.pageY - $holder.offset().top - (self.sH / 2); self.changeOffset(true, true); self._wheel = false; if (self.rA < self.zoom_max && dbl_click_dir == -1 && dClickedX != self.focusOffX && dClickedY != self.focusOffY) { dbl_click_dir = 1; } dClickedX = self.focusOffX; dClickedY = self.focusOffY; if (self.rA >= self.zoom_max && dbl_click_dir == 1) { dbl_click_dir = -1; } if (self.rA <= self.rF && dbl_click_dir == -1) { dbl_click_dir = 1; } if (dbl_click_dir > 0) { self.rA *= 2; self.rA = self.rA > self.zoom_max ? self.zoom_max : self.rA; cFlag._zi = true; clearTimeout(self.ani_timer); self._playing = true; self.Animate(); cFlag._zi = false; } else { self.rA /= 2; self.rA = self.rA < self.rF ? self.rF : self.rA; cFlag._zo = true; clearTimeout(self.ani_timer); self._playing = true; self.Animate(); cFlag._zo = false; } e.preventDefault(); e.stopPropagation(); }); } //Add mouse wheel event if enabled if (self.mouse_wheel) { $holder.bind('mousewheel.sz', { self: this }, self.mouseWheel); } //Auto Hide the control buttons if enabled if (self.bu_auto) { $holder.bind('mouseleave.sz', { self: this }, self.autoHide); } //Prevent Controls Bg from start dragging image $controls.bind(self.event_down, function (e) { e.preventDefault(); e.stopPropagation(); }); //Prevent Controls Bg from double click zoom if (self.mouse_dbl_click) { $controls.bind('dblclick.sz', function (e) { e.preventDefault(); e.stopPropagation(); }); } //Prevent text selection for smoother dragging and button focus $('.noSel').each(function () { this.onselectstart = function () { return false; }; }); self.$holder = $holder; self.$controls = $controls; self.sW = sW; self.sH = sH; self.cBW = cBW; self.cBH = cBH; //Apply initial transformation self.Animate(); }, /*Prepare the container (holder) element and get landmarks if available ***********************************************************************************************************************/ setContainer: function () { var self = this, $image = self.$image, bord_size = self.bord_size, border = self.border, $holder = self.$holder; //Wrap a container for image or get the container if specified through options: if (self.container == '' && self.image_url == '') { $holder = self.$image.wrap( '
\
' ).parent(); } else { if (self.image_url == '') { $holder = $('#' + self.container); } else { $holder = self.$elem; } $holder.addClass('noSel smooth_zoom_preloader'); self.locations = []; self.$loc_cont = $holder.find('.landmarks'); if (self.$loc_cont[0]) { var locs = self.$loc_cont.children('.item'); self.loc_clone = self.$loc_cont.clone(); self.show_at_zoom = parseInt(self.$loc_cont.data('show-at-zoom'), 10) / 100; self.allow_scale = checkBoolean(self.$loc_cont.data('allow-scale')); self.allow_drag = checkBoolean(self.$loc_cont.data('allow-drag')); locs.each(function () { self.setLocation($(this)); }); } } $holder.css({ 'position': 'relative', 'overflow': 'hidden', 'text-align': 'left', '-moz-user-select': 'none', '-khtml-user-select': 'none', '-webkit-user-select': 'none', 'user-select': 'none', '-webkit-touch-callout': 'none', '-webkit-tap-highlight-color': 'rgba(255, 255, 255, 0)', 'background-color': self.bg_color, 'background-position': 'center center', 'background-repeat': 'no-repeat' }) self.$hitArea = $('
').appendTo($holder); self.getContainerSize(self.sW, self.sH, $holder, self.w_max, self.h_max); if (self.responsive) { $(window).bind("orientationchange.sz" + self.id + " resize.sz" + self.id, { self: self }, self.resize); } var sW = self.sW; var sH = self.sH; //Add Image container properties $holder.css({ 'width': sW, 'height': sH }); //Add border if needed if (bord_size > 0) { border[0] = $('
').css('opacity', self.bord_alpha); border[1] = $('
').css('opacity', self.bord_alpha); border[2] = $('
').css('opacity', self.bord_alpha); border[3] = $('
').css('opacity', self.bord_alpha); $holder.append(border[0], border[1], border[2], border[3]); } //Get Image maps if exists if ($image.attr('usemap') != undefined) { self.mapAreas = $("map[name='" + ($image.attr('usemap').split('#').join('')) + "']").children('area'); self.mapAreas.each(function (i) { var area = $(this); area.css('cursor', 'pointer'); if (self.mouse_drag) { area.bind(self.event_down, { self: self }, self.mouseDown); } if (self.mouse_wheel) { area.bind('mousewheel.sz', { self: self }, self.mouseWheel); } self.map_coordinates.push(area.attr('coords').split(',')); }); } self.$holder = $holder; self.sW = sW; self.sH = sH; }, getContainerSize: function (sW, sH, $holder, w_max, h_max) { if (sW === '' || sW === 0) { if (this.image_url == '') { sW = Math.max($holder.parent().width(), 100); } else { sW = Math.max($holder.width(), 100); } } else if (!isNaN(sW) || String(sW).indexOf('px') > -1) { sW = this.oW = parseInt(sW); if (this.responsive) { sW = Math.min($holder.parent().width(), sW); } } else if (String(sW).indexOf('%') > -1) { sW = $holder.parent().width() * (sW.split('%')[0] / 100); } else { sW = 100; } if (w_max !== 0 && w_max !== '') { sW = Math.min(sW, w_max); } if (sH === '' || sH === 0) { if (this.image_url == '') { sH = Math.max($holder.parent().height(), 100); } else { sH = Math.max($holder.height(), 100); } } else if (!isNaN(sH) || String(sH).indexOf('px') > -1) { sH = this.oH = parseInt(sH); } else if (String(sH).indexOf('%') > -1) { sH = $holder.parent().height() * (sH.split('%')[0] / 100); } else { sH = 100; } if (h_max !== 0 && h_max !== '') { sH = Math.min(sH, h_max); } if (this.oW && sW !== this.oW) { if (this.oH && this.maintain_ratio) { sH = sW / (this.oW / this.oH); } } this.sW = sW; this.sH = sH; }, /*Each landmark / location / lable initiated here ***********************************************************************************************************************/ setLocation: function (lc) { var self = this, ob = lc, w2, h2, pos, sc; if (prop_origin) { ob.css(prop_origin, '0 0'); } ob.css({ 'display': 'block', 'z-index': 2 }) if (self.use3D) { ob.css({ '-webkit-backface-visibility': 'hidden', '-webkit-perspective': 1000 }); } w2 = ob.outerWidth() / 2; h2 = ob.outerHeight() / 2; pos = ob.data('position').split(','); sc = ob.data('allow-scale'); if (sc == undefined) { sc = self.allow_scale; } else { sc = checkBoolean(sc); } if (ob.hasClass('mark')) { var imgw = ob.find('img').css('vertical-align', 'bottom').width(); $(ob.children()[0]).css({ 'position': 'absolute', 'left': (-ob.width() / 2), 'bottom': parseInt(ob.css('padding-bottom')) * 2 }); var txt = ob.find('.text'); self.locations.push({ ob: ob, x: parseInt(pos[0]), y: parseInt(pos[1]), w2: w2, h2: h2, w2pad: w2 + (txt[0] ? parseInt(txt.css('padding-left')) : 0), vis: false, lab: false, lpx: '0', lpy: '0', showAt: isNaN(ob.data('show-at-zoom')) ? self.show_at_zoom : parseInt(ob.data('show-at-zoom'), 10) / 100, scale: sc }); } else if (ob.hasClass('lable')) { var bg = ob.data('bg-color'), opacity = ob.data('bg-opacity'), cont = $(ob.eq(0).children()[0]) .css({ 'position': 'absolute', 'z-index': 2, left: -w2, top: -h2 }); self.locations.push({ ob: ob, x: parseInt(pos[0]), y: parseInt(pos[1]), w2: w2, h2: h2, w2pad: w2, vis: false, lab: true, lpx: '0', lpy: '0', showAt: isNaN(ob.data('show-at-zoom')) ? self.show_at_zoom : parseInt(ob.data('show-at-zoom'), 10) / 100, scale: sc }); if (bg !== "") { if (!bg) { bg = "#000000"; opacity = .7; } var bgob = $('
').appendTo(ob); if (opacity) { bgob.css('opacity', opacity); } } } ob.hide(); if (prop_transform) { ob.css('opacity', 0); } if (!self.allow_drag) { ob.bind(self.event_down, function (e) { //e.preventDefault(); e.stopPropagation(); }) } }, /*Storing the original style of image (needed only when destroying) ***********************************************************************************************************************/ getStyle: function () { var el = this.$image; return { prop_origin: [prop_origin, prop_origin !== false && prop_origin !== undefined ? el.css(prop_origin) : null], prop_transform: [prop_transform, prop_transform !== false && prop_transform !== undefined ? el.css(prop_transform) : null], 'position': ['position', el.css('position')], 'z-index': ['z-index', el.css('z-index')], 'cursor': ['cursor', el.css('cursor')], 'left': ['left', el.css('left')], 'top': ['top', el.css('top')], 'width': ['width', el.css('width')], 'height': ['height', el.css('height')] }; }, /*Find the scale ratios ***********************************************************************************************************************/ checkRatio: function (sW, sH, iW, iH, zoom_fit) { var rF; if (iW == sW && iH == sH) { rF = 1; } else if (iW < sW && iH < sH) { rF = sW / iW; if (zoom_fit) { if (rF * iH > sH) { rF = sH / iH; } } else { if (rF * iH < sH) { rF = sH / iH; } if (sW / iW !== sH / iH && this.mouse_drag) { this._moveCursor = true; this.$image.css('cursor', 'move'); this.$hitArea.css('cursor', 'move'); } } } else { rF = sW / iW; if (zoom_fit) { if (rF * iH > sH) { rF = sH / iH; } if (rF < this.init_zoom && this.mouse_drag) { this._moveCursor = true; this.$image.css('cursor', 'move'); this.$hitArea.css('cursor', 'move'); } } else { if (rF * iH < sH) { rF = sH / iH; } if (sW / iW !== sH / iH && this.mouse_drag) { this._moveCursor = true; this.$image.css('cursor', 'move'); this.$hitArea.css('cursor', 'move'); } } } return rF; }, /*Returns distance between 2 points (used for touch gesture) ***********************************************************************************************************************/ getDistance: function (x1, y1, x2, y2) { return Math.sqrt(Math.abs(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))); }, /*Image Events for Dragging and Mouse Wheel ***********************************************************************************************************************/ mouseDown: function (e) { var self = e.data.self; self._onfocus = self._dragging = false; if (self.cFlag._nd) { if (self.fixMoz) { self.correctTransValue(); } self.samePointRelease = false; if (e.type == 'mousedown') { self.stX = e.pageX; self.stY = e.pageY; self.offX = e.pageX - self.$holder.offset().left - self.$image.position().left; self.offY = e.pageY - self.$holder.offset().top - self.$image.position().top; $(document).bind(self.event_move + self.id, { self: self }, self.mouseDrag); } else { var te = e.originalEvent; if (te.targetTouches.length > 1) { self._pinching = true; self._rA = self.rA; self.dStart = self.getDistance(te.touches[0].pageX, te.touches[0].pageY, te.touches[1].pageX, te.touches[1].pageY); } else { self.offX = te.touches[0].pageX - self.$holder.offset().left - self.$image.position().left; self.offY = te.touches[0].pageY - self.$holder.offset().top - self.$image.position().top; self.setDraggedPos(te.touches[0].pageX - self.$holder.offset().left - self.offX, te.touches[0].pageY - self.$holder.offset().top - self.offY, self._sc); self._recent = 'drag'; self._dragging = true; } } self._onHitArea = true; } if (e.type == 'mousedown') { e.preventDefault(); } }, /*Mouse Drag / Touch swipe operations handled here ***********************************************************************************************************************/ mouseDrag: function (e) { var self = e.data.self; //Mouse if (e.type == 'mousemove') { self.setDraggedPos(e.pageX - self.$holder.offset().left - self.offX, e.pageY - self.$holder.offset().top - self.offY, self._sc); self._recent = 'drag'; self._dragging = true; if (!self._playing) { self.Animate(); } return false; //Touch } else { if (self._dragging || self._pinching) { e.preventDefault(); } if (self._onHitArea) { var touches = e.originalEvent.touches; if (self._pinching || touches.length > 1) { if (!self._pinching) { self._pinching = true; self._rA = self.rA; if (touches.length > 1) { self.dStart = self.getDistance(touches[0].pageX, touches[0].pageY, touches[1].pageX, touches[1].pageY); } } if (touches.length > 1) { self._centx = (touches[0].pageX + touches[1].pageX) / 2; self._centy = (touches[0].pageY + touches[1].pageY) / 2; self.focusOffX = self._centx - self.$holder.offset().left - (self.sW / 2); self.focusOffY = self._centy - self.$holder.offset().top - (self.sH / 2); self.changeOffset(true, true); self._wheel = true; self._dragging = false; if (self.zoom_single) { self.sRed = 300; } else { self.dEnd = self.getDistance(touches[0].pageX, touches[0].pageY, touches[1].pageX, touches[1].pageY); self.rA = self._rA * (self.dEnd / self.dStart); self.rA = self.rA > self.zoom_max ? self.zoom_max : self.rA; self.rA = self.rA < self.rF ? self.rF : self.rA; } if (self._sc < self.rA) { self.cFlag._zo = false; self.cFlag._zi = true; } else { self.cFlag._zi = false; self.cFlag._zo = true; } if (!self._playing) { self.Animate(); } } } else if (self._dragging && touches.length < 2) { self.setDraggedPos(touches[0].pageX - self.$holder.offset().left - self.offX, touches[0].pageY - self.$holder.offset().top - self.offY, self._sc); self._recent = 'drag'; if (!self._playing) { self.Animate(); } } } } }, /*Global Mouse Up / Touch End ***********************************************************************************************************************/ mouseUp: function (e) { var self = e.data.self; //If one of the buttons released if (self._onButton) { self.$holder.find('#' + self.buttons[self.cButtId]._var + 'norm').show(); self.$holder.find('#' + self.buttons[self.cButtId]._var + 'over').hide(); if (self.cButtId !== 6) { self.cFlag[self.buttons[self.cButtId]._var] = false; } if (e.type == 'touchend' && self.buttons[self.cButtId].$ob.css('opacity') > .5) { self.buttons[self.cButtId].$ob.css('opacity', .7); } self._onButton = false; e.stopPropagation(); return false; //If the mouse drag or touch swipe completed } else if (self._onHitArea) { if (self.mouse_drag || self.touch_drag) { //Mouse if (e.type == 'mouseup') { $(document).unbind(self.event_move + self.id); if (self.stX == e.pageX && self.stY == e.pageY) { self.samePointRelease = true; } self._recent = 'drag'; self._dragging = false; if (!self._playing) { self.Animate(); } self._onHitArea = false; //Touch } else { e.preventDefault(); self._dragging = false; if (self._pinching) { self._pinching = false; self._wheel = false; self.cFlag._nd = true; self.cFlag._zi = false; self.cFlag._zo = false; } else { self._recent = 'drag'; if (!self._playing) { self.Animate(); } } self._onHitArea = false; } } } }, /*Mouse wheel zoom in-out ***********************************************************************************************************************/ mouseWheel: function (e, delta) { var self = e.data.self; self._onfocus = self._dragging = false; if (self.mouse_wheel_cur) { self.focusOffX = e.pageX - self.$holder.offset().left - (self.sW / 2); self.focusOffY = e.pageY - self.$holder.offset().top - (self.sH / 2); self.changeOffset(true, true); } self._dragging = false; if (delta > 0) { if (self.rA != self.zoom_max) { if (self.zoom_single) { if (!self._wheel) { self.sRed = 300; } } else { self.rA *= delta < 1 ? 1 + (.3 * delta) : 1.3; self.rA = self.rA > self.zoom_max ? self.zoom_max : self.rA; } self._wheel = true; self.cFlag._zi = true; clearTimeout(self.ani_timer); self._playing = true; self.Animate(); self.cFlag._zi = false; } } else { if (self.rA != self.rF) { if (self.zoom_single) { if (!self._wheel) { self.sRed = 300; } } else { self.rA /= delta > -1 ? 1 + (.3 * -delta) : 1.3; self.rA = self.rA < self.rF ? self.rF : self.rA; } self._wheel = true; self.cFlag._zo = true; clearTimeout(self.ani_timer); self._playing = true; self.Animate(); self.cFlag._zo = false; } } return false; }, /*Control buttons Auto hide ***********************************************************************************************************************/ autoHide: function (e) { var self = e.data.self; clearTimeout(self.auto_timer); self.auto_timer = setTimeout(function () { self.$controls.fadeOut(600); }, self.bu_delay); self.$holder.bind('mouseenter.sz', function (e) { clearTimeout(self.auto_timer); self.$controls.fadeIn(300); }); }, /*Mozilla works differently than others when getting translated positions. So this correction needed ***********************************************************************************************************************/ correctTransValue: function () { var v = this.$image.css('-moz-transform').toString().replace(')', '').split(','); this.transOffX = parseInt(v[4]); this.transOffY = parseInt(v[5]); }, /*Make sure the dragged position obeying limits ***********************************************************************************************************************/ setDraggedPos: function (xp, yp, s) { var self = this; if (xp !== '') { self.dX = xp + self.transOffX; if (self.pan_limit) { self.dX = self.dX + (s * self.iW) < self.sW ? self.sW - (s * self.iW) : self.dX; self.dX = self.dX > 0 ? 0 : self.dX; if ((s * self.iW) < self.sW) { self.dX = (self.sW - (s * self.iW)) / 2; } } else { self.dX = self.dX + (s * self.iW) < self.sW / 2 ? (self.sW / 2) - (s * self.iW) : self.dX; self.dX = self.dX > self.sW / 2 ? self.sW / 2 : self.dX; } } if (yp !== '') { self.dY = yp + self.transOffY; if (self.pan_limit) { self.dY = self.dY + (s * self.iH) < self.sH ? self.sH - (s * self.iH) : self.dY; self.dY = self.dY > 0 ? 0 : self.dY; if ((s * self.iH) < self.sH) { self.dY = (self.sH - (s * self.iH)) / 2; } } else { self.dY = self.dY + (s * self.iH) < self.sH / 2 ? (self.sH / 2) - (s * self.iH) : self.dY; self.dY = self.dY > self.sH / 2 ? self.sH / 2 : self.dY; } } }, /*Called to animate image transformation whenever the navigation events occur ***********************************************************************************************************************/ Animate: function () { var self = this; var pixTol = .5; self.cFlag._nd = true; self.ani_end = false; //Zoom In if (self.cFlag._zi) { if (!self._wheel && !self.zoom_single) { self.rA *= self.zoom_speed; } if (self.rA > self.zoom_max) { self.rA = self.zoom_max; } self.cFlag._nd = false; self.cFlag._rs = false; self._recent = 'zoomIn'; self._onfocus = self._dragging = false; } //Zoom Out if (self.cFlag._zo) { if (!self._wheel && !self.zoom_single) { self.rA /= self.zoom_speed; } if (self.zoom_min != 0) { if (self.rA < self.zoom_min) { self.rA = self.zoom_min; } } else { if (self.rA < self.rF) { self.rA = self.rF; } } self.cFlag._nd = false; self.cFlag._rs = false; self._recent = 'zoomOut'; self._onfocus = self._dragging = false; } //Zoom In or Out - Single Step if (self.zoom_single && !self.cFlag._rs) { if (self._recent == 'zoomIn') { self.sRed += (10 - self.sRed) / 6; self.rA += (self.zoom_max - self.rA) / (((1 / (self.pan_speed_o + 1)) * self.sRed) + 1); } else if (self._recent == 'zoomOut') { self.sRed += (3 - self.sRed) / 3; self.rA += (self.rF - self.rA) / (((1 / self.pan_speed_o + 1) * self.sRed) + 1); } } //Pan speed needs to adjust according to application dimension and zoom level self.pan_speed = (Math.max(1, 1 + ((self.sW + self.sH) / 500)) + (self.pan_speed_o * self.pan_speed_o / 4)) / Math.max(1, self.rA / 2); //Move Left if (self.cFlag._ml) { self.oX -= self.pan_speed; self.cFlag._nd = false; self.cFlag._rs = false; self._recent = 'left'; self._onfocus = self._dragging = false; } //Move Right if (self.cFlag._mr) { self.oX += self.pan_speed; self.cFlag._nd = false; self.cFlag._rs = false; self._recent = 'right'; self._onfocus = self._dragging = false; } //Move Up if (self.cFlag._mu) { self.oY -= self.pan_speed; self.cFlag._nd = false; self.cFlag._rs = false; self._recent = 'up'; self._onfocus = self._dragging = false; } //Move Down if (self.cFlag._md) { self.oY += self.pan_speed; self.cFlag._nd = false; self.cFlag._rs = false; self._recent = 'down'; self._onfocus = self._dragging = false; } //Reset if (self.cFlag._rs) { self.oX += (self.fX - self.oX) / 8; self.oY += (self.fY - self.oY) / 8; self.cFlag._nd = false; self._recent = 'reset'; self._onfocus = self._dragging = false; } //Find scale value, width and height //Case 1: Single Step Zoom if (self.zoom_single && (self._recent !== 'reset')) { if (self._onfocus) { self._sc += (self.rA - self._sc) / self.reduction; } else { self._sc = self.rA; } //Case 2: Normal Zoom } else { self._sc += (self.rA - self._sc) / (self.ani_smooth / (self._onfocus ? self.reduction : 1)); } self._w = self._sc * self.iW; self._h = self._sc * self.iH; //Dragging if (self._dragging) { self.tX = self.dX; self.tY = self.dY; self.changeOffset(true, true); } //Check if Zoom In completed if (self._recent == "zoomIn") { if (self._w > (self.rA * self.iW) - pixTol && !self.zoom_single) { if (self.cFlag._nd) { self.ani_end = true; } self._sc = self.rA; } else if (self._w > (self.zoom_max * self.iW) - pixTol && self.zoom_single) { if (self.cFlag._nd) { self.ani_end = true; } self._sc = self.rA = self.zoom_max; } if (self.ani_end) { self._w = self._sc * self.iW; self._h = self._sc * self.iH; } //Check if Zoom Out completed } else if (self._recent == "zoomOut") { if (self._w < (self.rA * self.iW) + pixTol && !self.zoom_single) { if (self.cFlag._nd) { self.ani_end = true; } self._sc = self.rA; } else if (self._w < (self.rF * self.iW) + pixTol && self.zoom_single) { if (self.cFlag._nd) { self.ani_end = true; } self._sc = self.rA = self.rF; } if (self.ani_end) { self._w = self._sc * self.iW; self._h = self._sc * self.iH; } } //Move image according to boundary limits self.limitX = (((self._w - self.sW) / (self._w / self.sW)) / 2); self.limitY = (((self._h - self.sH) / (self._h / self.sH)) / 2); if (!self._dragging) { if (self.pan_limit) { if (self.oX < -self.limitX - self.focusOffX) { self.oX = -self.limitX - self.focusOffX; } if (self.oX > self.limitX - self.focusOffX) { self.oX = self.limitX - self.focusOffX; } if (self._w < self.sW) { self.tX = (self.sW - self._w) / 2; self.changeOffset(true, false); } if (self.oY < -self.limitY - self.focusOffY) { self.oY = -self.limitY - self.focusOffY; } if (self.oY > self.limitY - self.focusOffY) { self.oY = self.limitY - self.focusOffY; } if (self._h < self.sH) { self.tY = (self.sH - self._h) / 2; self.changeOffset(false, true); } } else { if (self.oX < -self.limitX - (self.focusOffX / self._w * self.sW) - ((self.sW / 2) / (self._w / self.sW))) { self.oX = -self.limitX - (self.focusOffX / self._w * self.sW) - ((self.sW / 2) / (self._w / self.sW)); } if (self.oX > self.limitX - (self.focusOffX / self._w * self.sW) + ((self.sW / 2) / (self._w / self.sW))) { self.oX = self.limitX - (self.focusOffX / self._w * self.sW) + ((self.sW / 2) / (self._w / self.sW)); } if (self.oY < -self.limitY - (self.focusOffY / self._h * self.sH) - (self.sH / (self._h / self.sH * 2))) { self.oY = -self.limitY - (self.focusOffY / self._h * self.sH) - (self.sH / (self._h / self.sH * 2)); } if (self.oY > self.limitY - (self.focusOffY / self._h * self.sH) + (self.sH / (self._h / self.sH * 2))) { self.oY = self.limitY - (self.focusOffY / self._h * self.sH) + (self.sH / (self._h / self.sH * 2)); } } } if (!self._dragging && self._recent != "drag") { self.tX = ((self.sW - self._w) / 2) + self.focusOffX + (self.oX * (self._w / self.sW)); self.tY = ((self.sH - self._h) / 2) + self.focusOffY + (self.oY * (self._h / self.sH)); if (self.ani_smooth === 1) { self.cFlag._nd = true; self.ani_end = true; } } if (self._recent == "zoomIn" || self._recent == "zoomOut" || self.cFlag._rs) { self._x = self.tX; self._y = self.tY; } else { self._x += (self.tX - self._x) / (self.ani_smooth / (self._onfocus ? self.reduction : 1)); self._y += (self.tY - self._y) / (self.ani_smooth / (self._onfocus ? self.reduction : 1)); } //Check if Left movement completed if (self._recent == "left") { if (self._x < self.tX + pixTol || self.ani_smooth === 1) { self.cFlag._nd ? self.ani_end = true : ""; self._recent = ''; self._x = self.tX; } //Check if Right movement completed } else if (self._recent == "right") { if (self._x > self.tX - pixTol || self.ani_smooth === 1) { self.cFlag._nd ? self.ani_end = true : ""; self._recent = ''; self._x = self.tX; } //Check if Up movement completed } else if (self._recent == "up") { if (self._y < self.tY + pixTol || self.ani_smooth === 1) { self.cFlag._nd ? self.ani_end = true : ""; self._recent = ''; self._y = self.tY; } //Check if Down movement completed } else if (self._recent == "down") { if (self._y > self.tY - pixTol || self.ani_smooth === 1) { self.cFlag._nd ? self.ani_end = true : ""; self._recent = ''; self._y = self.tY; } //Check if Dragging completed } else if (self._recent == "drag") { if (self._x + pixTol >= self.tX && self._x - pixTol <= self.tX && self._y + pixTol >= self.tY && self._y - pixTol <= self.tY || self.ani_smooth === 1) { if (self._onfocus) { self._dragging = false; } self.cFlag._nd ? self.ani_end = true : ""; self._recent = ''; self._x = self.tX; self._y = self.tY; } } //Check if Reset completed if (self.cFlag._rs && self._w + pixTol >= (self.rA * self.iW) && self._w - pixTol <= (self.rA * self.iW) && self.oX <= self.fX + pixTol && self.oX >= self.fX - pixTol && self.oY <= self.fY + pixTol && self.oY >= self.fY - pixTol) { self.ani_end = true; self._recent = ''; self.cFlag._rs = false; self.cFlag._nd = true; self._x = self.tX; self._y = self.tY; self._sc = self.rA; self._w = self._sc * self.iW; self._h = self._sc * self.iH; } //When the image fits exactly to container size, disable the pan, zoom out and reset buttons if (self.rA == self.rF && self.iW * self.rA <= self.sW && self.iH * self.rA <= self.sH) { if (self.buttons[1].$ob.css('opacity') > .5) { if (self.rA >= self.rF && (self.init_zoom == '' || self.rA < self.init_zoom) && (self.zoom_min == '' || self.rA < self.zoom_min)) { if (self.pan_limit && self._moveCursor && !self._moveCursor) { self.$image.css('cursor', 'default'); self.$hitArea.css('cursor', 'default'); } for (var bEn = 1; bEn < (self.pan_limit && !self._moveCursor ? self.buttons_total : 2) ; bEn++) { self.buttons[bEn].$ob.css({ 'opacity': .4 }); self._wheel = false; self.$holder.find('#' + self.buttons[bEn]._var + 'norm').show(); self.$holder.find('#' + self.buttons[bEn]._var + 'over').hide(); } } } } else { if (self.buttons[1].$ob.css('opacity') < .5) { if (self._moveCursor && self.mouse_drag) { self.$image.css('cursor', 'move'); self.$hitArea.css('cursor', 'move'); } for (var bEn = 1; bEn < self.buttons_total; bEn++) { self.buttons[bEn].$ob.css('opacity', .7); } } } //When the image reaches max zoom, disable the zoom button if (self.rA == self.zoom_max) { if (self.buttons[0].$ob.css('opacity') > .5) { self.buttons[0].$ob.css('opacity', .4); self._wheel = false; self.$holder.find('#' + self.buttons[0]._var + 'norm').show(); self.$holder.find('#' + self.buttons[0]._var + 'over').hide(); } } else { if (self.buttons[0].$ob.css('opacity') < .5) { self.buttons[0].$ob.css('opacity', .7); } } //Apply Scale and position to the image: if (prop_transform) { self.$image.css(prop_transform, 'translate(' + self._x.toFixed(14) + 'px,' + self._y.toFixed(14) + 'px) scale(' + self._sc + ')'); } else { self.$image.css({ left: self._x, top: self._y, width: self._w, height: self._h }); } if (self.$loc_cont) { self.updateLocations(self._x, self._y, self._sc, self.locations); } //In case image Maps used, update them if (!prop_transform && self.map_coordinates.length > 0) { self.updateMap(); } //If the animation completed, stop running; else continue if (self.ani_end && !self._dragging && self._recent != "drag") { self._playing = false; self._recent = ''; self.cX = (-self._x + (self.sW / 2)) / self.rA; self.cY = (-self._y + (self.sH / 2)) / self.rA; if (self.onUPDATE) { self.onUPDATE(self.getZoomData(), false); } if (self.onZOOM_PAN) { self.onZOOM_PAN(self.getZoomData()); } clearTimeout(self.ani_timer); } else { self._playing = true; if (self.onUPDATE) { self.onUPDATE(self.getZoomData(), true); } self.ani_timer = setTimeout(function () { self.Animate(); }, 28); } }, /*Relocate the landmarks according to main image's position ***********************************************************************************************************************/ updateLocations: function (_x, _y, _sc, loc) { if (this.onLANDMARK !== '') { if (_sc >= this.show_at_zoom) { if (!this._landmark) { this._landmark = true this.onLANDMARK(true); } } else { if (this._landmark) { this._landmark = false; this.onLANDMARK(false); } } } for (var p = 0; p < loc.length; p++) { var wScaled, hScaled, lpx = (loc[p].x * _sc) + _x, lpy = (loc[p].y * _sc) + _y; if (_sc >= loc[p].showAt) { if (loc[p].scale && prop_transform) { wScaled = loc[p].w2pad * this._sc; hScaled = loc[p].h2 * this._sc; } else { wScaled = loc[p].w2pad; hScaled = loc[p].h2; } if (lpx > -wScaled && lpx < this.sW + wScaled && ((lpy > -hScaled && lpy < this.sH + hScaled && loc[p].lab) || (lpy > 0 && lpy < this.sH + (hScaled * 2) && !loc[p].lab))) { if (!loc[p].vis) { loc[p].vis = true; if (prop_transform) { loc[p].ob.stop() .css('display', 'block') .animate({ opacity: 1 }, 300); } else { loc[p].ob.show(); } } } else { if (loc[p].vis) { loc[p].vis = false; if (prop_transform) { loc[p].ob.stop() .animate({ opacity: 0 }, 200, function () { $(this).hide(); }); } else { loc[p].ob.hide(); } } } } else { if (loc[p].vis) { loc[p].vis = false; if (prop_transform) { loc[p].ob.stop() .animate({ opacity: 0 }, 200, function () { $(this).hide(); }); } else { loc[p].ob.hide(); } } } if (lpx !== loc[p].lpx || lpy !== loc[p].lpy && loc[p].vis) { if (prop_transform) { loc[p].ob.css(prop_transform, 'translate(' + lpx.toFixed(14) + 'px,' + lpy.toFixed(14) + 'px)' + (loc[p].scale ? ' scale(' + this._sc + ')' : '')); } else { loc[p].ob.css({ left: lpx, top: lpy }); } } loc[p].lpx = lpx; loc[p].lpy = lpy; } }, /*If the broswer doesn't supports css border radius, we need to go with old school png image for rounded corner ***********************************************************************************************************************/ roundBG: function (el, _name, _w, _h, _r, _p, _c, _i, _z, _yoff) { var w = 50 / 2; el.append($( '
\
\
\
\
\
\
' )); $('.bgi' + _name).css({ position: 'absolute', width: _r, height: _r, 'background-image': 'url(' + _i + ')', 'background-repeat': 'no-repeat', '-ms-filter': 'progid:DXImageTransform.Microsoft.gradient(startColorstr=#00FFFFFF,endColorstr=#00FFFFFF)', 'filter': 'progid:DXImageTransform.Microsoft.gradient(startColorstr=#00FFFFFF,endColorstr=#00FFFFFF)', 'zoom': 1 }); $('.bgh' + _name).css({ position: 'absolute', width: _w - _r * 2, height: _r, 'background-color': _c, left: _r }); $('.bgc' + _name).css({ position: 'absolute', width: _w, height: _h - _r * 2, 'background-color': _c, top: _r, left: 0 }); }, /*To calibrate position offset when navigation events supposed to be overlapped ***********************************************************************************************************************/ changeOffset: function (x, y) { if (x) this.oX = (this.tX - ((this.sW - this._w) / 2) - this.focusOffX) / (this._w / this.sW); if (y) this.oY = (this.tY - ((this.sH - this._h) / 2) - this.focusOffY) / (this._h / this.sH); }, /*Transform Image Maps (hot spots) if any ***********************************************************************************************************************/ updateMap: function () { var self = this, mapId = 0; self.mapAreas.each(function () { var new_vals = []; for (var i = 0; i < self.map_coordinates[mapId].length; i++) { new_vals[i] = self.map_coordinates[mapId][i] * self._sc; } new_vals = new_vals.join(","); $(this).attr('coords', new_vals); mapId++; }); }, /*To stop the timer loops immediatly ***********************************************************************************************************************/ haltAnimation: function () { clearTimeout(this.ani_timer); this._playing = false; this._recent = ''; }, /*Method to Remove the plugin instance ***********************************************************************************************************************/ destroy: function () { var self = this; if (self.assetsLoaded) { self.haltAnimation(); for (prop in self.orig_style) { if (self.orig_style[prop][0] !== false && self.orig_style[prop][0] !== undefined) { if (self.orig_style[prop][0] === 'width' || self.orig_style[prop][0] === 'height') { if (parseInt(self.orig_style[prop][1]) !== 0) { self.$image.css(self.orig_style[prop][0], self.orig_style[prop][1]); } } else { self.$image.css(self.orig_style[prop][0], self.orig_style[prop][1]); } } } clearTimeout(self.auto_timer); $(document).unbind('.sz' + self.id); $(window).unbind('.sz' + self.id); self.$holder.unbind('.sz'); self.$controls = undefined; } else { self.$image.show(); } if (self.container == '') { if (self.image_url == '') { self.$image.insertBefore(self.$holder); if (self.$holder !== undefined) { self.$holder.remove(); } } else { self.$elem.empty(); if (self.$loc_cont[0]) { self.$elem.append(self.loc_clone); } } } else { self.$image.insertBefore(self.$holder); self.$holder.empty(); self.$image.wrap(self.$holder); if (self.$loc_cont[0]) { self.$holder.append(self.loc_clone); } } self.$elem.removeData('smoothZoom'); self.$holder = undefined; self.Buttons = undefined; self.op = undefined; self.$image = undefined; }, /*Method to change focus point and level ***********************************************************************************************************************/ focusTo: function (params) { var self = this; if (self.assetsLoaded) { if (params.zoom === undefined || params.zoom === '' || params.zoom == 0) { params.zoom = self.rA; } else { params.zoom /= 100; } self._onfocus = true; if (params.zoom > self.rA && self.rA != self.zoom_max) { self.rA = params.zoom; self.rA = self.rA > self.zoom_max ? self.zoom_max : self.rA; } else if (params.zoom < self.rA && self.rA != self.rF) { self.rA = params.zoom; self.rA = self.rA < self.rF ? self.rF : self.rA; } self.transOffX = self.transOffY = 0; self.setDraggedPos(params.x === undefined || params.x === '' ? "" : (-params.x * self.rA) + (self.sW / 2), params.y === undefined || params.y === '' ? "" : (-params.y * self.rA) + (self.sH / 2), self.rA); self.reduction = params.speed ? params.speed / 10 : self.focusSpeed; self._recent = 'drag'; self._dragging = true; if (!self._playing) { self.Animate(); } } }, zoomIn: function (params) { this.buttons[0].$ob.trigger(this.event_down, { id: 0 }); }, zoomOut: function (params) { this.buttons[1].$ob.trigger(this.event_down, { id: 1 }); }, moveRight: function (params) { this.buttons[2].$ob.trigger(this.event_down, { id: 2 }); }, moveLeft: function (params) { this.buttons[3].$ob.trigger(this.event_down, { id: 3 }); }, moveUp: function (params) { this.buttons[4].$ob.trigger(this.event_down, { id: 4 }); }, moveDown: function (params) { this.buttons[5].$ob.trigger(this.event_down, { id: 5 }); }, Reset: function (params) { this.buttons[6].$ob.trigger(this.event_down, { id: 6 }); }, getZoomData: function (params) { return { //x offset (without scale ratio multiplied) normX: (-this._x / this.rA).toFixed(14), //y offset (without scale ratio multiplied) normY: (-this._y / this.rA).toFixed(14), //Original image Width normWidth: this.iW, //Original image height normHeight: this.iH, //x offset (with scale ratio multiplied) scaledX: -this._x.toFixed(14), //y offset (with scale ratio multiplied) scaledY: -this._y.toFixed(14), //Scaled image width scaledWidth: this._w, //Scaled image height scaledHeight: this._h, //The X location on image which is currently on center of canvas centerX: (-this._x.toFixed(14) + (this.sW / 2)) / this.rA, //The Y location on image which is currently on center of canvas centerY: (-this._y.toFixed(14) + (this.sH / 2)) / this.rA, //Scale ratio ratio: this.rA }; }, addLandmark: function (loc) { if (this.$loc_cont) { var total = loc.length; for (var i = 0; i < total; i++) { var $loc = $(loc[i]); this.$loc_cont.append($loc); this.setLocation($loc); } if (total > 0) { this.updateLocations(this._x, this._y, this._sc, this.locations); } } }, attachLandmark: function (loc) { if (this.$loc_cont) { var total = loc.length; for (var i = 0; i < total; i++) { this.setLocation(loc[i] instanceof jQuery ? loc[i] : $('#' + loc[i])); } if (total > 0) { this.updateLocations(this._x, this._y, this._sc, this.locations); } } }, removeLandmark: function (loc) { if (this.$loc_cont) { if (loc) { var total = loc.length; for (var i = 0; i < total; i++) { for (var j = 0; j < this.locations.length; j++) { if ((loc[i] instanceof jQuery && this.locations[j].ob[0] == loc[i][0]) || (!(loc[i] instanceof jQuery) && this.locations[j].ob.attr('id') == loc[i])) { this.locations[j].ob.remove(); this.locations.splice(j, 1); j--; } } } } else { if (this.locations.length > 0) { this.locations[this.locations.length - 1].ob.remove(); this.locations.pop(); } } if (total > 0) { this.updateLocations(this._x, this._y, this._sc, this.locations); } } }, refreshAllLandmarks: function () { var self = this; var locs = self.$loc_cont.children('.item'); self.show_at_zoom = parseInt(self.$loc_cont.data('show-at-zoom'), 10) / 100; self.allow_scale = checkBoolean(self.$loc_cont.data('allow-scale')); self.allow_drag = checkBoolean(self.$loc_cont.data('allow-drag')); //Step 1: Remove records for which the elements no longer exist for (var i = 0; i < self.locations.length; i++) { var exists = false; locs.each(function () { if (self.locations[i].ob[0] == $(this)[0]) { exists = true; } }); if (!exists) { self.locations.splice(i, 1); i--; } } //Step 2: Add new elements to record locs.each(function () { var exists = false; for (var i = 0; i < self.locations.length; i++) { if (self.locations[i].ob[0] == $(this)[0]) { exists = true; break; } } if (!exists) { self.setLocation($(this)); } }); this.updateLocations(this._x, this._y, this._sc, this.locations); }, /*On windows resize, adjust some defaults ***********************************************************************************************************************/ resize: function (e) { var self; if (e.data) { e.preventDefault(); self = e.data.self; var pw = self.$holder.parent().width(); var ph = self.$holder.parent().height(); if (self.oW) { pw = Math.min(pw, self.oW); } self.sW = pw; if (self.oH) { if (self.oW && self.maintain_ratio) { self.sH = pw / (self.oW / self.oH); } } else { self.sH = ph; } } else { self = this; if (e.width) { self.sW = e.width; } if (e.height) { self.sH = e.height; } if (e.max_WIDTH) { self.w_max = e.max_WIDTH; } if (e.max_HEIGHT) { self.h_max = e.max_HEIGHT; } } if (self.w_max !== 0 && self.w_max !== '') { self.sW = Math.min(self.sW, self.w_max); } if (self.h_max !== 0 && self.h_max !== '') { self.sH = Math.min(self.sH, self.h_max); } self.$holder.css({ 'width': self.sW, 'height': self.sH }); if (self.bord_size > 0) { self.border[0].height(self.sH); self.border[1].css({ height: self.sH, left: self.sW - self.bord_size }); self.border[2].width(self.sW - (self.bord_size * 2)); self.border[3].css({ width: self.sW - (self.bord_size * 2), top: self.sH - self.bord_size }); } if (self.bu_align[1] == 'center') { self.$controls.css('left', parseInt((self.sW - self.cBW) / 2)); } if (self.bu_align[0] == 'center') { self.$controls.css('top', parseInt((self.sH - self.cBH) / 2)); } self.rF = self.rR = self.checkRatio(self.sW, self.sH, self.iW, self.iH, self.zoom_fit); if (self.zoom_min == 0) { if (self.rA < self.rF) { self.rA = self.rF; } } self.focusTo({ x: self.cX, y: self.cY, zoom: '', speed: 10 }); } } $.fn.smoothZoom = function (params) { var self = this; var l = self.length; //For single or more than one plugin instance for (var i = 0; i < l; i++) { var $elem = $(self[i]); var instance = $elem.data('smoothZoom'); // Case 1: Initiate the plugin if not already have an instance if (!instance) { if (typeof params === 'object' || !params) { $elem.data('smoothZoom', new Zoomer($elem, params)); } // Case 2: If the instance already available - Check for method call } else { // getZoomData - Returns {sourceX, sourceY, sourceWidth, sourceHeight, distX, distY, distWidth, distHeight, centerX, centerY, ratio} if (params == "getZoomData") { return instance[params].apply(instance, Array.prototype.slice.call(arguments, 1)); // destroy | focusTo | zoomIn | zoomOut | moveRight| moveLeft | moveUp | moveDown | Reset | addLandmark | removeLandmark | attachLandmark | refreshAllLandmarks } else if (instance[params]) { instance[params].apply(instance, Array.prototype.slice.call(arguments, 1)); } } } //return for chainability for possible cases if (params !== "getZoomData") { return this; } }; function checkBoolean(_var) { if (_var === true) { return true; } else if (_var) { _var = _var.toLowerCase(); if (_var == 'yes' || _var == 'true') { return true; } } return false; } //................................................................................................................... //Using Modernizr to check browser capabilities and property names prefixed /* Modernizr 2.5.3 (Custom Build) | MIT & BSD * Build: http://www.modernizr.com/download/#-borderradius-csstransforms-csstransforms3d-touch-prefixed-teststyles-testprop-testallprops-prefixes-domprefixes */ var Modernizr = function (a, b, c) { function y(a) { i.cssText = a } function z(a, b) { return y(l.join(a + ";") + (b || "")) } function A(a, b) { return typeof a === b } function B(a, b) { return !!~("" + a).indexOf(b) } function C(a, b) { for (var d in a) if (i[a[d]] !== c) return b == "pfx" ? a[d] : !0; return !1 } function D(a, b, d) { for (var e in a) { var f = b[a[e]]; if (f !== c) return d === !1 ? a[e] : A(f, "function") ? f.bind(d || b) : f } return !1 } function E(a, b, c) { var d = a.charAt(0).toUpperCase() + a.substr(1), e = (a + " " + n.join(d + " ") + d).split(" "); return A(b, "string") || A(b, "undefined") ? C(e, b) : (e = (a + " " + o.join(d + " ") + d).split(" "), D(e, b, c)) } var d = "2.5.3", e = {}, f = b.documentElement, g = "modernizr", h = b.createElement(g), i = h.style, j, k = {}.toString, l = " -webkit- -moz- -o- -ms- ".split(" "), m = "Webkit Moz O ms", n = m.split(" "), o = m.toLowerCase().split(" "), p = {}, q = {}, r = {}, s = [], t = s.slice, u, v = function (a, c, d, e) { var h, i, j, k = b.createElement("div"), l = b.body, m = l ? l : b.createElement("body"); if (parseInt(d, 10)) while (d--) j = b.createElement("div"), j.id = e ? e[d] : g + (d + 1), k.appendChild(j); return h = ["­", ""].join(""), k.id = g, (l ? k : m).innerHTML += h, m.appendChild(k), l || (m.style.background = "", f.appendChild(m)), i = c(k, a), l ? k.parentNode.removeChild(k) : m.parentNode.removeChild(m), !!i }, w = {}.hasOwnProperty, x; !A(w, "undefined") && !A(w.call, "undefined") ? x = function (a, b) { return w.call(a, b) } : x = function (a, b) { return b in a && A(a.constructor.prototype[b], "undefined") }, Function.prototype.bind || (Function.prototype.bind = function (b) { var c = this; if (typeof c != "function") throw new TypeError; var d = t.call(arguments, 1), e = function () { if (this instanceof e) { var a = function () { }; a.prototype = c.prototype; var f = new a, g = c.apply(f, d.concat(t.call(arguments))); return Object(g) === g ? g : f } return c.apply(b, d.concat(t.call(arguments))) }; return e }); var F = function (c, d) { var f = c.join(""), g = d.length; v(f, function (c, d) { var f = b.styleSheets[b.styleSheets.length - 1], h = f ? f.cssRules && f.cssRules[0] ? f.cssRules[0].cssText : f.cssText || "" : "", i = c.childNodes, j = {}; while (g--) j[i[g].id] = i[g]; e.touch = "ontouchstart" in a || a.DocumentTouch && b instanceof DocumentTouch || (j.touch && j.touch.offsetTop) === 9, e.csstransforms3d = (j.csstransforms3d && j.csstransforms3d.offsetLeft) === 9 && j.csstransforms3d.offsetHeight === 3 }, g, d) }([, ["@media (", l.join("touch-enabled),("), g, ")", "{#touch{top:9px;position:absolute}}"].join(""), ["@media (", l.join("transform-3d),("), g, ")", "{#csstransforms3d{left:9px;position:absolute;height:3px;}}"].join("")], [, "touch", "csstransforms3d"]); p.touch = function () { return e.touch }, p.borderradius = function () { return E("borderRadius") }, p.csstransforms = function () { return !!E("transform") }, p.csstransforms3d = function () { var a = !!E("perspective"); return a && "webkitPerspective" in f.style && (a = e.csstransforms3d), a }; for (var G in p) x(p, G) && (u = G.toLowerCase(), e[u] = p[G](), s.push((e[u] ? "" : "no-") + u)); return y(""), h = j = null, e._version = d, e._prefixes = l, e._domPrefixes = o, e._cssomPrefixes = n, e.testProp = function (a) { return C([a]) }, e.testAllProps = E, e.testStyles = v, e.prefixed = function (a, b, c) { return b ? E(a, b, c) : E(a, "pfx") }, e }(this, this.document); var prop_transform = Modernizr.prefixed('transform'); var prop_origin = Modernizr.prefixed('transformOrigin'); var prop_radius = Modernizr.prefixed('borderRadius'); var supportsTrans3D = Modernizr.csstransforms3d; var supportsTouch = Modernizr.touch; })(jQuery, window, document); //End - smoothZoom //................................................................................................................... //For mouse wheel support /*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net) * Licensed under the MIT License (LICENSE.txt). * * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers. * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix. * Thanks to: Seamus Leahy for adding deltaX and deltaY * * Version: 3.0.6 * * Requires: 1.2.2+ */ (function (a) { function d(b) { var c = b || window.event, d = [].slice.call(arguments, 1), e = 0, f = !0, g = 0, h = 0; return b = a.event.fix(c), b.type = "mousewheel", c.wheelDelta && (e = c.wheelDelta / 120), c.detail && (e = -c.detail / 3), h = e, c.axis !== undefined && c.axis === c.HORIZONTAL_AXIS && (h = 0, g = -1 * e), c.wheelDeltaY !== undefined && (h = c.wheelDeltaY / 120), c.wheelDeltaX !== undefined && (g = -1 * c.wheelDeltaX / 120), d.unshift(b, e, g, h), (a.event.dispatch || a.event.handle).apply(this, d) } var b = ["DOMMouseScroll", "mousewheel"]; if (a.event.fixHooks) for (var c = b.length; c;) a.event.fixHooks[b[--c]] = a.event.mouseHooks; a.event.special.mousewheel = { setup: function () { if (this.addEventListener) for (var a = b.length; a;) this.addEventListener(b[--a], d, !1); else this.onmousewheel = d }, teardown: function () { if (this.remove_eventListener) for (var a = b.length; a;) this.remove_eventListener(b[--a], d, !1); else this.onmousewheel = null } }, a.fn.extend({ mousewheel: function (a) { return a ? this.bind("mousewheel", a) : this.trigger("mousewheel") }, unmousewheel: function (a) { return this.unbind("mousewheel", a) } }) })(jQuery) //...................................................................................................................