if (typeof addEvent != 'function') {
	var addEvent = function(o, t, f, l) {
		var d = 'addEventListener', n = 'on' + t, rO = o, rT = t, rF = f, rL = l;
		if (o[d] && !l) return o[d](t, f, false);
		if (!o._evts) o._evts = {};
		if (!o._evts[t]) {
			o._evts[t] = o[n] ? { b: o[n] } : {};
			o[n] = new Function('e',
				'var r = true, o = this, a = o._evts["' + t + '"], i; for (i in a) {' +
				'o._f = a[i]; r = o._f(e||window.event) != false && r; o._f = null;' +
				'} return r'
			);
			if (t != 'unload') addEvent(window, 'unload', function() {
				removeEvent(rO, rT, rF, rL);
			});
		}
		
		if (!f._i) f._i = addEvent._i++;
		o._evts[t][f._i] = f;
	};
   
	addEvent._i = 1;
	var removeEvent = function(o, t, f, l) {
		var d = 'removeEventListener';
		if (o[d] && !l) return o[d](t, f, false);
		if (o._evts && o._evts[t] && f._i) delete o._evts[t][f._i];
	};
}

function cancelEvent(e, c) {
	e.returnValue = false;
	if (e.preventDefault) e.preventDefault();
	if (c) {
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
	}
};


