if(!window.Base)
  ZtVWeb.RequireLibrary('../Base.js');

/*
dependencies:
- Base.js
- stdFunctions.js
*/
if(typeof(Ctrl)=='undefined') Ctrl=function(id){return document.getElementById(id);};
if(typeof(Z)=='undefined') Z={};

Z.Version= '0.1alfa';

Z.Merge=function(dest, src) {
	for (var prop in src) dest[prop] = src[prop];
	return dest;
}

/*Z.Droplet*/
//instance constructor
Z.Droplet = Base.extend({
	constructor: function(element_or_id){
		//set private vars
		this.draglets={}, this.a_draglets=[], this.a_dragletsHtml=[];
		//set public vars
		if(typeof(element_or_id)=='string') element_or_id=Ctrl(element_or_id);
		this.htmlElement = element_or_id;
		Z.Droplet.registerInstance(this);
		this.htmlElement[Z.Droplet.REF_PROP] = this.ID;
		this.UpdateUICoord();
	},

	getHtmlDropArea: function(){
		return this.htmlElement;
	},

	resetDraglets: function(){
		 this.draglets={}; this.a_draglets=[]; this.a_dragletsHtml=[];
	},

	getDraglets: function(){
		return this.a_draglets;
	},

	getDragletsHtml: function(){
		return this.a_dragletsHtml;
	},

	addDraglet: function(o_draglet){
		if(!this.draglets[o_draglet.ID]){
			this.draglets[o_draglet.ID]=o_draglet;
			this.a_draglets.push(o_draglet);
			this.a_dragletsHtml.push(o_draglet.htmlElement);
		}//else throw new Error;
		this.UpdateUICoord();
	},

	remDraglet: function(o_draglet){
		if (delete this.draglets[o_draglet.ID]){
			for(var i=0, draglet; draglet=this.a_draglets[i]; i++){
				if(this.a_draglets[i]==o_draglet){
					this.a_draglets.splice(i,1);
					this.a_dragletsHtml.splice(i,1);
					break;
				}
			}
			return o_draglet;
		}
		return false;
	},

	swapDraglets: function(o_draglet1,o_draglet2){
		if(this.draglets[o_draglet1.ID] && this.draglets[o_draglet2.ID]){
			for(var i=0, l=this.a_draglets.length, idx1, idx2, tmp; i<l; i++){
				tmp = this.a_draglets[i];
				if(tmp==o_draglet1){
					idx1 = i;
					if(idx2) break;
				} else if(tmp==o_draglet2){
					idx2 = i;
					if(idx1) break;
				}
			}
			this.a_draglets[idx1]=o_draglet2;
			this.a_dragletsHtml[idx1]=o_draglet2.htmlElement;
			this.a_draglets[idx2]=o_draglet1;
			this.a_dragletsHtml[idx2]=o_draglet1.htmlElement;
			this.UpdateUICoord();
		}else{throw new Error;}
		return false;
	},

	classes: {
		setEvident: 'droplet_setEvident',
		setActive: 'droplet_setActive'
	},

  LJ_CCNU:LibJavascript.CssClassNameUtils,

	setEvident: function(){
    this.LJ_CCNU.addClass(this.htmlElement, this.classes.setEvident,true);
	},

	setActive: function(){
    this.LJ_CCNU.addClass(this.htmlElement, this.classes.setActive,true);
	},

	remEvident: function(){
    this.LJ_CCNU.removeClass(this.htmlElement, this.classes.setEvident,true);
	},

	remActive: function(){
    this.LJ_CCNU.removeClass(this.htmlElement, this.classes.setActive,true);
	},

	x: NaN, y: NaN, w: NaN, h: NaN,

	UpdateUICoord: function(){
		var _position = Z.LSUtils.toDocumentPosition(this.htmlElement);
		this.x = _position.x
		this.y = _position.y
		this.w = this.htmlElement.offsetWidth;
		this.h = this.htmlElement.offsetHeight;
	},

	toString: function(){
		return this.htmlElement.id+"={x:"+this.x+"; y:"+this.y+"; w:"+this.w+"; h:"+this.h+"}"
	}
});
//Z.Droplet.prototype.constructor = Z.Droplet;
//static
Z.Droplet._ID = 0;
Z.Droplet.REF_PROP="Z_Droplet";
Z.Droplet.registerInstance=function(o_instance){
	o_instance.ID = Z.Droplet._ID;
	Z.Droplet[Z.Droplet._ID]=o_instance;
	Z.Droplet._ID++;
};
Z.Droplet.getInstance=function(c_instRef){
	return Z.Droplet[c_instRef];
};

