text: /*
  * Aloha Image Plugin - Allow image manipulation in Aloha Editor
  *
  * Author & Copyright (c) 2011 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
  *
  * Licensed under the terms of http://www.aloha-editor.com/license.html
  */
  
  define([
    // js
    'aloha/jquery',
    'aloha/plugin',
    'aloha/floatingmenu',
    'i18n!aloha/nls/i18n',
    'i18n!image/nls/i18n',
    'jquery-plugin!image/vendor/ui/jquery-ui-1.8.10.custom.min',
    'jquery-plugin!image/vendor/jcrop/jquery.jcrop.min',
    'jquery-plugin!image/vendor/mousewheel/mousewheel',
    // css
    'css!image/css/image.css',
    'css!image/vendor/ui/ui-lightness/jquery-ui-1.8.10.cropnresize.css',
    'css!image/vendor/jcrop/jquery.jcrop.css'
  ],
  function AlohaImagePlugin ( aQuery, Plugin, FloatingMenu, i18nCore, i18n ) {
  
  
  
    var jQuery = aQuery;
    var $ = aQuery;
    var GENTICS = window.GENTICS;
    var Aloha = window.Aloha;
  
    // 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;
        // Check
        if ( !obj.length ) {
          return obj;
        }
        // Calculate
        value = obj.css(attr).toFloat();
        newValue = Math.round((value||1)*1.2);
        // Apply
        if (value == newValue) { // when value is 2, won't increase
          newValue++;
        }
        // Apply
        obj.css(attr,newValue);
        // Chain
        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', {
  
      languages: ['en', 'fr', 'de', 'ru', 'cz'],
  
      defaultSettings: {
        'maxWidth': 1600,
        'minWidth': 3,
        'maxHeight': 1200,
        'minHeight': 3,
        // This setting will correct manually 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: {
          oneTab    : false, //Place all ui components within one tab
          insert      : true,
          reset     : true,
          aspectRatioToggle: true, // Toggle button for the aspect ratio
          align    : true,  // Menu elements to show/hide in menu
          resize    : true,  // Resize buttons
          meta    : true,
          margin    : true,
          crop    : true,
          resizable  : true,  // Resizable ui-drag image
          handles     : 'ne, se, sw, nw'
        },
  
        /**
         * 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 beeing 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) {
        $('#' + this.imgResizeHeightField.id).val($image.height());
        $('#' + this.imgResizeWidthField.id).val($image.width());
  
  
        $('body').trigger('aloha-image-cropped', [$image, props]);
  
        // Call the custom onCropped function
        this.onCropped($image, props);
      },
  
      /**
       * Internal callback hook which gets invoked when resetting images
       */
      _onReset: function ($image) {
  
        $('#' + this.imgResizeHeightField.id).val($image.height());
        $('#' + this.imgResizeWidthField.id).val($image.width());
  
        // No default behaviour defined besides event triggering
        $('body').trigger('aloha-image-reset', $image);
  
        // Call the custom resize function
        return this.onReset($image);
      },
  
      /**
       * Internal callback hook which gets invoked while the image is beeing resized
       */
      _onResize: function ($image) {
  
        $('#' + this.imgResizeHeightField.id).val($image.height());
        $('#' + this.imgResizeWidthField.id).val($image.width());
  
        // No default behaviour defined besides event triggering
        $('body').trigger('aloha-image-resize', $image);
  
        // Call the custom resize function
        this.onResize($image);
      },
  
      /**
       * Internal callback hook which gets invoked when the current resizing action has stopped
       */
      _onResized: function ($image) {
  
        $('#' + this.imgResizeHeightField.id).val($image.height());
        $('#' + this.imgResizeWidthField.id).val($image.width());
  
        $('body').trigger('aloha-image-resized', $image);
  
        // 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,
  
      /**
       * Variable that will hold the start aspect ratio. This ratio will be used once starResize will be called.
       */
      startAspectRatio: 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
       * referenct, to prevent for double entries
       */
      restoreProps: [],
  
      objectTypeFilter: [],
  
      /**
       * Plugin initialization method
       */
      init: function() {
  
        var that = this;
  
        var imagePluginUrl = Aloha.getPluginUrl('image');
  
        // Extend the default settings with the custom ones (done by default)
        this.startAspectRatio = this.settings.fixedAspectRatio;
        this.config = this.defaultSettings;
        this.settings = jQuery.extend(true, this.defaultSettings, this.settings);
  
        that.initializeButtons();
        that.bindInteractions();
        that.subscribeEvents();
  
      },
  
      /**
      * Create buttons
      */
      initializeButtons: function() {
  
        var that = this,
          tabInsert = i18nCore.t('floatingmenu.tab.insert'),
          tabImage = i18n.t('floatingmenu.tab.img'),
          tabFormatting = i18n.t('floatingmenu.tab.formatting'),
          tabCrop = i18n.t('floatingmenu.tab.crop'),
          tabResize = i18n.t('floatingmenu.tab.resize');
  
        FloatingMenu.createScope(this.name, 'Aloha.empty');
  
        if (this.settings.ui.insert) {
          var tabId = this.settings.ui.oneTab ? tabImage : tabInsert;
          that._addUIInsertButton(tabId);
        }
  
        if (this.settings.ui.meta) {
          var tabId = this.settings.ui.oneTab ? tabImage : tabImage;
          that._addUIMetaButtons(tabId);
        }
  
        if (this.settings.ui.reset) {
          var tabId = this.settings.ui.reset ? tabImage : tabImage;
          that._addUIResetButton(tabId);
        }
  
        if (this.settings.ui.align) {
          var tabId = this.settings.ui.oneTab ? tabImage : tabFormatting;
          that._addUIAlignButtons(tabId);
        }
  
        if (this.settings.ui.margin) {
          var tabId = this.settings.ui.oneTab ? tabImage : tabFormatting;
          that._addUIMarginButtons(tabId);
        }
  
        if (this.settings.ui.crop) {
          var tabId = this.settings.ui.oneTab ? tabImage : tabCrop;
          that._addUICropButtons(tabId);
        }
  
        if (this.settings.ui.resize) {
          var tabId = this.settings.ui.oneTab ? tabImage : tabResize;
          that._addUIResizeButtons(tabId);
        }
  
        if (this.settings.ui.aspectRatioToggle) {
          var tabId = this.settings.ui.oneTab ? tabImage : tabResize;
          that.__addUIAspectRatioToggleButton(tabId);
        }
  
        // TODO fix the function and reenable this button
        //that._addNaturalSizeButton();
      },
  
  
      /**
       * Adds the aspect ratio toggle button to the floating menu
       */
      __addUIAspectRatioToggleButton: function(tabId) {
        var that = this;
        var toggleButton = new Aloha.ui.Button({
          'size' : 'small',
          'tooltip' : i18n.t('button.toggle.tooltip'),
          'toggle' : true,
          'iconClass' : 'cnr-ratio',
          'onclick' : function (btn, event) {
            that.toggleKeepAspectRatio();
          }
        });
  
  
        // If the setting has been set to a number or false we need to activate the
        // toggle button to indicate that the aspect ratio will be preserved.
        if (this.settings.fixedAspectRatio != false) {
          toggleButton.pressed = true;
          this.keepAspectRatio = true;
        }
  
        FloatingMenu.addButton(
          that.name,
          toggleButton,
          tabId,
          20
        );
  
  
      },
  
  
      /**
       * Adds the reset button to the floating menu for the given tab
       */
      _addUIResetButton: function(tabId) {
        var that = this;
        // Reset button
        var resetButton = new Aloha.ui.Button({
          'size' : 'small',
          'tooltip' : i18n.t('Reset'),
          'toggle' : false,
          'iconClass' : 'cnr-reset',
          'onclick' : function (btn, event) {
            that.reset();
          }
        });
  
        FloatingMenu.addButton(
          that.name,
          resetButton,
          tabId,
          2
        );
      },
  
      /**
       * Adds the insert button to the floating menu
       */
      _addUIInsertButton: function(tabId) {
        var that = this;
        this.insertImgButton = new Aloha.ui.Button({
          'name' : 'insertimage',
          'iconClass': 'aloha-button aloha-image-insert',
          'size' : 'small',
          'onclick' : function () { that.insertImg(); },
          'tooltip' : i18n.t('button.addimg.tooltip'),
          'toggle' : false
        });
  
        FloatingMenu.addButton(
          'Aloha.continuoustext',
          this.insertImgButton,
          tabId,
          1
        );
      },
  
      /**
        * Adds the ui meta fields (search, title) to the floating menu.
       */
      _addUIMetaButtons: function(tabId) {
        var that = this;
        var imgSrcLabel = new Aloha.ui.Button({
          'label': i18n.t('field.img.src.label'),
          'tooltip': i18n.t('field.img.src.tooltip'),
          'size': 'small'
        });
        this.imgSrcField = new Aloha.ui.AttributeField({'name' : 'imgsrc'});
        this.imgSrcField.setObjectTypeFilter( this.objectTypeFilter );
  
        // add the title field for images
        var imgTitleLabel = new Aloha.ui.Button({
          'label': i18n.t('field.img.title.label'),
          'tooltip': i18n.t('field.img.title.tooltip'),
          'size': 'small'
        });
  
        this.imgTitleField = new Aloha.ui.AttributeField();
        this.imgTitleField.setObjectTypeFilter();
        FloatingMenu.addButton(
          this.name,
          this.imgSrcField,
          tabId,
          1
        );
  
      },
  
      /**
       * Adds the ui align buttons to the floating menu
       */
      _addUIAlignButtons: function(tabId) {
        var that = this;
  
        var  alignLeftButton = new Aloha.ui.Button({
          'iconClass': 'aloha-img aloha-image-align-left',
          'size': 'small',
          'onclick' : function() {
            var el = jQuery(that.findImgMarkup());
            el.add(el.parent()).css('float', 'left');
          },
          'tooltip': i18n.t('button.img.align.left.tooltip')
        });
  
        FloatingMenu.addButton(
          that.name,
          alignLeftButton,
          tabId,
          1
        );
  
        var alignRightButton = new Aloha.ui.Button({
          'iconClass': 'aloha-img aloha-image-align-right',
          'size': 'small',
          'onclick' : function() {
            var el = jQuery(that.findImgMarkup());
            el.add(el.parent()).css('float', 'right');
          },
          'tooltip': i18n.t('button.img.align.right.tooltip')
        });
  
        FloatingMenu.addButton(
          that.name,
          alignRightButton,
          tabId,
          1
        );
  
        var alignNoneButton = new Aloha.ui.Button({
          'iconClass': 'aloha-img aloha-image-align-none',
          'size': 'small',
          'onclick' : function() {
            var el = jQuery(that.findImgMarkup());
            el.add(el.parent()).css({
              'float': 'none',
              display: 'inline-block'
            });
          },
          'tooltip': i18n.t('button.img.align.none.tooltip')
        });
  
        FloatingMenu.addButton(
          that.name,
          alignNoneButton,
          tabId,
          1
        );
  
      },
  
      /**
       * Adds the ui margin buttons to the floating menu
       */
      _addUIMarginButtons: function(tabId) {
        var that = this;
        var incPadding = new Aloha.ui.Button({
          iconClass: 'aloha-img aloha-image-padding-increase',
          toggle: false,
          size: 'small',
          onclick: function() {
            jQuery(that.findImgMarkup()).increase('padding');
          },
          tooltip: i18n.t('padding.increase')
        });
        FloatingMenu.addButton(
          that.name,
          incPadding,
          tabId,
          2
        );
  
        var decPadding = new Aloha.ui.Button({
          iconClass: 'aloha-img aloha-image-padding-decrease',
          toggle: false,
          size: 'small',
          onclick: function() {
            jQuery(that.findImgMarkup()).decrease('padding');
          },
          tooltip: i18n.t('padding.decrease')
        });
        FloatingMenu.addButton(
          that.name,
          decPadding,
          tabId,
          2
        );
      },
  
      /**
       * Adds the crop buttons to the floating menu
       */
       _addUICropButtons: function (tabId) {
         var that = this;
  
         FloatingMenu.createScope('Aloha.img', ['Aloha.global']);
  
        this.cropButton = new Aloha.ui.Button({
          'size' : 'small',
          'tooltip' : i18n.t('Crop'),
          'toggle' : true,
          'iconClass' : 'cnr-crop',
          'onclick' : function (btn, event) {
            if (btn.pressed) {
              that.crop();
            } else {
              that.endCrop();
            }
          }
        });
  
        FloatingMenu.addButton(
          this.name,
          this.cropButton,
          tabId,
          3
        );
  
       },
  
       /**
        * Adds the resize buttons to the floating menu
        */
       _addUIResizeButtons: function (tabId) {
         var that = this;
  
        // Manual resize fields
        this.imgResizeHeightField = new Aloha.ui.AttributeField();
        this.imgResizeHeightField.maxValue = that.settings.maxHeight;
        this.imgResizeHeightField.minValue = that.settings.minHeight;
  
        this.imgResizeWidthField = new Aloha.ui.AttributeField();
        this.imgResizeWidthField.maxValue = that.settings.maxWidth;
        this.imgResizeWidthField.minValue = that.settings.minWidth;
  
        this.imgResizeWidthField.width = 50;
        this.imgResizeHeightField.width = 50;
  
        var widthLabel = new Aloha.ui.Button({
          'label':  i18n.t('width'),
          'tooltip': i18n.t('width'),
          'size': 'small'
        });
  
        FloatingMenu.addButton(
            this.name,
            widthLabel,
            tabId,
            30
        );
  
        FloatingMenu.addButton(
            this.name,
            this.imgResizeWidthField,
            tabId,
            40
        );
  
  
        var heightLabel = new Aloha.ui.Button({
          'label':  i18n.t('height'),
          'tooltip': i18n.t('height'),
          'size': 'small'
        });
  
        FloatingMenu.addButton(
            this.name,
            heightLabel,
            tabId,
            50
        );
  
        FloatingMenu.addButton(
            this.name,
            this.imgResizeHeightField,
            tabId,
            60
        );
  
  
       },
  
       /**
       * Adds the natural size button to the floating menu
       */
       _addNaturalSizeButton: function () {
          var that = this;
        var naturalSize = new Aloha.ui.Button({
          iconClass: 'aloha-img aloha-image-size-natural',
          size: 'small',
          toggle: false,
          onclick: function() {
            var  img = new Image();
            img.onload = function() {
              var myimage = that.findImgMarkup();
              if (that.settings.ui.resizable) {
                that.endResize();
              }
              jQuery(myimage).css({
                  'width': img.width + 'px',
                  'height': img.height + 'px',
                  'max-width': '',
                  'max-height': ''
                });
              if (that.settings.ui.resizable) {
                that.resize();
              }
            };
            img.src = that.findImgMarkup().src;
  
          },
          tooltip: i18n.t('size.natural')
        });
        FloatingMenu.addButton(
          this.name,
          naturalSize,
          tabResize,
          2
        );
      },
  
      /**
       * Bind plugin interactions
       */
      bindInteractions: function () {
        var  that = this;
  
        if (this.settings.ui.resizable) {
          try {
            // this will disable mozillas image resizing facilities
            document.execCommand( 'enableObjectResizing', false, false );
          } catch (e) {
            Aloha.Log.error( e, 'Could not disable enableObjectResizing' );
            // this is just for internet explorer, who will not support disabling enableObjectResizing
          }
        }
  
        if (this.settings.ui.meta) {
          // update image object when src changes
          this.imgSrcField.addListener('keyup', function(obj, event) {
            that.srcChange();
          });
  
          this.imgSrcField.addListener('blur', function(obj, event) {
            // TODO remove image or do something usefull if the user leaves the
            // image without defining a valid image src.
            var img = jQuery(obj.getTargetObject());
            if ( img.attr('src') === '' ) {
              img.remove();
            } // image removal when src field is blank
          });
        }
  
        // Override the default method by using the given one
        if (this.settings.onCropped && typeof this.settings.onCropped === "function") {
          this.onCropped = this.settings.onCropped;
        }
  
        // Override the default method by using the given one
        if (this.settings.onReset && typeof this.settings.onReset === "function") {
          this.onReset = this.settings.onReset;
        }
  
        // Override the default method by using the given one
        if (this.settings.onResized && typeof this.settings.onResized === "function") {
          this.onResized = this.settings.onResized;
        }
  
        // Override the default method by using the given one
        if (this.settings.onResize && typeof this.settings.onResize === "function") {
          this.onResize = this.settings.onResize;
        }
  
      },
  
      /**
       * Subscribe to Aloha events and DragAndDropPlugin Event
       */
      subscribeEvents: function() {
        var  that = this;
        var config = this.settings;
  
        jQuery('img').filter(config.globalselector).unbind();
        jQuery('img').filter(config.globalselector).click(function(event) {
          that.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 = that.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 beeing resized
            if (that.settings.ui.resizable && !jQuery(originalEvent.target).hasClass('ui-resizable-handle')) {
              that.endResize();
            }
          }
  
          if (Aloha.activeEditable !== null) {
            foundMarkup = that.findImgMarkup( rangeObject );
            //var config = that.getEditableConfig(Aloha.activeEditable.obj);
            config = that.getEditableConfig(Aloha.activeEditable.obj);
  
            // FIXME (Did)
            if (that.settings.ui.insert)
              if (typeof config !== 'undefined' ) {
                that.insertImgButton.show();
              } else {
                that.insertImgButton.hide();
                return;
              }
  
            // Enable image specific ui components if the element is an image
            if (foundMarkup) {
              that.insertImgButton.hide();
              FloatingMenu.setScope(that.name);
              if (that.settings.ui.meta) {
                that.imgSrcField.setTargetObject(foundMarkup, 'src');
                that.imgTitleField.setTargetObject(foundMarkup, 'title');
              }
              that.imgSrcField.focus();
              FloatingMenu.activateTabOfButton('imgsrc');
            } else {
              if (that.settings.ui.meta) {
                that.imgSrcField.setTargetObject(null);
              }
            }
            // TODO this should not be necessary here!
            FloatingMenu.doLayout();
          }
  
        });
  
        Aloha.bind('aloha-editable-created', function( event, editable) {
  
          try {
            // this will disable mozillas image resizing facilities
            document.execCommand( 'enableObjectResizing', false, false);
          } catch (e) {
            Aloha.Log.error( e, 'Could not disable enableObjectResizing');
            // this is just for others, who will 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) {
            that.clickImage(event);
            event.stopPropagation();
          });
        });
  
        that._subscribeToResizeFieldEvents();
  
      },
  
      /**
       * Automatically resize the image to fit into defined bounds.
       */
      autoResize: function() {
        var that = this;
  
        var width = that.imageObj.width();
        var height = that.imageObj.height();
  
        // Only normalize the field values when the image exeeds the definded bounds
        if (width < that.settings.minWidth || width > that.settings.maxWidth || height < that.settings.minHeight || height > that.settings.maxHeight) {
          that._setNormalizedFieldValues('width');
          that.setSizeByFieldValue();
          return true;
        } else {
          return false;
        }
      },
  
      /**
       * Toggle the keep aspect ratio functionallity
       */
      toggleKeepAspectRatio: function() {
  
        this.keepAspectRatio = !this.keepAspectRatio;
  
        this.endResize();
        if (!this.keepAspectRatio) {
          this.startAspectRatio = false;
        } else {
          // If no fixed aspect ratio was given we will calculate a new start
          // aspect ratio that will be used for the next starResize action.
          if ( typeof this.settings.fixedAspectRatio !== 'number' ) {
            var currentRatio = this.imageObj.width() / this.imageObj.height();
            this.startAspectRatio = currentRatio;
          } else {
            this.startAspectRatio = this.settings.fixedAspectRatio;
          }
        }
        this.startResize();
      },
  
      /**
       * Bind interaction events that are invoked on the resize fields
       */
      _subscribeToResizeFieldEvents: function() {
        var that = this;
  
        /**
         * Helper function that will update the fields
         */
        function updateField( $field, delta, maxValue, minValue ) {
  
          if ( typeof minValue === 'undefined' ) {
            minValue = 0;
          }
  
          if ( typeof maxValue === 'undefined' ) {
            maxValue = 8000;
          }
  
          // If the current value of the field can't be parsed we don't update it
          var oldValue = parseInt( $field.val() );
          if ( isNaN(oldValue) ) {
            $field.css( 'background-color', 'red' );
            return false;
          }
  
          var newValue = oldValue + delta;
          // Exit if the newValue is above the maxValue limit (only if the user tries to increment)
          if (delta>=0 && newValue > maxValue) {
  
            // Auto correct out of bounds values
            if (that.settings.autoCorrectManualInput) {
              $field.val(maxValue);
              return true;
            } else {
              $field.css('background-color','red');
              return false;
            }
           // Exit if the newValue is below the minValue (only if the user tries to decrement)
           } else if (delta<=0 && newValue<minValue) {
  
            // Auto correct out of bounds values
            if (that.settings.autoCorrectManualInput) {
              $field.val(minValue);
              return true;
            } else {
              $field.css('background-color','red');
              return false;
            }
          } else {
            $field.css('background-color','');
          }
          $field.val(oldValue + delta);
          return true;
        };
  
        /**
         * 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) {
            if($(this).val() >= minValue) {
  
              // Check if we are currently in cropping mode
              if(typeof that.jcAPI !== 'undefined' && that.jcAPI != null) {
                that.setCropAreaByFieldValue();
              } else {
                // 1. Normalize the size
                that._setNormalizedFieldValues(fieldName);
                // 2. Set the final size to the image
                that.setSizeByFieldValue();
              }
            }
          // 0-9 keys
          } else if (e.keyCode <= 57 && e.keyCode >= 48 || e.keyCode <= 105 && e.keyCode >= 96 ) {
            if($(this).val() >= minValue) {
  
              // Check if we are currently in cropping mode
              if(typeof that.jcAPI !== 'undefined' && that.jcAPI != null) {
                that.setCropAreaByFieldValue();
              } else {
                // 1. Normalize the size
                that._setNormalizedFieldValues(fieldName);
                // 2. Set the final size to the image
                that.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;
            }
  
            // Only resize when field values are ok
            if(updateField($(this), delta, maxValue, minValue)) {
              // Check if we are currently in cropping mode
              if(typeof that.jcAPI !== 'undefined' && that.jcAPI != null) {
                that.setCropAreaByFieldValue();
              } else {
                // 1. Normalize the size
                that._setNormalizedFieldValues(fieldName);
                // 2. Set the final size to the image
                that.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;
          }
  
          // Only resize when field values are ok
          if(updateField($(this), delta, maxValue, minValue)) {
  
            // Check if we are currently in cropping mode
            if(typeof that.jcAPI !== 'undefined' && that.jcAPI != null) {
              that.setCropAreaByFieldValue();
            } else {
              // 1. Normalize the size
              that._setNormalizedFieldValues(fieldName);
              // 2. Set the final size to the image
              that.setSizeByFieldValue();
            }
          }
              return false;
        };
  
        /**
         * Handle mousewheel,keyup actions on both fields
         */
        var $heightField = $('#' + that.imgResizeHeightField.id );
        var heightEventData = {fieldName: 'height', maxValue: that.imgResizeHeightField.maxValue, minValue: that.imgResizeHeightField.minValue };
        $heightField.live('keyup', heightEventData, handleKeyUpEventOnField);
        $heightField.live('mousewheel', heightEventData, handleMouseWheelEventOnField);
  
        var $widthField = $('#' + that.imgResizeWidthField.id );
        var widthEventData = {fieldName: 'width', maxValue: that.imgResizeWidthField.maxValue , minValue: that.imgResizeWidthField.minValue };
        $widthField.live('keyup',widthEventData , handleKeyUpEventOnField);
        $widthField.live('mousewheel', widthEventData, handleMouseWheelEventOnField);
  
      },
  
  
      /**
       * This helper function will keep the aspect ratio for the field with the given name.
       */
      _setNormalizedFieldValues: function(primaryFieldName) {
  
        var that = this;
        var widthField = jQuery("#" + that.imgResizeWidthField.id);
        var heightField = jQuery("#" + that.imgResizeHeightField.id);
        var width = widthField.val();
        var height = heightField.val();
  
        var size = that._normalizeSize(width, height, primaryFieldName);
  
        widthField.val(size.width);
        heightField.val(size.height);
  
      },
  
      /**
       * Manually set the given size for the current image
       */
      setSize: function(width, height) {
  
        var that = this;
        this.imageObj.width(width);
        this.imageObj.height(height);
        var $wrapper = this.imageObj.closest('.Aloha_Image_Resize');
        $wrapper.height(height);
        $wrapper.width(width);
  
        this._onResize(this.imageObj);
        this._onResized(this.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 that = this;
        that.imageObj = jQuery(e.target);
        var currentImage = that.imageObj;
  
        FloatingMenu.setScope(that.name);
  
        var editable = currentImage.closest('.aloha-editable');
  
        // Disabling the content editable. This will disable the resizeHandles in internet explorer
        jQuery(editable).contentEditable(false);
  
        //Store the current props of the image
        this.restoreProps.push({
          obj : e.srcElement,
          src : that.imageObj.attr('src'),
          width : that.imageObj.width(),
          height : that.imageObj.height()
        });
  
        // Update the resize input fields with the new width and height
        $('#' + that.imgResizeHeightField.id).val(that.imageObj.height());
        $('#' + that.imgResizeWidthField.id).val(that.imageObj.width());
  
  
        if (this.settings.ui.resizable) {
          this.startResize();
        }
  
  
        if (this.settings.autoResize) {
          this.autoResize();
        }
  
      },
  
      /**
       * This method extracts determins if the range selection contains an image
       */
      findImgMarkup: function ( range ) {
  
        var that = 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;
  
      },
  
  
      /**
       * This helper function will calculate the new width and height while keeping
       * the aspect ratio when the keepAspectRatio flat is set to true. The primarySize
       * can be 'width' or 'height'. The function will first try to normalize the oposite size.
       */
      _normalizeSize: function(width, height, primarySize) {
  
        var that = this;
        // Convert string values to numbers
        width = parseInt(width);
        height = parseInt(height);
  
        /**
         * Inner function that calculates the new height by examining the width
         */
        function handleHeight(callHandleWidth) {
  
          // Check whether the value is within bounds
          if (height > that.settings.maxHeight) {
  
            // Throw a notification event
            var eventProps = { 'org': height, 'new': that.settings.maxHeight};
            $('body').trigger('aloha-image-resize-outofbounds', ["height", "max", eventProps]);
            height = that.settings.maxHeight;
  
          } else if (height < that.settings.minHeight) {
  
            // Throw a notification event
            var eventProps = { 'org': height, 'new': that.settings.minHeight};
            $('body').trigger('aloha-image-resize-outofbounds', ["height", "min", eventProps]);
            height = that.settings.minHeight;
          }
  
          if (that.keepAspectRatio) {
            width = height * aspectRatio;
  
            // We don't want to invoke handleWidth again. This would mess up our previously calculated width
            if (callHandleWidth) {
              handleWidth(false);
            }
          }
  
  
        }
  
        /**
         * Inner function that calculates the new width by examining the width
         */
        function handleWidth(callHandleHeight) {
  
          // Check whether the value is within bounds
          if (width > that.settings.maxWidth) {
  
            // Throw a notification event
            var eventProps = { 'org': width, 'new': that.settings.maxWidth};
            $('body').trigger('aloha-image-resize-outofbounds', ["width", "max", eventProps]);
  
            width = that.settings.maxWidth;
          } else if (width < that.settings.minWidth) {
  
            // Throw a notification event
            var eventProps = { 'org': width, 'new': that.settings.minWidth};
            $('body').trigger('aloha-image-resize-outofbounds', ["width", "min", eventProps]);
  
            width = that.settings.minWidth;
          }
  
          // Calculate the new height
          if (that.keepAspectRatio) {
            height = width / aspectRatio;
  
            // We don't want to invoke handleHeight again. This would mess up our previously calculated height
            if (callHandleHeight) {
              handleHeight(false);
            }
  
          }
  
        }
  
        // Load the aspect ratio and use the 4:3 ratio as default value.
        var aspectRatio = 1.33333;
        if (typeof that.startAspectRatio === 'number') {
          aspectRatio = that.startAspectRatio;
        }
  
        // Determin which size should be handled
        if (primarySize == 'width') {
          handleWidth(true);
        }
  
        if (primarySize == 'height') {
          handleHeight(true);
        }
  
        // Floor the values return them
        return {'width': Math.floor(width), 'height': Math.floor(height)};
      },
  
  
      /**
       * Helper function that will set the new image size using the field values
       */
      setSizeByFieldValue: function() {
        var that = this;
        var width =  $('#' + that.imgResizeWidthField.id ).val();
        var height = $('#' + that.imgResizeHeightField.id ).val();
        that.setSize(width, height);
      },
  
      /**
       * Helper function that will set the new crop area width and height using the field values
       */
      setCropAreaByFieldValue: function() {
        var that = this;
        var currentCropArea = that.jcAPI.tellSelect();
  
        var width =  $('#' + that.imgResizeWidthField.id ).val();
        width = parseInt(width);
        var height = $('#' + that.imgResizeHeightField.id ).val();
        height = parseInt(height);
  
        var selection = [currentCropArea['x'], currentCropArea['y'], currentCropArea['x'] + width,currentCropArea['y'] + height];
        that.jcAPI.setSelect(selection);
      },
  
      /**
      * 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.imgSrcField.getQueryValue() ); // (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 = setInterval(function () {
          that.positionCropButtons();
        }, 10);
      },
  
      /**
       * Destroy crop confirm and cancel buttons
       */
      destroyCropButtons: function () {
        jQuery('#aloha-CropNResize-btns').remove();
        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 that = this;
        var config = this.config;
  
        this.initCropButtons();
        if (this.settings.ui.resizable) {
          this.endResize();
        }
  
        this.jcAPI = jQuery.Jcrop(this.imageObj, {
          onSelect : function () {
            that._onCropSelect();
            // ugly hack to keep scope :(
            setTimeout(function () {
              FloatingMenu.setScope(that.name);
            }, 10);
          }
        });
  
        that._disableSelection($('.jcrop-holder'));
        that._disableSelection($('#imageContainer'));
        that._disableSelection($('#aloha-CropNResize-btns'));
        $('body').trigger('aloha-image-crop-start', [this.imageObj]);
      },
  
      /**
       * Internal on crop select method
       */
      _onCropSelect: function() {
        var that = this;
  
        jQuery('#aloha-CropNResize-btns').fadeIn('slow');
  
        // 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 intiial active crop area values
        if(typeof that.jcAPI !== 'undefined' && that.jcAPI != null) {
  
          that.positionCropButtons();
          var currentCropArea = that.jcAPI.tellSelect();
  
          var widthField = jQuery("#" + that.imgResizeWidthField.id).val(currentCropArea['w']);
          var heightField = jQuery("#" + that.imgResizeHeightField.id).val(currentCropArea['h']);
        }
  
      },
  
  
      /**
       * Terminates a crop
       */
      endCrop: function () {
        if ( this.jcAPI ) {
          this.jcAPI.destroy();
          this.jcAPI = null;
        }
  
        this.destroyCropButtons();
        this.cropButton.extButton.toggle(false);
  
        if ( this.settings.ui.resizable ) {
          this.startResize();
        }
  
        if ( this.keepAspectRatio ) {
          var currentRatio = this.imageObj.width() / this.imageObj.height();
          this.startAspectRatio = currentRatio;
        }
  
        $('body').trigger('aloha-image-crop-stop', [this.imageObj]);
      },
  
      /**
       * 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 that = this;
        var currentImageObj = this.imageObj;
  
        currentImageObj = this.imageObj.css({
          height    : this.imageObj.height(),
          width    : this.imageObj.width(),
          position  : 'relative',
          'max-height': '',
          'max-width'  : ''
        });
  
        currentImageObj.resizable({
  
          maxHeight : that.settings.maxHeight,
          minHeight : that.settings.minHeight,
          maxWidth  : that.settings.maxWidth,
          minWidth  : that.settings.minWidth,
          aspectRatio : that.startAspectRatio,
          handles: that.settings.handles,
          grid : that.settings.grid,
              resize: function(event, ui) {
            that._onResize(that.imageObj);
          },
          stop : function (event, ui) {
            that._onResized(that.imageObj);
  
            // Workaround to finish cropping
            if (this.enableCrop) {
              setTimeout(function () {
                FloatingMenu.setScope(that.name);
                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': that.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');
          jQuery(editable).contentEditable(true);
        }
  
        if (this.imageObj) {
          this.imageObj
            .resizable('destroy')
            .css({
              top   : 0,
              left : 0
            });
        }
      },
  
      /**
       * Reset the image to it's original properties
       */
      reset: function() {
        if (this.settings.ui.crop) {
          this.endCrop();
        }
  
        if (this.settings.ui.resizable) {
          this.endResize();
        }
  
        if (this._onReset(this.imageObj)) {
          // the external reset procedure has already performed a reset, so there is no need to apply an internal reset
          return;
        }
  
        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) {
            this.imageObj.attr('src', this.restoreProps[i].src);
            this.imageObj.width(this.restoreProps[i].width);
            this.imageObj.height(this.restoreProps[i].height);
            return;
          }
        }
      }
    });
  
  });
  