function CropEdit(myName, config) {
	var props = {
		myName: myName,                  // Name of the object
		enabled: true,                   // Global toggle of drag/resize
		handles: ['tl', 'tm', 'tr', 'ml', 'mr', 'bl', 'bm', 'br'], // Array of drag handles: top/mid/bot/right
		isElement: null,                 // Function ref to test for an element
		isHandle: null,                  // Function ref to test for move handle
		element: null,                   // The currently selected element
		handle: null,                  // Active handle reference of the element
		minWidth: 10, minHeight: 10,     // Minimum pixel size of elements
		minLeft: 0, maxLeft: 9999,       // Bounding box area, in pixels
		minTop: 0, maxTop: 0,
		zIndex: 1,                       // The highest Z-Index yet allocated
		mouseX: 0, mouseY: 0,            // Current mouse position, recorded live
		lastMouseX: 0, lastMouseY: 0,    // Last processed mouse positions
		lastWidth: 0, lastHeight: 0,     // Last processed width and height of selector
		startSelectorWidth: 0,           // Width and height of the selector before resize
      startSelectorHeight: 0,
      wasResized: 0,
		startX: 0, startY: 0,            // X and Y positions of the selector before resize
		mOffX: 0, mOffY: 0,              // A known offset between position & mouse
		elmX: 0, elmY: 0,                // Element position
		elmW: 0, elmH: 0,                // Element size
		allowBlur: true,                 // Whether to allow automatic blur onclick
		ondragfocus: null,               // Event handler functions
		ondragstart: null,
		ondragmove: null,
		ondragend: null,
		ondragblur: null,
		ratio: -1,
		ratioViewToOriginal: -1,
		viewPhotoWidth: 0,
		viewPhotoHeight: 0,
		originalPhotoWidth: 0,           // Width of original photo in pixels
		originalPhotoHeight: 0,		 // Height of original photo in pixels
		minimumResolution: 0,            // Minimum resolution
		printingSizeWidth: 0,            // Size of printing medium in inches width and height
		printingSizeHeight: 0,           // 
		selected_box: null,               // what box was clicked left-top, left-middle, left-right and other
      resizeRatio:1                     // this ration is being used when screen is smaller then image
	};
   
   if (config) {
      for (var p in props) {
         this[p] = (typeof config[p] == 'undefined') ? props[p] : config[p];
      }
   } else {
      alert('config not found, please contact fotki support');
      return;
   }

	// we initialize maxTop and maxLeft automatically
	var s = document.getElementById(this['myName']);
   
   // we save selector initial size
   var s2 = document.getElementById('selector');

   // resize cropping image if needed (in case it doesn't fit to the screen)
   var itc = document.getElementById('image_to_crop');
   var table_itc = document.getElementById('table_itc');
   //var browserArea = Core.getBrowserVisibleArea();

   if (itc == null || table_itc == null) {
      alert('crop image or crop table not found, please contact fotki support');
   } else {
      var show_sizes_alert = 0;
      var max_photo_size_to_show = 500;
      
      var table_itc_width = table_itc.getAttribute('width');
      var table_itd_height = table_itc.getAttribute('height');
      
      if (show_sizes_alert == 1) {
         alert('Cropper\nWidth: ' + s.style.width + '\nHeight: ' + s.style.height);
         alert('Table\nWidth: ' + table_itc_width + '\nHeight: ' + table_itd_height);
         alert('Image\nWidth: ' + itc['width'] + '\nHeight: ' + itc['height']);
         alert('Selector\nWidth: ' + s2.style.width + '\nHeight: ' + s2.style.height);
      }
      
      if (this['viewPhotoWidth'] > this['viewPhotoHeight'] && this['viewPhotoWidth'] > max_photo_size_to_show) {
         this['resizeRatio'] = (this['viewPhotoWidth'] / max_photo_size_to_show);
      } else if (this['viewPhotoHeight'] > this['viewPhotoWidth'] && this['viewPhotoHeight'] > max_photo_size_to_show) {
         this['resizeRatio'] = (this['viewPhotoHeight'] / max_photo_size_to_show);
      }

      // change viewPhoto size for internal use
      this['viewPhotoWidth'] = (this['viewPhotoWidth'] / this['resizeRatio']);
      this['viewPhotoHeight'] = (this['viewPhotoHeight'] / this['resizeRatio']);
      //this['originalPhotoWidth'] = (this['originalPhotoWidth'] / this['resizeRatio']);
      //this['originalPhotoheight'] = (this['originalPhotoheight'] / this['resizeRatio']); 

      // resize all objects (cropper, table, image, selector
      itc['height'] = itc['height'] / this['resizeRatio'];
      table_itc.setAttribute('height', table_itd_height / this['resizeRatio']);
      s.style.height = (parseInt(s.style.height) / this['resizeRatio']) + "px";
      s2.style.height = (parseInt(s2.style.height) / this['resizeRatio']) + "px";

      itc['width'] = itc['width'] / this['resizeRatio'];
      table_itc.setAttribute('width', table_itc_width / this['resizeRatio']);
      s.style.width = (parseInt(s.style.width) / this['resizeRatio']) + "px";
      s2.style.width = (parseInt(s2.style.width) / this['resizeRatio']) + "px";
      
      s2.style.top = (parseInt(s2.style.top) / this['resizeRatio']) + "px";
      s2.style.left = (parseInt(s2.style.left) / this['resizeRatio']) + "px";
      
      DialogCropperDojo.dialog.center(); // center dialog after resize
      Core.setText('resizeRatio', this['resizeRatio']);
   }
   
   if (s) {
      this['maxTop'] = parseInt(s.style.height);
      this['maxLeft'] = parseInt(s.style.width);
   } else {
      alert('element ' + this['myName'] + ' not found, please contact fotki support');
      return;
   }

	// we calculate ratio from given us maxLeft and maxTop
   if (this['maxTop'] && this['maxLeft']) {
      this['ratio'] = this['maxTop'] / this['maxLeft'];
   } else {
      alert('error init of params 1, please contact fotki support');
      return;
   }
   
   if (s2) {
      this['startSelectorWidth'] = parseInt(s2.style.width);
      this['startSelectorHeight'] = parseInt(s2.style.height);
   } else {
      alert('selector not found, please contact fotki support');
      return;
   } 
   
	// we calculate ratio from view size to original, and if original width not given
	// we default it 3000 pixels and calculate ratio from it
	if (this['originalPhotoWidth'] > 0) {
		this['ratioViewToOriginal'] = this['originalPhotoWidth'] / this['maxLeft'];
	} else {
      // there is no original
		this['ratioViewToOriginal'] = 1; // 2000 / this['maxLeft']
      
      if (this['maxLeft'] > this['maxTop']) {
         // horizontal
         this['minimumResolution'] = this['startSelectorWidth'] / this['printingSizeWidth'];
      } else {
         // vertical
         this['minimumResolution'] = this['startSelectorHeight'] / this['printingSizeHeight'];
      }
      
      this['minimumResolution'] = this['minimumResolution'] * .977;      
      //var currentWidthResolution = elmW * ratioViewToOriginal / printingSizeWidth;
	}

   // checking that we have the right printing size
   if (this['maxLeft'] > this['maxTop']) {
      if (this['printingSizeWidth'] < this['printingSizeHeight']) {
         alert('wrong printing size, please contact fotki support');
      }
   } else {
      if (this['printingSizeWidth'] > this['printingSizeHeight']) {
         alert('wrong printing size, please contact fotki support');
      }
   }
}