/*Z.Draglet*/
//instance constructor
Z.Draglet = Base.extend({
	htmlElement: null,
	itemObj: null,
  hookableZone: null,

  constructor: function(element_or_id, hookable_zone, obj){
    if(IsA(element_or_id,'C'))
      element_or_id = Ctrl(element_or_id);
		this.htmlElement = element_or_id;
    if(IsA(hookable_zone,'C'))
      hookable_zone = Ctrl(hookable_zone);
    this.hookableZone= hookable_zone || this.htmlElement;
		this.itemObj = obj;

    Z.Draglet.registerInstance(this);
		this.htmlElement[Z.Draglet.REF_PROP] = this.ID;
	},

  getHookableZone: function(){
    return this.hookableZone;
  },

	getDraggableGUI: function() {
		return this.htmlElement;
	},

	classes: {
		placeHolder: 'draglet_placeHolder'
	}
});
//static
Z.Draglet._ID = 0;
Z.Draglet.REF_PROP = "Z_Draglet";
Z.Draglet.registerInstance = function(o_instance){
	o_instance.ID = Z.Draglet._ID;
	Z.Draglet[Z.Draglet._ID]=o_instance;
	Z.Draglet._ID++;
};
Z.Draglet.getInstance = function(c_instRef){
	return Z.Draglet[c_instRef];
};

/*Z.DnD*/
Z.DnD = {};

