diff --git a/README.markdown b/README.markdown
index 4698f85..def0249 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,74 +1,78 @@
-Description
-===========
-Easy to use jQuery plugin for zoom & pan image cropping.
-
-Demo
-====
-Visit: http://www.tmatthew.net/jwindowcrop
-
-Usage
-=====
- // minimum
- $('img.crop_me').jWindowCrop();
-
- // typical
- $('img.crop_me').jWindowCrop({
- targetWidth:300,
- targetHeight:300,
- onChange: function(result) {
- console.log($(this).attr('id'));
- console.log('x: '+result.cropX);
- }
- });
-
-Options
-=======
-
-
- Option |
- Type |
- Default |
- Required |
- Description |
-
-
- targetWidth | integer | 320 | no |
- Width in pixels of the cropping window |
-
-
- targetHeight | integer | 180 | no |
- Height in pixels of the cropping window |
-
-
- onChange | function | function(){} | no |
- Callback function that gets called whenever the values change. cropX, cropY, cropW, cropH, mustStretch (boolean) values are passed to this function in a hash. Use the this keyword in the function for a reference to the element that was updated. |
-
-
- zoomSteps | integer | 10 | no |
- Number of incremental zoom steps. With the default of 10, you have to click the zoom-in button 9 times to reach 100%. |
-
-
- loadingText | string | "Loading..." | no |
- Text (can be HTML) to display within frame until image is loaded. |
-
-
- smartControls | boolean | true | no |
- If true, controls will hide on mouseleave and appear on mouseenter. |
-
-
- showControlsOnStart | boolean | true | no |
- If true, controls will be hidden on start. Note: Do not set both this and smartControls to false. |
-
-
-
-Advanced
-========
-The structure for this plugin comes from http://starter.pixelgraphics.us/. An object is created for each dom element jWindowCrop is initialized on. A reverse reference to that object can be accessed like so:
-
- var jwc = $('img#beach').getjWindowCrop();
-
-You then have access to all the properties and methods used for that specific element.
-
-Questions
-=========
-Email tyler at tmatthew dot net
+Description
+===========
+Easy to use jQuery plugin for zoom & pan image cropping.
+
+Demo
+====
+Visit: http://www.tmatthew.net/jwindowcrop
+
+Usage
+=====
+ // minimum
+ $('img.crop_me').jWindowCrop();
+
+ // typical
+ $('img.crop_me').jWindowCrop({
+ targetWidth:300,
+ targetHeight:300,
+ onChange: function(result) {
+ console.log($(this).attr('id'));
+ console.log('x: '+result.cropX);
+ }
+ });
+
+Options
+=======
+
+
+ Option |
+ Type |
+ Default |
+ Required |
+ Description |
+
+
+ targetWidth | integer | 320 | no |
+ Width in pixels of the cropping window |
+
+
+ targetHeight | integer | 180 | no |
+ Height in pixels of the cropping window |
+
+
+ onChange | function | function(){} | no |
+ Callback function that gets called whenever the values change. cropX, cropY, cropW, cropH, mustStretch (boolean) values are passed to this function in a hash. Use the this keyword in the function for a reference to the element that was updated. |
+
+
+ zoomSteps | integer | 10 | no |
+ Number of incremental zoom steps. With the default of 10, you have to click the zoom-in button 9 times to reach 100%. |
+
+
+ loadingText | string | "Loading..." | no |
+ Text (can be HTML) to display within frame until image is loaded. |
+
+
+ smartControls | boolean | true | no |
+ If true, controls will hide on mouseleave and appear on mouseenter. |
+
+
+ showControlsOnStart | boolean | true | no |
+ If true, controls will be hidden on start. Note: Do not set both this and smartControls to false. |
+
+
+ customButtons | array | [] | no |
+ Buttons can be added to the toolbar here. e.g: [{class:'jwc_replace',name:'Replace Image', handler:triggerUpload, content:'Replace Image'}] |
+
+
+
+Advanced
+========
+The structure for this plugin comes from http://starter.pixelgraphics.us/. An object is created for each dom element jWindowCrop is initialized on. A reverse reference to that object can be accessed like so:
+
+ var jwc = $('img#beach').getjWindowCrop();
+
+You then have access to all the properties and methods used for that specific element.
+
+Questions
+=========
+Email tyler at tmatthew dot net
diff --git a/jquery.jWindowCrop.js b/jquery.jWindowCrop.js
index a2f85d4..2cea2f6 100644
--- a/jquery.jWindowCrop.js
+++ b/jquery.jWindowCrop.js
@@ -1,179 +1,191 @@
-/*
- * jWindowCrop v1.0.0
- *
- * Copyright (c) 2012 Tyler Brown
- * Licensed under the MIT license.
- *
- */
-
-(function($){
- function fillContainer(val, targetLength, containerLength) { // ensure that no gaps are between target's edges and container's edges
- if(val + targetLength < containerLength) val = containerLength-targetLength;
- if(val > 0) val = 0;
- return val;
- }
-
- $.jWindowCrop = function(image, options){
- var base = this;
- base.$image = $(image); // target image jquery element
- base.image = image; // target image dom element
- base.$image.data("jWindowCrop", base); // target frame jquery element
-
- base.namespace = 'jWindowCrop';
- base.originalWidth = 0;
- base.isDragging = false;
-
- base.init = function(){
- base.$image.css({display:'none'}); // hide image until loaded
- base.options = $.extend({},$.jWindowCrop.defaultOptions, options);
- if(base.options.zoomSteps < 2) base.options.zoomSteps = 2;
-
- base.$image.addClass('jwc_image').wrap(''); // wrap image in frame
- base.$frame = base.$image.parent();
- base.$frame.append('' + base.options.loadingText + '
');
- base.$frame.append('');
- base.$frame.css({'overflow': 'hidden', 'position': 'relative', 'width': base.options.targetWidth, 'height': base.options.targetHeight});
- base.$image.css({'position': 'absolute', 'top': '0px', 'left': '0px'});
- initializeDimensions();
-
- base.$frame.find('.jwc_zoom_in').on('click.'+base.namespace, base.zoomIn);
- base.$frame.find('.jwc_zoom_out').on('click.'+base.namespace, base.zoomOut);
- base.$frame.on('mouseenter.'+base.namespace, handleMouseEnter);
- base.$frame.on('mouseleave.'+base.namespace, handleMouseLeave);
- base.$image.on('load.'+base.namespace, handeImageLoad);
- base.$image.on('mousedown.'+base.namespace, handleMouseDown);
- $(document).on('mousemove.'+base.namespace, handleMouseMove);
- $(document).on('mouseup.'+base.namespace, handleMouseUp);
- };
-
- base.destroy = function() {
- base.$image.removeData("jWindowCrop"); // remove data
- $(document).unbind(); // remove body binds
- base.$image.unbind(); // remove image binds
- base.$frame.unbind(); // remove frame binds
- base.$frame.find('.jwc_zoom_out').unbind(); // remove zoom triggers
- base.$frame.find('.jwc_zoom_in').unbind(); // remove zoom triggers
- $('.jwc_loader').remove(); // remove the added text
- $('.jwc_controls').remove(); // remove the added controls
- base.$image.removeAttr( 'style' ); // undo the style
- base.$image.unwrap(); // undo the wrap
- };
-
- base.setZoom = function(percent) {
- if(base.minPercent >= 1) {
- percent = base.minPercent;
- } else if(percent > 1.0) {
- percent = 1;
- } else if(percent < base.minPercent) {
- percent = base.minPercent;
- }
- base.$image.width(Math.ceil(base.originalWidth*percent));
- base.workingPercent = percent;
- focusOnCenter();
- updateResult();
- };
- base.zoomIn = function() {
- var zoomIncrement = (1.0 - base.minPercent) / (base.options.zoomSteps-1);
- base.setZoom(base.workingPercent+zoomIncrement);
- return false;
- };
- base.zoomOut = function() {
- var zoomIncrement = (1.0 - base.minPercent) / (base.options.zoomSteps-1);
- base.setZoom(base.workingPercent-zoomIncrement);
- return false;
- };
-
- function initializeDimensions() {
- if(base.originalWidth == 0) {
- base.originalWidth = base.$image.width();
- base.originalHeight = base.$image.height();
- }
- if(base.originalWidth > 0) {
- var widthRatio = base.options.targetWidth / base.originalWidth;
- var heightRatio = base.options.targetHeight / base.originalHeight;
- //base.minPercent = (widthRatio >= heightRatio) ? widthRatio : heightRatio;
- if(widthRatio >= heightRatio) {
- base.minPercent = (base.originalWidth < base.options.targetWidth) ? (base.options.targetWidth / base.originalWidth) : widthRatio;
- } else {
- base.minPercent = (base.originalHeight < base.options.targetHeight) ? (base.options.targetHeight / base.originalHeight) : heightRatio;
- }
- base.focalPoint = {'x': Math.round(base.originalWidth/2), 'y': Math.round(base.originalHeight/2)};
- base.setZoom(base.minPercent);
- base.$image.fadeIn('fast'); //display image now that it has loaded
- }
- }
- function storeFocalPoint() {
- var x = (parseInt(base.$image.css('left'))*-1 + base.options.targetWidth/2) / base.workingPercent;
- var y = (parseInt(base.$image.css('top'))*-1 + base.options.targetHeight/2) / base.workingPercent;
- base.focalPoint = {'x': Math.round(x), 'y': Math.round(y)};
- }
- function focusOnCenter() {
- var left = fillContainer((Math.round((base.focalPoint.x*base.workingPercent) - base.options.targetWidth/2)*-1), base.$image.width(), base.options.targetWidth);
- var top = fillContainer((Math.round((base.focalPoint.y*base.workingPercent) - base.options.targetHeight/2)*-1), base.$image.height(), base.options.targetHeight);
- base.$image.css({'left': (left.toString()+'px'), 'top': (top.toString()+'px')})
- storeFocalPoint();
- }
- function updateResult() {
- base.result = {
- cropX: Math.floor(parseInt(base.$image.css('left'))/base.workingPercent*-1),
- cropY: Math.floor(parseInt(base.$image.css('top'))/base.workingPercent*-1),
- cropW: Math.round(base.options.targetWidth/base.workingPercent),
- cropH: Math.round(base.options.targetHeight/base.workingPercent),
- mustStretch: (base.minPercent > 1)
- };
- base.options.onChange.call(base.image, base.result);
- }
- function handeImageLoad() {
- initializeDimensions();
- }
- function handleMouseDown(event) {
- event.preventDefault(); //some browsers do image dragging themselves
- base.isDragging = true;
- base.dragMouseCoords = {x: event.pageX, y: event.pageY};
- base.dragImageCoords = {x: parseInt(base.$image.css('left')), y: parseInt(base.$image.css('top'))}
- }
- function handleMouseUp() {
- base.isDragging = false;
- }
- function handleMouseMove(event) {
- if(base.isDragging) {
- var xDif = event.pageX - base.dragMouseCoords.x;
- var yDif = event.pageY - base.dragMouseCoords.y;
- var newLeft = fillContainer((base.dragImageCoords.x + xDif), base.$image.width(), base.options.targetWidth);
- var newTop = fillContainer((base.dragImageCoords.y + yDif), base.$image.height(), base.options.targetHeight);
- base.$image.css({'left' : (newLeft.toString()+'px'), 'top' : (newTop.toString()+'px')});
- storeFocalPoint();
- updateResult();
- }
- }
- function handleMouseEnter() {
- if(base.options.smartControls) base.$frame.find('.jwc_controls').fadeIn('fast');
- }
- function handleMouseLeave() {
- if(base.options.smartControls) base.$frame.find('.jwc_controls').fadeOut('fast');
- }
-
- base.init();
- };
-
- $.jWindowCrop.defaultOptions = {
- targetWidth: 320,
- targetHeight: 180,
- zoomSteps: 10,
- loadingText: 'Loading...',
- smartControls: true,
- showControlsOnStart: true,
- onChange: function() {}
- };
-
- $.fn.jWindowCrop = function(options){
- return this.each(function(){
- (new $.jWindowCrop(this, options));
- });
- };
-
- $.fn.getjWindowCrop = function(){
- return this.data("jWindowCrop");
- };
-})(jQuery);
-
+/*
+ * jWindowCrop v1.0.0
+ *
+ * Copyright (c) 2012 Tyler Brown
+ * Licensed under the MIT license.
+ *
+ */
+
+(function($){
+ function fillContainer(val, targetLength, containerLength) { // ensure that no gaps are between target's edges and container's edges
+ if(val + targetLength < containerLength) val = containerLength-targetLength;
+ if(val > 0) val = 0;
+ return val;
+ }
+
+ $.jWindowCrop = function(image, options){
+ var base = this;
+ base.$image = $(image); // target image jquery element
+ base.image = image; // target image dom element
+ base.$image.data("jWindowCrop", base); // target frame jquery element
+
+ base.namespace = 'jWindowCrop';
+ base.originalWidth = 0;
+ base.isDragging = false;
+
+ base.init = function(){
+ base.$image.css({display:'none'}); // hide image until loaded
+ base.options = $.extend(true,{},$.jWindowCrop.defaultOptions, options);
+ if(base.options.zoomSteps < 2) base.options.zoomSteps = 2;
+
+ base.$image.addClass('jwc_image').wrap(''); // wrap image in frame
+ base.$frame = base.$image.parent();
+ base.$frame.append('' + base.options.loadingText + '
');
+ base.$frame.append('click to drag
');
+ base.$frame.css({'overflow': 'hidden', 'position': 'relative', 'width': base.options.targetWidth, 'height': base.options.targetHeight});
+ base.$image.css({'position': 'absolute', 'top': '0px', 'left': '0px'});
+ initializeDimensions();
+
+ //Add custom buttons to default buttons array.
+ $.merge(base.options.buttons,base.options.customButtons);
+ $.each(base.options.buttons,function (i,button){
+ base.$frame.find('.jwc_controls').append('' + ((button.content) ? button.content : '')+ '');
+ if (button.function && base[button.function]) { //Bind function to button.
+ base.$frame.find('.'+button.class).on('click.'+base.namespace, base[button.function]);
+ } else if (button.handler){
+ base.$frame.find('.'+button.class).on('click.'+base.namespace,button.handler);
+ }
+ });
+ base.$frame.on('mouseenter.'+base.namespace, handleMouseEnter);
+ base.$frame.on('mouseleave.'+base.namespace, handleMouseLeave);
+ base.$image.on('load.'+base.namespace, handeImageLoad);
+ base.$image.on('mousedown.'+base.namespace, handleMouseDown);
+ $(document).on('mousemove.'+base.namespace, handleMouseMove);
+ $(document).on('mouseup.'+base.namespace, handleMouseUp);
+ };
+
+ base.destroy = function() {
+ base.$image.removeData("jWindowCrop"); // remove data
+ $(document).unbind(); // remove body binds
+ base.$image.unbind(); // remove image binds
+ base.$frame.unbind(); // remove frame binds
+ $.each(base.options.buttons,function (i,button){
+ base.$frame.find('.jwc_controls').unbind();// remove button triggers
+ });
+ $('.jwc_loader').remove(); // remove the added text
+ $('.jwc_controls').remove(); // remove the added controls
+ base.$image.removeAttr( 'style' ); // undo the style
+ base.$image.unwrap(); // undo the wrap
+ base.options = null;
+ };
+
+ base.setZoom = function(percent) {
+ if(base.minPercent >= 1) {
+ percent = base.minPercent;
+ } else if(percent > 1.0) {
+ percent = 1;
+ } else if(percent < base.minPercent) {
+ percent = base.minPercent;
+ }
+ base.$image.width(Math.ceil(base.originalWidth*percent));
+ base.workingPercent = percent;
+ focusOnCenter();
+ updateResult();
+ };
+ base.zoomIn = function() {
+ var zoomIncrement = (1.0 - base.minPercent) / (base.options.zoomSteps-1);
+ base.setZoom(base.workingPercent+zoomIncrement);
+ return false;
+ };
+ base.zoomOut = function() {
+ var zoomIncrement = (1.0 - base.minPercent) / (base.options.zoomSteps-1);
+ base.setZoom(base.workingPercent-zoomIncrement);
+ return false;
+ };
+
+ function initializeDimensions() {
+ if(base.originalWidth == 0) {
+ base.originalWidth = base.$image.width();
+ base.originalHeight = base.$image.height();
+ }
+ if(base.originalWidth > 0) {
+ var widthRatio = base.options.targetWidth / base.originalWidth;
+ var heightRatio = base.options.targetHeight / base.originalHeight;
+ //base.minPercent = (widthRatio >= heightRatio) ? widthRatio : heightRatio;
+ if(widthRatio >= heightRatio) {
+ base.minPercent = (base.originalWidth < base.options.targetWidth) ? (base.options.targetWidth / base.originalWidth) : widthRatio;
+ } else {
+ base.minPercent = (base.originalHeight < base.options.targetHeight) ? (base.options.targetHeight / base.originalHeight) : heightRatio;
+ }
+ base.focalPoint = {'x': Math.round(base.originalWidth/2), 'y': Math.round(base.originalHeight/2)};
+ base.setZoom(base.minPercent);
+ base.$image.fadeIn('fast'); //display image now that it has loaded
+ }
+ }
+ function storeFocalPoint() {
+ var x = (parseInt(base.$image.css('left'))*-1 + base.options.targetWidth/2) / base.workingPercent;
+ var y = (parseInt(base.$image.css('top'))*-1 + base.options.targetHeight/2) / base.workingPercent;
+ base.focalPoint = {'x': Math.round(x), 'y': Math.round(y)};
+ }
+ function focusOnCenter() {
+ var left = fillContainer((Math.round((base.focalPoint.x*base.workingPercent) - base.options.targetWidth/2)*-1), base.$image.width(), base.options.targetWidth);
+ var top = fillContainer((Math.round((base.focalPoint.y*base.workingPercent) - base.options.targetHeight/2)*-1), base.$image.height(), base.options.targetHeight);
+ base.$image.css({'left': (left.toString()+'px'), 'top': (top.toString()+'px')})
+ storeFocalPoint();
+ }
+ function updateResult() {
+ base.result = {
+ cropX: Math.floor(parseInt(base.$image.css('left'))/base.workingPercent*-1),
+ cropY: Math.floor(parseInt(base.$image.css('top'))/base.workingPercent*-1),
+ cropW: Math.round(base.options.targetWidth/base.workingPercent),
+ cropH: Math.round(base.options.targetHeight/base.workingPercent),
+ mustStretch: (base.minPercent > 1)
+ };
+ base.options.onChange.call(base.image, base.result);
+ }
+ function handeImageLoad() {
+ initializeDimensions();
+ }
+ function handleMouseDown(event) {
+ event.preventDefault(); //some browsers do image dragging themselves
+ base.isDragging = true;
+ base.dragMouseCoords = {x: event.pageX, y: event.pageY};
+ base.dragImageCoords = {x: parseInt(base.$image.css('left')), y: parseInt(base.$image.css('top'))}
+ }
+ function handleMouseUp() {
+ base.isDragging = false;
+ }
+ function handleMouseMove(event) {
+ if(base.isDragging) {
+ var xDif = event.pageX - base.dragMouseCoords.x;
+ var yDif = event.pageY - base.dragMouseCoords.y;
+ var newLeft = fillContainer((base.dragImageCoords.x + xDif), base.$image.width(), base.options.targetWidth);
+ var newTop = fillContainer((base.dragImageCoords.y + yDif), base.$image.height(), base.options.targetHeight);
+ base.$image.css({'left' : (newLeft.toString()+'px'), 'top' : (newTop.toString()+'px')});
+ storeFocalPoint();
+ updateResult();
+ }
+ }
+ function handleMouseEnter() {
+ if(base.options.smartControls) base.$frame.find('.jwc_controls').fadeIn('fast');
+ }
+ function handleMouseLeave() {
+ if(base.options.smartControls) base.$frame.find('.jwc_controls').fadeOut('fast');
+ }
+
+ base.init();
+ };
+
+ $.jWindowCrop.defaultOptions = {
+ targetWidth: 320,
+ targetHeight: 180,
+ zoomSteps: 10,
+ loadingText: 'Loading...',
+ smartControls: true,
+ showControlsOnStart: true,
+ onChange: function() {},
+ buttons: [{class:'jwc_zoom_in',name:'Zoom In', function:'zoomIn'},{class:'jwc_zoom_out',name:'Zoom Out', function:'zoomOut'}],
+ customButtons:[]
+ };
+
+ $.fn.jWindowCrop = function(options){
+ return this.each(function(){
+ (new $.jWindowCrop(this, options));
+ });
+ };
+
+ $.fn.getjWindowCrop = function(){
+ return this.data("jWindowCrop");
+ };
+})(jQuery);
+