CropEdit.prototype.noCrop = function(e) { with (this) {
	s = document.getElementById("selector");

	if (viewPhotoWidth > viewPhotoHeight) {
		autoCropWidth = viewPhotoHeight / ratio;
		autoCropHeight = viewPhotoHeight;
	} else if (viewPhotoWidth < viewPhotoHeight) {
		autoCropWidth = viewPhotoWidth;
		autoCropHeight = viewPhotoWidth / ratio;
	} else {
		autoCropWidth = viewPhotoWidth;
		autoCropHeight = viewPhotoHeight;
	}

	ratio = maxTop / maxLeft;
	
	s.style.width  = maxLeft + "px";
	s.style.height = maxTop + "px";
	s.style.left   = "0px";
	s.style.top    = "0px";
   
   elmW = maxLeft;
   elmH = maxTop;
   
   wasResized = 1;
}};


CropEdit.prototype.rotateCrop = function(e) { with (this) {
	if (maxLeft > maxTop) {
		if (ratio < maxLeft / maxTop) {
			ratio = maxLeft / maxTop;
		} else {
			ratio = maxTop / maxLeft;
		}
	} else {
		if (ratio < maxTop / maxLeft) {
			ratio = maxTop / maxLeft;
		} else {
			ratio = maxLeft / maxTop;
		}
	}
	
	autoCrop();
}};


CropEdit.prototype.autoCrop = function(e) { with (this) {
	var s = document.getElementById("selector");
	var autoCropWidth;
	var autoCropHeight;

	if (viewPhotoWidth > viewPhotoHeight) {
		if (viewPhotoWidth < maxLeft) {
			autoCropWidth  = viewPhotoWidth + 1;
			autoCropHeight = viewPhotoWidth * ratio;
		} else {
			autoCropWidth  = viewPhotoHeight / ratio;
			autoCropHeight = viewPhotoHeight;
		}
	} else if (viewPhotoWidth < viewPhotoHeight) {
		if (viewPhotoWidth < maxLeft) {
			autoCropWidth  = viewPhotoWidth;
			autoCropHeight = viewPhotoWidth * ratio;
		} else {
			autoCropWidth  = viewPhotoHeight / ratio;
			autoCropHeight = viewPhotoHeight;
		}
	} else {
		autoCropWidth  = viewPhotoWidth;
		autoCropHeight = viewPhotoHeight;
	}
				
	if (autoCropHeight > maxTop) {
		autoCropHeight = maxTop;
		autoCropWidth = autoCropHeight / ratio;
	}
	
	autoCropWidth = autoCropWidth - 2;
	
	s.style.width  = autoCropWidth  + "px";
	s.style.height = autoCropHeight + "px";
	s.style.left   = Math.abs((maxLeft - autoCropWidth)  / 2) + "px";
	s.style.top    = Math.abs((maxTop  - autoCropHeight) / 2) + "px";
   
   var warning = document.getElementById('warning');
   warning.style.display = 'none';
   wasResized = 1;
}};

