2010-07-02 01:48:07 +02:00
/ * *
* Image Cropper ( v . 1.2 . 0 - 2006 - 10 - 30 )
* Copyright ( c ) 2006 David Spurr ( http : //www.defusion.org.uk/)
*
* The image cropper provides a way to draw a crop area on an image and capture
* the coordinates of the drawn crop area .
*
* Features include :
* - Based on Prototype and Scriptaculous
* - Image editing package styling , the crop area functions and looks
* like those found in popular image editing software
* - Dynamic inclusion of required styles
* - Drag to draw areas
* - Shift drag to draw / resize areas as squares
* - Selection area can be moved
* - Seleciton area can be resized using resize handles
* - Allows dimension ratio limited crop areas
* - Allows minimum dimension crop areas
* - Allows maximum dimesion crop areas
* - If both min & max dimension options set to the same value for a single axis , then the cropper will not
* display the resize handles as appropriate ( when min & max dimensions are passed for both axes this
* results in a 'fixed size' crop area )
* - Allows dynamic preview of resultant crop ( if minimum width & height are provided ) , this is
* implemented as a subclass so can be excluded when not required
* - Movement of selection area by arrow keys ( shift + arrow key will move selection area by
* 10 pixels )
* - All operations stay within bounds of image
* - All functionality & display compatible with most popular browsers supported by Prototype :
* PC : IE 7 , 6 & 5.5 , Firefox 1.5 , Opera 8.5 ( see known issues ) & 9.0 b
* MAC : Camino 1.0 , Firefox 1.5 , Safari 2.0
*
* Requires :
* - Prototype v . 1.5 . 0_ rc0 > ( as packaged with Scriptaculous 1.6 . 1 )
* - Scriptaculous v . 1.6 . 1 > modules : builder , dragdrop
*
* Known issues :
* - Safari animated gifs , only one of each will animate , this seems to be a known Safari issue
*
* - After drawing an area and then clicking to start a new drag in IE 5.5 the rendered height
* appears as the last height until the user drags , this appears to be the related to the error
* that the forceReRender ( ) method fixes for IE 6 , i . e . IE 5.5 is not redrawing the box properly .
*
* - Lack of CSS opacity support in Opera before version 9 mean we disable those style rules , these
* could be fixed by using PNGs with transparency if Opera 8.5 support is high priority for you
*
* - Marching ants keep reloading in IE < 6 ( not tested in IE7 ) , it is a known issue in IE and I have
* found no viable workarounds that can be included in the release . If this really is an issue for you
* either try this post : http : //mir.aculo.us/articles/2005/08/28/internet-explorer-and-ajax-image-caching-woes
* or uncomment the 'FIX MARCHING ANTS IN IE' rules in the CSS file
*
* - Styling & borders on image , any CSS styling applied directly to the image itself ( floats , borders , padding , margin , etc . ) will
* cause problems with the cropper . The use of a wrapper element to apply these styles to is recommended .
*
* - overflow : auto or overflow : scroll on parent will cause cropper to burst out of parent in IE and Opera ( maybe Mac browsers too )
* I ' m not sure why yet .
*
* Usage :
* See Cropper . Img & Cropper . ImgWithPreview for usage details
*
* Changelog :
* v1 . 2.0 - 2006 - 10 - 30
* + Added id to the preview image element using 'imgCrop_[originalImageID]'
* * # 00001 - Fixed bug : Doesn ' t account for scroll offsets
* * # 00009 - Fixed bug : Placing the cropper inside differently positioned elements causes incorrect co - ordinates and display
* * # 00013 - Fixed bug : I - bar cursor appears on drag plane
* * # 00014 - Fixed bug : If ID for image tag is not found in document script throws error
* * Fixed bug with drag start co - ordinates if wrapper element has moved in browser ( e . g . dragged to a new position )
* * Fixed bug with drag start co - ordinates if image contained in a wrapper with scrolling - this may be buggy if image
* has other ancestors with scrolling applied ( except the body )
* * # 00015 - Fixed bug : When cropper removed and then reapplied onEndCrop callback gets called multiple times , solution suggestion from Bill Smith
* * Various speed increases & code cleanup which meant improved performance in Mac - which allowed removal of different overlay methods for
* IE and all other browsers , which led to a fix for :
* * # 00010 - Fixed bug : Select area doesn ' t adhere to image size when image resized using img attributes
* - # 00006 - Removed default behaviour of automatically setting a ratio when both min width & height passed , the ratioDimensions must be passed in
* + # 00005 - Added ability to set maximum crop dimensions , if both min & max set as the same value then we ' ll get a fixed cropper size on the axes as appropriate
* and the resize handles will not be displayed as appropriate
* * Switched keydown for keypress for moving select area with cursor keys ( makes for nicer action ) - doesn ' t appear to work in Safari
*
* v1 . 1.3 - 2006 - 08 - 21
* * Fixed wrong cursor on western handle in CSS
* + # 00008 & # 00003 - Added feature : Allow to set dimensions & position for cropper on load
* * # 00002 - Fixed bug : Pressing 'remove cropper' twice removes image in IE
*
* v1 . 1.2 - 2006 - 06 - 09
* * Fixed bugs with ratios when GCD is low ( patch submitted by Andy Skelton )
*
* v1 . 1.1 - 2006 - 06 - 03
* * Fixed bug with rendering issues fix in IE 5.5
* * Fixed bug with endCrop callback issues once cropper had been removed & reset in IE
*
* v1 . 1.0 - 2006 - 06 - 02
* * Fixed bug with IE constantly trying to reload select area background image
* * Applied more robust fix to Safari & IE rendering issues
* + Added method to reset parameters - useful for when dynamically changing img cropper attached to
* + Added method to remove cropper from image
*
* v1 . 0.0 - 2006 - 05 - 18
* + Initial verison
*
*
* Copyright ( c ) 2006 , David Spurr ( http : //www.defusion.org.uk/)
* All rights reserved .
*
*
* Redistribution and use in source and binary forms , with or without modification , are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright notice , this list of conditions and the following disclaimer in the documentation and / or other materials provided with the distribution .
* * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* http : //www.opensource.org/licenses/bsd-license.php
*
* See scriptaculous . js for full scriptaculous licence
2013-06-01 18:42:51 +02:00
*
* Modified 2013 / 06 / 01 Zach P to change $ ( ) to $PR ( ) for eliminating conflicts with jQuery
2010-07-02 01:48:07 +02:00
* /
/ * *
* Extend the Draggable class to allow us to pass the rendering
* down to the Cropper object .
* /
var CropDraggable = Class . create ( ) ;
Object . extend ( Object . extend ( CropDraggable . prototype , Draggable . prototype ) , {
initialize : function ( element ) {
this . options = Object . extend (
{
/ * *
* The draw method to defer drawing to
* /
drawMethod : function ( ) { }
} ,
arguments [ 1 ] || { }
) ;
2013-06-01 18:42:51 +02:00
this . element = $PR ( element ) ;
2010-07-02 01:48:07 +02:00
this . handle = this . element ;
this . delta = this . currentDelta ( ) ;
this . dragging = false ;
this . eventMouseDown = this . initDrag . bindAsEventListener ( this ) ;
Event . observe ( this . handle , "mousedown" , this . eventMouseDown ) ;
Draggables . register ( this ) ;
} ,
/ * *
* Defers the drawing of the draggable to the supplied method
* /
draw : function ( point ) {
var pos = Position . cumulativeOffset ( this . element ) ;
var d = this . currentDelta ( ) ;
pos [ 0 ] -= d [ 0 ] ;
pos [ 1 ] -= d [ 1 ] ;
var p = [ 0 , 1 ] . map ( function ( i ) {
return ( point [ i ] - pos [ i ] - this . offset [ i ] )
} . bind ( this ) ) ;
this . options . drawMethod ( p ) ;
}
} ) ;
/ * *
* The Cropper object , this will attach itself to the provided image by wrapping it with
* the generated xHTML structure required by the cropper .
*
* Usage :
* @ param obj Image element to attach to
* @ param obj Optional options :
* - ratioDim obj
* The pixel dimensions to apply as a restrictive ratio , with properties x & y
*
* - minWidth int
* The minimum width for the select area in pixels
*
* - minHeight int
* The mimimum height for the select area in pixels
*
* - maxWidth int
* The maximum width for the select areas in pixels ( if both minWidth & maxWidth set to same the width of the cropper will be fixed )
*
* - maxHeight int
* The maximum height for the select areas in pixels ( if both minHeight & maxHeight set to same the height of the cropper will be fixed )
*
* - displayOnInit int
* Whether to display the select area on initialisation , only used when providing minimum width & height or ratio
*
* - onEndCrop func
* The callback function to provide the crop details to on end of a crop ( see below )
*
* - captureKeys boolean
* Whether to capture the keys for moving the select area , as these can cause some problems at the moment
*
* - onloadCoords obj
* A coordinates object with properties x1 , y1 , x2 & y2 ; for the coordinates of the select area to display onload
*
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
*
* The callback function provided via the onEndCrop option should accept the following parameters :
* - coords obj
* The coordinates object with properties x1 , y1 , x2 & y2 ; for the coordinates of the select area
*
* - dimensions obj
* The dimensions object with properites width & height ; for the dimensions of the select area
*
*
* Example :
* function onEndCrop ( coords , dimensions ) {
2013-06-01 18:42:51 +02:00
* $PR ( 'x1' ) . value = coords . x1 ;
* $PR ( 'y1' ) . value = coords . y1 ;
* $PR ( 'x2' ) . value = coords . x2 ;
* $PR ( 'y2' ) . value = coords . y2 ;
* $PR ( 'width' ) . value = dimensions . width ;
* $PR ( 'height' ) . value = dimensions . height ;
2010-07-02 01:48:07 +02:00
* }
*
* /
var Cropper = { } ;
Cropper . Img = Class . create ( ) ;
Cropper . Img . prototype = {
/ * *
* Initialises the class
*
* @ access public
* @ param obj Image element to attach to
* @ param obj Options
* @ return void
* /
initialize : function ( element , options ) {
this . options = Object . extend (
{
/ * *
* @ var obj
* The pixel dimensions to apply as a restrictive ratio
* /
ratioDim : { x : 0 , y : 0 } ,
/ * *
* @ var int
* The minimum pixel width , also used as restrictive ratio if min height passed too
* /
minWidth : 0 ,
/ * *
* @ var int
* The minimum pixel height , also used as restrictive ratio if min width passed too
* /
minHeight : 0 ,
/ * *
* @ var boolean
* Whether to display the select area on initialisation , only used when providing minimum width & height or ratio
* /
displayOnInit : false ,
/ * *
* @ var function
* The call back function to pass the final values to
* /
onEndCrop : Prototype . emptyFunction ,
/ * *
* @ var boolean
* Whether to capture key presses or not
* /
captureKeys : true ,
/ * *
* @ var obj Coordinate object x1 , y1 , x2 , y2
* The coordinates to optionally display the select area at onload
* /
onloadCoords : null ,
/ * *
* @ var int
* The maximum width for the select areas in pixels ( if both minWidth & maxWidth set to same the width of the cropper will be fixed )
* /
maxWidth : 0 ,
/ * *
* @ var int
* The maximum height for the select areas in pixels ( if both minHeight & maxHeight set to same the height of the cropper will be fixed )
* /
maxHeight : 0
} ,
options || { }
) ;
/ * *
* @ var obj
* The img node to attach to
* /
2013-06-01 18:42:51 +02:00
this . img = $PR ( element ) ;
2010-07-02 01:48:07 +02:00
/ * *
* @ var obj
* The x & y coordinates of the click point
* /
this . clickCoords = { x : 0 , y : 0 } ;
/ * *
* @ var boolean
* Whether the user is dragging
* /
this . dragging = false ;
/ * *
* @ var boolean
* Whether the user is resizing
* /
this . resizing = false ;
/ * *
* @ var boolean
* Whether the user is on a webKit browser
* /
this . isWebKit = /Konqueror|Safari|KHTML/ . test ( navigator . userAgent ) ;
/ * *
* @ var boolean
* Whether the user is on IE
* /
this . isIE = /MSIE/ . test ( navigator . userAgent ) ;
/ * *
* @ var boolean
* Whether the user is on Opera below version 9
* /
this . isOpera8 = /Opera\s[1-8]/ . test ( navigator . userAgent ) ;
/ * *
* @ var int
* The x ratio
* /
this . ratioX = 0 ;
/ * *
* @ var int
* The y ratio
* /
this . ratioY = 0 ;
/ * *
* @ var boolean
* Whether we ' ve attached sucessfully
* /
this . attached = false ;
/ * *
* @ var boolean
* Whether we ' ve got a fixed width ( if minWidth EQ or GT maxWidth then we have a fixed width
* in the case of minWidth > maxWidth maxWidth wins as the fixed width )
* /
this . fixedWidth = ( this . options . maxWidth > 0 && ( this . options . minWidth >= this . options . maxWidth ) ) ;
/ * *
* @ var boolean
* Whether we ' ve got a fixed height ( if minHeight EQ or GT maxHeight then we have a fixed height
* in the case of minHeight > maxHeight maxHeight wins as the fixed height )
* /
this . fixedHeight = ( this . options . maxHeight > 0 && ( this . options . minHeight >= this . options . maxHeight ) ) ;
// quit if the image element doesn't exist
if ( typeof this . img == 'undefined' ) return ;
// include the stylesheet
$A ( document . getElementsByTagName ( 'script' ) ) . each (
function ( s ) {
if ( s . src . match ( /cropper\.js/ ) ) {
var path = s . src . replace ( /cropper\.js(.*)?/ , '' ) ;
// '<link rel="stylesheet" type="text/css" href="' + path + 'cropper.css" media="screen" />';
var style = document . createElement ( 'link' ) ;
style . rel = 'stylesheet' ;
style . type = 'text/css' ;
style . href = path + 'cropper.css' ;
style . media = 'screen' ;
document . getElementsByTagName ( 'head' ) [ 0 ] . appendChild ( style ) ;
}
}
) ;
// calculate the ratio when neccessary
if ( this . options . ratioDim . x > 0 && this . options . ratioDim . y > 0 ) {
var gcd = this . getGCD ( this . options . ratioDim . x , this . options . ratioDim . y ) ;
this . ratioX = this . options . ratioDim . x / gcd ;
this . ratioY = this . options . ratioDim . y / gcd ;
// dump( 'RATIO : ' + this.ratioX + ':' + this.ratioY + '\n' );
}
// initialise sub classes
this . subInitialize ( ) ;
// only load the event observers etc. once the image is loaded
// this is done after the subInitialize() call just in case the sub class does anything
// that will affect the result of the call to onLoad()
if ( this . img . complete || this . isWebKit ) this . onLoad ( ) ; // for some reason Safari seems to support img.complete but returns 'undefined' on the this.img object
else Event . observe ( this . img , 'load' , this . onLoad . bindAsEventListener ( this ) ) ;
} ,
/ * *
* The Euclidean algorithm used to find the greatest common divisor
*
* @ acces private
* @ param int Value 1
* @ param int Value 2
* @ return int
* /
getGCD : function ( a , b ) {
if ( b == 0 ) return a ;
return this . getGCD ( b , a % b ) ;
} ,
/ * *
* Attaches the cropper to the image once it has loaded
*
* @ access private
* @ return void
* /
onLoad : function ( ) {
/ *
* Build the container and all related elements , will result in the following
*
* < div class = "imgCrop_wrap" >
* < img ... this . img ... / >
* < div class = "imgCrop_dragArea" >
* <!-- the inner spans are only required for IE to stop it making the divs 1 px high / wide -- >
* < div class = "imgCrop_overlay imageCrop_north" > < span > < / s p a n > < / d i v >
* < div class = "imgCrop_overlay imageCrop_east" > < span > < / s p a n > < / d i v >
* < div class = "imgCrop_overlay imageCrop_south" > < span > < / s p a n > < / d i v >
* < div class = "imgCrop_overlay imageCrop_west" > < span > < / s p a n > < / d i v >
* < div class = "imgCrop_selArea" >
* <!-- marquees -- >
* <!-- the inner spans are only required for IE to stop it making the divs 1 px high / wide -- >
* < div class = "imgCrop_marqueeHoriz imgCrop_marqueeNorth" > < span > < / s p a n > < / d i v >
* < div class = "imgCrop_marqueeVert imgCrop_marqueeEast" > < span > < / s p a n > < / d i v >
* < div class = "imgCrop_marqueeHoriz imgCrop_marqueeSouth" > < span > < / s p a n > < / d i v >
* < div class = "imgCrop_marqueeVert imgCrop_marqueeWest" > < span > < / s p a n > < / d i v >
* <!-- handles -- >
* < div class = "imgCrop_handle imgCrop_handleN" > < / d i v >
* < div class = "imgCrop_handle imgCrop_handleNE" > < / d i v >
* < div class = "imgCrop_handle imgCrop_handleE" > < / d i v >
* < div class = "imgCrop_handle imgCrop_handleSE" > < / d i v >
* < div class = "imgCrop_handle imgCrop_handleS" > < / d i v >
* < div class = "imgCrop_handle imgCrop_handleSW" > < / d i v >
* < div class = "imgCrop_handle imgCrop_handleW" > < / d i v >
* < div class = "imgCrop_handle imgCrop_handleNW" > < / d i v >
* < div class = "imgCrop_clickArea" > < / d i v >
* < / d i v >
* < div class = "imgCrop_clickArea" > < / d i v >
* < / d i v >
* < / d i v >
* /
var cNamePrefix = 'imgCrop_' ;
// get the point to insert the container
var insertPoint = this . img . parentNode ;
// apply an extra class to the wrapper to fix Opera below version 9
var fixOperaClass = '' ;
if ( this . isOpera8 ) fixOperaClass = ' opera8' ;
this . imgWrap = Builder . node ( 'div' , { 'class' : cNamePrefix + 'wrap' + fixOperaClass } ) ;
this . north = Builder . node ( 'div' , { 'class' : cNamePrefix + 'overlay ' + cNamePrefix + 'north' } , [ Builder . node ( 'span' ) ] ) ;
this . east = Builder . node ( 'div' , { 'class' : cNamePrefix + 'overlay ' + cNamePrefix + 'east' } , [ Builder . node ( 'span' ) ] ) ;
this . south = Builder . node ( 'div' , { 'class' : cNamePrefix + 'overlay ' + cNamePrefix + 'south' } , [ Builder . node ( 'span' ) ] ) ;
this . west = Builder . node ( 'div' , { 'class' : cNamePrefix + 'overlay ' + cNamePrefix + 'west' } , [ Builder . node ( 'span' ) ] ) ;
var overlays = [ this . north , this . east , this . south , this . west ] ;
this . dragArea = Builder . node ( 'div' , { 'class' : cNamePrefix + 'dragArea' } , overlays ) ;
this . handleN = Builder . node ( 'div' , { 'class' : cNamePrefix + 'handle ' + cNamePrefix + 'handleN' } ) ;
this . handleNE = Builder . node ( 'div' , { 'class' : cNamePrefix + 'handle ' + cNamePrefix + 'handleNE' } ) ;
this . handleE = Builder . node ( 'div' , { 'class' : cNamePrefix + 'handle ' + cNamePrefix + 'handleE' } ) ;
this . handleSE = Builder . node ( 'div' , { 'class' : cNamePrefix + 'handle ' + cNamePrefix + 'handleSE' } ) ;
this . handleS = Builder . node ( 'div' , { 'class' : cNamePrefix + 'handle ' + cNamePrefix + 'handleS' } ) ;
this . handleSW = Builder . node ( 'div' , { 'class' : cNamePrefix + 'handle ' + cNamePrefix + 'handleSW' } ) ;
this . handleW = Builder . node ( 'div' , { 'class' : cNamePrefix + 'handle ' + cNamePrefix + 'handleW' } ) ;
this . handleNW = Builder . node ( 'div' , { 'class' : cNamePrefix + 'handle ' + cNamePrefix + 'handleNW' } ) ;
this . selArea = Builder . node ( 'div' , { 'class' : cNamePrefix + 'selArea' } ,
[
Builder . node ( 'div' , { 'class' : cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeNorth' } , [ Builder . node ( 'span' ) ] ) ,
Builder . node ( 'div' , { 'class' : cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeEast' } , [ Builder . node ( 'span' ) ] ) ,
Builder . node ( 'div' , { 'class' : cNamePrefix + 'marqueeHoriz ' + cNamePrefix + 'marqueeSouth' } , [ Builder . node ( 'span' ) ] ) ,
Builder . node ( 'div' , { 'class' : cNamePrefix + 'marqueeVert ' + cNamePrefix + 'marqueeWest' } , [ Builder . node ( 'span' ) ] ) ,
this . handleN ,
this . handleNE ,
this . handleE ,
this . handleSE ,
this . handleS ,
this . handleSW ,
this . handleW ,
this . handleNW ,
Builder . node ( 'div' , { 'class' : cNamePrefix + 'clickArea' } )
]
) ;
this . imgWrap . appendChild ( this . img ) ;
this . imgWrap . appendChild ( this . dragArea ) ;
this . dragArea . appendChild ( this . selArea ) ;
this . dragArea . appendChild ( Builder . node ( 'div' , { 'class' : cNamePrefix + 'clickArea' } ) ) ;
insertPoint . appendChild ( this . imgWrap ) ;
// add event observers
this . startDragBind = this . startDrag . bindAsEventListener ( this ) ;
Event . observe ( this . dragArea , 'mousedown' , this . startDragBind ) ;
this . onDragBind = this . onDrag . bindAsEventListener ( this ) ;
Event . observe ( document , 'mousemove' , this . onDragBind ) ;
this . endCropBind = this . endCrop . bindAsEventListener ( this ) ;
Event . observe ( document , 'mouseup' , this . endCropBind ) ;
this . resizeBind = this . startResize . bindAsEventListener ( this ) ;
this . handles = [ this . handleN , this . handleNE , this . handleE , this . handleSE , this . handleS , this . handleSW , this . handleW , this . handleNW ] ;
this . registerHandles ( true ) ;
if ( this . options . captureKeys ) {
this . keysBind = this . handleKeys . bindAsEventListener ( this ) ;
Event . observe ( document , 'keypress' , this . keysBind ) ;
}
// attach the dragable to the select area
new CropDraggable ( this . selArea , { drawMethod : this . moveArea . bindAsEventListener ( this ) } ) ;
this . setParams ( ) ;
} ,
/ * *
* Manages adding or removing the handle event handler and hiding or displaying them as appropriate
*
* @ access private
* @ param boolean registration true = add , false = remove
* @ return void
* /
registerHandles : function ( registration ) {
for ( var i = 0 ; i < this . handles . length ; i ++ ) {
2013-06-01 18:42:51 +02:00
var handle = $PR ( this . handles [ i ] ) ;
2010-07-02 01:48:07 +02:00
if ( registration ) {
var hideHandle = false ; // whether to hide the handle
// disable handles asappropriate if we've got fixed dimensions
// if both dimensions are fixed we don't need to do much
if ( this . fixedWidth && this . fixedHeight ) hideHandle = true ;
else if ( this . fixedWidth || this . fixedHeight ) {
// if one of the dimensions is fixed then just hide those handles
var isCornerHandle = handle . className . match ( /([S|N][E|W])$/ )
var isWidthHandle = handle . className . match ( /(E|W)$/ ) ;
var isHeightHandle = handle . className . match ( /(N|S)$/ ) ;
if ( isCornerHandle ) hideHandle = true ;
else if ( this . fixedWidth && isWidthHandle ) hideHandle = true ;
else if ( this . fixedHeight && isHeightHandle ) hideHandle = true ;
}
if ( hideHandle ) handle . hide ( ) ;
else Event . observe ( handle , 'mousedown' , this . resizeBind ) ;
} else {
handle . show ( ) ;
Event . stopObserving ( handle , 'mousedown' , this . resizeBind ) ;
}
}
} ,
/ * *
* Sets up all the cropper parameters , this can be used to reset the cropper when dynamically
* changing the images
*
* @ access private
* @ return void
* /
setParams : function ( ) {
/ * *
* @ var int
* The image width
* /
this . imgW = this . img . width ;
/ * *
* @ var int
* The image height
* /
this . imgH = this . img . height ;
2013-06-01 18:42:51 +02:00
$PR ( this . north ) . setStyle ( { height : 0 } ) ;
$PR ( this . east ) . setStyle ( { width : 0 , height : 0 } ) ;
$PR ( this . south ) . setStyle ( { height : 0 } ) ;
$PR ( this . west ) . setStyle ( { width : 0 , height : 0 } ) ;
2010-07-02 01:48:07 +02:00
// resize the container to fit the image
2013-06-01 18:42:51 +02:00
$PR ( this . imgWrap ) . setStyle ( { 'width' : this . imgW + 'px' , 'height' : this . imgH + 'px' } ) ;
2010-07-02 01:48:07 +02:00
// hide the select area
2013-06-01 18:42:51 +02:00
$PR ( this . selArea ) . hide ( ) ;
2010-07-02 01:48:07 +02:00
// setup the starting position of the select area
var startCoords = { x1 : 0 , y1 : 0 , x2 : 0 , y2 : 0 } ;
var validCoordsSet = false ;
// display the select area
if ( this . options . onloadCoords != null ) {
// if we've being given some coordinates to
startCoords = this . cloneCoords ( this . options . onloadCoords ) ;
validCoordsSet = true ;
} else if ( this . options . ratioDim . x > 0 && this . options . ratioDim . y > 0 ) {
// if there is a ratio limit applied and the then set it to initial ratio
startCoords . x1 = Math . ceil ( ( this . imgW - this . options . ratioDim . x ) / 2 ) ;
startCoords . y1 = Math . ceil ( ( this . imgH - this . options . ratioDim . y ) / 2 ) ;
startCoords . x2 = startCoords . x1 + this . options . ratioDim . x ;
startCoords . y2 = startCoords . y1 + this . options . ratioDim . y ;
validCoordsSet = true ;
}
this . setAreaCoords ( startCoords , false , false , 1 ) ;
if ( this . options . displayOnInit && validCoordsSet ) {
this . selArea . show ( ) ;
this . drawArea ( ) ;
this . endCrop ( ) ;
}
this . attached = true ;
} ,
/ * *
* Removes the cropper
*
* @ access public
* @ return void
* /
remove : function ( ) {
if ( this . attached ) {
this . attached = false ;
// remove the elements we inserted
this . imgWrap . parentNode . insertBefore ( this . img , this . imgWrap ) ;
this . imgWrap . parentNode . removeChild ( this . imgWrap ) ;
// remove the event observers
Event . stopObserving ( this . dragArea , 'mousedown' , this . startDragBind ) ;
Event . stopObserving ( document , 'mousemove' , this . onDragBind ) ;
Event . stopObserving ( document , 'mouseup' , this . endCropBind ) ;
this . registerHandles ( false ) ;
if ( this . options . captureKeys ) Event . stopObserving ( document , 'keypress' , this . keysBind ) ;
}
} ,
/ * *
* Resets the cropper , can be used either after being removed or any time you wish
*
* @ access public
* @ return void
* /
reset : function ( ) {
if ( ! this . attached ) this . onLoad ( ) ;
else this . setParams ( ) ;
this . endCrop ( ) ;
} ,
/ * *
* Handles the key functionality , currently just using arrow keys to move , if the user
* presses shift then the area will move by 10 pixels
* /
handleKeys : function ( e ) {
var dir = { x : 0 , y : 0 } ; // direction to move it in & the amount in pixels
if ( ! this . dragging ) {
// catch the arrow keys
switch ( e . keyCode ) {
case ( 37 ) : // left
dir . x = - 1 ;
break ;
case ( 38 ) : // up
dir . y = - 1 ;
break ;
case ( 39 ) : // right
dir . x = 1 ;
break
case ( 40 ) : // down
dir . y = 1 ;
break ;
}
if ( dir . x != 0 || dir . y != 0 ) {
// if shift is pressed then move by 10 pixels
if ( e . shiftKey ) {
dir . x *= 10 ;
dir . y *= 10 ;
}
this . moveArea ( [ this . areaCoords . x1 + dir . x , this . areaCoords . y1 + dir . y ] ) ;
Event . stop ( e ) ;
}
}
} ,
/ * *
* Calculates the width from the areaCoords
*
* @ access private
* @ return int
* /
calcW : function ( ) {
return ( this . areaCoords . x2 - this . areaCoords . x1 )
} ,
/ * *
* Calculates the height from the areaCoords
*
* @ access private
* @ return int
* /
calcH : function ( ) {
return ( this . areaCoords . y2 - this . areaCoords . y1 )
} ,
/ * *
* Moves the select area to the supplied point ( assumes the point is x1 & y1 of the select area )
*
* @ access public
* @ param array Point for x1 & y1 to move select area to
* @ return void
* /
moveArea : function ( point ) {
// dump( 'moveArea : ' + point[0] + ',' + point[1] + ',' + ( point[0] + ( this.areaCoords.x2 - this.areaCoords.x1 ) ) + ',' + ( point[1] + ( this.areaCoords.y2 - this.areaCoords.y1 ) ) + '\n' );
this . setAreaCoords (
{
x1 : point [ 0 ] ,
y1 : point [ 1 ] ,
x2 : point [ 0 ] + this . calcW ( ) ,
y2 : point [ 1 ] + this . calcH ( )
} ,
true ,
false
) ;
this . drawArea ( ) ;
} ,
/ * *
* Clones a co - ordinates object , stops problems with handling them by reference
*
* @ access private
* @ param obj Coordinate object x1 , y1 , x2 , y2
* @ return obj Coordinate object x1 , y1 , x2 , y2
* /
cloneCoords : function ( coords ) {
return { x1 : coords . x1 , y1 : coords . y1 , x2 : coords . x2 , y2 : coords . y2 } ;
} ,
/ * *
* Sets the select coords to those provided but ensures they don ' t go
* outside the bounding box
*
* @ access private
* @ param obj Coordinates x1 , y1 , x2 , y2
* @ param boolean Whether this is a move
* @ param boolean Whether to apply squaring
* @ param obj Direction of mouse along both axis x , y ( - 1 = negative , 1 = positive ) only required when moving etc .
* @ param string The current resize handle || null
* @ return void
* /
setAreaCoords : function ( coords , moving , square , direction , resizeHandle ) {
// dump( 'setAreaCoords (in) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 );
if ( moving ) {
// if moving
var targW = coords . x2 - coords . x1 ;
var targH = coords . y2 - coords . y1 ;
// ensure we're within the bounds
if ( coords . x1 < 0 ) {
coords . x1 = 0 ;
coords . x2 = targW ;
}
if ( coords . y1 < 0 ) {
coords . y1 = 0 ;
coords . y2 = targH ;
}
if ( coords . x2 > this . imgW ) {
coords . x2 = this . imgW ;
coords . x1 = this . imgW - targW ;
}
if ( coords . y2 > this . imgH ) {
coords . y2 = this . imgH ;
coords . y1 = this . imgH - targH ;
}
} else {
// ensure we're within the bounds
if ( coords . x1 < 0 ) coords . x1 = 0 ;
if ( coords . y1 < 0 ) coords . y1 = 0 ;
if ( coords . x2 > this . imgW ) coords . x2 = this . imgW ;
if ( coords . y2 > this . imgH ) coords . y2 = this . imgH ;
// This is passed as null in onload
if ( direction != null ) {
// apply the ratio or squaring where appropriate
if ( this . ratioX > 0 ) this . applyRatio ( coords , { x : this . ratioX , y : this . ratioY } , direction , resizeHandle ) ;
else if ( square ) this . applyRatio ( coords , { x : 1 , y : 1 } , direction , resizeHandle ) ;
var mins = [ this . options . minWidth , this . options . minHeight ] ; // minimum dimensions [x,y]
var maxs = [ this . options . maxWidth , this . options . maxHeight ] ; // maximum dimensions [x,y]
// apply dimensions where appropriate
if ( mins [ 0 ] > 0 || mins [ 1 ] > 0 || maxs [ 0 ] > 0 || maxs [ 1 ] > 0 ) {
var coordsTransX = { a1 : coords . x1 , a2 : coords . x2 } ;
var coordsTransY = { a1 : coords . y1 , a2 : coords . y2 } ;
var boundsX = { min : 0 , max : this . imgW } ;
var boundsY = { min : 0 , max : this . imgH } ;
// handle squaring properly on single axis minimum dimensions
if ( ( mins [ 0 ] != 0 || mins [ 1 ] != 0 ) && square ) {
if ( mins [ 0 ] > 0 ) mins [ 1 ] = mins [ 0 ] ;
else if ( mins [ 1 ] > 0 ) mins [ 0 ] = mins [ 1 ] ;
}
if ( ( maxs [ 0 ] != 0 || maxs [ 0 ] != 0 ) && square ) {
// if we have a max x value & it is less than the max y value then we set the y max to the max x (so we don't go over the minimum maximum of one of the axes - if that makes sense)
if ( maxs [ 0 ] > 0 && maxs [ 0 ] <= maxs [ 1 ] ) maxs [ 1 ] = maxs [ 0 ] ;
else if ( maxs [ 1 ] > 0 && maxs [ 1 ] <= maxs [ 0 ] ) maxs [ 0 ] = maxs [ 1 ] ;
}
if ( mins [ 0 ] > 0 ) this . applyDimRestriction ( coordsTransX , mins [ 0 ] , direction . x , boundsX , 'min' ) ;
if ( mins [ 1 ] > 1 ) this . applyDimRestriction ( coordsTransY , mins [ 1 ] , direction . y , boundsY , 'min' ) ;
if ( maxs [ 0 ] > 0 ) this . applyDimRestriction ( coordsTransX , maxs [ 0 ] , direction . x , boundsX , 'max' ) ;
if ( maxs [ 1 ] > 1 ) this . applyDimRestriction ( coordsTransY , maxs [ 1 ] , direction . y , boundsY , 'max' ) ;
coords = { x1 : coordsTransX . a1 , y1 : coordsTransY . a1 , x2 : coordsTransX . a2 , y2 : coordsTransY . a2 } ;
}
}
}
// dump( 'setAreaCoords (out) : ' + coords.x1 + ',' + coords.y1 + ',' + coords.x2 + ',' + coords.y2 + '\n' );
this . areaCoords = coords ;
} ,
/ * *
* Applies the supplied dimension restriction to the supplied coordinates along a single axis
*
* @ access private
* @ param obj Single axis coordinates , a1 , a2 ( e . g . for the x axis a1 = x1 & a2 = x2 )
* @ param int The restriction value
* @ param int The direction ( - 1 = negative , 1 = positive )
* @ param obj The bounds of the image ( for this axis )
* @ param string The dimension restriction type ( 'min' | 'max' )
* @ return void
* /
applyDimRestriction : function ( coords , val , direction , bounds , type ) {
var check ;
if ( type == 'min' ) check = ( ( coords . a2 - coords . a1 ) < val ) ;
else check = ( ( coords . a2 - coords . a1 ) > val ) ;
if ( check ) {
if ( direction == 1 ) coords . a2 = coords . a1 + val ;
else coords . a1 = coords . a2 - val ;
// make sure we're still in the bounds (not too pretty for the user, but needed)
if ( coords . a1 < bounds . min ) {
coords . a1 = bounds . min ;
coords . a2 = val ;
} else if ( coords . a2 > bounds . max ) {
coords . a1 = bounds . max - val ;
coords . a2 = bounds . max ;
}
}
} ,
/ * *
* Applies the supplied ratio to the supplied coordinates
*
* @ access private
* @ param obj Coordinates , x1 , y1 , x2 , y2
* @ param obj Ratio , x , y
* @ param obj Direction of mouse , x & y : - 1 == negative 1 == positive
* @ param string The current resize handle || null
* @ return void
* /
applyRatio : function ( coords , ratio , direction , resizeHandle ) {
// dump( 'direction.y : ' + direction.y + '\n');
var newCoords ;
if ( resizeHandle == 'N' || resizeHandle == 'S' ) {
// dump( 'north south \n');
// if moving on either the lone north & south handles apply the ratio on the y axis
newCoords = this . applyRatioToAxis (
{ a1 : coords . y1 , b1 : coords . x1 , a2 : coords . y2 , b2 : coords . x2 } ,
{ a : ratio . y , b : ratio . x } ,
{ a : direction . y , b : direction . x } ,
{ min : 0 , max : this . imgW }
) ;
coords . x1 = newCoords . b1 ;
coords . y1 = newCoords . a1 ;
coords . x2 = newCoords . b2 ;
coords . y2 = newCoords . a2 ;
} else {
// otherwise deal with it as if we're applying the ratio on the x axis
newCoords = this . applyRatioToAxis (
{ a1 : coords . x1 , b1 : coords . y1 , a2 : coords . x2 , b2 : coords . y2 } ,
{ a : ratio . x , b : ratio . y } ,
{ a : direction . x , b : direction . y } ,
{ min : 0 , max : this . imgH }
) ;
coords . x1 = newCoords . a1 ;
coords . y1 = newCoords . b1 ;
coords . x2 = newCoords . a2 ;
coords . y2 = newCoords . b2 ;
}
} ,
/ * *
* Applies the provided ratio to the provided coordinates based on provided direction & bounds ,
* use to encapsulate functionality to make it easy to apply to either axis . This is probably
* quite hard to visualise so see the x axis example within applyRatio ( )
*
* Example in parameter details & comments is for requesting applying ratio to x axis .
*
* @ access private
* @ param obj Coords object ( a1 , b1 , a2 , b2 ) where a = x & b = y in example
* @ param obj Ratio object ( a , b ) where a = x & b = y in example
* @ param obj Direction object ( a , b ) where a = x & b = y in example
* @ param obj Bounds ( min , max )
* @ return obj Coords object ( a1 , b1 , a2 , b2 ) where a = x & b = y in example
* /
applyRatioToAxis : function ( coords , ratio , direction , bounds ) {
var newCoords = Object . extend ( coords , { } ) ;
var calcDimA = newCoords . a2 - newCoords . a1 ; // calculate dimension a (e.g. width)
var targDimB = Math . floor ( calcDimA * ratio . b / ratio . a ) ; // the target dimension b (e.g. height)
var targB ; // to hold target b (e.g. y value)
var targDimA ; // to hold target dimension a (e.g. width)
var calcDimB = null ; // to hold calculated dimension b (e.g. height)
// dump( 'newCoords[0]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
if ( direction . b == 1 ) { // if travelling in a positive direction
// make sure we're not going out of bounds
targB = newCoords . b1 + targDimB ;
if ( targB > bounds . max ) {
targB = bounds . max ;
calcDimB = targB - newCoords . b1 ; // calcuate dimension b (e.g. height)
}
newCoords . b2 = targB ;
} else { // if travelling in a negative direction
// make sure we're not going out of bounds
targB = newCoords . b2 - targDimB ;
if ( targB < bounds . min ) {
targB = bounds . min ;
calcDimB = targB + newCoords . b2 ; // calcuate dimension b (e.g. height)
}
newCoords . b1 = targB ;
}
// dump( 'newCoords[1]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
// apply the calculated dimensions
if ( calcDimB != null ) {
targDimA = Math . floor ( calcDimB * ratio . a / ratio . b ) ;
if ( direction . a == 1 ) newCoords . a2 = newCoords . a1 + targDimA ;
else newCoords . a1 = newCoords . a1 = newCoords . a2 - targDimA ;
}
// dump( 'newCoords[2]: ' + newCoords.a1 + ',' + newCoords.b1 + ','+ newCoords.a2 + ',' + newCoords.b2 + '\n');
return newCoords ;
} ,
/ * *
* Draws the select area
*
* @ access private
* @ return void
* /
drawArea : function ( ) {
/ *
* NOTE : I ' m not using the Element . setStyle ( ) shortcut as they make it
* quite sluggish on Mac based browsers
* /
// dump( 'drawArea : ' + this.areaCoords.x1 + ',' + this.areaCoords.y1 + ',' + this.areaCoords.x2 + ',' + this.areaCoords.y2 + '\n' );
var areaWidth = this . calcW ( ) ;
var areaHeight = this . calcH ( ) ;
/ *
* Calculate all the style strings before we use them , allows reuse & produces quicker
* rendering ( especially noticable in Mac based browsers )
* /
var px = 'px' ;
var params = [
this . areaCoords . x1 + px , // the left of the selArea
this . areaCoords . y1 + px , // the top of the selArea
areaWidth + px , // width of the selArea
areaHeight + px , // height of the selArea
this . areaCoords . x2 + px , // bottom of the selArea
this . areaCoords . y2 + px , // right of the selArea
( this . img . width - this . areaCoords . x2 ) + px , // right edge of selArea
( this . img . height - this . areaCoords . y2 ) + px // bottom edge of selArea
] ;
// do the select area
var areaStyle = this . selArea . style ;
areaStyle . left = params [ 0 ] ;
areaStyle . top = params [ 1 ] ;
areaStyle . width = params [ 2 ] ;
areaStyle . height = params [ 3 ] ;
// position the north, east, south & west handles
var horizHandlePos = Math . ceil ( ( areaWidth - 6 ) / 2 ) + px ;
var vertHandlePos = Math . ceil ( ( areaHeight - 6 ) / 2 ) + px ;
this . handleN . style . left = horizHandlePos ;
this . handleE . style . top = vertHandlePos ;
this . handleS . style . left = horizHandlePos ;
this . handleW . style . top = vertHandlePos ;
// draw the four overlays
this . north . style . height = params [ 1 ] ;
var eastStyle = this . east . style ;
eastStyle . top = params [ 1 ] ;
eastStyle . height = params [ 3 ] ;
eastStyle . left = params [ 4 ] ;
eastStyle . width = params [ 6 ] ;
var southStyle = this . south . style ;
southStyle . top = params [ 5 ] ;
southStyle . height = params [ 7 ] ;
var westStyle = this . west . style ;
westStyle . top = params [ 1 ] ;
westStyle . height = params [ 3 ] ;
westStyle . width = params [ 0 ] ;
// call the draw method on sub classes
this . subDrawArea ( ) ;
this . forceReRender ( ) ;
} ,
/ * *
* Force the re - rendering of the selArea element which fixes rendering issues in Safari
* & IE PC , especially evident when re - sizing perfectly vertical using any of the south handles
*
* @ access private
* @ return void
* /
forceReRender : function ( ) {
if ( this . isIE || this . isWebKit ) {
var n = document . createTextNode ( ' ' ) ;
var d , el , fixEL , i ;
if ( this . isIE ) fixEl = this . selArea ;
else if ( this . isWebKit ) {
fixEl = document . getElementsByClassName ( 'imgCrop_marqueeSouth' , this . imgWrap ) [ 0 ] ;
/ * w e h a v e t o b e a b i t m o r e f o r c e f u l f o r S a f a r i , o t h e r w i s e t h e t h e m a r q u e e &
* the south handles still don ' t move
* /
d = Builder . node ( 'div' , '' ) ;
d . style . visibility = 'hidden' ;
var classList = [ 'SE' , 'S' , 'SW' ] ;
for ( i = 0 ; i < classList . length ; i ++ ) {
el = document . getElementsByClassName ( 'imgCrop_handle' + classList [ i ] , this . selArea ) [ 0 ] ;
if ( el . childNodes . length ) el . removeChild ( el . childNodes [ 0 ] ) ;
el . appendChild ( d ) ;
}
}
fixEl . appendChild ( n ) ;
fixEl . removeChild ( n ) ;
}
} ,
/ * *
* Starts the resize
*
* @ access private
* @ param obj Event
* @ return void
* /
startResize : function ( e ) {
this . startCoords = this . cloneCoords ( this . areaCoords ) ;
this . resizing = true ;
this . resizeHandle = Event . element ( e ) . classNames ( ) . toString ( ) . replace ( /([^N|NE|E|SE|S|SW|W|NW])+/ , '' ) ;
// dump( 'this.resizeHandle : ' + this.resizeHandle + '\n' );
Event . stop ( e ) ;
} ,
/ * *
* Starts the drag
*
* @ access private
* @ param obj Event
* @ return void
* /
startDrag : function ( e ) {
this . selArea . show ( ) ;
this . clickCoords = this . getCurPos ( e ) ;
this . setAreaCoords ( { x1 : this . clickCoords . x , y1 : this . clickCoords . y , x2 : this . clickCoords . x , y2 : this . clickCoords . y } , false , false , null ) ;
this . dragging = true ;
this . onDrag ( e ) ; // incase the user just clicks once after already making a selection
Event . stop ( e ) ;
} ,
/ * *
* Gets the current cursor position relative to the image
*
* @ access private
* @ param obj Event
* @ return obj x , y pixels of the cursor
* /
getCurPos : function ( e ) {
// get the offsets for the wrapper within the document
var el = this . imgWrap , wrapOffsets = Position . cumulativeOffset ( el ) ;
// remove any scrolling that is applied to the wrapper (this may be buggy) - don't count the scroll on the body as that won't affect us
while ( el . nodeName != 'BODY' ) {
wrapOffsets [ 1 ] -= el . scrollTop || 0 ;
wrapOffsets [ 0 ] -= el . scrollLeft || 0 ;
el = el . parentNode ;
}
return curPos = {
x : Event . pointerX ( e ) - wrapOffsets [ 0 ] ,
y : Event . pointerY ( e ) - wrapOffsets [ 1 ]
}
} ,
/ * *
* Performs the drag for both resize & inital draw dragging
*
* @ access private
* @ param obj Event
* @ return void
* /
onDrag : function ( e ) {
if ( this . dragging || this . resizing ) {
var resizeHandle = null ;
var curPos = this . getCurPos ( e ) ;
var newCoords = this . cloneCoords ( this . areaCoords ) ;
var direction = { x : 1 , y : 1 } ;
if ( this . dragging ) {
if ( curPos . x < this . clickCoords . x ) direction . x = - 1 ;
if ( curPos . y < this . clickCoords . y ) direction . y = - 1 ;
this . transformCoords ( curPos . x , this . clickCoords . x , newCoords , 'x' ) ;
this . transformCoords ( curPos . y , this . clickCoords . y , newCoords , 'y' ) ;
} else if ( this . resizing ) {
resizeHandle = this . resizeHandle ;
// do x movements first
if ( resizeHandle . match ( /E/ ) ) {
// if we're moving an east handle
this . transformCoords ( curPos . x , this . startCoords . x1 , newCoords , 'x' ) ;
if ( curPos . x < this . startCoords . x1 ) direction . x = - 1 ;
} else if ( resizeHandle . match ( /W/ ) ) {
// if we're moving an west handle
this . transformCoords ( curPos . x , this . startCoords . x2 , newCoords , 'x' ) ;
if ( curPos . x < this . startCoords . x2 ) direction . x = - 1 ;
}
// do y movements second
if ( resizeHandle . match ( /N/ ) ) {
// if we're moving an north handle
this . transformCoords ( curPos . y , this . startCoords . y2 , newCoords , 'y' ) ;
if ( curPos . y < this . startCoords . y2 ) direction . y = - 1 ;
} else if ( resizeHandle . match ( /S/ ) ) {
// if we're moving an south handle
this . transformCoords ( curPos . y , this . startCoords . y1 , newCoords , 'y' ) ;
if ( curPos . y < this . startCoords . y1 ) direction . y = - 1 ;
}
}
this . setAreaCoords ( newCoords , false , e . shiftKey , direction , resizeHandle ) ;
this . drawArea ( ) ;
Event . stop ( e ) ; // stop the default event (selecting images & text) in Safari & IE PC
}
} ,
/ * *
* Applies the appropriate transform to supplied co - ordinates , on the
* defined axis , depending on the relationship of the supplied values
*
* @ access private
* @ param int Current value of pointer
* @ param int Base value to compare current pointer val to
* @ param obj Coordinates to apply transformation on x1 , x2 , y1 , y2
* @ param string Axis to apply transformation on 'x' || 'y'
* @ return void
* /
transformCoords : function ( curVal , baseVal , coords , axis ) {
var newVals = [ curVal , baseVal ] ;
if ( curVal > baseVal ) newVals . reverse ( ) ;
coords [ axis + '1' ] = newVals [ 0 ] ;
coords [ axis + '2' ] = newVals [ 1 ] ;
} ,
/ * *
* Ends the crop & passes the values of the select area on to the appropriate
* callback function on completion of a crop
*
* @ access private
* @ return void
* /
endCrop : function ( ) {
this . dragging = false ;
this . resizing = false ;
this . options . onEndCrop (
this . areaCoords ,
{
width : this . calcW ( ) ,
height : this . calcH ( )
}
) ;
} ,
/ * *
* Abstract method called on the end of initialization
*
* @ access private
* @ abstract
* @ return void
* /
subInitialize : function ( ) { } ,
/ * *
* Abstract method called on the end of drawArea ( )
*
* @ access private
* @ abstract
* @ return void
* /
subDrawArea : function ( ) { }
} ;
/ * *
* Extend the Cropper . Img class to allow for presentation of a preview image of the resulting crop ,
* the option for displayOnInit is always overridden to true when displaying a preview image
*
* Usage :
* @ param obj Image element to attach to
* @ param obj Optional options :
* - see Cropper . Img for base options
*
* - previewWrap obj
* HTML element that will be used as a container for the preview image
* /
Cropper . ImgWithPreview = Class . create ( ) ;
Object . extend ( Object . extend ( Cropper . ImgWithPreview . prototype , Cropper . Img . prototype ) , {
/ * *
* Implements the abstract method from Cropper . Img to initialize preview image settings .
* Will only attach a preview image is the previewWrap element is defined and the minWidth
* & minHeight options are set .
*
* @ see Croper . Img . subInitialize
* /
subInitialize : function ( ) {
/ * *
* Whether or not we ' ve attached a preview image
* @ var boolean
* /
this . hasPreviewImg = false ;
if ( typeof ( this . options . previewWrap ) != 'undefined'
&& this . options . minWidth > 0
&& this . options . minHeight > 0
) {
/ * *
* The preview image wrapper element
* @ var obj HTML element
* /
2013-06-01 18:42:51 +02:00
this . previewWrap = $PR ( this . options . previewWrap ) ;
2010-07-02 01:48:07 +02:00
/ * *
* The preview image element
* @ var obj HTML IMG element
* /
this . previewImg = this . img . cloneNode ( false ) ;
// set the ID of the preview image to be unique
this . previewImg . id = 'imgCrop_' + this . previewImg . id ;
// set the displayOnInit option to true so we display the select area at the same time as the thumbnail
this . options . displayOnInit = true ;
this . hasPreviewImg = true ;
this . previewWrap . addClassName ( 'imgCrop_previewWrap' ) ;
this . previewWrap . setStyle (
{
width : this . options . minWidth + 'px' ,
height : this . options . minHeight + 'px'
}
) ;
this . previewWrap . appendChild ( this . previewImg ) ;
}
} ,
/ * *
* Implements the abstract method from Cropper . Img to draw the preview image
*
* @ see Croper . Img . subDrawArea
* /
subDrawArea : function ( ) {
if ( this . hasPreviewImg ) {
// get the ratio of the select area to the src image
var calcWidth = this . calcW ( ) ;
var calcHeight = this . calcH ( ) ;
// ratios for the dimensions of the preview image
var dimRatio = {
x : this . imgW / calcWidth ,
y : this . imgH / calcHeight
} ;
//ratios for the positions within the preview
var posRatio = {
x : calcWidth / this . options . minWidth ,
y : calcHeight / this . options . minHeight
} ;
// setting the positions in an obj before apply styles for rendering speed increase
var calcPos = {
w : Math . ceil ( this . options . minWidth * dimRatio . x ) + 'px' ,
h : Math . ceil ( this . options . minHeight * dimRatio . y ) + 'px' ,
x : '-' + Math . ceil ( this . areaCoords . x1 / posRatio . x ) + 'px' ,
y : '-' + Math . ceil ( this . areaCoords . y1 / posRatio . y ) + 'px'
}
var previewStyle = this . previewImg . style ;
previewStyle . width = calcPos . w ;
previewStyle . height = calcPos . h ;
previewStyle . left = calcPos . x ;
previewStyle . top = calcPos . y ;
}
}
} ) ;