/*Z.DnD.Manager*/
//instance constructor
Z.DnD.Manager = Base.extend({

	droplets: [],
	draglets: [],
	currentDraglet: null,
	placeholder: null,

	constructor: function(overmode){
    this.droplets=[];
    this.draglets=[];
		this.OVER_MODE = overmode ? overmode : null;
		Z.DnD.Manager.registerManager(this);
	},

	registerDroplet: function(o_droplet){
		this.droplets.push(o_droplet);
	},

	registerDraglet: function(o_draglet){
		o_draglet.htmlElement[Z.DnD.Manager.REF_PROP] = this.ID;
    LibJavascript.Events.addEvent(o_draglet.getHookableZone(), 'mousedown', Z.DnD.Manager.dragStart)
		this.draglets.push(o_draglet);
	},

	dragStart: function(e,h_element){
		this.currentDraglet = Z.Draglet.getInstance(h_element[Z.Draglet.REF_PROP]);

		var dragObj = new Object();
		var	obj = this.currentDraglet.getDraggableGUI();

		if (obj) dragObj.css = obj.style;
		if (!dragObj.css) return;

		//salva le proprieta' originarie
		var cStyle=Z.LSUtils.getElementsComputedStyle;
		var down = dragObj.down = {};
		down.position = cStyle(obj,'position');
		down.width = cStyle(obj,'width');
		down.height = cStyle(obj,'height');
		down.top = cStyle(obj,'top');
		down.left = cStyle(obj,'left');
		down.zIndex = cStyle(obj,'zIndex','z-index');

		var pos = Z.LSUtils.toDocumentPosition(obj);

		//il place holder "tiene" il posto all'oggetto che si dragga
		var ph = this.placeholder = document.createElement('div'), phs=ph.style;
		for(var prop in down)
			phs[prop] = down[prop];
		ph.innerHTML='<P></P>';
		LibJavascript.CssClassNameUtils.addClass(ph, this.currentDraglet.classes.placeHolder);
		phs.width = obj.offsetWidth+'px';
		phs.height = obj.offsetHeight+'px';
		phs.marginTop = obj.style.marginTop;
		phs.marginRight = obj.style.marginRight;
		phs.marginBottom = obj.style.marginBottom;
		phs.marginLeft = obj.style.marginLeft;
		phs.MozBoxSizing = 'border-box';

		//aggiusta le proprieta'
		dragObj.css.width = obj.offsetWidth+'px';
		dragObj.css.height = obj.offsetHeight+'px';

		var pos = Z.LSUtils.toDocumentPosition(obj);//non invertire con la seguente
		obj.parentNode.insertBefore(this.placeholder, obj);//non invertire con la precendente

		dragObj.css.top = pos.y;
		dragObj.css.left = pos.x;
    dragObj.css.position = 'absolute';//non eseguire prima dell'assegnazione di width,height,top e left: in FF sbaglia

		// Save mousedown location
		dragObj.down.X  = e.screenX - pos.x;
		dragObj.down.Y  = e.screenY - pos.y;
		dragObj.down.leftOffset = e.offsetX ? document.body.scrollLeft : 0; //risparmia calcoli nel dragstart
    dragObj.down.topOffset = e.offsetY ? document.body.scrollTop : 0; //risparmia calcoli nel dragstart

		//copre tutto perche' gli iframe interferiscono con il dragging
		var iframeSaver = Ctrl('iframe_saver');
		if(!iframeSaver){
			iframeSaver = document.createElement('div');
			iframeSaver.id = 'iframe_saver';
			iframeSaver.style.position = 'absolute';
			iframeSaver.style.top = 0;
			iframeSaver.style.left = 0;
			iframeSaver.style.height = '100%';
			iframeSaver.style.width = '100%';
			iframeSaver.style.zIndex = 1001;
			document.body.appendChild(iframeSaver)
		}
		iframeSaver.style.display = 'block';

		dragObj.css.zIndex = 1000;
		dragObj.manager = this;
		this.currentDraglet.dragObj = dragObj;

    //alert([down.left, dragObj.css.left, dragObj.down.Y, dragObj.down.leftOffset])
		return dragObj;
	},

	dragMove: function(e){},

	dragEnd: function(e){
		var backDroplet;
		for(var i=0; i<this.droplets.length; i++){
			if(this.currentDraglet.htmlElement!=this.droplets[i].htmlElement && this.isOverDroplet(e, this.droplets[i])){
				backDroplet = this.droplets[i];
				break;
			}
		}
		if(backDroplet){
			this.swap(backDroplet,this.currentDraglet);
			this.endDrag();
		}else{
			this.dragCancel();
		}
		this.currentDraglet=null;
	},

	isOverDroplet: function(e, o_droplet){
		switch(this.OVER_MODE){
		case Z.DnD.Manager.OVER_INTERSECTING:
		case Z.DnD.Manager.OVER_MOUSE_POINTING:
		default:
			return Z.DnD.Manager.mousePointInHTMLElement(e, o_droplet.getHtmlDropArea())
		};
	},

	swap: function(o_droplet){
		this.restoreProps();
		var n1 = o_droplet.htmlElement;
		var n2 = this.currentDraglet.htmlElement;
		var p = n2.parentNode;
		var s = n2.nextSibling;
		n1.parentNode.replaceChild(n2,n1);
		p.insertBefore(n1,s);
	},

	dragCancel: function(){
		this.restoreProps();
	},

	endDrag: function(){
		this.restoreProps();
	},

	restoreProps: function(){
		//toglie il placeholder
		if(this.placeholder && this.placeholder.parentNode){
			this.placeholder.parentNode.removeChild(this.placeholder);
			this.placeholder = null;
		}
		//ripristina le proprieta'
		var cd = this.currentDraglet.dragObj
		for(var prop in cd.down)
			cd.css[prop] = cd.down[prop];
		//nasconde il div di copertura
		var iframeSaver = Ctrl('iframe_saver');
		if(iframeSaver)
			iframeSaver.style.display = 'none';
	}
});
//static
Z.DnD.Manager.dragObj = null;
Z.DnD.Manager.REF_PROP = 'Z_DnD_Manager';
Z.DnD.Manager.OVER_INTERSECTING = 'INTERSECTING';
Z.DnD.Manager.OVER_MOUSE_POINTING = 'MOUSE_POINTING';
Z.DnD.Manager.REF_PROP = 'Z_DnD_Manager';
Z.DnD.Manager.dragStart = function(e){
  e = e||event;
  var eventTarget = LibJavascript.Events.getElement(e);

	//non avvia il drag per elementi cliccabili
  switch(eventTarget.tagName.toLowerCase()){
    case 'input':  case 'select': case 'button': case 'a':
			return;
    default:
      if(typeof(eventTarget.onclick)=='function')
        return;
	}

  var candidate = eventTarget,
      Z_DnD_M = Z.DnD.Manager,
      Z_DnD_M_REF_PROP = Z_DnD_M.REF_PROP,
      mgr_ref = candidate[Z_DnD_M_REF_PROP];
	while (mgr_ref == null && candidate.parentNode) {
		candidate = candidate.parentNode;
    mgr_ref = candidate[Z_DnD_M_REF_PROP];
	}
  if (mgr_ref == null)
		return;

  var manager = Z_DnD_M.getManager(mgr_ref)
	if (!manager)
		return;

  Z_DnD_M.dragObj = manager.dragStart(e, candidate);

	if (document.addEventListener) {
    document.addEventListener("mousemove", Z_DnD_M.dragMove, true);
    document.addEventListener("mouseup", Z_DnD_M.dragEnd, true);
		e.preventDefault();
	} else if (document.attachEvent) {
    document.attachEvent("onmousemove", Z_DnD_M.dragMove);
    document.attachEvent("onmouseup", Z_DnD_M.dragEnd);
		return false;
	}
};
Z.DnD.Manager.dragMove = function(e){

	e = (e) ? e: window.event;

  var Z_DnD_M_dO=Z.DnD.Manager.dragObj,
      Z_DnD_M_dO_c=Z_DnD_M_dO.css,
      Z_DnD_M_dO_d=Z_DnD_M_dO.down,
      Z_DnD_M_dO_m=Z_DnD_M_dO.manager;
  Z_DnD_M_dO_c.left = (e.screenX + Z_DnD_M_dO_d.leftOffset - Z_DnD_M_dO_d.X) + "px"
  Z_DnD_M_dO_c.top = (e.screenY + Z_DnD_M_dO_d.topOffset - Z_DnD_M_dO_d.Y) + "px";

  if(Z_DnD_M_dO_m.DRAG_MOVE_NOT_OPTIMIZED){
    Z_DnD_M_dO_m.dragMove(e);
  }else{
    var _e={//IE da errore se si prova ad accedere alle proprieta' dell'evento dopo che e' terminato, per cui copio le pu' rilevanti. In Moz/FF non da errore ma undefined
      x:e.x,
      y:e.y,
      offsetX:e.offsetX,
      offsetY:e.offsetY,
      clientX:e.clientX,
      clientY:e.clientY,
      screenX:e.screenX,
      screenY:e.screenY,
      keyCode:e.keyCode,
      srcElement:e.srcElement
    };

    if(Z_DnD_M_dO_m.DRAGMOVE_TIMEOUT_ID)
      window.clearTimeout(Z_DnD_M_dO_m.DRAGMOVE_TIMEOUT_ID)

    Z_DnD_M_dO_m.DRAGMOVE_TIMEOUT_ID=window.setTimeout(function(){
      Z_DnD_M_dO_m.dragMove(_e);
      Z_DnD_M_dO_m.DRAGMOVE_TIMEOUT_ID=null;
    }, 30);
  }

  if (e.preventDefault) e.preventDefault();
	else return false;
}
Z.DnD.Manager.dragEnd = function(e){
	if (document.removeEventListener) {
    document.removeEventListener("mousemove",Z.DnD.Manager.dragMove,true);
    document.removeEventListener("mouseup",Z.DnD.Manager.dragEnd,true);
  } else if (document.detachEvent) {
    document.detachEvent("onmousemove",Z.DnD.Manager.dragMove);
    document.detachEvent("onmouseup",Z.DnD.Manager.dragEnd);
  }
	Z.DnD.Manager.dragObj.manager.dragEnd(e);
	Z.DnD.Manager.dragObj = null;
}
Z.DnD.Manager.mousePointInHTMLElement = function( e, h_element) {
	var pos = Z.LSUtils.toViewportPosition(h_element);
	var top =  pos.y;
	var left =  pos.x;
	var bottom = top + h_element.offsetHeight;
	var right = left + h_element.offsetWidth;
	var leftOffset = e.offsetX ? document.body.scrollLeft : 0;
	var topOffset = e.offsetX ? document.body.scrollTop : 0;
	return e.clientX  > left + leftOffset &&
				 e.clientX  < right + leftOffset &&
				 e.clientY  > top + topOffset   &&
				 e.clientY  < bottom + topOffset;
}
Z.DnD.Manager._ID=0;
Z.DnD.Manager.registerManager=function(o_manager){
	o_manager.ID = Z.DnD.Manager._ID;
	Z.DnD.Manager[Z.DnD.Manager._ID]=o_manager;
	Z.DnD.Manager._ID++;
};
Z.DnD.Manager.getManager=function(c_id){
	return Z.DnD.Manager[c_id];
};