CropEdit.prototype.getCropperParams = function(e) { with (this) {
	var params = new Object();
	params.width = null;
	params.height = null;
	params.left = null;
	params.top = null;

	var s = document.getElementById("selector");
   
	if (s) {
		params.width = (this['resizeRatio'] * s.style.width.replace(/px/g, ''));
		params.height = (this['resizeRatio'] * s.style.height.replace(/px/g, ''));
		params.left = (this['resizeRatio'] * s.style.left.replace(/px/g, ''));
		params.top = (this['resizeRatio'] * s.style.top.replace(/px/g, ''));
		params.error = 0;
	} else {
		params.error = 1;
	}

	return params;
}};

CropEdit.prototype.apply = function(node) {
	var obj = this;
	
	addEvent(node, 'mousedown', function(e) { obj.mouseDown(e) } );
	addEvent(node, 'mousemove', function(e) { obj.mouseMove(e) } );
	addEvent(node, 'mouseup',   function(e) { obj.mouseUp(e)   } );
};


CropEdit.prototype.select = function(newElement) { with (this) {
	if (!document.getElementById || !enabled) return;

	if (newElement && (newElement != element) && enabled) {
		element = newElement;
		element.style.zIndex = ++zIndex;
		
		if (this.resizeHandleSet) this.resizeHandleSet(element, true);

		// Record element attributes for mouseMove().
		elmX = parseInt(element.style.left);
		elmY = parseInt(element.style.top);
		elmW = element.offsetWidth;
		elmH = element.offsetHeight;
		
		if (ondragfocus) this.ondragfocus();
	}
}};


CropEdit.prototype.deselect = function(delHandles) { with (this) {
	if (!document.getElementById || !enabled) return;

	if (delHandles) {
		if (ondragblur) this.ondragblur();
		if (this.resizeHandleSet) this.resizeHandleSet(element, false);
		element = null;
	}

	handle = null;
	mOffX = 0;
	mOffY = 0;
}};


CropEdit.prototype.mouseDown = function(e) { with (this) {
	if (!document.getElementById || !enabled) return true;

	var elm = e.target || e.srcElement, newElement = null, newHandle = null, hRE = new RegExp(myName + '-([trmbl]{2})', '');

	while (elm) {
		if (elm.className) {
			// hack to get corner (by vsinitski)
			var copy_classname = elm.className;
			var shorted_class = copy_classname.replace(/cropper cropper-/g, "");
			var e_regexp = new RegExp("[trmbl]{2,4}");
			
			if (shorted_class.match(e_regexp)) {
				selected_box = shorted_class;
			}
			// end hack
   
			if (!newHandle && (hRE.test(elm.className) || isHandle(elm))) newHandle = elm;
			if (isElement(elm)) { newElement = elm; break }
		}
		
		elm = elm.parentNode;
	}

	// If this isn't on the last dragged element, call deselect(),
	// which will hide its handles and clear element.
	if (element && (element != newElement) && allowBlur) deselect(true);

	// If we have a new matching element, call select().
	if (newElement && (!element || (newElement == element))) {
		// Stop mouse selections if we're dragging a handle.
		if (newHandle) cancelEvent(e);
		select(newElement, newHandle);
		handle = newHandle;
	
		if (handle && ondragstart) {
			this.ondragstart(hRE.test(handle.className));
			startResizeWidth = elmW;
			startResizeHeight = elmH;
			startX = elmX;
			startY = elmY;
		}
	}
}};


