WordPress.org

Make WordPress Core

Ticket #17446: 17446.diff

File 17446.diff, 40.4 KB (added by duck_, 3 years ago)
  • wp-includes/js/imgareaselect/jquery.imgareaselect.dev.js

     
    11/* 
    22 * imgAreaSelect jQuery plugin 
    3  * version 0.9.1 
     3 * version 0.9.6 
    44 * 
    5  * Copyright (c) 2008-2009 Michal Wojciechowski (odyniec.net) 
     5 * Copyright (c) 2008-2011 Michal Wojciechowski (odyniec.net) 
    66 * 
    77 * Dual licensed under the MIT (MIT-LICENSE.txt) 
    88 * and GPL (GPL-LICENSE.txt) licenses. 
     
    1313 
    1414(function($) { 
    1515 
     16/* 
     17 * Math functions will be used extensively, so it's convenient to make a few 
     18 * shortcuts 
     19 */     
    1620var abs = Math.abs, 
    1721    max = Math.max, 
    1822    min = Math.min, 
    1923    round = Math.round; 
    2024 
     25/** 
     26 * Create a new HTML div element 
     27 *  
     28 * @return A jQuery object representing the new element 
     29 */ 
    2130function div() { 
    2231    return $('<div/>'); 
    2332} 
    2433 
     34/** 
     35 * imgAreaSelect initialization 
     36 *  
     37 * @param img 
     38 *            A HTML image element to attach the plugin to 
     39 * @param options 
     40 *            An options object 
     41 */ 
    2542$.imgAreaSelect = function (img, options) { 
    26     var 
    27  
     43    var  
     44        /* jQuery object representing the image */  
    2845        $img = $(img), 
    29  
     46         
     47        /* Has the image finished loading? */ 
    3048        imgLoaded, 
    31  
     49         
     50        /* Plugin elements */ 
     51         
     52        /* Container box */ 
    3253        $box = div(), 
     54        /* Selection area */ 
    3355        $area = div(), 
     56        /* Border (four divs) */ 
    3457        $border = div().add(div()).add(div()).add(div()), 
     58        /* Outer area (four divs) */ 
    3559        $outer = div().add(div()).add(div()).add(div()), 
     60        /* Handles (empty by default, initialized in setOptions()) */ 
    3661        $handles = $([]), 
    37  
     62         
     63        /* 
     64         * Additional element to work around a cursor problem in Opera 
     65         * (explained later) 
     66         */ 
    3867        $areaOpera, 
    39  
     68         
     69        /* Image position (relative to viewport) */ 
    4070        left, top, 
    41  
    42         imgOfs, 
    43  
     71         
     72        /* Image offset (as returned by .offset()) */ 
     73        imgOfs = { left: 0, top: 0 }, 
     74         
     75        /* Image dimensions (as returned by .width() and .height()) */ 
    4476        imgWidth, imgHeight, 
    45  
     77         
     78        /* 
     79         * jQuery object representing the parent element that the plugin 
     80         * elements are appended to 
     81         */ 
    4682        $parent, 
    47  
    48         parOfs, 
    49  
     83         
     84        /* Parent element offset (as returned by .offset()) */ 
     85        parOfs = { left: 0, top: 0 }, 
     86         
     87        /* Base z-index for plugin elements */ 
    5088        zIndex = 0, 
    51  
     89                 
     90        /* Plugin elements position */ 
    5291        position = 'absolute', 
    53  
     92         
     93        /* X/Y coordinates of the starting point for move/resize operations */  
    5494        startX, startY, 
    55  
     95         
     96        /* Horizontal and vertical scaling factors */ 
    5697        scaleX, scaleY, 
    57  
     98         
     99        /* 
     100         * Width of the margin inside the selection area where resize mode is 
     101         * triggered 
     102         */ 
    58103        resizeMargin = 10, 
    59  
     104         
     105        /* Current resize mode ("nw", "se", etc.) */ 
    60106        resize, 
    61  
     107         
     108        /* Selection area constraints */ 
     109        minWidth, minHeight, maxWidth, maxHeight, 
     110         
     111        /* Aspect ratio to maintain (floating point number) */ 
    62112        aspectRatio, 
    63  
     113         
     114        /* Are the plugin elements currently displayed? */ 
    64115        shown, 
    65  
     116         
     117        /* Current selection (relative to parent element) */ 
    66118        x1, y1, x2, y2, 
    67  
     119         
     120        /* Current selection (relative to scaled image) */ 
    68121        selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 }, 
    69  
     122         
     123        /* Document element */ 
     124        docElem = document.documentElement, 
     125         
     126        /* Various helper variables used throughout the code */  
    70127        $p, d, i, o, w, h, adjusted; 
    71128 
     129    /* 
     130     * Translate selection coordinates (relative to scaled image) to viewport 
     131     * coordinates (relative to parent element) 
     132     */ 
     133     
     134    /** 
     135     * Translate selection X to viewport X 
     136     *  
     137     * @param x 
     138     *            Selection X 
     139     * @return Viewport X 
     140     */ 
    72141    function viewX(x) { 
    73142        return x + imgOfs.left - parOfs.left; 
    74143    } 
    75144 
     145    /** 
     146     * Translate selection Y to viewport Y 
     147     *  
     148     * @param y 
     149     *            Selection Y 
     150     * @return Viewport Y 
     151     */ 
    76152    function viewY(y) { 
    77153        return y + imgOfs.top - parOfs.top; 
    78154    } 
    79155 
     156    /* 
     157     * Translate viewport coordinates to selection coordinates 
     158     */ 
     159     
     160    /** 
     161     * Translate viewport X to selection X 
     162     *  
     163     * @param x 
     164     *            Viewport X 
     165     * @return Selection X 
     166     */ 
    80167    function selX(x) { 
    81168        return x - imgOfs.left + parOfs.left; 
    82169    } 
    83170 
     171    /** 
     172     * Translate viewport Y to selection Y 
     173     *  
     174     * @param y 
     175     *            Viewport Y 
     176     * @return Selection Y 
     177     */ 
    84178    function selY(y) { 
    85179        return y - imgOfs.top + parOfs.top; 
    86180    } 
    87  
     181     
     182    /* 
     183     * Translate event coordinates (relative to document) to viewport 
     184     * coordinates 
     185     */ 
     186     
     187    /** 
     188     * Get event X and translate it to viewport X 
     189     *  
     190     * @param event 
     191     *            The event object 
     192     * @return Viewport X 
     193     */ 
    88194    function evX(event) { 
    89195        return event.pageX - parOfs.left; 
    90196    } 
    91197 
     198    /** 
     199     * Get event Y and translate it to viewport Y 
     200     *  
     201     * @param event 
     202     *            The event object 
     203     * @return Viewport Y 
     204     */ 
    92205    function evY(event) { 
    93206        return event.pageY - parOfs.top; 
    94207    } 
    95208 
     209    /** 
     210     * Get the current selection 
     211     *  
     212     * @param noScale 
     213     *            If set to <code>true</code>, scaling is not applied to the 
     214     *            returned selection 
     215     * @return Selection object 
     216     */ 
    96217    function getSelection(noScale) { 
    97218        var sx = noScale || scaleX, sy = noScale || scaleY; 
    98  
     219         
    99220        return { x1: round(selection.x1 * sx), 
    100221            y1: round(selection.y1 * sy), 
    101222            x2: round(selection.x2 * sx), 
     
    103224            width: round(selection.x2 * sx) - round(selection.x1 * sx), 
    104225            height: round(selection.y2 * sy) - round(selection.y1 * sy) }; 
    105226    } 
    106  
     227     
     228    /** 
     229     * Set the current selection 
     230     *  
     231     * @param x1 
     232     *            X coordinate of the upper left corner of the selection area 
     233     * @param y1 
     234     *            Y coordinate of the upper left corner of the selection area 
     235     * @param x2 
     236     *            X coordinate of the lower right corner of the selection area 
     237     * @param y2 
     238     *            Y coordinate of the lower right corner of the selection area 
     239     * @param noScale 
     240     *            If set to <code>true</code>, scaling is not applied to the 
     241     *            new selection 
     242     */ 
    107243    function setSelection(x1, y1, x2, y2, noScale) { 
    108244        var sx = noScale || scaleX, sy = noScale || scaleY; 
    109  
     245         
    110246        selection = { 
    111             x1: round(x1 / sx), 
    112             y1: round(y1 / sy), 
    113             x2: round(x2 / sx), 
    114             y2: round(y2 / sy) 
     247            x1: round(x1 / sx || 0), 
     248            y1: round(y1 / sy || 0), 
     249            x2: round(x2 / sx || 0), 
     250            y2: round(y2 / sy || 0) 
    115251        }; 
    116  
    117         selection.width = (x2 = viewX(selection.x2)) - (x1 = viewX(selection.x1)); 
    118         selection.height = (y2 = viewX(selection.y2)) - (y1 = viewX(selection.y1)); 
     252         
     253        selection.width = selection.x2 - selection.x1; 
     254        selection.height = selection.y2 - selection.y1; 
    119255    } 
    120256 
     257    /** 
     258     * Recalculate image and parent offsets 
     259     */ 
    121260    function adjust() { 
     261        /* 
     262         * Do not adjust if image width is not a positive number. This might 
     263         * happen when imgAreaSelect is put on a parent element which is then 
     264         * hidden. 
     265         */ 
    122266        if (!$img.width()) 
    123267            return; 
    124  
     268         
     269        /* 
     270         * Get image offset. The .offset() method returns float values, so they 
     271         * need to be rounded. 
     272         */ 
    125273        imgOfs = { left: round($img.offset().left), top: round($img.offset().top) }; 
     274         
     275        /* Get image dimensions */ 
     276        imgWidth = $img.innerWidth(); 
     277        imgHeight = $img.innerHeight(); 
     278         
     279        imgOfs.top += ($img.outerHeight() - imgHeight) >> 1; 
     280        imgOfs.left += ($img.outerWidth() - imgWidth) >> 1; 
    126281 
    127         imgWidth = $img.width(); 
    128         imgHeight = $img.height(); 
    129  
    130         if ($().jquery == '1.3.2' && $.browser.safari && position == 'fixed') { 
    131             imgOfs.top += max(document.documentElement.scrollTop, $('body').scrollTop()); 
    132  
    133             imgOfs.left += max(document.documentElement.scrollLeft, $('body').scrollLeft()); 
     282        /* Set minimum and maximum selection area dimensions */ 
     283        minWidth = options.minWidth || 0; 
     284        minHeight = options.minHeight || 0; 
     285        maxWidth = min(options.maxWidth || 1<<24, imgWidth); 
     286        maxHeight = min(options.maxHeight || 1<<24, imgHeight); 
     287         
     288        /* 
     289         * Workaround for jQuery 1.3.2 incorrect offset calculation, originally 
     290         * observed in Safari 3. Firefox 2 is also affected. 
     291         */ 
     292        if ($().jquery == '1.3.2' && position == 'fixed' && 
     293            !docElem['getBoundingClientRect']) 
     294        { 
     295            imgOfs.top += max(document.body.scrollTop, docElem.scrollTop); 
     296            imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft); 
    134297        } 
    135298 
     299        /* Determine parent element offset */  
    136300        parOfs = $.inArray($parent.css('position'), ['absolute', 'relative']) + 1 ? 
    137301            { left: round($parent.offset().left) - $parent.scrollLeft(), 
    138302                top: round($parent.offset().top) - $parent.scrollTop() } : 
    139303            position == 'fixed' ? 
    140304                { left: $(document).scrollLeft(), top: $(document).scrollTop() } : 
    141305                { left: 0, top: 0 }; 
    142  
     306                 
    143307        left = viewX(0); 
    144308        top = viewY(0); 
     309         
     310        /* 
     311         * Check if selection area is within image boundaries, adjust if 
     312         * necessary 
     313         */ 
     314        if (selection.x2 > imgWidth || selection.y2 > imgHeight) 
     315            doResize(); 
    145316    } 
    146317 
     318    /** 
     319     * Update plugin elements 
     320     *  
     321     * @param resetKeyPress 
     322     *            If set to <code>false</code>, this instance's keypress 
     323     *            event handler is not activated 
     324     */ 
    147325    function update(resetKeyPress) { 
     326        /* If plugin elements are hidden, do nothing */ 
    148327        if (!shown) return; 
    149328 
     329        /* 
     330         * Set the position and size of the container box and the selection area 
     331         * inside it 
     332         */ 
    150333        $box.css({ left: viewX(selection.x1), top: viewY(selection.y1) }) 
    151334            .add($area).width(w = selection.width).height(h = selection.height); 
    152335 
     336        /* 
     337         * Reset the position of selection area, borders, and handles (IE6/IE7 
     338         * position them incorrectly if we don't do this) 
     339         */  
    153340        $area.add($border).add($handles).css({ left: 0, top: 0 }); 
    154341 
     342        /* Set border dimensions */ 
    155343        $border 
    156344            .width(max(w - $border.outerWidth() + $border.innerWidth(), 0)) 
    157345            .height(max(h - $border.outerHeight() + $border.innerHeight(), 0)); 
    158346 
     347        /* Arrange the outer area elements */ 
    159348        $($outer[0]).css({ left: left, top: top, 
    160349            width: selection.x1, height: imgHeight }); 
    161350        $($outer[1]).css({ left: left + selection.x1, top: top, 
     
    164353            width: imgWidth - selection.x2, height: imgHeight }); 
    165354        $($outer[3]).css({ left: left + selection.x1, top: top + selection.y2, 
    166355            width: w, height: imgHeight - selection.y2 }); 
    167  
     356         
    168357        w -= $handles.outerWidth(); 
    169358        h -= $handles.outerHeight(); 
    170  
     359         
     360        /* Arrange handles */ 
    171361        switch ($handles.length) { 
    172362        case 8: 
    173             $($handles[4]).css({ left: w / 2 }); 
    174             $($handles[5]).css({ left: w, top: h / 2 }); 
    175             $($handles[6]).css({ left: w / 2, top: h }); 
    176             $($handles[7]).css({ top: h / 2 }); 
     363            $($handles[4]).css({ left: w >> 1 }); 
     364            $($handles[5]).css({ left: w, top: h >> 1 }); 
     365            $($handles[6]).css({ left: w >> 1, top: h }); 
     366            $($handles[7]).css({ top: h >> 1 }); 
    177367        case 4: 
    178368            $handles.slice(1,3).css({ left: w }); 
    179369            $handles.slice(2,4).css({ top: h }); 
    180370        } 
    181371 
    182372        if (resetKeyPress !== false) { 
     373            /* 
     374             * Need to reset the document keypress event handler -- unbind the 
     375             * current handler 
     376             */ 
    183377            if ($.imgAreaSelect.keyPress != docKeyPress) 
    184378                $(document).unbind($.imgAreaSelect.keyPress, 
    185379                    $.imgAreaSelect.onKeyPress); 
    186380 
    187381            if (options.keys) 
     382                /* 
     383                 * Set the document keypress event handler to this instance's 
     384                 * docKeyPress() function 
     385                 */ 
    188386                $(document)[$.imgAreaSelect.keyPress]( 
    189387                    $.imgAreaSelect.onKeyPress = docKeyPress); 
    190388        } 
    191389 
     390        /* 
     391         * Internet Explorer displays 1px-wide dashed borders incorrectly by 
     392         * filling the spaces between dashes with white. Toggling the margin 
     393         * property between 0 and "auto" fixes this in IE6 and IE7 (IE8 is still 
     394         * broken). This workaround is not perfect, as it requires setTimeout() 
     395         * and thus causes the border to flicker a bit, but I haven't found a 
     396         * better solution. 
     397         *  
     398         * Note: This only happens with CSS borders, set with the borderWidth, 
     399         * borderOpacity, borderColor1, and borderColor2 options (which are now 
     400         * deprecated). Borders created with GIF background images are fine. 
     401         */  
    192402        if ($.browser.msie && $border.outerWidth() - $border.innerWidth() == 2) { 
    193403            $border.css('margin', 0); 
    194404            setTimeout(function () { $border.css('margin', 'auto'); }, 0); 
    195405        } 
    196406    } 
    197  
     407     
     408    /** 
     409     * Do the complete update sequence: recalculate offsets, update the 
     410     * elements, and set the correct values of x1, y1, x2, and y2. 
     411     *  
     412     * @param resetKeyPress 
     413     *            If set to <code>false</code>, this instance's keypress 
     414     *            event handler is not activated 
     415     */ 
    198416    function doUpdate(resetKeyPress) { 
    199417        adjust(); 
    200418        update(resetKeyPress); 
    201419        x1 = viewX(selection.x1); y1 = viewY(selection.y1); 
    202420        x2 = viewX(selection.x2); y2 = viewY(selection.y2); 
    203421    } 
    204  
     422     
     423    /** 
     424     * Hide or fade out an element (or multiple elements) 
     425     *  
     426     * @param $elem 
     427     *            A jQuery object containing the element(s) to hide/fade out 
     428     * @param fn 
     429     *            Callback function to be called when fadeOut() completes 
     430     */ 
    205431    function hide($elem, fn) { 
    206         options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide(); 
    207  
     432        options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide();  
    208433    } 
    209434 
     435    /** 
     436     * Selection area mousemove event handler 
     437     *  
     438     * @param event 
     439     *            The event object 
     440     */ 
    210441    function areaMouseMove(event) { 
    211442        var x = selX(evX(event)) - selection.x1, 
    212443            y = selY(evY(event)) - selection.y1; 
    213  
     444         
    214445        if (!adjusted) { 
    215446            adjust(); 
    216447            adjusted = true; 
     
    218449            $box.one('mouseout', function () { adjusted = false; }); 
    219450        } 
    220451 
     452        /* Clear the resize mode */ 
    221453        resize = ''; 
    222454 
    223455        if (options.resizable) { 
    224             if (y <= resizeMargin) 
     456            /* 
     457             * Check if the mouse pointer is over the resize margin area and set 
     458             * the resize mode accordingly 
     459             */ 
     460            if (y <= options.resizeMargin) 
    225461                resize = 'n'; 
    226             else if (y >= selection.height - resizeMargin) 
     462            else if (y >= selection.height - options.resizeMargin) 
    227463                resize = 's'; 
    228             if (x <= resizeMargin) 
     464            if (x <= options.resizeMargin) 
    229465                resize += 'w'; 
    230             else if (x >= selection.width - resizeMargin) 
     466            else if (x >= selection.width - options.resizeMargin) 
    231467                resize += 'e'; 
    232468        } 
    233469 
     
    237473            $areaOpera.toggle(); 
    238474    } 
    239475 
     476    /** 
     477     * Document mouseup event handler 
     478     *  
     479     * @param event 
     480     *            The event object 
     481     */ 
    240482    function docMouseUp(event) { 
     483        /* Set back the default cursor */ 
    241484        $('body').css('cursor', ''); 
    242  
     485        /* 
     486         * If autoHide is enabled, or if the selection has zero width/height, 
     487         * hide the selection and the outer area 
     488         */ 
    243489        if (options.autoHide || selection.width * selection.height == 0) 
    244490            hide($box.add($outer), function () { $(this).hide(); }); 
    245491 
    246         options.onSelectEnd(img, getSelection()); 
    247  
    248492        $(document).unbind('mousemove', selectingMouseMove); 
    249493        $box.mousemove(areaMouseMove); 
     494         
     495        options.onSelectEnd(img, getSelection()); 
    250496    } 
    251497 
     498    /** 
     499     * Selection area mousedown event handler 
     500     *  
     501     * @param event 
     502     *            The event object 
     503     * @return false 
     504     */ 
    252505    function areaMouseDown(event) { 
    253506        if (event.which != 1) return false; 
    254507 
    255508        adjust(); 
    256509 
    257510        if (resize) { 
     511            /* Resize mode is in effect */ 
    258512            $('body').css('cursor', resize + '-resize'); 
    259513 
    260514            x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']); 
     
    284538        return false; 
    285539    } 
    286540 
    287     function aspectRatioXY() { 
    288         x2 = max(left, min(left + imgWidth, 
    289             x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1))); 
    290  
    291         y2 = round(max(top, min(top + imgHeight, 
    292             y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)))); 
    293         x2 = round(x2); 
     541    /** 
     542     * Adjust the x2/y2 coordinates to maintain aspect ratio (if defined) 
     543     *  
     544     * @param xFirst 
     545     *            If set to <code>true</code>, calculate x2 first. Otherwise, 
     546     *            calculate y2 first. 
     547     */ 
     548    function fixAspectRatio(xFirst) { 
     549        if (aspectRatio) 
     550            if (xFirst) { 
     551                x2 = max(left, min(left + imgWidth, 
     552                    x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)));     
     553                y2 = round(max(top, min(top + imgHeight, 
     554                    y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)))); 
     555                x2 = round(x2); 
     556            } 
     557            else { 
     558                y2 = max(top, min(top + imgHeight, 
     559                    y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))); 
     560                x2 = round(max(left, min(left + imgWidth, 
     561                    x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)))); 
     562                y2 = round(y2); 
     563            } 
    294564    } 
    295565 
    296     function aspectRatioYX() { 
    297         y2 = max(top, min(top + imgHeight, 
    298             y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))); 
    299         x2 = round(max(left, min(left + imgWidth, 
    300             x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)))); 
    301         y2 = round(y2); 
    302     } 
    303  
     566    /** 
     567     * Resize the selection area respecting the minimum/maximum dimensions and 
     568     * aspect ratio 
     569     */ 
    304570    function doResize() { 
    305         if (abs(x2 - x1) < options.minWidth) { 
    306             x2 = x1 - options.minWidth * (x2 < x1 || -1); 
     571        /* 
     572         * Make sure the top left corner of the selection area stays within 
     573         * image boundaries (it might not if the image source was dynamically 
     574         * changed). 
     575         */ 
     576        x1 = min(x1, left + imgWidth); 
     577        y1 = min(y1, top + imgHeight); 
     578         
     579        if (abs(x2 - x1) < minWidth) { 
     580            /* Selection width is smaller than minWidth */ 
     581            x2 = x1 - minWidth * (x2 < x1 || -1); 
    307582 
    308583            if (x2 < left) 
    309                 x1 = left + options.minWidth; 
     584                x1 = left + minWidth; 
    310585            else if (x2 > left + imgWidth) 
    311                 x1 = left + imgWidth - options.minWidth; 
     586                x1 = left + imgWidth - minWidth; 
    312587        } 
    313588 
    314         if (abs(y2 - y1) < options.minHeight) { 
    315             y2 = y1 - options.minHeight * (y2 < y1 || -1); 
     589        if (abs(y2 - y1) < minHeight) { 
     590            /* Selection height is smaller than minHeight */ 
     591            y2 = y1 - minHeight * (y2 < y1 || -1); 
    316592 
    317593            if (y2 < top) 
    318                 y1 = top + options.minHeight; 
     594                y1 = top + minHeight; 
    319595            else if (y2 > top + imgHeight) 
    320                 y1 = top + imgHeight - options.minHeight; 
     596                y1 = top + imgHeight - minHeight; 
    321597        } 
    322598 
    323599        x2 = max(left, min(x2, left + imgWidth)); 
    324600        y2 = max(top, min(y2, top + imgHeight)); 
     601         
     602        fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio); 
    325603 
    326         if (aspectRatio) 
    327             if (abs(x2 - x1) / aspectRatio > abs(y2 - y1)) 
    328                 aspectRatioYX(); 
    329             else 
    330                 aspectRatioXY(); 
    331  
    332         if (abs(x2 - x1) > options.maxWidth) { 
    333             x2 = x1 - options.maxWidth * (x2 < x1 || -1); 
    334             if (aspectRatio) aspectRatioYX(); 
     604        if (abs(x2 - x1) > maxWidth) { 
     605            /* Selection width is greater than maxWidth */ 
     606            x2 = x1 - maxWidth * (x2 < x1 || -1); 
     607            fixAspectRatio(); 
    335608        } 
    336609 
    337         if (abs(y2 - y1) > options.maxHeight) { 
    338             y2 = y1 - options.maxHeight * (y2 < y1 || -1); 
    339             if (aspectRatio) aspectRatioXY(); 
     610        if (abs(y2 - y1) > maxHeight) { 
     611            /* Selection height is greater than maxHeight */ 
     612            y2 = y1 - maxHeight * (y2 < y1 || -1); 
     613            fixAspectRatio(true); 
    340614        } 
    341615 
    342616        selection = { x1: selX(min(x1, x2)), x2: selX(max(x1, x2)), 
     
    348622        options.onSelectChange(img, getSelection()); 
    349623    } 
    350624 
     625    /** 
     626     * Mousemove event handler triggered when the user is selecting an area 
     627     *  
     628     * @param event 
     629     *            The event object 
     630     * @return false 
     631     */ 
    351632    function selectingMouseMove(event) { 
    352633        x2 = resize == '' || /w|e/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2); 
    353634        y2 = resize == '' || /n|s/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2); 
    354635 
    355636        doResize(); 
    356637 
    357         return false; 
    358  
     638        return false;         
    359639    } 
    360640 
     641    /** 
     642     * Move the selection area 
     643     *  
     644     * @param newX1 
     645     *            New viewport X1 
     646     * @param newY1 
     647     *            New viewport Y1 
     648     */ 
    361649    function doMove(newX1, newY1) { 
    362650        x2 = (x1 = newX1) + selection.width; 
    363651        y2 = (y1 = newY1) + selection.height; 
    364652 
    365         selection = $.extend(selection, { x1: selX(x1), y1: selY(y1), 
    366             x2: selX(x2), y2: selY(y2) }); 
     653        $.extend(selection, { x1: selX(x1), y1: selY(y1), x2: selX(x2), 
     654            y2: selY(y2) }); 
    367655 
    368656        update(); 
    369657 
    370658        options.onSelectChange(img, getSelection()); 
    371659    } 
    372660 
     661    /** 
     662     * Mousemove event handler triggered when the selection area is being moved 
     663     *  
     664     * @param event 
     665     *            The event object 
     666     * @return false 
     667     */ 
    373668    function movingMouseMove(event) { 
    374669        x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width)); 
    375670        y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height)); 
    376671 
    377672        doMove(x1, y1); 
    378673 
    379         event.preventDefault(); 
    380  
     674        event.preventDefault();      
    381675        return false; 
    382676    } 
    383677 
     678    /** 
     679     * Start selection 
     680     */ 
    384681    function startSelection() { 
     682        $(document).unbind('mousemove', startSelection); 
    385683        adjust(); 
    386684 
    387685        x2 = x1; 
    388         y2 = y1; 
    389  
     686        y2 = y1;        
    390687        doResize(); 
    391688 
    392689        resize = ''; 
    393690 
    394691        if ($outer.is(':not(:visible)')) 
     692            /* Show the plugin elements */ 
    395693            $box.add($outer).hide().fadeIn(options.fadeSpeed||0); 
    396694 
    397695        shown = true; 
     
    403701        options.onSelectStart(img, getSelection()); 
    404702    } 
    405703 
     704    /** 
     705     * Cancel selection 
     706     */ 
    406707    function cancelSelection() { 
    407         $(document).unbind('mousemove', startSelection); 
     708        $(document).unbind('mousemove', startSelection) 
     709            .unbind('mouseup', cancelSelection); 
    408710        hide($box.add($outer)); 
    409  
    410         selection = { x1: selX(x1), y1: selY(y1), x2: selX(x1), y2: selY(y1), 
    411                 width: 0, height: 0 }; 
    412  
     711         
     712        setSelection(selX(x1), selY(y1), selX(x1), selY(y1)); 
     713         
    413714        options.onSelectChange(img, getSelection()); 
    414715        options.onSelectEnd(img, getSelection()); 
    415716    } 
    416717 
     718    /** 
     719     * Image mousedown event handler 
     720     *  
     721     * @param event 
     722     *            The event object 
     723     * @return false 
     724     */ 
    417725    function imgMouseDown(event) { 
     726        /* Ignore the event if animation is in progress */ 
    418727        if (event.which != 1 || $outer.is(':animated')) return false; 
    419728 
    420729        adjust(); 
    421730        startX = x1 = evX(event); 
    422731        startY = y1 = evY(event); 
    423732 
    424         $(document).one('mousemove', startSelection) 
    425             .one('mouseup', cancelSelection); 
     733        /* Selection will start when the mouse is moved */ 
     734        $(document).mousemove(startSelection).mouseup(cancelSelection); 
    426735 
    427736        return false; 
    428737    } 
    429  
    430     function parentScroll() { 
     738     
     739    /** 
     740     * Window resize event handler 
     741     */ 
     742    function windowResize() { 
    431743        doUpdate(false); 
    432744    } 
    433745 
     746    /** 
     747     * Image load event handler. This is the final part of the initialization 
     748     * process. 
     749     */ 
    434750    function imgLoad() { 
    435751        imgLoaded = true; 
    436752 
     753        /* Set options */ 
    437754        setOptions(options = $.extend({ 
    438755            classPrefix: 'imgareaselect', 
    439756            movable: true, 
     757            parent: 'body', 
    440758            resizable: true, 
    441             parent: 'body', 
     759            resizeMargin: 10, 
    442760            onInit: function () {}, 
    443761            onSelectStart: function () {}, 
    444762            onSelectChange: function () {}, 
     
    454772            $box.add($outer).hide().fadeIn(options.fadeSpeed||0); 
    455773        } 
    456774 
     775        /* 
     776         * Call the onInit callback. The setTimeout() call is used to ensure 
     777         * that the plugin has been fully initialized and the object instance is 
     778         * available (so that it can be obtained in the callback). 
     779         */ 
    457780        setTimeout(function () { options.onInit(img, getSelection()); }, 0); 
    458781    } 
    459782 
     783    /** 
     784     * Document keypress event handler 
     785     *  
     786     * @param event 
     787     *            The event object 
     788     * @return false 
     789     */ 
    460790    var docKeyPress = function(event) { 
    461         var k = options.keys, d, t, key = event.keyCode || event.which; 
    462  
     791        var k = options.keys, d, t, key = event.keyCode; 
     792         
    463793        d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt : 
    464794            !isNaN(k.ctrl) && event.ctrlKey ? k.ctrl : 
    465795            !isNaN(k.shift) && event.shiftKey ? k.shift : 
     
    469799            (k.ctrl == 'resize' && event.ctrlKey) || 
    470800            (k.alt == 'resize' && (event.altKey || event.originalEvent.altKey))) 
    471801        { 
     802            /* Resize selection */ 
     803             
    472804            switch (key) { 
    473805            case 37: 
     806                /* Left */ 
    474807                d = -d; 
    475808            case 39: 
     809                /* Right */ 
    476810                t = max(x1, x2); 
    477811                x1 = min(x1, x2); 
    478812                x2 = max(t + d, x1); 
    479                 if (aspectRatio) aspectRatioYX(); 
     813                fixAspectRatio(); 
    480814                break; 
    481815            case 38: 
     816                /* Up */ 
    482817                d = -d; 
    483818            case 40: 
     819                /* Down */ 
    484820                t = max(y1, y2); 
    485821                y1 = min(y1, y2); 
    486822                y2 = max(t + d, y1); 
    487                 if (aspectRatio) aspectRatioXY(); 
     823                fixAspectRatio(true); 
    488824                break; 
    489825            default: 
    490826                return; 
     
    493829            doResize(); 
    494830        } 
    495831        else { 
     832            /* Move selection */ 
     833             
    496834            x1 = min(x1, x2); 
    497835            y1 = min(y1, y2); 
    498836 
    499837            switch (key) { 
    500838            case 37: 
     839                /* Left */ 
    501840                doMove(max(x1 - d, left), y1); 
    502841                break; 
    503842            case 38: 
     843                /* Up */ 
    504844                doMove(x1, max(y1 - d, top)); 
    505845                break; 
    506846            case 39: 
     847                /* Right */ 
    507848                doMove(x1 + min(d, imgWidth - selX(x2)), y1); 
    508849                break; 
    509850            case 40: 
     851                /* Down */ 
    510852                doMove(x1, y1 + min(d, imgHeight - selY(y2))); 
    511853                break; 
    512854            default: 
     
    517859        return false; 
    518860    }; 
    519861 
     862    /** 
     863     * Apply style options to plugin element (or multiple elements) 
     864     *  
     865     * @param $elem 
     866     *            A jQuery object representing the element(s) to style 
     867     * @param props 
     868     *            An object that maps option names to corresponding CSS 
     869     *            properties 
     870     */ 
    520871    function styleOptions($elem, props) { 
    521872        for (option in props) 
    522873            if (options[option] !== undefined) 
    523874                $elem.css(props[option], options[option]); 
    524875    } 
    525876 
     877    /** 
     878     * Set plugin options 
     879     *  
     880     * @param newOptions 
     881     *            The new options object 
     882     */ 
    526883    function setOptions(newOptions) { 
    527884        if (newOptions.parent) 
    528885            ($parent = $(newOptions.parent)).append($box.add($outer)); 
     886         
     887        /* Merge the new options with the existing ones */ 
     888        $.extend(options, newOptions); 
    529889 
    530         options = $.extend(options, newOptions); 
    531  
    532890        adjust(); 
    533891 
    534892        if (newOptions.handles != null) { 
     893            /* Recreate selection area handles */ 
    535894            $handles.remove(); 
    536895            $handles = $([]); 
    537896 
     
    539898 
    540899            while (i--) 
    541900                $handles = $handles.add(div()); 
    542  
     901             
     902            /* Add a class to handles and set the CSS properties */ 
    543903            $handles.addClass(options.classPrefix + '-handle').css({ 
    544904                position: 'absolute', 
     905                /* 
     906                 * The font-size property needs to be set to zero, otherwise 
     907                 * Internet Explorer makes the handles too large 
     908                 */ 
    545909                fontSize: 0, 
    546910                zIndex: zIndex + 1 || 1 
    547911            }); 
    548  
    549             if (!parseInt($handles.css('width'))) 
     912             
     913            /* 
     914             * If handle width/height has not been set with CSS rules, set the 
     915             * default 5px 
     916             */ 
     917            if (!parseInt($handles.css('width')) >= 0) 
    550918                $handles.width(5).height(5); 
    551  
     919             
     920            /* 
     921             * If the borderWidth option is in use, add a solid border to 
     922             * handles 
     923             */ 
    552924            if (o = options.borderWidth) 
    553925                $handles.css({ borderWidth: o, borderStyle: 'solid' }); 
    554926 
     927            /* Apply other style options */ 
    555928            styleOptions($handles, { borderColor1: 'border-color', 
    556929                borderColor2: 'background-color', 
    557930                borderOpacity: 'opacity' }); 
    558931        } 
    559932 
     933        /* Calculate scale factors */ 
    560934        scaleX = options.imageWidth / imgWidth || 1; 
    561935        scaleY = options.imageHeight / imgHeight || 1; 
    562936 
     937        /* Set selection */ 
    563938        if (newOptions.x1 != null) { 
    564939            setSelection(newOptions.x1, newOptions.y1, newOptions.x2, 
    565                     newOptions.y2); 
     940                newOptions.y2); 
    566941            newOptions.show = !newOptions.hide; 
    567942        } 
    568943 
    569944        if (newOptions.keys) 
     945            /* Enable keyboard support */ 
    570946            options.keys = $.extend({ shift: 1, ctrl: 'resize' }, 
    571947                newOptions.keys); 
    572948 
     949        /* Add classes to plugin elements */ 
    573950        $outer.addClass(options.classPrefix + '-outer'); 
    574951        $area.addClass(options.classPrefix + '-selection'); 
    575952        for (i = 0; i++ < 4;) 
    576953            $($border[i-1]).addClass(options.classPrefix + '-border' + i); 
    577954 
     955        /* Apply style options */ 
    578956        styleOptions($area, { selectionColor: 'background-color', 
    579957            selectionOpacity: 'opacity' }); 
    580958        styleOptions($border, { borderOpacity: 'opacity', 
     
    586964        if (o = options.borderColor2) 
    587965            $($border[1]).css({ borderStyle: 'dashed', borderColor: o }); 
    588966 
    589         $box.append($area.add($border).add($handles).add($areaOpera)); 
     967        /* Append all the selection area elements to the container box */ 
     968        $box.append($area.add($border).add($areaOpera).add($handles)); 
    590969 
    591970        if ($.browser.msie) { 
    592971            if (o = $outer.css('filter').match(/opacity=([0-9]+)/)) 
     
    594973            if (o = $border.css('filter').match(/opacity=([0-9]+)/)) 
    595974                $border.css('opacity', o[1]/100); 
    596975        } 
    597  
     976         
    598977        if (newOptions.hide) 
    599978            hide($box.add($outer)); 
    600979        else if (newOptions.show && imgLoaded) { 
     
    603982            doUpdate(); 
    604983        } 
    605984 
     985        /* Calculate the aspect ratio factor */ 
    606986        aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1]; 
    607987 
     988        $img.add($outer).unbind('mousedown', imgMouseDown); 
     989         
    608990        if (options.disable || options.enable === false) { 
     991            /* Disable the plugin */ 
    609992            $box.unbind('mousemove', areaMouseMove).unbind('mousedown', areaMouseDown); 
    610             $img.add($outer).unbind('mousedown', imgMouseDown); 
    611             $(window).unbind('resize', parentScroll); 
    612             $img.add($img.parents()).unbind('scroll', parentScroll); 
     993            $(window).unbind('resize', windowResize); 
    613994        } 
    614         else if (options.enable || options.disable === false) { 
    615             if (options.resizable || options.movable) 
    616                 $box.mousemove(areaMouseMove).mousedown(areaMouseDown); 
     995        else { 
     996            if (options.enable || options.disable === false) { 
     997                /* Enable the plugin */ 
     998                if (options.resizable || options.movable) 
     999                    $box.mousemove(areaMouseMove).mousedown(areaMouseDown); 
     1000     
     1001                $(window).resize(windowResize); 
     1002            } 
    6171003 
    6181004            if (!options.persistent) 
    6191005                $img.add($outer).mousedown(imgMouseDown); 
    620             $(window).resize(parentScroll); 
    621             $img.add($img.parents()).scroll(parentScroll); 
    6221006        } 
    623  
     1007         
    6241008        options.enable = options.disable = undefined; 
    6251009    } 
    626  
     1010     
     1011    /** 
     1012     * Remove plugin completely 
     1013     */ 
     1014    this.remove = function () { 
     1015        /* 
     1016         * Call setOptions with { disable: true } to unbind the event handlers 
     1017         */ 
     1018        setOptions({ disable: true }); 
     1019        $box.add($outer).remove(); 
     1020    }; 
     1021     
     1022    /* 
     1023     * Public API 
     1024     */ 
     1025     
     1026    /** 
     1027     * Get current options 
     1028     *  
     1029     * @return An object containing the set of options currently in use 
     1030     */ 
    6271031    this.getOptions = function () { return options; }; 
    628  
     1032     
     1033    /** 
     1034     * Set plugin options 
     1035     *  
     1036     * @param newOptions 
     1037     *            The new options object 
     1038     */ 
    6291039    this.setOptions = setOptions; 
    630  
     1040     
     1041    /** 
     1042     * Get the current selection 
     1043     *  
     1044     * @param noScale 
     1045     *            If set to <code>true</code>, scaling is not applied to the 
     1046     *            returned selection 
     1047     * @return Selection object 
     1048     */ 
    6311049    this.getSelection = getSelection; 
    632  
     1050     
     1051    /** 
     1052     * Set the current selection 
     1053     *  
     1054     * @param x1 
     1055     *            X coordinate of the upper left corner of the selection area 
     1056     * @param y1 
     1057     *            Y coordinate of the upper left corner of the selection area 
     1058     * @param x2 
     1059     *            X coordinate of the lower right corner of the selection area 
     1060     * @param y2 
     1061     *            Y coordinate of the lower right corner of the selection area 
     1062     * @param noScale 
     1063     *            If set to <code>true</code>, scaling is not applied to the 
     1064     *            new selection 
     1065     */ 
    6331066    this.setSelection = setSelection; 
    634  
     1067     
     1068    /** 
     1069     * Update plugin elements 
     1070     *  
     1071     * @param resetKeyPress 
     1072     *            If set to <code>false</code>, this instance's keypress 
     1073     *            event handler is not activated 
     1074     */ 
    6351075    this.update = doUpdate; 
    6361076 
     1077    /*  
     1078     * Traverse the image's parent elements (up to <body>) and find the 
     1079     * highest z-index 
     1080     */ 
    6371081    $p = $img; 
    6381082 
    639     while ($p.length && !$p.is('body')) { 
    640         if (!isNaN($p.css('z-index')) && $p.css('z-index') > zIndex) 
    641             zIndex = $p.css('z-index'); 
     1083    while ($p.length) { 
     1084        zIndex = max(zIndex, 
     1085            !isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex); 
     1086        /* Also check if any of the ancestor elements has fixed position */  
    6421087        if ($p.css('position') == 'fixed') 
    6431088            position = 'fixed'; 
    6441089 
    645         $p = $p.parent(); 
     1090        $p = $p.parent(':not(body)'); 
    6461091    } 
     1092     
     1093    /* 
     1094     * If z-index is given as an option, it overrides the one found by the 
     1095     * above loop 
     1096     */ 
     1097    zIndex = options.zIndex || zIndex; 
    6471098 
    648     if (!isNaN(options.zIndex)) 
    649         zIndex = options.zIndex; 
    650  
    6511099    if ($.browser.msie) 
    6521100        $img.attr('unselectable', 'on'); 
    6531101 
     1102    /* 
     1103     * In MSIE and WebKit, we need to use the keydown event instead of keypress 
     1104     */ 
    6541105    $.imgAreaSelect.keyPress = $.browser.msie || 
    6551106        $.browser.safari ? 'keydown' : 'keypress'; 
    6561107 
     1108    /* 
     1109     * There is a bug affecting the CSS cursor property in Opera (observed in 
     1110     * versions up to 10.00) that prevents the cursor from being updated unless 
     1111     * the mouse leaves and enters the element again. To trigger the mouseover 
     1112     * event, we're adding an additional div to $box and we're going to toggle 
     1113     * it when mouse moves inside the selection area. 
     1114     */ 
    6571115    if ($.browser.opera) 
    6581116        $areaOpera = div().css({ width: '100%', height: '100%', 
    6591117            position: 'absolute', zIndex: zIndex + 2 || 2 }); 
    6601118 
     1119    /* 
     1120     * We initially set visibility to "hidden" as a workaround for a weird 
     1121     * behaviour observed in Google Chrome 1.0.154.53 (on Windows XP). Normally 
     1122     * we would just set display to "none", but, for some reason, if we do so 
     1123     * then Chrome refuses to later display the element with .show() or 
     1124     * .fadeIn(). 
     1125     */ 
    6611126    $box.add($outer).css({ visibility: 'hidden', position: position, 
    6621127        overflow: 'hidden', zIndex: zIndex || '0' }); 
    6631128    $box.css({ zIndex: zIndex + 2 || 2 }); 
    664     $area.add($border).css({ position: 'absolute' }); 
    665  
     1129    $area.add($border).css({ position: 'absolute', fontSize: 0 }); 
     1130     
     1131    /* 
     1132     * If the image has been fully loaded, or if it is not really an image (eg. 
     1133     * a div), call imgLoad() immediately; otherwise, bind it to be called once 
     1134     * on image load event. 
     1135     */ 
    6661136    img.complete || img.readyState == 'complete' || !$img.is('img') ? 
    6671137        imgLoad() : $img.one('load', imgLoad); 
    6681138 
     1139    /*  
     1140     * MSIE 9.0 doesn't always fire the image load event -- resetting the src 
     1141     * attribute seems to trigger it. 
     1142     */    
     1143    if ($.browser.msie && $.browser.version >= 9) 
     1144        img.src = img.src; 
    6691145}; 
    6701146 
     1147/** 
     1148 * Invoke imgAreaSelect on a jQuery object containing the image(s) 
     1149 *  
     1150 * @param options 
     1151 *            Options object 
     1152 * @return The jQuery object or a reference to imgAreaSelect instance (if the 
     1153 *         <code>instance</code> option was specified) 
     1154 */ 
    6711155$.fn.imgAreaSelect = function (options) { 
    6721156    options = options || {}; 
    6731157 
    6741158    this.each(function () { 
    675         if ($(this).data('imgAreaSelect')) 
    676             $(this).data('imgAreaSelect').setOptions(options); 
    677         else { 
     1159        /* Is there already an imgAreaSelect instance bound to this element? */ 
     1160        if ($(this).data('imgAreaSelect')) { 
     1161            /* Yes there is -- is it supposed to be removed? */ 
     1162            if (options.remove) { 
     1163                /* Remove the plugin */ 
     1164                $(this).data('imgAreaSelect').remove(); 
     1165                $(this).removeData('imgAreaSelect'); 
     1166            } 
     1167            else 
     1168                /* Reset options */ 
     1169                $(this).data('imgAreaSelect').setOptions(options); 
     1170        } 
     1171        else if (!options.remove) { 
     1172            /* No exising instance -- create a new one */ 
     1173             
     1174            /* 
     1175             * If neither the "enable" nor the "disable" option is present, add 
     1176             * "enable" as the default 
     1177             */  
    6781178            if (options.enable === undefined && options.disable === undefined) 
    6791179                options.enable = true; 
    6801180 
    6811181            $(this).data('imgAreaSelect', new $.imgAreaSelect(this, options)); 
    6821182        } 
    6831183    }); 
    684  
     1184     
    6851185    if (options.instance) 
     1186        /* 
     1187         * Return the imgAreaSelect instance bound to the first element in the 
     1188         * set 
     1189         */ 
    6861190        return $(this).data('imgAreaSelect'); 
    6871191 
    6881192    return this;