| /* |
| * imgAreaSelect jQuery plugin |
| * version 0.9.6 |
| * |
| * Copyright (c) 2008-2011 Michal Wojciechowski (odyniec.net) |
| * |
| * Dual licensed under the MIT (MIT-LICENSE.txt) |
| * and GPL (GPL-LICENSE.txt) licenses. |
| * |
| * http://odyniec.net/projects/imgareaselect/ |
| * |
| */ |
| |
| (function($) { |
| |
| var abs = Math.abs, |
| max = Math.max, |
| min = Math.min, |
| round = Math.round; |
| |
| function div() { |
| return $('<div/>'); |
| } |
| |
| $.imgAreaSelect = function (img, options) { |
| var |
| |
| $img = $(img), |
| |
| imgLoaded, |
| |
| $box = div(), |
| $area = div(), |
| $border = div().add(div()).add(div()).add(div()), |
| $outer = div().add(div()).add(div()).add(div()), |
| $handles = $([]), |
| |
| $areaOpera, |
| |
| left, top, |
| |
| imgOfs = { left: 0, top: 0 }, |
| |
| imgWidth, imgHeight, |
| |
| $parent, |
| |
| parOfs = { left: 0, top: 0 }, |
| |
| zIndex = 0, |
| |
| position = 'absolute', |
| |
| startX, startY, |
| |
| scaleX, scaleY, |
| |
| resizeMargin = 10, |
| |
| resize, |
| |
| minWidth, minHeight, maxWidth, maxHeight, |
| |
| aspectRatio, |
| |
| shown, |
| |
| x1, y1, x2, y2, |
| |
| selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 }, |
| |
| docElem = document.documentElement, |
| |
| $p, d, i, o, w, h, adjusted; |
| |
| function viewX(x) { |
| return x + imgOfs.left - parOfs.left; |
| } |
| |
| function viewY(y) { |
| return y + imgOfs.top - parOfs.top; |
| } |
| |
| function selX(x) { |
| return x - imgOfs.left + parOfs.left; |
| } |
| |
| function selY(y) { |
| return y - imgOfs.top + parOfs.top; |
| } |
| |
| function evX(event) { |
| return event.pageX - parOfs.left; |
| } |
| |
| function evY(event) { |
| return event.pageY - parOfs.top; |
| } |
| |
| function getSelection(noScale) { |
| var sx = noScale || scaleX, sy = noScale || scaleY; |
| |
| return { x1: round(selection.x1 * sx), |
| y1: round(selection.y1 * sy), |
| x2: round(selection.x2 * sx), |
| y2: round(selection.y2 * sy), |
| width: round(selection.x2 * sx) - round(selection.x1 * sx), |
| height: round(selection.y2 * sy) - round(selection.y1 * sy) }; |
| } |
| |
| function setSelection(x1, y1, x2, y2, noScale) { |
| var sx = noScale || scaleX, sy = noScale || scaleY; |
| |
| selection = { |
| x1: round(x1 / sx || 0), |
| y1: round(y1 / sy || 0), |
| x2: round(x2 / sx || 0), |
| y2: round(y2 / sy || 0) |
| }; |
| |
| selection.width = selection.x2 - selection.x1; |
| selection.height = selection.y2 - selection.y1; |
| } |
| |
| function adjust() { |
| if (!$img.width()) |
| return; |
| |
| imgOfs = { left: round($img.offset().left), top: round($img.offset().top) }; |
| |
| imgWidth = $img.innerWidth(); |
| imgHeight = $img.innerHeight(); |
| |
| imgOfs.top += ($img.outerHeight() - imgHeight) >> 1; |
| imgOfs.left += ($img.outerWidth() - imgWidth) >> 1; |
| |
| minWidth = options.minWidth || 0; |
| minHeight = options.minHeight || 0; |
| maxWidth = min(options.maxWidth || 1<<24, imgWidth); |
| maxHeight = min(options.maxHeight || 1<<24, imgHeight); |
| |
| if ($().jquery == '1.3.2' && position == 'fixed' && |
| !docElem['getBoundingClientRect']) |
| { |
| imgOfs.top += max(document.body.scrollTop, docElem.scrollTop); |
| imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft); |
| } |
| |
| parOfs = $.inArray($parent.css('position'), ['absolute', 'relative']) + 1 ? |
| { left: round($parent.offset().left) - $parent.scrollLeft(), |
| top: round($parent.offset().top) - $parent.scrollTop() } : |
| position == 'fixed' ? |
| { left: $(document).scrollLeft(), top: $(document).scrollTop() } : |
| { left: 0, top: 0 }; |
| |
| left = viewX(0); |
| top = viewY(0); |
| |
| if (selection.x2 > imgWidth || selection.y2 > imgHeight) |
| doResize(); |
| } |
| |
| function update(resetKeyPress) { |
| if (!shown) return; |
| |
| $box.css({ left: viewX(selection.x1), top: viewY(selection.y1) }) |
| .add($area).width(w = selection.width).height(h = selection.height); |
| |
| $area.add($border).add($handles).css({ left: 0, top: 0 }); |
| |
| $border |
| .width(max(w - $border.outerWidth() + $border.innerWidth(), 0)) |
| .height(max(h - $border.outerHeight() + $border.innerHeight(), 0)); |
| |
| $($outer[0]).css({ left: left, top: top, |
| width: selection.x1, height: imgHeight }); |
| $($outer[1]).css({ left: left + selection.x1, top: top, |
| width: w, height: selection.y1 }); |
| $($outer[2]).css({ left: left + selection.x2, top: top, |
| width: imgWidth - selection.x2, height: imgHeight }); |
| $($outer[3]).css({ left: left + selection.x1, top: top + selection.y2, |
| width: w, height: imgHeight - selection.y2 }); |
| |
| w -= $handles.outerWidth(); |
| h -= $handles.outerHeight(); |
| |
| switch ($handles.length) { |
| case 8: |
| $($handles[4]).css({ left: w >> 1 }); |
| $($handles[5]).css({ left: w, top: h >> 1 }); |
| $($handles[6]).css({ left: w >> 1, top: h }); |
| $($handles[7]).css({ top: h >> 1 }); |
| case 4: |
| $handles.slice(1,3).css({ left: w }); |
| $handles.slice(2,4).css({ top: h }); |
| } |
| |
| if (resetKeyPress !== false) { |
| if ($.imgAreaSelect.keyPress != docKeyPress) |
| $(document).unbind($.imgAreaSelect.keyPress, |
| $.imgAreaSelect.onKeyPress); |
| |
| if (options.keys) |
| $(document)[$.imgAreaSelect.keyPress]( |
| $.imgAreaSelect.onKeyPress = docKeyPress); |
| } |
| |
| if ($.browser.msie && $border.outerWidth() - $border.innerWidth() == 2) { |
| $border.css('margin', 0); |
| setTimeout(function () { $border.css('margin', 'auto'); }, 0); |
| } |
| } |
| |
| function doUpdate(resetKeyPress) { |
| adjust(); |
| update(resetKeyPress); |
| x1 = viewX(selection.x1); y1 = viewY(selection.y1); |
| x2 = viewX(selection.x2); y2 = viewY(selection.y2); |
| } |
| |
| function hide($elem, fn) { |
| options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide(); |
| |
| } |
| |
| function areaMouseMove(event) { |
| var x = selX(evX(event)) - selection.x1, |
| y = selY(evY(event)) - selection.y1; |
| |
| if (!adjusted) { |
| adjust(); |
| adjusted = true; |
| |
| $box.one('mouseout', function () { adjusted = false; }); |
| } |
| |
| resize = ''; |
| |
| if (options.resizable) { |
| if (y <= options.resizeMargin) |
| resize = 'n'; |
| else if (y >= selection.height - options.resizeMargin) |
| resize = 's'; |
| if (x <= options.resizeMargin) |
| resize += 'w'; |
| else if (x >= selection.width - options.resizeMargin) |
| resize += 'e'; |
| } |
| |
| $box.css('cursor', resize ? resize + '-resize' : |
| options.movable ? 'move' : ''); |
| if ($areaOpera) |
| $areaOpera.toggle(); |
| } |
| |
| function docMouseUp(event) { |
| $('body').css('cursor', ''); |
| if (options.autoHide || selection.width * selection.height == 0) |
| hide($box.add($outer), function () { $(this).hide(); }); |
| |
| $(document).unbind('mousemove', selectingMouseMove); |
| $box.mousemove(areaMouseMove); |
| |
| options.onSelectEnd(img, getSelection()); |
| } |
| |
| function areaMouseDown(event) { |
| if (event.which != 1) return false; |
| |
| adjust(); |
| |
| if (resize) { |
| $('body').css('cursor', resize + '-resize'); |
| |
| x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']); |
| y1 = viewY(selection[/n/.test(resize) ? 'y2' : 'y1']); |
| |
| $(document).mousemove(selectingMouseMove) |
| .one('mouseup', docMouseUp); |
| $box.unbind('mousemove', areaMouseMove); |
| } |
| else if (options.movable) { |
| startX = left + selection.x1 - evX(event); |
| startY = top + selection.y1 - evY(event); |
| |
| $box.unbind('mousemove', areaMouseMove); |
| |
| $(document).mousemove(movingMouseMove) |
| .one('mouseup', function () { |
| options.onSelectEnd(img, getSelection()); |
| |
| $(document).unbind('mousemove', movingMouseMove); |
| $box.mousemove(areaMouseMove); |
| }); |
| } |
| else |
| $img.mousedown(event); |
| |
| return false; |
| } |
| |
| function fixAspectRatio(xFirst) { |
| if (aspectRatio) |
| if (xFirst) { |
| x2 = max(left, min(left + imgWidth, |
| x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1))); |
| |
| y2 = round(max(top, min(top + imgHeight, |
| y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)))); |
| x2 = round(x2); |
| } |
| else { |
| y2 = max(top, min(top + imgHeight, |
| y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))); |
| x2 = round(max(left, min(left + imgWidth, |
| x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)))); |
| y2 = round(y2); |
| } |
| } |
| |
| function doResize() { |
| x1 = min(x1, left + imgWidth); |
| y1 = min(y1, top + imgHeight); |
| |
| if (abs(x2 - x1) < minWidth) { |
| x2 = x1 - minWidth * (x2 < x1 || -1); |
| |
| if (x2 < left) |
| x1 = left + minWidth; |
| else if (x2 > left + imgWidth) |
| x1 = left + imgWidth - minWidth; |
| } |
| |
| if (abs(y2 - y1) < minHeight) { |
| y2 = y1 - minHeight * (y2 < y1 || -1); |
| |
| if (y2 < top) |
| y1 = top + minHeight; |
| else if (y2 > top + imgHeight) |
| y1 = top + imgHeight - minHeight; |
| } |
| |
| x2 = max(left, min(x2, left + imgWidth)); |
| y2 = max(top, min(y2, top + imgHeight)); |
| |
| fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio); |
| |
| if (abs(x2 - x1) > maxWidth) { |
| x2 = x1 - maxWidth * (x2 < x1 || -1); |
| fixAspectRatio(); |
| } |
| |
| if (abs(y2 - y1) > maxHeight) { |
| y2 = y1 - maxHeight * (y2 < y1 || -1); |
| fixAspectRatio(true); |
| } |
| |
| selection = { x1: selX(min(x1, x2)), x2: selX(max(x1, x2)), |
| y1: selY(min(y1, y2)), y2: selY(max(y1, y2)), |
| width: abs(x2 - x1), height: abs(y2 - y1) }; |
| |
| update(); |
| |
| options.onSelectChange(img, getSelection()); |
| } |
| |
| function selectingMouseMove(event) { |
| x2 = resize == '' || /w|e/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2); |
| y2 = resize == '' || /n|s/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2); |
| |
| doResize(); |
| |
| return false; |
| |
| } |
| |
| function doMove(newX1, newY1) { |
| x2 = (x1 = newX1) + selection.width; |
| y2 = (y1 = newY1) + selection.height; |
| |
| $.extend(selection, { x1: selX(x1), y1: selY(y1), x2: selX(x2), |
| y2: selY(y2) }); |
| |
| update(); |
| |
| options.onSelectChange(img, getSelection()); |
| } |
| |
| function movingMouseMove(event) { |
| x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width)); |
| y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height)); |
| |
| doMove(x1, y1); |
| |
| event.preventDefault(); |
| |
| return false; |
| } |
| |
| function startSelection() { |
| $(document).unbind('mousemove', startSelection); |
| adjust(); |
| |
| x2 = x1; |
| y2 = y1; |
| |
| doResize(); |
| |
| resize = ''; |
| |
| if ($outer.is(':not(:visible)')) |
| $box.add($outer).hide().fadeIn(options.fadeSpeed||0); |
| |
| shown = true; |
| |
| $(document).unbind('mouseup', cancelSelection) |
| .mousemove(selectingMouseMove).one('mouseup', docMouseUp); |
| $box.unbind('mousemove', areaMouseMove); |
| |
| options.onSelectStart(img, getSelection()); |
| } |
| |
| function cancelSelection() { |
| $(document).unbind('mousemove', startSelection) |
| .unbind('mouseup', cancelSelection); |
| hide($box.add($outer)); |
| |
| setSelection(selX(x1), selY(y1), selX(x1), selY(y1)); |
| |
| options.onSelectChange(img, getSelection()); |
| options.onSelectEnd(img, getSelection()); |
| } |
| |
| function imgMouseDown(event) { |
| if (event.which != 1 || $outer.is(':animated')) return false; |
| |
| adjust(); |
| startX = x1 = evX(event); |
| startY = y1 = evY(event); |
| |
| $(document).mousemove(startSelection).mouseup(cancelSelection); |
| |
| return false; |
| } |
| |
| function windowResize() { |
| doUpdate(false); |
| } |
| |
| function imgLoad() { |
| imgLoaded = true; |
| |
| setOptions(options = $.extend({ |
| classPrefix: 'imgareaselect', |
| movable: true, |
| parent: 'body', |
| resizable: true, |
| resizeMargin: 10, |
| onInit: function () {}, |
| onSelectStart: function () {}, |
| onSelectChange: function () {}, |
| onSelectEnd: function () {} |
| }, options)); |
| |
| $box.add($outer).css({ visibility: '' }); |
| |
| if (options.show) { |
| shown = true; |
| adjust(); |
| update(); |
| $box.add($outer).hide().fadeIn(options.fadeSpeed||0); |
| } |
| |
| setTimeout(function () { options.onInit(img, getSelection()); }, 0); |
| } |
| |
| var docKeyPress = function(event) { |
| var k = options.keys, d, t, key = event.keyCode; |
| |
| d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt : |
| !isNaN(k.ctrl) && event.ctrlKey ? k.ctrl : |
| !isNaN(k.shift) && event.shiftKey ? k.shift : |
| !isNaN(k.arrows) ? k.arrows : 10; |
| |
| if (k.arrows == 'resize' || (k.shift == 'resize' && event.shiftKey) || |
| (k.ctrl == 'resize' && event.ctrlKey) || |
| (k.alt == 'resize' && (event.altKey || event.originalEvent.altKey))) |
| { |
| switch (key) { |
| case 37: |
| d = -d; |
| case 39: |
| t = max(x1, x2); |
| x1 = min(x1, x2); |
| x2 = max(t + d, x1); |
| fixAspectRatio(); |
| break; |
| case 38: |
| d = -d; |
| case 40: |
| t = max(y1, y2); |
| y1 = min(y1, y2); |
| y2 = max(t + d, y1); |
| fixAspectRatio(true); |
| break; |
| default: |
| return; |
| } |
| |
| doResize(); |
| } |
| else { |
| x1 = min(x1, x2); |
| y1 = min(y1, y2); |
| |
| switch (key) { |
| case 37: |
| doMove(max(x1 - d, left), y1); |
| break; |
| case 38: |
| doMove(x1, max(y1 - d, top)); |
| break; |
| case 39: |
| doMove(x1 + min(d, imgWidth - selX(x2)), y1); |
| break; |
| case 40: |
| doMove(x1, y1 + min(d, imgHeight - selY(y2))); |
| break; |
| default: |
| return; |
| } |
| } |
| |
| return false; |
| }; |
| |
| function styleOptions($elem, props) { |
| for (option in props) |
| if (options[option] !== undefined) |
| $elem.css(props[option], options[option]); |
| } |
| |
| function setOptions(newOptions) { |
| if (newOptions.parent) |
| ($parent = $(newOptions.parent)).append($box.add($outer)); |
| |
| $.extend(options, newOptions); |
| |
| adjust(); |
| |
| if (newOptions.handles != null) { |
| $handles.remove(); |
| $handles = $([]); |
| |
| i = newOptions.handles ? newOptions.handles == 'corners' ? 4 : 8 : 0; |
| |
| while (i--) |
| $handles = $handles.add(div()); |
| |
| $handles.addClass(options.classPrefix + '-handle').css({ |
| position: 'absolute', |
| fontSize: 0, |
| zIndex: zIndex + 1 || 1 |
| }); |
| |
| if (!parseInt($handles.css('width')) >= 0) |
| $handles.width(5).height(5); |
| |
| if (o = options.borderWidth) |
| $handles.css({ borderWidth: o, borderStyle: 'solid' }); |
| |
| styleOptions($handles, { borderColor1: 'border-color', |
| borderColor2: 'background-color', |
| borderOpacity: 'opacity' }); |
| } |
| |
| scaleX = options.imageWidth / imgWidth || 1; |
| scaleY = options.imageHeight / imgHeight || 1; |
| |
| if (newOptions.x1 != null) { |
| setSelection(newOptions.x1, newOptions.y1, newOptions.x2, |
| newOptions.y2); |
| newOptions.show = !newOptions.hide; |
| } |
| |
| if (newOptions.keys) |
| options.keys = $.extend({ shift: 1, ctrl: 'resize' }, |
| newOptions.keys); |
| |
| $outer.addClass(options.classPrefix + '-outer'); |
| $area.addClass(options.classPrefix + '-selection'); |
| for (i = 0; i++ < 4;) |
| $($border[i-1]).addClass(options.classPrefix + '-border' + i); |
| |
| styleOptions($area, { selectionColor: 'background-color', |
| selectionOpacity: 'opacity' }); |
| styleOptions($border, { borderOpacity: 'opacity', |
| borderWidth: 'border-width' }); |
| styleOptions($outer, { outerColor: 'background-color', |
| outerOpacity: 'opacity' }); |
| if (o = options.borderColor1) |
| $($border[0]).css({ borderStyle: 'solid', borderColor: o }); |
| if (o = options.borderColor2) |
| $($border[1]).css({ borderStyle: 'dashed', borderColor: o }); |
| |
| $box.append($area.add($border).add($areaOpera).add($handles)); |
| |
| if ($.browser.msie) { |
| if (o = $outer.css('filter').match(/opacity=([0-9]+)/)) |
| $outer.css('opacity', o[1]/100); |
| if (o = $border.css('filter').match(/opacity=([0-9]+)/)) |
| $border.css('opacity', o[1]/100); |
| } |
| |
| if (newOptions.hide) |
| hide($box.add($outer)); |
| else if (newOptions.show && imgLoaded) { |
| shown = true; |
| $box.add($outer).fadeIn(options.fadeSpeed||0); |
| doUpdate(); |
| } |
| |
| aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1]; |
| |
| $img.add($outer).unbind('mousedown', imgMouseDown); |
| |
| if (options.disable || options.enable === false) { |
| $box.unbind('mousemove', areaMouseMove).unbind('mousedown', areaMouseDown); |
| $(window).unbind('resize', windowResize); |
| } |
| else { |
| if (options.enable || options.disable === false) { |
| if (options.resizable || options.movable) |
| $box.mousemove(areaMouseMove).mousedown(areaMouseDown); |
| |
| $(window).resize(windowResize); |
| } |
| |
| if (!options.persistent) |
| $img.add($outer).mousedown(imgMouseDown); |
| } |
| |
| options.enable = options.disable = undefined; |
| } |
| |
| this.remove = function () { |
| setOptions({ disable: true }); |
| $box.add($outer).remove(); |
| }; |
| |
| this.getOptions = function () { return options; }; |
| |
| this.setOptions = setOptions; |
| |
| this.getSelection = getSelection; |
| |
| this.setSelection = setSelection; |
| |
| this.update = doUpdate; |
| |
| $p = $img; |
| |
| while ($p.length) { |
| zIndex = max(zIndex, |
| !isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex); |
| if ($p.css('position') == 'fixed') |
| position = 'fixed'; |
| |
| $p = $p.parent(':not(body)'); |
| } |
| |
| zIndex = options.zIndex || zIndex; |
| |
| if ($.browser.msie) |
| $img.attr('unselectable', 'on'); |
| |
| $.imgAreaSelect.keyPress = $.browser.msie || |
| $.browser.safari ? 'keydown' : 'keypress'; |
| |
| if ($.browser.opera) |
| $areaOpera = div().css({ width: '100%', height: '100%', |
| position: 'absolute', zIndex: zIndex + 2 || 2 }); |
| |
| $box.add($outer).css({ visibility: 'hidden', position: position, |
| overflow: 'hidden', zIndex: zIndex || '0' }); |
| $box.css({ zIndex: zIndex + 2 || 2 }); |
| $area.add($border).css({ position: 'absolute', fontSize: 0 }); |
| |
| img.complete || img.readyState == 'complete' || !$img.is('img') ? |
| imgLoad() : $img.one('load', imgLoad); |
| |
| if ($.browser.msie && $.browser.version >= 9) |
| img.src = img.src; |
| }; |
| |
| $.fn.imgAreaSelect = function (options) { |
| options = options || {}; |
| |
| this.each(function () { |
| if ($(this).data('imgAreaSelect')) { |
| if (options.remove) { |
| $(this).data('imgAreaSelect').remove(); |
| $(this).removeData('imgAreaSelect'); |
| } |
| else |
| $(this).data('imgAreaSelect').setOptions(options); |
| } |
| else if (!options.remove) { |
| if (options.enable === undefined && options.disable === undefined) |
| options.enable = true; |
| |
| $(this).data('imgAreaSelect', new $.imgAreaSelect(this, options)); |
| } |
| }); |
| |
| if (options.instance) |
| return $(this).data('imgAreaSelect'); |
| |
| return this; |
| }; |
| |
| })(jQuery); |