CropEdit.prototype.mouseMove = function(e) { with (this) {
	// This continually offsets the dragged element by the difference between the
	// last recorded mouse position (mouseX/Y) and the current mouse position.
	if (!document.getElementById || !enabled) return true;
	
	// We always record the current mouse position.
	mouseX = e.pageX || e.clientX + document.documentElement.scrollLeft;
	mouseY = e.pageY || e.clientY + document.documentElement.scrollTop;
	
	// Record the relative mouse movement, in case we're dragging.
	// Add any previously stored & ignored offset to the calculations.
	var diffX = mouseX - lastMouseX + mOffX;
	var diffY = mouseY - lastMouseY + mOffY;
	mOffX = mOffY = 0;
	
	// Update last processed mouse positions.
	lastMouseX = mouseX;
	lastMouseY = mouseY;
	
	// That's all we do if we're not dragging anything.
	if (!handle) return true;
	
	// If included in the script, run the resize handle drag routine.
	// Let it create an object representing the drag offsets.
	var isResize = false;
 
	if (this.resizeHandleDrag && this.resizeHandleDrag(diffX, diffY)) {
		isResize = true;
	} else {
		// If the resize drag handler isn't set or returns fase (to indicate the drag was
		// not on a resize handle), we must be dragging the whole element, so move that.
		// Bounds check left-right...
		var dX = diffX;
      var dY = diffY;
      
		if (elmX + dX < minLeft) mOffX = (dX - (diffX = minLeft - elmX));
		else if (elmX + elmW + dX > maxLeft) mOffX = (dX - (diffX = maxLeft - elmX - elmW));
		
		// ...and up-down.
		if (elmY + dY < minTop) mOffY = (dY - (diffY = minTop - elmY));
		else if (elmY + elmH + dY > maxTop) mOffY = (dY - (diffY = maxTop - elmY - elmH));
		elmX += diffX;
		elmY += diffY;
	}

   var currentWidthResolution  = elmW * ratioViewToOriginal / printingSizeWidth;
   var currentHeightResolution = elmH * ratioViewToOriginal / printingSizeHeight;
			
   if (0) {
      Core.setText('log', 
         '=================== debug info ====================== <br>' + 
         'elmX: '                + elmX   + ' | ' +
         'elmY: '                + elmY   + ' | ' +
         'elmW: '                + elmW   + ' | ' +
         'elmH: '                + elmH   + ' | ' +
         'selected_box: '        + '\'' + selected_box + '\'<br>' + 
         'current ratio: '       + Math.abs(elmH / elmW) + ' | ' + 
         'ratio: '               + ratio + ' | ' +
         'ratioViewToOriginal: ' + ratioViewToOriginal + '<br>' +  
         'maxTop: '              + maxTop +  ' | ' +
         'maxLeft: '             + maxLeft + ' | '+
         'mouseX: '              + mouseX +  ' | ' +
         'mouseY: '              + mouseY +  '<br>' +
         'minumal resolution: '  + minimumResolution + ' | ' +
         'current width resolution: '  + currentWidthResolution + ' | ' +
         'current height resolution: '  + currentHeightResolution + ' | ' +
         'printingSize: ' + printingSizeWidth + ' x ' + printingSizeHeight + ' inches<br>' +
         '====================================================='
      );
   }
   
	// Assign new info back to the element, with minimum dimensions.
	with (element.style) {
		if (isResize) {
         if (wasResized == 0) wasResized = 1;
         
			var warning = document.getElementById('warning');
			
			if ((currentWidthResolution > minimumResolution && currentHeightResolution > minimumResolution) || (selected_box == 'bm' && lastHeight < elmH)) { 
				warning.style.display = 'none';
				
				if (selected_box == 'ml' || selected_box == 'mr' || selected_box == 'br' || selected_box == 'bl') {
					newSelectorHeight = elmW * ratio;
					
					if ((newSelectorHeight + elmY) < maxTop) {
						elmH = newSelectorHeight;
					} else {
						elmW = elmH / ratio;
						
						if (selected_box == 'bl' || selected_box == 'ml') {
							elmX = startResizeWidth + startX - elmW;
						}
					}
				} else if (selected_box == 'tm' || selected_box == 'bm' || selected_box == 'tr') {
					newSelectionWidth = elmH / ratio;
					
					if ((newSelectionWidth + elmX) < maxLeft) {
						elmW = newSelectionWidth;
					} else {
						elmH = elmW * ratio;
						
						if (selected_box == 'tm' || selected_box == 'tr') {
							elmY = startResizeHeight + startY - elmH;
						}
					}
				} else if (selected_box == 'tl') {
					if (elmY > 0) {
						elmH = elmW * ratio;
						elmY = startY + (startResizeHeight - elmH);
					} else {
						elmX = startResizeWidth + startX - elmH / ratio;
						elmW = elmH / ratio;
					}
				}
			} else {
				elmW = printingSizeWidth * minimumResolution / ratioViewToOriginal;
				elmH = elmW * ratio;
            
            if (elmW > maxLeft || elmH > maxTop) {
               noCrop();
               return;
            }
				
				if (selected_box != 'br' && selected_box != 'mr' && selected_box != 'bm') {
					elmX = startResizeWidth + startX - elmW;
					elmY = startY + (startResizeHeight - elmH);
				}
            
            if (selected_box == 'bl' || selected_box == 'ml') {
               elmY = startY;
               elmX = startX + (startResizeWidth - elmW);
            }
				
				warning.style.display = 'block';
			}
		}
		
      // fixing the problem with strange increase of selection for 2 pixels
		if (!wasResized) {
			elmW = startSelectorWidth;
			elmH = startSelectorHeight;
		}
		
		left   = elmX + 'px';
		width  = elmW + 'px';
		top    = elmY + 'px';
		height = elmH + 'px';
      
      lastWidth = elmW;
      lastHeight = elmH;
	}

	// Evil, dirty, hackish Opera select-as-you-drag fix.
	if (window.opera && document.documentElement) {
		var oDF = document.getElementById('op-drag-fix');
		
		if (!oDF) {
			var oDF = document.createElement('input');
			oDF.id = 'op-drag-fix';
			oDF.style.display = 'none';
			document.body.appendChild(oDF);
		}
		
		oDF.focus();
	}

	if (ondragmove) this.ondragmove(isResize);

	cancelEvent(e);
}};


