/**
* ___ ___ _____ ______ __ __ _____ ______ __ __
* | \/ |/ _ \ / __ \| \ | |_ _| ___| \ / |
* | | / \ | | \__| \| | | | | |__ \ \/ /
* | |\/| | |_| | | ___ | | | | __| \ /
* | | | | _ | \_/ | |\ |_| |_| | | |
* |__| |__|__| |__|\____/|_|__| \__|_____|__| |__|
*
* 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 = '
\
\
\
![]()
\
\
\
';
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;
}
});