text: /*global documents: true define: true*/
  /*
  * Aloha Image Plugin - Allow image manipulation in Aloha Editor
  *
  * Author & Copyright (c) 2013 Gentics Software GmbH
  * aloha-sales@gentics.com
  * Contributors
  *		Johannes Schüth - http://jotschi.de
  *		Nicolas karageuzian - http://nka.me/
  *		Benjamin Athur Lupton - http://www.balupton.com/
  *		Thomas Lete
  *		Nils Dehl
  *		Christopher Hlubek
  *		Edward Tsech
  *		Haymo Meran
  *		Martin Schönberger
  *
  * Licensed under the terms of http://www.aloha-editor.org/license.php
  */
  
  define([
  	// js
  	'jquery',
  	'aloha/plugin',
  	'image/image-floatingMenu',
  	'i18n!aloha/nls/i18n',
  	'i18n!image/nls/i18n',
  	'jqueryui',
  	'image/vendor/jcrop/jquery.jcrop.min',
  	'image/vendor/mousewheel/mousewheel'
  ], function AlohaImagePlugin(
  	aQuery,
  	Plugin,
  	ImageFloatingMenu,
  	i18nCore,
  	i18n
  ) {
  
  	'use strict';
  
  	var jQuery = aQuery;
  	var $ = aQuery;
  	var GENTICS = window.GENTICS;
  	var Aloha = window.Aloha;
  	var resizing = false;
  
  	var aspectRatioValue = false;
  	var cropRatioValue = false;
  
  	// Attributes manipulation utilities
  	// Aloha team may want to factorize, it could be useful for other plugins
  	// Prototypes
  	String.prototype.toInteger = String.prototype.toInteger || function () {
  		return parseInt(String(this).replace(/px$/, '') || 0, 10);
  	};
  	String.prototype.toFloat = String.prototype.toInteger || function () {
  		return parseFloat(String(this).replace(/px$/, '') || 0, 10);
  	};
  	Number.prototype.toInteger = Number.prototype.toInteger || String.prototype.toInteger;
  	Number.prototype.toFloat = Number.prototype.toFloat || String.prototype.toFloat;
  
  	// Insert jQuery Prototypes
  	jQuery.extend(true, jQuery.fn, {
  		increase: jQuery.fn.increase || function (attr) {
  			var	obj = jQuery(this), value, newValue;
  			if (!obj.length) {
  				return obj;
  			}
  			value = obj.css(attr).toFloat();
  			newValue = Math.round((value || 1) * 1.2);
  			// when value is 2, won't increase
  			if (value === newValue) {
  				newValue++;
  			}
  			obj.css(attr, newValue);
  			return obj;
  		},
  		decrease: jQuery.fn.decrease || function (attr) {
  			var	obj = jQuery(this), value, newValue;
  			// Check
  			if (!obj.length) {
  				return obj;
  			}
  			// Calculate
  			value = obj.css(attr).toFloat();
  			newValue = Math.round((value || 0) * 0.8);
  			// Apply
  			if (value === newValue && newValue > 0) {
  				// when value is 2, won't increase
  				newValue--;
  			}
  			obj.css(attr, newValue);
  			// Chain
  			return obj;
  		}
  	});
  
  	// Create and register Image Plugin
  	return Plugin.create('image', {
  		defaultSettings: {
  			'maxWidth': 1600,
  			'minWidth': 3,
  			'maxHeight': 1200,
  			'minHeight': 3,
  			// This setting will manually correct values that are out of bounds
  			'autoCorrectManualInput': true,
  			// This setting will define a fixed aspect ratio for all resize actions
  			'fixedAspectRatio' : false,
  			// When enabled this setting will order the plugin to automatically resize images to given bounds
  			'autoResize': false,
  			//Image manipulation options - ONLY in default config section
  			ui: {
  				meta		: true, // If imageResizeWidth and imageResizeHeight are displayed, then you will want to set this to true, so that the width and height text fields are updated automatically.
  				crop		: true, // If imageCropButton is displayed, then you have to enable this.
  				resizable	: true	// Resizable ui-drag image
  			},
  			handles     : 'ne, se, sw, nw',   // set handles for resize
  
  			/**
  			 * Crop callback is triggered after the user clicked accept to accept his crop
  			 * @param image jquery image object reference
  			 * @param props cropping properties
  			 */
  			onCropped: function ($image, props) {
  				Aloha.Log.info('Default onCropped invoked', $image, props);
  			},
  
  			/**
  			 * Reset callback is triggered before the internal reset procedure is applied
  			 * if this function returns true, then the reset has been handled by the callback
  			 * which means that no other reset will be applied
  			 * if false is returned the internal reset procedure will be applied
  			 * @param image jquery image object reference
  			 * @return true if a reset has been applied, false otherwise
  			 */
  			onReset: function ($image) {
  				Aloha.Log.info('Default onReset invoked', $image);
  				return false;
  			},
  
  			/**
  			 * Example callback method which gets called while the resize process is being executed.
  			 */
  			onResize: function ($image) {
  				Aloha.Log.info('Default onResize invoked', $image);
  			},
  
  			/**
  			 * Resize callback is triggered after the internal resize procedure is applied.
  			 */
  			onResized: function ($image) {
  				Aloha.Log.info('Default onResized invoked', $image);
  			}
  		},
  
  		/**
  		 * Internal callback hook which gets invoked when cropping has been finished
  		 */
  		_onCropped: function ($image, props) {
  
  			$('body').trigger('aloha-image-cropped', [$image, props]);
  
  			// After successful cropping, the aspect ratio value has to be recalculated
  			if (this.keepAspectRatio) {
  				aspectRatioValue = this.imageObj.width() / this.imageObj.height();
  			}
  
  			// Call the custom onCropped function
  			this.onCropped($image, props);
  		},
  
  		/**
  		 * Internal callback hook which gets invoked when resetting images
  		 */
  		_onReset: function ($image) {
  
  			// No default behaviour defined besides event triggering
  			$('body').trigger('aloha-image-reset', $image);
  
  			// Call the custom reset function
  			return this.onReset($image);
  		},
  
  		/**
  		 * Internal callback hook which gets invoked while the image is being resized
  		 */
  		_onResize: function ($image) {
  
  			// No default behaviour defined besides event triggering
  			$('body').trigger('aloha-image-resize', $image);
  
  			this._applyValuesToFields($image.width(), $image.height());
  
  			// Call the custom resize function
  			this.onResize($image);
  		},
  
  		/**
  		 * Internal callback hook which gets invoked when the current resizing action has stopped
  		 */
  		_onResized: function ($image) {
  
  			$('body').trigger('aloha-image-resized', $image);
  
  			this._applyValuesToFields($image.width(), $image.height());
  
  			// Call the custom resize function
  			this.onResized($image);
  		},
  
  		/**
  		 * The image that is currently edited
  		 */
  		imageObj: null,
  
  
  		/**
  		 * The Jcrop API reference
  		 * this is needed to be able to destroy the cropping frame later on
  		 * the variable is linked to the api object whilst cropping, or set to null otherwise
  		 * strange, but done as documented http://deepliquid.com/content/Jcrop_API.html
  		 */
  		jcAPI: null,
  
  
  		/**
  		 * State variable for the aspect ratio toggle feature
  		 */
  		keepAspectRatio: false,
  
  		/**
  		 * This will contain an image's original properties to be able to undo previous settings
  		 *
  		 * when an image is clicked for the first time, a new object will be added to the array
  		 * {
  		 *		obj : [the image object reference],
  		 *		src : [the original src url],
  		 *		width : [initial width],
  		 *		height : [initial height]
  		 * }
  		 *
  		 * when an image is clicked the second time, the array will be checked for the image object
  		 * reference, to prevent double entries
  		 */
  		restoreProps: [],
  
  		/**
  		 * the defined object types to be used for this instance
  		 */
  		objectTypeFilter: [],
  
  		/**
  		 * Plugin initialization method
  		 */
  		init: function () {
  
  			var plugin = this;
  
  			var imagePluginUrl = Aloha.getPluginUrl('image');
  
  			if (typeof this.settings.objectTypeFilter != 'undefined') {
  				this.objectTypeFilter = this.settings.objectTypeFilter;
  			}
  
  			// Extend the default settings with the custom ones (done by default)
  			plugin.config = plugin.defaultSettings;
  			plugin.settings = jQuery.extend(true, plugin.defaultSettings, plugin.settings);
  
  			// Determine the flag and the value of the aspect ratio depending on settings
  			if (typeof this.settings.fixedAspectRatio === 'number') {
  				aspectRatioValue = cropRatioValue = this.settings.fixedAspectRatio;
  				plugin.keepAspectRatio = true;
  			} else {
  				if ((plugin.settings.fixedAspectRatio) === true) {
  					plugin.keepAspectRatio = true;
  				} else {
  					plugin.keepAspectRatio = false;
  				}
  			}
  
  			plugin.initializeUI();
  			plugin.bindInteractions();
  			plugin.subscribeEvents();
  
  		},
  
  		/**
  		* Create buttons
  		*/
  		initializeUI: function () {
  
  			var that = this;
  
  			this.ui = new ImageFloatingMenu();
  			this.ui.init(this);
  		},
  		/**
  		 * Bind plugin interactions
  		 */
  		bindInteractions: function () {
  			var	plugin = this;
  
  			if (plugin.settings.ui.resizable) {
  				try {
  					// this will disable mozillas image resizing facilities
  					document.execCommand('enableObjectResizing', false, false);
  				} catch (e) {
  					Aloha.Log.info(e, 'Could not disable enableObjectResizing');
  					// this is just for internet explorer, which will not support disabling enableObjectResizing
  				}
  			}
  
  			if (plugin.settings.ui.meta) {
  				// update image object when src changes
  				plugin.ui.imgSrcField.addListener('keyup', function (event) {
  					plugin.srcChange();
  				});
  
  				plugin.ui.imgSrcField.addListener('blur', function (event) {
  					// TODO remove image or do something useful if the user leaves the
  					// image without defining a valid image src
  					var img = jQuery(plugin.ui.imgSrcField.getTargetObject());
  					if (img.attr('src') === '') {
  						img.remove();
  					} // image removal when src field is blank
  				});
  			}
  
  			// Override the default method by using the given one
  			if (plugin.settings.onCropped && typeof plugin.settings.onCropped === "function") {
  				plugin.onCropped = plugin.settings.onCropped;
  			}
  
  			// Override the default method by using the given one
  			if (plugin.settings.onReset && typeof plugin.settings.onReset === "function") {
  				plugin.onReset = plugin.settings.onReset;
  			}
  
  			// Override the default method by using the given one
  			if (plugin.settings.onResized && typeof plugin.settings.onResized === "function") {
  				plugin.onResized = plugin.settings.onResized;
  			}
  
  			// Override the default method by using the given one
  			if (plugin.settings.onResize && typeof plugin.settings.onResize === "function") {
  				plugin.onResize = this.settings.onResize;
  			}
  
  		},
  
  		/**
  		 * Subscribe to Aloha events and DragAndDropPlugin Event
  		 */
  		subscribeEvents: function () {
  			var	plugin = this;
  			var config = this.settings;
  
  			jQuery('img').filter(config.globalselector).unbind();
  			jQuery('img').filter(config.globalselector).click(function (event) {
  				plugin.clickImage(event);
  			});
  
  			Aloha.bind('aloha-drop-files-in-editable', function (event, data) {
  				var img, len = data.filesObjs.length, fileObj, config;
  
  				while (--len >= 0) {
  					fileObj = data.filesObjs[len];
  					if (fileObj.file.type.match(/image\//)) {
  						config = plugin.getEditableConfig(data.editable);
  						// Prepare
  						img = jQuery('<img/>');
  						img.css({
  							"max-width": that.maxWidth,
  							"max-height": that.maxHeight
  						});
  						img.attr('id', fileObj.id);
  						if (typeof fileObj.src === 'undefined') {
  							img.attr('src', fileObj.data);
  							//fileObj.src = fileObj.data ;
  						} else {
  							img.attr('src', fileObj.src);
  						}
  						GENTICS.Utils.Dom.insertIntoDOM(img, data.range, jQuery(Aloha.activeEditable.obj));
  					}
  				}
  
  			});
  			/*
  			 * Add the event handler for selection change
  			 */
  			Aloha.bind('aloha-selection-changed', function (event, rangeObject, originalEvent) {
  				var config, foundMarkup;
  
  				if (originalEvent && originalEvent.target) {
  					// Check if the element is currently being resized
  					if (plugin.settings.ui.resizable && !jQuery(originalEvent.target).hasClass('ui-resizable-handle')) {
  						plugin.endResize();
  						plugin.imageObj = null;
  						Aloha.trigger('aloha-image-unselected');
  					}
  				}
  
  				if (Aloha.activeEditable !== null) {
  					foundMarkup = plugin.findImgMarkup(rangeObject);
  					config = plugin.getEditableConfig(Aloha.activeEditable.obj);
  
  					if (typeof config !== 'undefined') {
  						plugin.ui._insertImageButton.show();
  					} else {
  						plugin.ui._insertImageButton.hide();
  						return;
  					}
  
  					// Enable image specific ui components if the element is an image
  					if (foundMarkup) { // TODO : this is always null (below is dead code, moving it to clickImage)
  						plugin.ui._insertImageButton.show();
  						plugin.ui.setScope();
  						if (plugin.settings.ui.meta) {
  							plugin.ui.imgSrcField.setTargetObject(foundMarkup, 'src');
  							plugin.ui.imgTitleField.setTargetObject(foundMarkup, 'title');
  						}
  						plugin.ui.imgSrcField.foreground();
  						plugin.ui.imgSrcField.focus();
  					} else {
  						if (plugin.settings.ui.meta) {
  							plugin.ui.imgSrcField.setTargetObject(null);
  						}
  					}
  					// TODO this should not be necessary here!
  					plugin.ui.doLayout();
  				}
  
  			});
  
  			Aloha.bind('aloha-editable-created', function (event, editable) {
  				try {
  					// this disables mozillas image resizing facilities
  					document.execCommand('enableObjectResizing', false, false);
  				} catch (e) {
  					Aloha.Log.info(e, 'Could not disable enableObjectResizing');
  					// this is just for other browsers, which do not support disabling enableObjectResizing
  				}
  
  				// Inital click on images will be handled here
  				// editable.obj.find('img').attr('_moz_resizing', false);
  				// editable.obj.find('img').contentEditable(false);
  
  				editable.obj.delegate('img', 'mouseup', function (event) {
  					if (!resizing) {
  						plugin.clickImage(event);
  						event.stopPropagation();
  					}
  				});
  			});
  
  			plugin._subscribeToResizeFieldEvents();
  
  		},
  
  
  		/**
  		 * Automatically resize the image to fit into defined bounds.
  		 * @param doScaleUp if true, small images are scaled up to fit minimum size
  		 */
  		autoResize: function (doScaleUp) {
  			// @todo handle ratio mismatches (eg 4:3 is set but image is 16:9 --> image needs to be cut)
  
  			var that = this;
  
  			var widthField = jQuery("#" + that.ui.imgResizeWidthField.getInputId());
  			var heightField = jQuery("#" + that.ui.imgResizeHeightField.getInputId());
  
  			var width = that.imageObj.width();
  			var height = that.imageObj.height();
  			var resize = false;
  
  			// Only normalize the field values if the image exceeds the defined bounds
  			if (width < that.settings.minWidth ||
  				width > that.settings.maxWidth ||
  				height < that.settings.minHeight ||
  				height > that.settings.maxHeight) {
  				resize = true;
  			}
  
  			var aspectRatio = width / height;
  
  			if (resize) {
  				if (width > that.settings.maxWidth) {
  					width = that.settings.maxWidth;
  					height = width / aspectRatio;
  				}
  
  				if (height > that.settings.maxHeight) {
  					height = that.settings.maxHeight;
  					width = height * aspectRatio;
  				}
  
  				if ((width < that.settings.minWidth) && doScaleUp) {
  					width = that.settings.minWidth;
  					height = width / aspectRatio;
  				}
  
  				if ((height < that.settings.minHeight) && doScaleUp) {
  					height = that.settings.minHeight;
  					width = width * aspectRatio;
  				}
  
  				that._applyValuesToFields(width, height);
  
  				that.setSizeByFieldValue();
  				return true;
  			}
  			return false;
  		},
  
  		/**
  		 * Toggle the keep aspect ratio functionality
  		 */
  		toggleKeepAspectRatio: function () {
  
  			this.keepAspectRatio = !this.keepAspectRatio;
  
  			// while cropping: calculate the new aspect ratio value for the crop
  			if (typeof this.jcAPI !== 'undefined' && this.jcAPI !== null) {
  				var selection = this.jcAPI.tellSelect();
  
  				if (!this.keepAspectRatio) {
  					cropRatioValue = false;
  				} else {
  					if (typeof this.settings.fixedAspectRatio !== 'number') {
  						if (selection.w / selection.h > 0) {
  							cropRatioValue = selection.w / selection.h;
  						} else {
  							cropRatioValue = this.imageObj.width() / this.imageObj.height();
  						}
  					} else {
  						cropRatioValue = this.settings.fixedAspectRatio;
  					}
  				}
  
  				this.jcAPI.setOptions(this.keepAspectRatio ? { aspectRatio: cropRatioValue} : { aspectRatio: 0 });
  				this.jcAPI.focus();
  
  			// if not in cropping mode: calculate the new aspect ratio value for image resizing
  			} else {
  				this.endResize();
  				if (!this.keepAspectRatio) {
  					aspectRatioValue = false;
  				} else {
  					if ( typeof this.settings.fixedAspectRatio !== 'number' ) {
  						aspectRatioValue = this.imageObj.width() / this.imageObj.height();
  					} else {
  						aspectRatioValue = this.settings.fixedAspectRatio;
  					}
  				}
  				this.startResize();
  			}
  		},
  
  		/**
  		 * Bind interaction events that are invoked on the resize fields
  		 */
  		_subscribeToResizeFieldEvents: function () {
  			var plugin = this;
  
  			/**
  			 * Handle the keyup event on the field
  			 */
  			function handleKeyUpEventOnField(e) {
  
  				// Load the max/min from the data properties of this event
  				var minValue = e.data.minValue;
  				var maxValue = e.data.maxValue;
  				var fieldName = e.data.fieldName;
  
  				// Allow backspace and delete
  				if (e.keyCode === 8 || e.keyCode === 46) {
  
  					// Only resize if field values are ok
  					if (plugin._updateFields(fieldName, $(this).val(), false)) {
  						// Check if we are currently in cropping mode
  						if (typeof plugin.jcAPI !== 'undefined' && plugin.jcAPI !== null) {
  							plugin.setCropAreaByFieldValue();
  						} else {
  							plugin.setSizeByFieldValue();
  						}
  					}
  					// 0-9 keys
  				} else if (e.keyCode <= 57 && e.keyCode >= 48 || e.keyCode <= 105 && e.keyCode >= 96) {
  
  					// Only resize if field values are ok
  					if (plugin._updateFields(fieldName, $(this).val(), false)) {
  						// Check if we are currently in cropping mode
  						if (typeof plugin.jcAPI !== 'undefined' && plugin.jcAPI !== null) {
  							plugin.setCropAreaByFieldValue();
  						} else {
  							plugin.setSizeByFieldValue();
  						}
  					}
  				} else {
  					var delta = 0;
  					if (e.keyCode === 38 || e.keyCode === 107) {
  						delta = +1;
  					} else if (e.keyCode === 40 || e.keyCode === 109) {
  						delta = -1;
  					}
  					// Handle key combinations
  					if (e.shiftKey || e.metaKey || e.ctrlKey) {
  						delta = delta * 10;
  					}
  
  					var isDecrement = false;
  					if (delta < 0) {
  						isDecrement = true;
  					}
  					var newValue = parseInt($(this).val(), 10) + delta;
  
  					// Only resize if field values are ok
  					if (plugin._updateFields(fieldName, newValue, isDecrement)) {
  						// Check if we are currently in cropping mode
  						if (typeof plugin.jcAPI !== 'undefined' && plugin.jcAPI !== null) {
  							plugin.setCropAreaByFieldValue();
  						} else {
  							plugin.setSizeByFieldValue();
  						}
  					}
  				}
  
  				e.preventDefault();
  				return false;
  			}
  
  			/**
  			 * Handle the mouse wheel event on the field
  			 */
  			function handleMouseWheelEventOnField(e, delta) {
  				var minValue = e.data.minValue;
  				var maxValue = e.data.maxValue;
  				var fieldName = e.data.fieldName;
  
  				// Handle key combinations
  				if (e.shiftKey || e.metaKey || e.ctrlKey) {
  					delta = delta * 10;
  				}
  
  				var newValue = parseInt($(this).val(), 10) + delta;
  				var decrement = false;
  				if (delta < 0) {
  					decrement = true;
  				}
  
  				// Only resize if field values are ok
  				if (plugin._updateFields(fieldName, newValue, decrement)) {
  					// Check if we are currently in cropping mode
  					if (typeof plugin.jcAPI !== 'undefined' && plugin.jcAPI !== null) {
  						plugin.setCropAreaByFieldValue();
  					} else {
  						plugin.setSizeByFieldValue();
  					}
  				}
  				return false;
  			}
  
  			/**
  			 * Handle mousewheel,keyup actions on both fields
  			 */
  			var $heightField = $('#' + plugin.ui.imgResizeHeightField.getInputId());
  			var heightEventData = {fieldName: 'height', maxValue: plugin.ui.imgResizeHeightField.maxValue, minValue: plugin.ui.imgResizeHeightField.minValue };
  			$heightField.live('keyup', heightEventData, handleKeyUpEventOnField);
  			$heightField.live('mousewheel', heightEventData, handleMouseWheelEventOnField);
  
  			var $widthField = $('#' + plugin.ui.imgResizeWidthField.getInputId());
  			var widthEventData = {fieldName: 'width', maxValue: plugin.ui.imgResizeWidthField.maxValue, minValue: plugin.ui.imgResizeWidthField.minValue};
  			$widthField.live('keyup', widthEventData, handleKeyUpEventOnField);
  			$widthField.live('mousewheel', widthEventData, handleMouseWheelEventOnField);
  
  		},
  
  		/**
  		 * Manually set the given size for the current image
  		 */
  		setSize: function (width, height) {
  
  			var plugin = this;
  			plugin.imageObj.width(width);
  			plugin.imageObj.height(height);
  			var $wrapper = plugin.imageObj.closest('.Aloha_Image_Resize');
  			$wrapper.height(height);
  			$wrapper.width(width);
  
  			plugin._onResize(plugin.imageObj);
  			plugin._onResized(plugin.imageObj);
  		},
  
  		/**
  		 * This method will handle the mouseUp event on images (eg. within editables).
  		 * It will if enabled activate the resizing action.
  		 */
  		clickImage: function (e) {
  
  			var plugin = this;
  			plugin.endResize(); // removes previous resize handler
  			plugin.imageObj = jQuery(e.target);
  			var currentImage = plugin.imageObj;
  
  			// Ignore any images that are part of the ui (e.g. block edit and delete icons)
  			if (currentImage.hasClass('aloha-ui')) {
  				return;
  			}
  
  			plugin.ui.setScope();
  
  			var editable = currentImage.closest('.aloha-editable');
  
  			plugin.ui._imageCnrRatioButton.setState(this.keepAspectRatio);
  
  			// Disabling the content editable. This will disable the resizeHandles in internet explorer
  			// already done in resize on a smaller scope, this block next aloha-selection-change event
  			// to be thrown
  			// editable.contentEditable(false);
  
  			// Store the current props of the image
  			plugin.restoreProps.push({
  				obj : plugin.imageObj,
  				src : plugin.imageObj.attr('src'),
  				width : plugin.imageObj.width(),
  				height : plugin.imageObj.height()
  			});
  
  			// Update the resize input fields with the new width and height
  			plugin._applyValuesToFields(plugin.imageObj.width(), plugin.imageObj.height());
  
  			if (plugin.settings.ui.meta) {
  				plugin.ui.imgSrcField.setTargetObject(plugin.imageObj, 'src');
  				plugin.ui.imgTitleField.setTargetObject(plugin.imageObj, 'title');
  			}
  			Aloha.Selection.preventSelectionChanged();
  			try {
  				plugin.ui.imgSrcField.focus();
  			} catch(e) {
  				// FIXME for some reason execution breaks at this point
  			}
  
  			//to handle switching between images, aspect ratio is recalculated on click
  			cropRatioValue = aspectRatioValue = plugin.imageObj.width() / plugin.imageObj.height();
  
  			if (plugin.settings.ui.resizable) {
  				plugin.startResize();
  			}
  
  			if (plugin.settings.autoResize) {
  				plugin.autoResize(true);
  			}
  			Aloha.Selection.preventSelectionChangedFlag = false;
  			Aloha.trigger('aloha-image-selected');
  		},
  
  		/**
  		 * This method extracts determins if the range selection contains an image
  		 *
  		 * UNUSED as long as clickImage don't change the selection
  		 * @see getPluginFocus instead
  		 */
  		findImgMarkup: function (range) {
  
  			var plugin = this;
  			var config = this.config;
  			var result, targetObj;
  
  			if (typeof range === 'undefined') {
  				range = Aloha.Selection.getRangeObject();
  			}
  
  			targetObj = jQuery(range.startContainer);
  
  			try {
  				if (Aloha.activeEditable) {
  					if ((typeof range.startContainer !== 'undefined' &&
  						typeof range.startContainer.childNodes !== 'undefined' &&
  						typeof range.startOffset !== 'undefined' &&
  						typeof range.startContainer.childNodes[range.startOffset] !== 'undefined' &&
  						range.startContainer.childNodes[range.startOffset].nodeName.toLowerCase() === 'img' &&
  						range.startOffset + 1 === range.endOffset) ||
  						(targetObj.hasClass('Aloha_Image_Resize')))
  					{
  						result = targetObj.find('img')[0];
  						if (! result.css) {
  							result.css = '';
  						}
  
  						if (! result.title) {
  							result.title = '';
  						}
  
  						if (! result.src) {
  							result.src = '';
  						}
  						return result;
  					}
  					else {
  						return null;
  					}
  				}
  			} catch (e) {
  				Aloha.Log.debug(e, "Error finding img markup.");
  			}
  			return null;
  		},
  		/**
  		 * Gets the plugin focus target
  		 */
  		getPluginFocus: function () {
  			return this.imageObj;
  		},
  
  		/**
  		 * Helper function that checks a field-input (of height or width), adjusts
  		 * its value to conform to minimum/maximum values, and corrects the other field
  		 * if the aspect ratio is to be kept
  		 * @param primaryFieldName the field which is currently edited ('height' or 'width')
  		 * @param newValue the value as it was entered into the primary field
  		 * @param isDecrement true if value is being decreased (by key or mousewheel); prevents decreasing below minimum
  		 * @return true if values are correct or have been corrected, and image can be resized
  		 */
  		_updateFields: function (primaryFieldName, newValue, isDecrement) {
  			var plugin = this;
  
  			var $primaryField = null;
  			var $secondaryField = null;
  
  			var adjustedAspectRatio = null;
  			var primaryMin = null;
  			var primaryMax = null;
  			var secondaryMin = null;
  			var secondaryMax = null;
  
  			// If the aspect-ratio for cropping has been changed from the standard aspect-ratio,
  			// it is used for updating the fields (if keeping ratio is activated)
  			if (typeof plugin.jcAPI !== 'undefined' && plugin.jcAPI !== null) {
  				adjustedAspectRatio = cropRatioValue ? cropRatioValue : aspectRatioValue;
  			} else {
  				adjustedAspectRatio = aspectRatioValue;
  			}
  
  			// Depending on the field that is edited, primary and secondary values are set
  			if (primaryFieldName == 'width') {
  				$primaryField = $("#" + plugin.ui.imgResizeWidthField.getInputId());
  				$secondaryField = $("#" + plugin.ui.imgResizeHeightField.getInputId());
  				adjustedAspectRatio = (1 / adjustedAspectRatio);
  
  				primaryMin = plugin.settings.minWidth;
  				primaryMax = plugin.settings.maxWidth;
  				secondaryMin = plugin.settings.minHeight;
  				secondaryMax = plugin.settings.maxHeight;
  
  			} else if (primaryFieldName == 'height') {
  				$primaryField = $("#" + plugin.ui.imgResizeHeightField.getInputId());
  				$secondaryField = $("#" + plugin.ui.imgResizeWidthField.getInputId());
  				adjustedAspectRatio = adjustedAspectRatio;
  
  				primaryMin = plugin.settings.minHeight;
  				primaryMax = plugin.settings.maxHeight;
  				secondaryMin = plugin.settings.minWidth;
  				secondaryMax = plugin.settings.maxWidth;
  			} else {
  				// If primaryFieldName is neither width nor height, don't update the fields
  				return false;
  			}
  
  			if (isNaN(newValue)) {
  				$primaryField.css('background-color', 'red');
  				// If the current value of the field can't be parsed it is not updated
  				return false;
  			}
  			else {
  				$primaryField.val(newValue);
  			}
  
  			var correctPrimary = true;
  
  			if (newValue > primaryMax) {
  				// Auto-correct out of bounds values
  				if (plugin.settings.autoCorrectManualInput) {
  					$primaryField.val(primaryMax);
  					newValue = $primaryField.val();
  					$primaryField.css('background-color', '');
  					correctPrimary = true;
  				// Just notify the user, do nothing
  				} else {
  					$primaryField.css('background-color', 'red');
  					correctPrimary = false;
  				}
  			} else if (newValue < primaryMin) {
  				// Don't let the user decrement values below minimum
  				if (isDecrement) {
  					$primaryField.val(primaryMin);
  					newValue = $primaryField.val();
  					correctPrimary = true;
  				// Auto-correct out of bounds values
  				} else if (plugin.settings.autoCorrectManualInput) {
  					$primaryField.css('background-color', 'wheat');
  					correctPrimary = false;
  				// Just notify the user, do nothing
  				} else {
  					$primaryField.css('background-color', 'red');
  					correctPrimary = false;
  				}
  			} else {
  				$primaryField.css('background-color', '');
  				correctPrimary = true;
  			}
  
  			var correctSecondary = true;
  
  			// If keep aspect ratio is enabled, the field that is not edited is updated as well
  			if (plugin.keepAspectRatio) {
  				var secondary = Math.round(newValue * adjustedAspectRatio);
  
  				if (secondary > secondaryMax) {
  					// Auto-correct out of bounds values
  					if (plugin.settings.autoCorrectManualInput) {
  						$secondaryField.val(secondaryMax);
  						$primaryField.val($secondaryField.val() / adjustedAspectRatio);
  						newValue = $primaryField.val();
  						$primaryField.css('background-color', '');
  						$secondaryField.css('background-color', '');
  						correctSecondary = true;
  					// Just notify the user, do nothing
  					} else {
  						secondary.css('background-color', 'red');
  						correctSecondary = false;
  					}
  				} else if (secondary < secondaryMin) {
  					// Don't let the user decrement values below minimum
  					if (isDecrement) {
  						$secondaryField.val(secondaryMin);
  						$primaryField.val($secondaryField.val() / adjustedAspectRatio);
  						newValue = $primaryField.val();
  						correctPrimary = true;
  					// Auto-correct out of bounds values
  					} else if (plugin.settings.autoCorrectManualInput) {
  						$secondaryField.val(secondary);
  						$secondaryField.css('background-color', 'wheat');
  						correctSecondary = false;
  					// Just notify the user, do nothing
  					} else {
  						$secondaryField.css('background-color', 'red');
  						correctSecondary = false;
  					}
  				} else {
  					$secondaryField.val(secondary);
  					$secondaryField.css('background-color', '');
  					correctSecondary = true;
  				}
  			}
  
  			// Final check for value of secondary field, in case focus was changed
  			if ($secondaryField.val() < secondaryMin) {
  				correctSecondary = false;
  			}
  
  			// Success if values are correct or have been adjusted accordingly
  			if (correctPrimary && correctSecondary) {
  				return true;
  			}
  			return false;
  		},
  
  		/**
  		 * Helper function that will set the new image size using the field values
  		 */
  		setSizeByFieldValue: function () {
  			var width =  this.ui.imgResizeWidthField.getValue();
  			var height = this.ui.imgResizeHeightField.getValue();
  			this.setSize(width, height);
  		},
  
  		/**
  		 * Helper function that sets field values to specified parameters and resets background of fields
  		 * @param width new image-width in pixels
  		 * @param height new image-height in pixels
  		 */
  		_applyValuesToFields: function (width, height) {
  			this.ui.imgResizeWidthField.setValue(width);
  			this.ui.imgResizeHeightField.setValue(height);
  
  			this.ui.imgResizeWidthField.getInputJQuery().css('background-color', '');
  			this.ui.imgResizeHeightField.getInputJQuery().css('background-color', '');
  		},
  
  		/**
  		 * Helper function that will set the new crop area width and height using the field values
  		 *
  		 * note: because the _updateFields function does not consider the x and y of the croparea yet,
  		 * the value of the secondary field shown after setting croparea with manual input may be incorrect;
  		 * while this has no influence on the actual crop size, a future implementation should consider this
  		 * when calculating the field values
  		 */
  		setCropAreaByFieldValue: function () {
  			var currentCropArea = this.jcAPI.tellSelect();
  
  			var width =  parseInt(this.ui.imgResizeWidthField.getValue(), 10);
  			var height = parseInt(this.ui.imgResizeHeightField.getValue(), 10);
  
  			var selection = [currentCropArea.x, currentCropArea.y, currentCropArea.x + width, currentCropArea.y + height];
  
  			this.jcAPI.setSelect(selection);
  			this._onCropSelect();
  			this.jcAPI.enable();
  		},
  
  		/**
  		* This method will insert a new image dom element into the dom tree
  		*/
  		insertImg: function () {
  				var range = Aloha.Selection.getRangeObject(),
  				config = this.getEditableConfig(Aloha.activeEditable.obj),
  				imagePluginUrl = Aloha.getPluginUrl('image'),
  				imagestyle, imagetag, newImg;
  
  				if (range.isCollapsed()) {
  					// TODO I would suggest to call the srcChange method. So all image src
  					// changes are on one single point.
  					imagestyle = "max-width: " + config.maxWidth + "; max-height: " + config.maxHeight;
  					imagetag = '<img style="' + imagestyle + '" src="' + imagePluginUrl + '/img/blank.jpg" title="" />';
  					newImg = jQuery(imagetag);
  					// add the click selection handler
  					//newImg.click( Aloha.Image.clickImage ); - Using delegate now
  					GENTICS.Utils.Dom.insertIntoDOM(newImg, range, jQuery(Aloha.activeEditable.obj));
  
  				} else {
  					Aloha.Log.error('img cannot markup a selection');
  					// TODO the desired behavior could be me the selected content is
  					// replaced by an image.
  					// TODO it should be editor's choice, with an NON-Ext Dialog instead of alert
  				}
  			},
  
  		srcChange: function () {
  			// TODO the src changed. I suggest :
  			// 1. set an loading image (I suggest set src base64 enc) to show the user
  			// we are trying to load an image
  			// 2. start a request to get the image
  			// 3a. the image is ok change the src
  			// 3b. the image is not availbable show an error.
  			this.imageObj.attr('src', this.ui.imgSrcField.getValue()); // (the img tag)
  //			 jQuery(img).attr('src', this.imgSrcField.getQueryValue()); // (the query value in the inputfield)
  //			 this.imgSrcField.getItem(); // (optinal a selected resource item)
  			// TODO additionally implement an srcChange Handler to let implementer
  			// customize
  		},
  
  		/**
  		 * Reposition the crop buttons below the crop area
  		 */
  		positionCropButtons: function () {
  
  			var jt = jQuery('.jcrop-tracker:first'),
  				off = jt.offset(),
  				jtt = off.top,
  				jtl = off.left,
  				jth = jt.height(),
  				jtw = jt.width();
  
  			var oldLeft = 0,
  				oldTop = 0;
  
  			var btns = jQuery('#aloha-CropNResize-btns');
  
  			// Hack to hide the buttons when the user just clicked into the image
  			if (jtt === 0 && jtl === 0) {
  				btns.hide();
  			}
  
  			// move the icons to the bottom right side
  			jtt = parseInt(jtt + jth + 3, 10);
  			jtl = parseInt(jtl + (jtw / 2) - (btns.width() / 2) + 10, 10);
  
  			// comparison to old values hinders flickering bug in FF
  			if (oldLeft != jtl || oldTop != jtt) {
  				btns.offset({top: jtt, left: jtl});
  			}
  
  			oldLeft = jtl;
  			oldTop = jtt;
  		},
  
  		/**
  		 * Code imported from CropnResize Plugin
  		 *
  		 */
  		initCropButtons: function () {
  			var that = this,
  				btns;
  
  			jQuery('body').append(
  				'<div id="aloha-CropNResize-btns" display="none">' +
  					'<button class="cnr-crop-apply" title="' + i18n.t('Accept') + '"></button>' +
  					'<button class="cnr-crop-cancel" title="' + i18n.t('Cancel') + '"></button>' +
  				'</div>'
  			);
  
  			btns = jQuery('#aloha-CropNResize-btns');
  
  			btns.find('.cnr-crop-apply').click(function () {
  				that.acceptCrop();
  			});
  
  			btns.find('.cnr-crop-cancel').click(function () {
  				that.endCrop();
  			});
  
  			this.interval = window.setInterval(function () {
  				that.positionCropButtons();
  			}, 10);
  		},
  
  		/**
  		 * Destroy crop confirm and cancel buttons
  		 */
  		destroyCropButtons: function () {
  			jQuery('#aloha-CropNResize-btns').remove();
  			window.clearInterval(this.interval);
  		},
  
  		/**
  		 * Helper function that will disable selectability of elements
  		 */
  		_disableSelection: function (el) {
  			el.find('*').attr('unselectable', 'on')
  					.css({
  					'-moz-user-select': 'none',
  					'-webkit-user-select': 'none',
  					'user-select': 'none'
  				});
  				/*
  					.each(function () {
  					this.onselectstart = function () { return false; };
  					});
  					*/
  
  		},
  
  		/**
  		 * Initiate a crop action
  		 */
  		crop: function () {
  			var plugin = this;
  			var config = this.config;
  			var ratio = plugin.keepAspectRatio ? aspectRatioValue : false;
  
  			this.ui._imageCropButton.setState(true);
  
  			plugin.initCropButtons();
  			if (plugin.settings.ui.resizable) {
  				plugin.endResize();
  			}
  
  			plugin.jcAPI = jQuery.Jcrop(plugin.imageObj, {
  				onSelect : function () {
  					plugin._onCropSelect();
  					// ugly hack to keep scope :(
  					window.setTimeout(function () {
  						plugin.ui.setScope();
  					}, 10);
  				},
  				aspectRatio: ratio,
  				minSize  : [plugin.settings.minWidth, plugin.settings.minHeight]
  			});
  
  			plugin._disableSelection($('.jcrop-holder'));
  			plugin._disableSelection($('#imageContainer'));
  			plugin._disableSelection($('#aloha-CropNResize-btns'));
  			$('body').trigger('aloha-image-crop-start', [plugin.imageObj]);
  		},
  
  		/**
  		 * Internal on crop select method
  		 */
  		_onCropSelect: function () {
  			var plugin = this;
  
  			// Hide the crop buttons when the one of the handles is clicked
  			jQuery('.jcrop-handle').mousedown(function () {
  				jQuery('#aloha-CropNResize-btns').hide();
  			});
  
  			jQuery('.jcrop-tracker').mousedown(function () {
  				jQuery('#aloha-CropNResize-btns').hide();
  			});
  
  			// Update the width and height field using the initial active crop area values
  			if (typeof plugin.jcAPI !== 'undefined' && plugin.jcAPI !== null) {
  				plugin.positionCropButtons();
  				var currentCropArea = plugin.jcAPI.tellSelect();
  
  				if (currentCropArea.h > 0 && currentCropArea.w > 0) {
  					jQuery('#aloha-CropNResize-btns').fadeIn('slow');
  				}
  
  				plugin._applyValuesToFields(currentCropArea.w, currentCropArea.h);
  			}
  
  		},
  
  		/**
  		 * Terminates a crop
  		 */
  		endCrop: function () {
  			if (this.jcAPI) {
  				this.jcAPI.destroy();
  				this.jcAPI = null;
  			}
  
  			this.destroyCropButtons();
  			this.ui._imageCropButton.setState(false);
  
  			if (this.settings.ui.resizable) {
  				this.startResize();
  			}
  
  			$('body').trigger('aloha-image-crop-stop', [this.imageObj]);
  
  			//after cropping, field values are set to (once again) contain image width/height
  			this._applyValuesToFields(this.imageObj.width(), this.imageObj.height());
  		},
  
  		/**
  		 * Accept the current cropping area and apply the crop
  		 */
  		acceptCrop: function () {
  			this._onCropped(this.imageObj, this.jcAPI.tellSelect());
  			this.endCrop();
  		},
  
  		/**
  		 * This method will activate the jquery-ui resize functionality for the current image
  		 */
  		startResize: function () {
  			var plugin = this;
  			var currentImageObj = this.imageObj;
  			var ratio = plugin.keepAspectRatio ? aspectRatioValue : false;
  
  			currentImageObj = this.imageObj.css({
  				height		: this.imageObj.height(),
  				width		: this.imageObj.width(),
  				position	: 'relative',
  				'max-height': '',
  				'max-width'	: ''
  			});
  
  			currentImageObj.resizable({
  				maxHeight : plugin.settings.maxHeight,
  				minHeight : plugin.settings.minHeight,
  				maxWidth  : plugin.settings.maxWidth,
  				minWidth  : plugin.settings.minWidth,
  				aspectRatio : ratio,
  				handles: plugin.settings.handles,
  				grid : plugin.settings.grid,
  				resize: function (event, ui) {
  					resizing = true;
  					plugin._onResize(plugin.imageObj);
  				},
  				stop : function (event, ui) {
  					resizing = false;
  
  					plugin._onResized(plugin.imageObj);
  
  					// Workaround to finish cropping
  					if (this.enableCrop) {
  						window.setTimeout(function () {
  							plugin.ui.setScope();
  							that.done(event);
  						}, 10);
  					}
  				}
  
  			});
  
  			currentImageObj.css('display', 'inline-block');
  
  			// this will prevent the user from resizing an image
  			// using IE's resize handles
  			// however I could not manage to hide them completely
  			jQuery('.ui-wrapper')
  				.attr('contentEditable', false)
  				.addClass('aloha-image-box-active Aloha_Image_Resize aloha')
  				.css({
  					position: 'relative',
  					display: 'inline-block',
  					'float': plugin.imageObj.css('float')
  				})
  				.bind('resizestart', function (e) {
  					e.preventDefault();
  				})
  				.bind('mouseup', function (e) {
  					e.originalEvent.stopSelectionUpdate = true;
  				});
  		},
  
  		/**
  		 * This method will end resizing and toggle buttons accordingly and remove all markup that has been added for cropping
  		 */
  		endResize: function () {
  			// Find the nearest contenteditable and reenable it since resizing is finished
  			if (this.imageObj) {
  				var editable = this.imageObj.closest('.aloha-editable');
  				//this.imageObj.contentEditable(true);
  			}
  
  			if (this.imageObj && this.imageObj.is(":ui-resizable")) {
  				this.imageObj
  					.resizable('destroy')
  					.css({
  						top	 : 0,
  						left : 0
  					});
  			}
  		},
  
  		resetSize: function () {
  			var plugin = this,
  				img = new Image();
  			img.onload = function () {
  				var myimage = plugin.getPluginFocus();
  				if (plugin.settings.ui.resizable) {
  					plugin.endResize();
  				}
  				jQuery(myimage).add(myimage.parent()).css({
  						'width': img.width + 'px',
  						'height': img.height + 'px',
  						'max-width': '',
  						'max-height': ''
  					});
  				if (plugin.settings.ui.resizable) {
  					plugin.resize();
  				}
  			};
  			img.src = plugin.getPluginFocus().attr('src');
  		},
  
  		/**
  		 * Reset the image to its original properties
  		 */
  		reset: function () {
  			var externalReset;
  
  			if (this.settings.ui.crop) {
  				this.endCrop();
  			}
  
  			if (this.settings.ui.resizable) {
  				this.endResize();
  			}
  
  			externalReset = this._onReset(this.imageObj);
  
  			// if the external reset procedure has already performed a reset, there is no need to apply an internal reset
  			if (!externalReset) {
  				for (var i = 0;i < this.restoreProps.length;i++) {
  
  					// restore from restoreProps if there is a match
  					if (this.imageObj.get(0) === this.restoreProps[i].obj.get(0)) {
  						this.imageObj.attr('src', this.restoreProps[i].src);
  						this.imageObj.width(this.restoreProps[i].width);
  						this.imageObj.height(this.restoreProps[i].height);
  						break;
  					}
  				}
  			}
  
  			// readjust inputfields to show correct height/width
  			this._applyValuesToFields(this.imageObj.width(), this.imageObj.height());
  		}
  	});
  
  });
  