CropEdit.prototype.mouseUp = function(e) { with (this) {
	if (!document.getElementById || !enabled) return;
	
	var hRE = new RegExp(myName + '-([trmbl]{2})', '');
	if (handle && ondragend) this.ondragend(hRE.test(handle.className));
	deselect(false);
}};



CropEdit.prototype.resizeHandleSet = function(elm, show) { with (this) {
	if (!elm._handle_tr) {
		for (var h = 0; h < handles.length; h++) {
			var hDiv = document.createElement('div');
			hDiv.className = myName + ' ' +  myName + '-' + handles[h];
			elm['_handle_' + handles[h]] = elm.appendChild(hDiv);
		}
	}
	
	// We now have handles. Find them all and show/hide
	for (var h = 0; h < handles.length; h++) {
		elm['_handle_' + handles[h]].style.visibility = show ? 'inherit' : 'hidden';
	}
}};


CropEdit.prototype.resizeHandleDrag = function(diffX, diffY) { with (this) {
	var hClass = handle && handle.className && handle.className.match(new RegExp(myName + '-([tmblr]{2})')) ? RegExp.$1 : '';

	var dY = diffY, dX = diffX, processed = false;

	if (hClass.indexOf('t') >= 0) {
		rs = 1;
		if (elmH - dY < minHeight) mOffY = (dY - (diffY = elmH - minHeight));
		else if (elmY + dY < minTop) mOffY = (dY - (diffY = minTop - elmY));
		elmY += diffY;
		elmH -= diffY;
		processed = true;
	}
	
	if (hClass.indexOf('b') >= 0) {
		rs = 1;
		if (elmH + dY < minHeight) mOffY = (dY - (diffY = minHeight - elmH));
		else if (elmY + elmH + dY > maxTop) mOffY = (dY - (diffY = maxTop - elmY - elmH));
		elmH += diffY;
		processed = true;
	}
	
	if (hClass.indexOf('l') >= 0) {
		rs = 1;
		if (elmW - dX < minWidth) mOffX = (dX - (diffX = elmW - minWidth));
		else if (elmX + dX < minLeft) mOffX = (dX - (diffX = minLeft - elmX));
		elmX += diffX;
		elmW -= diffX;
		processed = true;
	}
	
	if (hClass.indexOf('r') >= 0) {
		rs = 1;
		if (elmW + dX < minWidth) mOffX = (dX - (diffX = minWidth - elmW));
		else if (elmX + elmW + dX > maxLeft) mOffX = (dX - (diffX = maxLeft - elmX - elmW));
		elmW += diffX;
		processed = true;
	}

	return processed;
}};
