/** * ___ ___ _____ ______ __ __ _____ ______ __ __ * | \/ |/ _ \ / __ \| \ | |_ _| ___| \ / | * | | / \ | | \__| \| | | | | |__ \ \/ / * | |\/| | |_| | | ___ | | | | __| \ / * | | | | _ | \_/ | |\ |_| |_| | | | * |__| |__|__| |__|\____/|_|__| \__|_____|__| |__| * * jquery.magnify.js v0.1.0 * * A jQuery plugin to view images just like in windows * * Copyright (c) 2017 nzbin * * Released under the MIT License * * */ ; (function(factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as anonymous module. define(['jquery'], factory); } else if (typeof exports === 'object') { // Node / CommonJS factory(require('jquery')); } else { // Browser globals. factory(jQuery); } })(function($) { 'use strict'; /** * Private vars */ var $W = $(window), $D = $(document), // plugin default options defaults = { draggable: true, resizable: true, movable: true, keyboard: true, title: true, modalWidth: 320, modalHeight: 320, fixedContent: false, fixedModalSize: false, initMaximized: false, gapThreshold: 0.02, ratioThreshold: 0.1, minRatio: 0.1, maxRatio: 16, toolbar: ['zoomIn', 'zoomOut', 'prev', 'fullscreen', 'next', 'actualSize', 'rotateRight'], icons: { maximize: 'fa fa-window-maximize', close: 'fa fa-close', zoomIn: 'fa fa-search-plus', zoomOut: 'fa fa-search-minus', prev: 'fa fa-arrow-left', next: 'fa fa-arrow-right', fullscreen: 'fa fa-photo', actualSize: 'fa fa-arrows-alt', rotateLeft: 'fa fa-rotate-left', rotateRight: 'fa fa-rotate-right' }, lang: 'en', i18n: {}, // beforeOpen:$.noop, // afterOpen:$.noop, // beforeClose:$.noop, // afterClose:$.noop }, // jquery element of calling plugin jqEl = null, // image moving flag isMoving = false, // modal resizing flag isResizing = false, // modal open flag isOpened = false, // modal maximize flag isMaximized = false, // image rotate 90*(2n+1) flag isRotated = false, // image rotate angle rotateAngle = 0; /** * Magnify Class */ var Magnify = function (el, options) { var self = this; this.options = $.extend(true, {}, defaults, options); if (options && $.isArray(options.toolbar)) { this.options.toolbar = options.toolbar; } this.init(el, self.options); this.isOpened = false; this.isMaximized = false; this.isRotated = false; this.rotateAngle = 0; // Store image data in every instance // this.imageData = {}; } /** * Mangify prototype */ Magnify.prototype = { init: function (el, options) { this.open(); // Get image src var imgSrc = this.getImgSrc(el); // Get image group this.groupName = null; var currentGroupName = $(el).attr('data-group'), groupList = $D.find('[data-group="' + currentGroupName + '"]'); if (currentGroupName !== undefined) { this.groupName = currentGroupName; this.getImgGroup(groupList, imgSrc); } else { this.getImgGroup(jqEl.not('[data-group]'), imgSrc); } this.loadImg(imgSrc); }, creatBtns: function (btns) { var footerBtnsStr = ''; $.each(this.options.toolbar, function (index, item) { footerBtnsStr += btns[item]; }); return footerBtnsStr; }, creatDOM: function () { var btnsTpl = { maximize: '', close: '', zoomIn: '', zoomOut: '', prev: '', next: '', fullscreen: '', actualSize: '', rotateLeft: '', rotateRight: '' } // magnify base HTML var magnifyHTML = '
\
\
\
' + btnsTpl.maximize + btnsTpl.close + '
\
\
\ \
\ \
'; return magnifyHTML; }, open: function () { // Fixed modal position bug if (!$('.magnify-modal').length) { $('html').css('overflow', 'hidden'); } this.isOpened = isOpened = true; this.build(); this.addEvent(); this.resize(); }, build: function () { // Create magnify HTML string var magnifyHTML = this.creatDOM(); // Make magnify HTML string to jQuery element var $magnify = $(magnifyHTML); // Get all magnify element this.$magnify = $magnify; this.$stage = $magnify.find('.magnify-stage'); this.$title = $magnify.find('.magnify-title'); this.$image = $magnify.find('.magnify-stage img'); this.$close = $magnify.find('.magnify-button-close'); this.$maximize = $magnify.find('.magnify-button-maximize'); this.$zoomIn = $magnify.find('.magnify-button-zoom-in'); this.$zoomOut = $magnify.find('.magnify-button-zoom-out'); this.$actualSize = $magnify.find('.magnify-button-actual-size'); this.$fullscreen = $magnify.find('.magnify-button-fullscreen'); this.$rotateLeft = $magnify.find('.magnify-button-rotate-left'); this.$rotateRight = $magnify.find('.magnify-button-rotate-right'); this.$prev = $magnify.find('.magnify-button-prev'); this.$next = $magnify.find('.magnify-button-next'); $('body').append($magnify); this.setModalPos($magnify); // draggable & movable & resizable if (this.options.draggable) { this.draggable($magnify); } if (this.options.movable) { this.movable(this.$image, this.$stage); } if (this.options.resizable) { this.resizable($magnify, this.$stage, this.$image, this.options.modalWidth, this.options.modalHeight); } }, close: function (el) { // off events // Remove instance this.$magnify.remove(); this.isMaximized = false; this.isRotated = false; this.rotateAngle = rotateAngle = 0; this.isOpened = isOpened = false; // Fixed modal position bug if (!$('.magnify-modal').length) { $('html').css('overflow', 'auto'); } }, setModalPos: function (modal) { var winWidth = $W.width(), winHeight = $W.height(), scrollLeft = $D.scrollLeft(), scrollTop = $D.scrollTop(); var modalWidth = modal.width(), modalHeight = modal.height(); // Make the modal in windows center modal.css({ left: (winWidth - modalWidth) / 2 + scrollLeft + 'px', top: (winHeight - modalHeight) / 2 + scrollTop + 'px' }); }, setModalSize: function (img) { var winWidth = $W.width(), winHeight = $W.height(), scrollLeft = $D.scrollLeft(), scrollTop = $D.scrollTop(); // stage css value var stageCSS = { left: this.$stage.css('left'), right: this.$stage.css('right'), top: this.$stage.css('top'), bottom: this.$stage.css('bottom'), borderLeft: this.$stage.css('border-left-width'), borderRight: this.$stage.css('border-right-width'), borderTop: this.$stage.css('border-top-width'), borderBottom: this.$stage.css('border-bottom-width'), }; // Modal size should calc with stage css value var modalWidth = img.width + getNumFromCSSValue(stageCSS.left) + getNumFromCSSValue(stageCSS.right) + getNumFromCSSValue(stageCSS.borderLeft) + getNumFromCSSValue(stageCSS.borderRight), modalHeight = img.height + getNumFromCSSValue(stageCSS.top) + getNumFromCSSValue(stageCSS.bottom) + getNumFromCSSValue(stageCSS.borderTop) + getNumFromCSSValue(stageCSS.borderBottom); var gapThreshold = (this.options.gapThreshold > 0 ? this.options.gapThreshold : 0) + 1, // modal scale to window scale = Math.min(winWidth / (modalWidth * gapThreshold), winHeight / (modalHeight * gapThreshold), 1); var minWidth = Math.max(modalWidth * scale, this.options.modalWidth), minHeight = Math.max(modalHeight * scale, this.options.modalHeight); minWidth = this.options.fixedModalSize ? this.options.modalWidth : Math.ceil(minWidth); minHeight = this.options.fixedModalSize ? this.options.modalHeight : Math.ceil(minHeight); this.$magnify.css({ width: minWidth + 'px', height: minHeight + 'px', left: (winWidth - minWidth) / 2 + scrollLeft + 'px', top: (winHeight - minHeight) / 2 + scrollTop + 'px' }); this.setImageSize(img); }, setImageSize: function (img) { var stageData = { w: this.$stage.width(), h: this.$stage.height() } // image scale to stage var scale = 1; if (!this.isRotated) { scale = Math.min(stageData.w / img.width, stageData.h / img.height, 1); } else { scale = Math.min(stageData.w / img.height, stageData.h / img.width, 1); } this.$image.css({ width: Math.ceil(img.width * scale) + 'px', height: Math.ceil(img.height * scale) + 'px', left: (stageData.w - img.width * scale) / 2 + 'px', top: (stageData.h - img.height * scale) / 2 + 'px' }); // Store image initial data $.extend(this.imageData, { width: img.width * scale, height: img.height * scale, left: (stageData.w - img.width * scale) / 2, top: (stageData.h - img.height * scale) / 2 }); }, loadImg: function (imgSrc) { var self = this; this.$image.attr('src', imgSrc); preloadImg(imgSrc, function (img) { // Store original data self.imageData = { originalWidth: img.width, originalHeight: img.height } if (self.isMaximized) { self.setImageSize(img); } else { self.setModalSize(img); } }); if (this.options.title) { this.setImgTitle(imgSrc); } }, getImgGroup: function (list, imgSrc) { var self = this; self.groupData = []; $(list).each(function (index, item) { var src = self.getImgSrc(this); self.groupData.push({ src: src, caption: $(this).attr('data-caption') }); // Get image index if (imgSrc === src) { self.groupIndex = index } }); }, setImgTitle: function (url) { var index = this.groupIndex, caption = this.groupData[index].caption, caption = caption ? caption : getImageNameFromUrl(url); this.$title.text(caption); }, getImgSrc: function (el) { // Get data-src as image src at first var src = $(el).attr('data-src') ? $(el).attr('data-src') : $(el).attr('href'); return src; }, jump: function (index) { this.groupIndex = this.groupIndex + index; this.jumpTo(this.groupIndex); }, jumpTo: function (index) { index = index % this.groupData.length; if (index >= 0) { index = index % this.groupData.length; } else if (index < 0) { index = (this.groupData.length + index) % this.groupData.length; } this.groupIndex = index; this.loadImg(this.groupData[index].src); }, wheel: function (e) { e.preventDefault(); var delta = 1; if (e.originalEvent.deltaY) { delta = e.originalEvent.deltaY > 0 ? 1 : -1; } else if (e.originalEvent.wheelDelta) { delta = -e.originalEvent.wheelDelta / 120; } else if (e.originalEvent.detail) { delta = e.originalEvent.detail > 0 ? 1 : -1; } // ratio threshold var ratio = -delta * this.options.ratioThreshold; // mouse point position var pointer = { x: e.originalEvent.clientX - this.$stage.offset().left, y: e.originalEvent.clientY - this.$stage.offset().top } this.zoom(ratio, pointer, e); }, zoom: function (ratio, origin, e) { // zoom out & zoom in ratio = ratio < 0 ? (1 / (1 - ratio)) : (1 + ratio); if (ratio > 0.95 && ratio < 1.05) { ratio = 1; } ratio = this.$image.width() / this.imageData.originalWidth * ratio; // min image size ratio = Math.max(ratio, this.options.minRatio); // max image size ratio = Math.min(ratio, this.options.maxRatio); this.zoomTo(ratio, origin, e); }, zoomTo: function (ratio, origin, e) { var $image = this.$image, $stage = this.$stage, imgData = { w: this.imageData.width, h: this.imageData.height, x: this.imageData.left, y: this.imageData.top }; // image stage position // We will use it to calc the relative position of image var stageData = { w: $stage.width(), h: $stage.height(), x: $stage.offset().left, y: $stage.offset().top } var newWidth = this.imageData.originalWidth * ratio, newHeight = this.imageData.originalHeight * ratio, // Think about it for a while ~~~ newLeft = origin.x - (origin.x - imgData.x) / imgData.w * newWidth, newTop = origin.y - (origin.y - imgData.y) / imgData.h * newHeight; var offsetX = stageData.w - newWidth, offsetY = stageData.h - newHeight, // Get the offsets when image rotate 90 deg offsetX2 = stageData.w - (newWidth + newHeight) / 2, offsetY2 = stageData.h - (newWidth + newHeight) / 2; // zoom out & zoom in condition // It's important and it takes me a lot of time to get it if (!this.isRotated) { if (newHeight <= stageData.h) { newTop = (stageData.h - newHeight) / 2; } else { newTop = newTop > 0 ? 0 : (newTop > offsetY ? newTop : offsetY); } if (newWidth <= stageData.w) { newLeft = (stageData.w - newWidth) / 2; } else { newLeft = newLeft > 0 ? 0 : (newLeft > offsetX ? newLeft : offsetX); } } else { // The conditions bellow drive me crazy alomst! if (newWidth <= stageData.h) { newTop = (stageData.h - newHeight) / 2; } else { newTop = newTop > (newWidth - newHeight) / 2 ? (newWidth - newHeight) / 2 : (newTop > offsetY2 ? newTop : offsetY2); } if (newHeight <= stageData.w) { newLeft = (stageData.w - newWidth) / 2; } else { newLeft = newLeft > (newHeight - newWidth) / 2 ? (newHeight - newWidth) / 2 : (newLeft > offsetX2 ? newLeft : offsetX2); } } $image.css({ width: Math.ceil(newWidth) + 'px', height: Math.ceil(newHeight) + 'px', left: newLeft + 'px', top: newTop + 'px' }); // Update image initial data $.extend(this.imageData, { width: newWidth, height: newHeight, left: newLeft, top: newTop }); }, rotate: function (angle) { this.rotateAngle = rotateAngle = rotateAngle + angle; if ((rotateAngle / 90) % 2 === 0) { this.isRotated = false; } else { this.isRotated = true; } this.rotateTo(rotateAngle); }, rotateTo: function (angle) { var self = this; this.$image.css({ transform: 'rotate(' + angle + 'deg)' }); this.setImageSize({ width: this.imageData.originalWidth, height: this.imageData.originalHeight }); }, resize: function () { var self = this; var resizeHandler = throttle(function(){ if (isOpened) { if (!self.isMaximized) { self.setModalSize({ width: self.imageData.originalWidth, height: self.imageData.originalHeight }); } self.setImageSize({ width: self.imageData.originalWidth, height: self.imageData.originalHeight }); } }, 500); $W.off('resize').on('resize', resizeHandler); }, maximize: function () { var self = this; var scrollLeft = $D.scrollLeft(), scrollTop = $D.scrollTop(); if (!this.isMaximized) { // Store modal data before maximize this.modalData = { width: this.$magnify.width(), height: this.$magnify.height(), left: this.$magnify.offset().left, top: this.$magnify.offset().top } this.$magnify.addClass('magnify-maximize'); this.$magnify.css({ width: '100%', height: '100%', left: scrollLeft, top: scrollTop }); this.isMaximized = true; } else { this.$magnify.removeClass('magnify-maximize'); this.$magnify.css({ width: this.modalData.width, height: this.modalData.height, left: this.modalData.left, top: this.modalData.top }); this.isMaximized = false; } this.setImageSize({ width: this.imageData.originalWidth, height: this.imageData.originalHeight }); }, fullscreen: function () { requestFullscreen(this.$magnify[0]); }, keydown: function (e) { e.preventDefault(); var self = this; if (!this.options.keyboard) { return false; } var keyCode = e.keyCode || e.which || e.charCode, ctrlKey = e.ctrlKey || e.metaKey, altKey = e.altKey || e.metaKey; switch (keyCode) { // ← case 37: self.jump(-1); break; // → case 39: self.jump(1); break; // + case 187: self.zoom(self.options.ratioThreshold * 3, { x: self.$stage.width() / 2, y: self.$stage.height() / 2 }, e); break; // - case 189: self.zoom(-self.options.ratioThreshold * 3, { x: self.$stage.width() / 2, y: self.$stage.height() / 2 }, e); break; // + Firefox case 61: self.zoom(self.options.ratioThreshold * 3, { x: self.$stage.width() / 2, y: self.$stage.height() / 2 }, e); break; // - Firefox case 173: self.zoom(-self.options.ratioThreshold * 3, { x: self.$stage.width() / 2, y: self.$stage.height() / 2 }, e); break; // ctrl + alt + 0 case 48: if (ctrlKey && altKey) { self.zoomTo(1, { x: self.$stage.width() / 2, y: self.$stage.height() / 2 }, e); } break; // ctrl + , case 188: if (ctrlKey) { self.rotate(-90); } break; // ctrl + . case 190: if (ctrlKey) { self.rotate(90); } break; default: } }, addEvent: function () { var self = this; this.$close.off('click').on('click', function (e) { self.close(); }); this.$stage.off('wheel mousewheel DOMMouseScroll').on('wheel mousewheel DOMMouseScroll', function (e) { self.wheel(e); }); this.$zoomIn.off('click').on('click', function (e) { self.zoom(self.options.ratioThreshold * 3, { x: self.$stage.width() / 2, y: self.$stage.height() / 2 }, e); }); this.$zoomOut.off('click').on('click', function (e) { self.zoom(-self.options.ratioThreshold * 3, { x: self.$stage.width() / 2, y: self.$stage.height() / 2 }, e); }); this.$actualSize.off('click').on('click', function (e) { self.zoomTo(1, { x: self.$stage.width() / 2, y: self.$stage.height() / 2 }, e); }); this.$prev.off('click').on('click', function () { self.jump(-1); }); this.$fullscreen.off('click').on('click', function () { self.fullscreen(); }); this.$next.off('click').on('click', function () { self.jump(1); }); this.$rotateLeft.off('click').on('click', function () { self.rotate(-90); }); this.$rotateRight.off('click').on('click', function () { self.rotate(90); }); this.$maximize.off('click').on('click', function () { self.maximize(); }); $D.off('keydown').on('keydown', function (e) { self.keydown(e); }); } } /** * Public static functions */ $.magnify = { instance: Magnify.prototype } $.fn.magnify = function (options) { jqEl = $(this); if (typeof options === 'string') { // $(this).data('magnify')[options](); } else { jqEl.off('click.magnify').on('click.magnify', function (e) { if (e.isDefaultPrevented()) { return; } e.preventDefault(); $(this).data('magnify', new Magnify(this, options)); }); } return jqEl; } /** * MAGNIFY DATA-API */ $D.on('click.magnify', '[data-magnify]', function (e) { jqEl = $('[data-magnify]'); if (e.isDefaultPrevented()) { return; } e.preventDefault(); $(this).data('magnify', new Magnify(this, {})); }); /** * draggable */ // modal draggable var draggable = function(modal) { var self = this; var isDragging = false; var startX = 0, startY = 0, left = 0, top = 0; var dragStart = function(e) { var e = e || window.event; e.preventDefault(); // Get clicked button var elemCancel = $(e.target).closest('.magnify-button'); // Stop modal moving when click buttons if(elemCancel.length){ return true; } isDragging = true; startX = e.clientX; startY = e.clientY; left = $(modal).offset().left; top = $(modal).offset().top; } var dragMove = function(e) { var e = e || window.event; e.preventDefault(); if (isDragging && !isMoving && !isResizing && !self.isMaximized) { var endX = e.clientX, endY = e.clientY, relativeX = endX - startX, relativeY = endY - startY; $(modal).css({ left: relativeX + left + 'px', top: relativeY + top + 'px' }); } return false; } var dragEnd = function(e) { isDragging = false; } $(modal).on('mousedown', dragStart); $D.on('mousemove', dragMove); $D.on('mouseup', dragEnd); } // Add to Magnify Prototype $.extend(Magnify.prototype, { draggable: draggable }); /** * image movable * -------------------------------------- * 1.no movable * 2.vertical movable * 3.horizontal movable * 4.vertical & horizontal movable * -------------------------------------- */ var movable = function(image, stage) { var self = this; var isDragging = false; var startX = 0, startY = 0, left = 0, top = 0, widthDiff = 0, heightDiff = 0, δ = 0; var dragStart = function(e) { var e = e || window.event; e.preventDefault(); var imageWidth = $(image).width(), imageHeight = $(image).height(), stageWidth = $(stage).width(), stageHeight = $(stage).height(); isDragging = true; isMoving = true; startX = e.clientX; startY = e.clientY; // δ is the difference between image width and height δ = !self.isRotated ? 0 : (imageWidth - imageHeight) / 2; // Width or height difference can be use to limit image right or top position widthDiff = !self.isRotated ? (imageWidth - stageWidth) : (imageHeight - stageWidth); heightDiff = !self.isRotated ? (imageHeight - stageHeight) : (imageWidth - stageHeight); // Reclac the element position when mousedown // Fixed the issue of stage with a border left = $(image).position().left - δ; top = $(image).position().top + δ; } var dragMove = function(e) { var e = e || window.event; e.preventDefault(); if (isDragging) { var endX = e.clientX, endY = e.clientY, relativeX = endX - startX, relativeY = endY - startY, newLeft = relativeX + left, newTop = relativeY + top; // vertical limit if (heightDiff > 0) { if ((relativeY + top) > δ) { newTop = δ; } else if ((relativeY + top) < -heightDiff + δ) { newTop = -heightDiff + δ; } } else { newTop = top; } // horizontal limit if (widthDiff > 0) { if ((relativeX + left) > -δ) { newLeft = -δ; } else if ((relativeX + left) < -widthDiff - δ) { newLeft = -widthDiff - δ; } } else { newLeft = left; } $(image).css({ left: newLeft + 'px', top: newTop + 'px', }); // Update image initial data $.extend(self.imageData, { left: newLeft, top: newTop }); } return false; } var dragEnd = function(e) { isDragging = false; isMoving = false; } $(image).on('mousedown', dragStart); $D.on('mousemove', dragMove); $D.on('mouseup', dragEnd); } // Add to Magnify Prototype $.extend(Magnify.prototype, { movable: movable }); /** * resizable * ------------------------------ * 1.modal resizable * 2.keep image in stage center * ------------------------------ */ var resizable = function(modal, stage, image, minWidth, minHeight) { var self = this; var resizableHandleE = $('
'), resizableHandleW = $('
'), resizableHandleS = $('
'), resizableHandleN = $('
'), resizableHandleSE = $('
'), resizableHandleSW = $('
'), resizableHandleNE = $('
'), resizableHandleNW = $('
'); var resizableHandles = { 'e': resizableHandleE, 's': resizableHandleS, 'se': resizableHandleSE, 'n': resizableHandleN, 'w': resizableHandleW, 'nw': resizableHandleNW, 'ne': resizableHandleNE, 'sw': resizableHandleSW, } $(modal).append( resizableHandleE, resizableHandleW, resizableHandleS, resizableHandleN, resizableHandleSE, resizableHandleSW, resizableHandleNE, resizableHandleNW ); var isDragging = false; var draggingLimit = false; var startX = 0, startY = 0, modalData = { w: 0, h: 0, l: 0, t: 0 }, stageData = { w: 0, h: 0, l: 0, t: 0 }, imageData = { w: 0, h: 0, l: 0, t: 0 }; var direction = ''; // modal CSS options var getModalOpts = function(dir, offsetX, offsetY) { // Modal should not move when its width to the minwidth var modalLeft = (-offsetX + modalData.w) > minWidth ? (offsetX + modalData.l) : (modalData.l + modalData.w - minWidth), modalTop = (-offsetY + modalData.h) > minHeight ? (offsetY + modalData.t) : (modalData.t + modalData.h - minHeight); var opts = { 'e': { width: Math.max((offsetX + modalData.w), minWidth) + 'px', }, 's': { height: Math.max((offsetY + modalData.h), minHeight) + 'px' }, 'se': { width: Math.max((offsetX + modalData.w), minWidth) + 'px', height: Math.max((offsetY + modalData.h), minHeight) + 'px' }, 'w': { width: Math.max((-offsetX + modalData.w), minWidth) + 'px', left: modalLeft + 'px' }, 'n': { height: Math.max((-offsetY + modalData.h), minHeight) + 'px', top: modalTop + 'px' }, 'nw': { width: Math.max((-offsetX + modalData.w), minWidth) + 'px', height: Math.max((-offsetY + modalData.h), minHeight) + 'px', top: modalTop + 'px', left: modalLeft + 'px' }, 'ne': { width: Math.max((offsetX + modalData.w), minWidth) + 'px', height: Math.max((-offsetY + modalData.h), minHeight) + 'px', top: modalTop + 'px' }, 'sw': { width: Math.max((-offsetX + modalData.w), minWidth) + 'px', height: Math.max((offsetY + modalData.h), minHeight) + 'px', left: modalLeft + 'px' } }; return opts[dir]; } // image CSS options var getImageOpts = function(dir, offsetX, offsetY) { // δ is the difference between image width and height var δ = !self.isRotated ? 0 : (imageData.w - imageData.h) / 2, imgWidth = !self.isRotated ? imageData.w : imageData.h, imgHeight = !self.isRotated ? imageData.h : imageData.w; // Image should not move when modal width to the min width // The minwidth is modal width, so we should clac the stage minwidth var widthDiff = (offsetX + modalData.w) > minWidth ? (stageData.w - imgWidth + offsetX - δ) : (minWidth - (modalData.w - stageData.w) - imgWidth - δ), heightDiff = (offsetY + modalData.h) > minHeight ? (stageData.h - imgHeight + offsetY + δ) : (minHeight - (modalData.h - stageData.h) - imgHeight + δ), widthDiff2 = (-offsetX + modalData.w) > minWidth ? (stageData.w - imgWidth - offsetX - δ) : (minWidth - (modalData.w - stageData.w) - imgWidth - δ), heightDiff2 = (-offsetY + modalData.h) > minHeight ? (stageData.h - imgHeight - offsetY + δ) : (minHeight - (modalData.h - stageData.h) - imgHeight + δ); // Get image position in dragging var imgLeft = $(image).position().left - δ, imgTop = $(image).position().top + δ; var opts = { 'e': { left: widthDiff >= -δ ? ((widthDiff - δ) / 2 + 'px') : (imgLeft > widthDiff ? (imgLeft + 'px') : (widthDiff + 'px')) }, 's': { top: heightDiff >= δ ? ((heightDiff + δ) / 2 + 'px') : (imgTop > heightDiff ? (imgTop + 'px') : (heightDiff + 'px')) }, 'se': { top: heightDiff >= δ ? ((heightDiff + δ) / 2 + 'px') : (imgTop > heightDiff ? (imgTop + 'px') : (heightDiff + 'px')), left: widthDiff >= -δ ? ((widthDiff - δ) / 2 + 'px') : (imgLeft > widthDiff ? (imgLeft + 'px') : (widthDiff + 'px')) }, 'w': { left: widthDiff2 >= -δ ? ((widthDiff2 - δ) / 2 + 'px') : (imgLeft > widthDiff2 ? (imgLeft + 'px') : (widthDiff2 + 'px')) }, 'n': { top: heightDiff2 >= δ ? ((heightDiff2 + δ) / 2 + 'px') : (imgTop > heightDiff2 ? (imgTop + 'px') : (heightDiff2 + 'px')) }, 'nw': { top: heightDiff2 >= δ ? ((heightDiff2 + δ) / 2 + 'px') : (imgTop > heightDiff2 ? (imgTop + 'px') : (heightDiff2 + 'px')), left: widthDiff2 >= -δ ? ((widthDiff2 - δ) / 2 + 'px') : (imgLeft > widthDiff2 ? (imgLeft + 'px') : (widthDiff2 + 'px')) }, 'ne': { top: heightDiff2 >= δ ? ((heightDiff2 + δ) / 2 + 'px') : (imgTop > heightDiff2 ? (imgTop + 'px') : (heightDiff2 + 'px')), left: widthDiff >= -δ ? ((widthDiff - δ) / 2 + 'px') : (imgLeft > widthDiff ? (imgLeft + 'px') : (widthDiff + 'px')) }, 'sw': { top: heightDiff >= δ ? ((heightDiff + δ) / 2 + 'px') : (imgTop > heightDiff ? (imgTop + 'px') : (heightDiff + 'px')), left: widthDiff2 >= -δ ? ((widthDiff2 - δ) / 2 + 'px') : (imgLeft > widthDiff2 ? (imgLeft + 'px') : (widthDiff2 + 'px')) } }; return opts[dir]; } var dragStart = function(dir, e) { var e = e || window.event; e.preventDefault(); isDragging = true; isResizing = true; startX = e.clientX; startY = e.clientY; // Reclac the modal data when mousedown modalData = { w: $(modal).width(), h: $(modal).height(), l: $(modal).offset().left, t: $(modal).offset().top }; stageData = { w: $(stage).width(), h: $(stage).height(), l: $(stage).offset().left, t: $(stage).offset().top }; imageData = { w: $(image).width(), h: $(image).height(), l: $(image).position().left, t: $(image).position().top }; direction = dir; } var dragMove = function(e) { var e = e || window.event; e.preventDefault(); if (isDragging && !self.isMaximized) { var endX = e.clientX, endY = e.clientY, relativeX = endX - startX, relativeY = endY - startY; var modalOpts = getModalOpts(direction, relativeX, relativeY); $(modal).css(modalOpts); // Limit dragging speed to prevent drag too fast // ? // if (draggingLimit) { // return false; // } // draggingLimit = true; // setTimeout(function() { // draggingLimit = false; // }, 50); var imageOpts = getImageOpts(direction, relativeX, relativeY); $(image).css(imageOpts); } return false; } var dragEnd = function(e) { isDragging = false; isResizing = false; } $.each(resizableHandles, function(dir, handle) { handle.on('mousedown', function(e) { dragStart(dir, e); }); }); $D.on('mousemove', dragMove); $D.on('mouseup', dragEnd); } // Add to Magnify Prototype $.extend(Magnify.prototype, { resizable: resizable }); /** * Private functions */ /** * [throttle] * @param {Function} fn [description] * @param {[Number]} delay [description] * @return {Function} [description] */ function throttle(fn, delay) { var timer = null; return function() { var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function() { fn.apply(context, args); }, delay); }; }; /** * [preloadImg] * @param {[String]} src [image src] * @param {Function} fn [callbacks] */ function preloadImg(src, fn) { var img = new Image(); if (!!window.ActiveXObject) { img.onreadystatechange = function() { if (this.readyState == 'complete') { fn(img); } } } else { img.onload = function() { fn(img); } } img.src = src; } /** * [requestFullscreen description] * @param {[type]} element [description] */ function requestFullscreen(element) { if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); } } /** * [exitFullscreen description] */ function exitFullscreen() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } } /** * [getImageNameFromUrl] * @param {[String]} url [description] * @return {[String]} [description] */ function getImageNameFromUrl(url) { var reg = /^.*?\/*([^/?]*)\.[a-z]+(\?.+|$)/ig, txt = url.replace(reg, '$1'); return txt; } /** * [getNumFromCSSValue description] * @param {[type]} value [description] * @return {[type]} [description] */ function getNumFromCSSValue(value) { var reg = /\d+/g, arr = value.match(reg), num = parseFloat(arr[0]); return num; } });