LibJavascript.Events.getElement=function(e,refuse_dx_click){
  if ( arguments.length == 0 )
    e = event;
    if(refuse_dx_click){//non accetta il tasto dx
      var nsEvent = e.which != undefined;
      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
        return;
    }
  return e.target ? e.target : e.srcElement;
}

/*Z.LSUtils*/
Z.LSUtils = {
	getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
		if(typeof(htmlElement)=='string') htmlElement = Ctrl(htmlElement);

		if ( arguments.length == 2 )
			mozillaEquivalentCSS = cssProperty;

		if ( htmlElement.currentStyle && !window.opera)
			return htmlElement.currentStyle[cssProperty];
		else
			return document.defaultView.getComputedStyle(htmlElement, null).getPropertyValue(mozillaEquivalentCSS);
	},
	toViewportPosition: function(element) {
		return this._toAbsolute(element,true);
  },
  toDocumentPosition: function(element) {
		return this._toAbsolute(element,false);
  },
  /**
	*  Compute the elements position in terms of the window viewport
	*  so that it can be compared to the position of the mouse (dnd)
	*  This is additions of all the offsetTop,offsetLeft values up the
	*  offsetParent hierarchy, ...taking into account any scrollTop,
	*  scrollLeft values along the way...
	*
	* IE has a bug reporting a correct offsetLeft of elements within a
	* a relatively positioned parent!!!
	**/
  _toAbsolute: function(element,accountForDocScroll) {
		if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
			return this._toAbsoluteMozilla(element,accountForDocScroll);

    var x = 0, y = 0;
    var parent = element, borderXOffset, borderYOffset;
    var gECS=this.getElementsComputedStyle;
    while ( parent ) {
      borderXOffset = borderYOffset = 0;
      if ( parent != element ) {
        borderXOffset = parseInt(gECS(parent, "borderLeftWidth" ))||0;
        borderYOffset = parseInt(gECS(parent, "borderTopWidth" ))||0;
			}
      x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
      y += parent.offsetTop - parent.scrollTop + borderYOffset;
      try{
        parent = parent.offsetParent;
      }catch(e){
        parent = null;
      }
		}

		if ( accountForDocScroll ) {
			x -= this.docScrollLeft();
			y -= this.docScrollTop();
		}

		return { x:x, y:y };
	},

	/**
	*  Mozilla did not report all of the parents up the hierarchy via the
	*  offsetParent property that IE did.  So for the calculation of the
	*  offsets we use the offsetParent property, but for the calculation of
	*  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
	*  property instead so as to get the scroll offsets...
	*
	**/
	_toAbsoluteMozilla: function(element,accountForDocScroll) {
		var x = 0;
    var y = 0;
    var parent = element;
    while ( parent ) {
			x += parent.offsetLeft;
      y += parent.offsetTop;
      parent = parent.offsetParent;
		}

		parent = element;
    while ( parent && parent != document.body && parent != document.documentElement ) {
			if ( parent.scrollLeft  )
				x -= parent.scrollLeft;
			if ( parent.scrollTop )
				y -= parent.scrollTop;
			parent = parent.parentNode;
		}

    if ( accountForDocScroll ) {
			x -= this.docScrollLeft();
      y -= this.docScrollTop();
		}
    return { x:x, y:y };
  },

  docScrollLeft: function() {
		if ( window.pageXOffset )
			return window.pageXOffset;
		else if ( document.documentElement && document.documentElement.scrollLeft )
			return document.documentElement.scrollLeft;
		else if ( document.body )
			return document.body.scrollLeft;
		else
			return 0;
  },

	docScrollTop: function() {
		if ( window.pageYOffset )
			return window.pageYOffset;
		else if ( document.documentElement && document.documentElement.scrollTop )
			return document.documentElement.scrollTop;
		else if ( document.body )
			return document.body.scrollTop;
		else
			return 0;
	},

	findPosY: function(obj) {
		var curtop = 0;
		if (obj.offsetParent) {
			while (obj.offsetParent) {
				curtop += obj.offsetTop;
				obj = obj.offsetParent;
			}
		} else if (obj.y) curtop += obj.y;
		return curtop;
  },

  findPosX: function(obj) {
    var curleft = 0;
		if (obj.offsetParent) {
			while (obj.offsetParent) {
        curleft += obj.offsetLeft;
				obj = obj.offsetParent;
			}
    } else if (obj.x) curleft += obj.x;
    return curleft;
  }
}
