/* Widget.js

	Purpose:
		
	Description:
		
	History:
		Mon Apr 23, 2010 17:29:18 AM , Created by sam

Copyright (C) 2010 Potix Corporation. All Rights Reserved.
*/
(function () {
	
	function ghosting(drag, ofs, evt) {
		var wgt = drag.control,
			dragType = drag.opts.type = wgt._dragType,
			borderSize = wgt._focusBorderSize,
			target = evt.domTarget,
			$n = jq(drag.node),
			width = $n.width() - borderSize * 2 + 2, //ZSS-578: ghost should surround image 
			height = $n.height() - borderSize * 2 + 2, //ZSS-578: ghost should surround image
			$dgElm = jq('<div id="zk_ddghost"></div>').attr('style', $n.attr('style')).addClass("zswidget zswidget-drag z-drag-ghost").width(width + 'px').height(height + 'px');
		drag._orgcursor = document.body.style.cursor;
		document.body.style.cursor = dragType == 'resize' ? 'crosshair' : 'move';
		if ('move' == dragType) {
			$dgElm.appendTo(wgt._sheet.comp); //allow in entire sheet
			var baseOfs = zk(drag.node).revisedOffset();
			drag.opts.innerOffset = [evt.pageX - baseOfs[0] + borderSize, evt.pageY - baseOfs[1] + borderSize];
			document.body.style.cursor = 'move';
		} else {//resize
			var panel = wgt._panel;
			//allow in corresponding panel
			if(panel=='top'){
				$dgElm.appendTo(wgt._sheet.tp.$n('topwp'));
			}else if(panel=='left'){
				$dgElm.appendTo(wgt._sheet.lp.$n('leftwp'));
			}else if(panel=='corner'){
				$dgElm.appendTo(wgt._sheet.cp.$n('cornerwp'));
			}else{
				$dgElm.appendTo(wgt._sheet.sp.comp); 
			}
			document.body.style.cursor = 'crosshair';
		}
		
		jq(wgt.getDragNode()).addClass('z-dragged'); //after clone
		return $dgElm[0];
	}
	
	function getRepositionedLeftAndWidth(baseOffset, dragNode, pageX) {
		var newLeft = pageX - baseOffset[0],
			maxLeft = dragNode.offsetLeft + dragNode.clientHeight - 20;
		if (newLeft > maxLeft) {
			newLeft = maxLeft;
		}
		var newWidth = dragNode.offsetLeft + dragNode.clientWidth - newLeft;
		return [newLeft, newWidth];
	}
	
	function getRepositionedTopAndHeight(baseOffset, dragNode, pageY) {
		var newTop = pageY - baseOffset[1],
			maxTop = dragNode.offsetTop + dragNode.clientHeight - 20;
		if (newTop > maxTop) {
			newTop = maxTop;
		}
		var	newHgh = dragNode.offsetTop + dragNode.clientHeight - newTop;
		return [newTop, newHgh];
	}
	
	function getBottomHeight(baseOffset, dragNode, pageY) {
		var minHgh = 20; //min height can not be less than certain number, will cause err msg
		return Math.max(minHgh, pageY - baseOffset[1]);
	}
	
	function getRightWidth(baseOffset, dragNode, pageX) {
		var minWidth = 20;
		return Math.max(minWidth, pageX - baseOffset[0]);
	}
	
	function constraint(drag, pt, evt) {
		var wgt = drag.control,
			dgNode = wgt.getDragNode(),
			n = drag.node,
			type = wgt._dragType,
			dir = wgt._dragDir,
			borderSize = wgt._focusBorderSize,
			opts = drag.opts;
		if ('resize' == type && dir) {
			var sheet = drag.control._sheet,
				spdom = sheet.sp.comp,
				focus = wgt._focus;
			if ('N' == dir) {
				var ofs = zk(spdom).revisedOffset(),
					dgofs = zk(dgNode).revisedOffset(),
					scrollTop = spdom.scrollTop,//ZSS-458 Strange behavior when re-sizing North or West of chart/image in scrolling
					topHgh = getRepositionedTopAndHeight(ofs, dgNode, evt.pageY + scrollTop),
					top = topHgh[0],
					newHgh = topHgh[1];
				jq(n).height(newHgh + 'px');
				opts.x1 = dgofs[0] + borderSize;
				opts.x2 = opts.x1 + dgNode.clientWidth - (borderSize * 2);
				opts.y1 = top + ofs[1] - scrollTop;
				opts.y2 = opts.y1 + newHgh - borderSize;
				return [n.offsetLeft, top];
			} else if ('NE' == dir) {
				var ofs = zk(spdom).revisedOffset(),
					dgofs = zk(dgNode).revisedOffset(),
					left = n.offsetLeft,
					scrollTop = spdom.scrollTop,//ZSS-458 Strange behavior when re-sizing North or West of chart/image in scrolling
					topHgh = getRepositionedTopAndHeight(ofs, dgNode, evt.pageY + scrollTop),
					top = topHgh[0],
					newHgh = topHgh[1],
					newWidth = getRightWidth(zk(n).revisedOffset(), dgNode, evt.pageX);
				jq(n).height(newHgh + 'px').width(newWidth + 'px');
				opts.x1 = dgofs[0] + borderSize;
				opts.x2 = opts.x1 + newWidth - 4; //4: a number for better control re-size with cursor's tyle
				opts.y1 = top + ofs[1] - scrollTop;
				opts.y2 = opts.y1 + newHgh - borderSize;
				return [left, top];
			} else if ('E' == dir) {
				var ofs = zk(n).revisedOffset(),
					dgofs = zk(dgNode).revisedOffset(),
					left = n.offsetLeft,
					top = n.offsetTop,
					newWidth = getRightWidth(ofs, dgNode, evt.pageX);
				jq(n).width(newWidth + 'px');
				opts.x1 = dgofs[0] + borderSize;
				opts.y1 = dgofs[1] + borderSize;
				opts.x2 = opts.x1 + newWidth - 4;
				opts.y2 = opts.y1 + dgNode.clientHeight - (borderSize * 2); //minus top/btm border
				return [left, top];
			} else if ('SE' == dir) {
				var ofs = zk(n).revisedOffset(),
					dgofs = zk(dgNode).revisedOffset(),
					left = n.offsetLeft,
					top = n.offsetTop,
					newWidth = getRightWidth(ofs, dgNode, evt.pageX),
					newHgh = getBottomHeight(ofs, dgNode, evt.pageY);
				jq(n).width(newWidth + 'px').height(newHgh + 'px');
				opts.x1 = dgofs[0] + borderSize; 
				opts.y1 = dgofs[1] + borderSize;
				opts.x2 = opts.x1 + newWidth - 4;
				opts.y2 = opts.y1 + newHgh - 4;
				return [left, top];
			} else if ('S' == dir) {
				var ofs = zk(n).revisedOffset(),
					dgofs = zk(dgNode).revisedOffset(),
					left = n.offsetLeft,
					top = n.offsetTop,
					newHgh = getBottomHeight(ofs, dgNode, evt.pageY);
				jq(n).height(newHgh + 'px');
				opts.x1 = dgofs[0] + borderSize; 
				opts.y1 = dgofs[1] + borderSize;
				opts.x2 = opts.x1 + dgNode.clientWidth - (borderSize * 2); //minus top/btm border
				opts.y2 = opts.y1 + newHgh - 4;
				return [left, top];
			} else if ('SW' == dir) {
				var ofs = zk(spdom).revisedOffset(),
					dgofs = zk(dgNode).revisedOffset(),
					newHgh = getBottomHeight(zk(n).revisedOffset(), dgNode, evt.pageY),
					scrollLeft = spdom.scrollLeft,//ZSS-458 Strange behavior when re-sizing North or West of chart/image in scrolling
					leftWidth = getRepositionedLeftAndWidth(ofs, dgNode, evt.pageX + scrollLeft),
					left = leftWidth[0],
					newWidth = leftWidth[1];
				jq(n).width(newWidth + 'px').height(newHgh + 'px');
				opts.x1 = left + ofs[0] - scrollLeft; 
				opts.y1 = dgofs[1] + borderSize; //remain the same top offset; minus extra focus border
				opts.x2 = opts.x1 + newWidth - 4; 
				opts.y2 = opts.y1 + newHgh - 4;
				return [left, dgNode.offsetTop];
			} else if ('W' == dir) {
				var ofs = zk(spdom).revisedOffset(),
					dgofs = zk(dgNode).revisedOffset(),
					scrollLeft = spdom.scrollLeft,//ZSS-458 Strange behavior when re-sizing North or West of chart/image in scrolling
					leftWidth = getRepositionedLeftAndWidth(ofs, dgNode, evt.pageX + scrollLeft),
					top = dgNode.offsetTop,
					left = leftWidth[0],
					newWidth = leftWidth[1];
				jq(n).width(newWidth + 'px');
				opts.x1 = left + ofs[0] - scrollLeft;
				opts.y1 = dgofs[1] + borderSize; //remain the same top offset; minus extra focus border
				opts.x2 = opts.x1 + newWidth - 4;
				opts.y2 = opts.y1 + dgNode.clientHeight - (borderSize * 2);
				return [left, top];
			} else if ('NW' == dir) {
				var ofs = zk(spdom).revisedOffset(),
					dgofs = zk(dgNode).revisedOffset(),
					scrollLeft = spdom.scrollLeft,//ZSS-458 Strange behavior when re-sizing North or West of chart/image in scrolling
					leftWidth = getRepositionedLeftAndWidth(ofs, dgNode, evt.pageX + scrollLeft),
					scrollTop = spdom.scrollTop,//ZSS-458 Strange behavior when re-sizing North or West of chart/image in scrolling
					topHgh = getRepositionedTopAndHeight(ofs, dgNode, evt.pageY + scrollTop),
					top = topHgh[0],
					newHgh = topHgh[1],
					left = leftWidth[0],
					newWidth = leftWidth[1];
				jq(n).width(newWidth + 'px').height(newHgh + 'px');
				opts.x1 = left + ofs[0] - scrollLeft;
				opts.y1 = top + ofs[1] - scrollTop;
				opts.x2 = opts.x1 + newWidth - 4;
				opts.y2 = opts.y1 + newHgh - borderSize;
				return [left, top];	
			}
		} else { //widget move
			var inOfs = drag.opts.innerOffset,
				//inOfs is undefined in some draging case, especially move at the edge of spraedsheet.
				inOfs = !inOfs?[0,0]:inOfs,
				sheet = drag.control._sheet,
				ofs = zk(sheet.comp).revisedOffset(),
				minX = sheet.leftWidth,
				minY = sheet.topHeight,
				x = Math.max(evt.pageX - ofs[0] - inOfs[0], minX), //ZSS-312, only update when in constraint
				y = Math.max(evt.pageY - ofs[1] - inOfs[1], minY); //ZSS-312, only update when in constraint
			
			opts.x1 = x; //Math.max(evt.pageX - inOfs[0], minX); //ZSS-578
			opts.x2 = opts.x1 + dgNode.clientWidth - (borderSize * 2) + 1; // clientWidth will not count the border line so we have to add 2, then -1 to get x2 end point
			
			opts.y1 = y; //Math.max(evt.pageY - inOfs[1], minY); //ZSS-578
			opts.y2 = opts.y1 + dgNode.clientHeight - (borderSize * 2) + 1;
			return [x, y];
		}
	}
	
	function endeffect(drag, evt) {
		var ctrl = drag.control,
			opts = drag.opts,
			sheet = drag.control._sheet,
			ofs = zk(sheet.dp.comp).revisedOffset(),
			type = ctrl._dragType;
		
		// ZSS-578: resize will come to this function, too.
		if ("resize" != type) {
			opts.x1 += ofs[0];
			opts.x2 += ofs[0];
			opts.y1 += ofs[1];
			opts.y2 += ofs[1];
		}
		var	panel = ctrl._panel,
			sheetid = sheet.sheetid,
			leftWidth = sheet.leftWidth,
			topHeight = sheet.topHeight,
			cave = drag.node.firstChild,
			custColWidth = sheet.custColWidth,
			custRowHeight = sheet.custRowHeight,
			leftTop = zss.SSheetCtrl._calCellPos(sheet, opts.x1, opts.y1, type=='resize' && panel=='default'),
			rightBtm = zss.SSheetCtrl._calCellPos(sheet, opts.x2, opts.y2, type=='resize' && panel=='default'),
			custw = custColWidth.getStartPixel(leftTop[1]),
			custh = custRowHeight.getStartPixel(leftTop[0]),
			custw2 = custColWidth.getStartPixel(rightBtm[1]),
			custh2 = custRowHeight.getStartPixel(rightBtm[0]),
			dx1 = opts.x1 - ofs[0] - leftWidth - custw,
			dy1 = opts.y1 - ofs[1] - topHeight - custh,
		 	sameCol = rightBtm[1] == leftTop[1],
			sameRow = rightBtm[0] == leftTop[0],
			dx2 = sameCol ? 
				dx1 + (opts.x2 - opts.x1) :
				opts.x2 - ofs[0] - leftWidth - custw2,
			dy2 = sameRow ?
				dy1 + (opts.y2 - opts.y1) :
				opts.y2 - ofs[1] - topHeight - custh2;
					
//console.log("endeffect:opts:"+opts.x1+","+opts.x2+","+opts.y1+","+opts.y2+
//		"; dx,dy:"+dx1+","+dx2+","+dy1+","+dy2+
//		"; leftTop:"+leftTop[0]+","+leftTop[1]+
//		"; rightBtm:"+rightBtm[0]+","+rightBtm[1]+
//		"; custColWidth:"+custw+","+custw2+","+custh+","+custh2+
//		"; ofs:"+ofs[0]+","+ofs[1]+
//		"; topHeight:"+leftWidth+","+topHeight);

		ctrl._sheet._wgt.fireWidgetUpdatEvt(ctrl.getType(), drag.opts.type, drag.control.getId(), Math.round(dx1), Math.round(dy1), Math.round(dx2), Math.round(dy2), 
					leftTop[1], leftTop[0], rightBtm[1], rightBtm[0]);
		drag.control._dragging = false;
		zcss.removeRule("#" + sheetid + ' .zswidget-focus', sheetid + '-sheet');
	}
	
zssex.Widget = zk.$extends(zul.Widget, {
	_leftPos: null,
	_topPos: null,
	_dragging: false,
	_delaySetDragMove: null,
	_dragType: null,
	_dragDir: null,
	_focus: false,
	_focusBorderSize: 6,
	$define: {
		col: function () {
			this.adjustLocation();
		},
		row: function () {
			this.adjustLocation();
		},
		left: function () {
			this.adjustLocation();
		},
		top: function () {
			this.adjustLocation();
		},
		visible: function (show) {
			var n = this.$n();
			if (n)
				jq(n).css('visibility', show ? 'visible' : 'hidden');
		},
		sizable: _zkf = function () {
			this.updateDraggable_();
		},
		movable: _zkf,
		/**
		 * The id of the widget
		 */
		id: null,
		/**
		 * The type of the widget
		 */
		type: null,
		/**
		 * the panel location
		 */
		panel: null
	},
	insertChildHTML_: function (child, before, desktop) {
		if (before) 
			jq(before).before(child.redrawHTML_());
		else
			jq(this.$n('real')).append(child.redrawHTML_());//should append child to real
		child.bind(desktop);
	},	
	adjustLocation: function (show) {
		var n = this.$n(),
			sheet = this._sheet;
		if (!n || !sheet)
			return;

		var col = this.getCol(),
			row = this.getRow(),
			custColWidth = sheet.custColWidth,
			custRowHeight = sheet.custRowHeight,
			cw = custColWidth.getSize(col),
			ch = custRowHeight.getSize(row),
			left = this._leftPos = sheet.leftWidth + custColWidth.getStartPixel(col) + (this.getLeft() > cw ? cw : this.getLeft()),
			top = this._topPos = sheet.topHeight + custRowHeight.getStartPixel(row) + (this.getTop() > ch ? ch : this.getTop()),
			f = this._focus,
			borderSize = this._focusBorderSize;
		jq(n).css({'left': f ? jq.px(left - borderSize) : jq.px(left), 'top': f ? jq.px(top - borderSize) : jq.px(top)});
		if (show)
			jq(n).css('visibility', 'visible');
	},
	_cloneAttrs: function (newWgt) {
		//while parent (zssex.Ghost) invoke replaceWidget
		//it's child (zssex.Widget) need to save original's value
		//because replaceWidget won't inoke redrawWidgetTo
		if (newWgt._id == this._id) {
			newWgt._sheet = this._sheet;
			newWgt._leftPos = this._leftPos;
			newWgt._topPos = this._topPos;
		}
	},
	/**
	 * Redraw DOM element(s) of this widget and append to a DOM element 
	 * @param DOMElement node 
	 */
	redrawWidgetTo: function (sheet) {
		if (!sheet)
			return;
		this._sheet = sheet;
		var panel = this._panel;
		var n;
		if(panel=='top'){
			n = sheet.tp.$n('topwp');
		}else if(panel=='left'){
			n = sheet.lp.$n('leftwp');
		}else if(panel=='corner'){
			n = sheet.cp.$n('cornerwp');
		}else{
			n = sheet.$n('wp');
		}
		jq(n).append(this.$n());//move from ghost to widget panel
		this.clearCache();
		this.adjustLocation(true);
		
		// ZSS-229: this should be called after redrawHTML_(), it needs DOM.
		this.updateDraggable_();
	},
	updateDraggable_: function () {
		this.setDraggable(this.isSizable() || this.isMovable());
	},
	getDragOptions_: function (map) {
		map.ghosting = ghosting;
		map.constraint = constraint;
		map.endeffect = endeffect;
		return map;
	},
	ignoreDrag_: function (pt) {
		var sheet = this._sheet;
		//can only darg when has focus, or size calculation will be wrong because of the focus border effect
		var r = !this._dragType 
			|| (sheet.state == zss.SSheetCtrl.NOFOCUS && !this._focus) || !this.isEditable();
		if(!r && !this._focus){
			this._gainFocus();
		}
		//mark draging to avoid type/direct change when moving to inside of widget (will cause type/direction change)
		this._dragging = !r
		return r;
	},
	doBlur_: function (evt) {
		jq(this.$n()).removeClass('zswidget-focus').css({left: jq.px(this._leftPos), top: jq.px(this._topPos)});
		this._updateListenFocus(false);
	},
	_onSheetFocus: function (evt) {
		var data = evt.data;
		if (data) {
			if (data.ctrl != this) {
				this.doBlur_();
			}
		} else {
			this.doBlur_();
		}
	},
	doMouseMove_: function (evt) {
		if (!this.isEditable()) {
			//this._sheet.dp.reFocus();
			return;
		}
		
		if (!this._dragging) {
			var target = evt.domTarget,
				n = this.$n(),
				sheetid = this._sheet.sheetid,
				cssId = sheetid + '-sheet',
				cssPrefix = "#" + sheetid;
			if ((target == n || target == this.$n('cave')) && this.isSizable()) {
				this._dragType = 'resize';
				var ofs = zk(n).revisedOffset(),
					size = this._focusBorderSize,
					x = evt.pageX - ofs[0],
					y = evt.pageY - ofs[1],
					w = n.clientWidth,
					h = n.clientHeight,
					hLeft = size,
					hRight = w - size,
					vTop = size,
					vBtm = h - size,
					dir;
				if (x <= hLeft && y <= vTop) {
					dir = 'NW'; //west north
				} else if (x <= hLeft && y <= vBtm) {
					dir = 'W';
				} else if (x <= hLeft && y > vBtm) {
					dir = 'SW'; //west south
				} else if (x <= hRight && y <= vTop) {
					dir = 'N';
				} else if (x > hRight && y <= vTop) {
					dir = 'NE'; //east north
				} else if (x > hRight && y <= vBtm) {
					dir = 'E';
				} else if (x <= hRight && y > vBtm) {
					dir = 'S';
				} else if(this._dragDir!='ES'){
					dir = 'SE'; //east south
				}
				
				if(dir != this._dragDir){
					this._dragDir = dir;
					zcss.setRule(cssPrefix + ' .zswidget-focus', 'cursor', dir.toLowerCase()+'-resize', true, cssId);
				}
				this._delaySetDragMove = jq.now();
			} else if (jq.isAncestor(this.$n('real'), target)) {
				this._dragDir = null;
				var delay = this._delaySetDragMove;
				if (!delay || (delay && (jq.now() - delay) > 500)) {
					this._delaySetDragMove = null;
					if(this._dragType!='move'){
						this._dragType = 'move';
						zcss.setRule(cssPrefix + ' .zswidget-focus', 'cursor', 'move', null, cssId);
					}
				}
			}
		}
	},
	doMouseDown_: function (evt) {
		if (!this.isEditable()) {
			//this._sheet.dp.reFocus();
			return;
		}
		if (jq.isAncestor(this.$n('real'), evt.domTarget)) {
			this._delaySetDragMove = null;
		}
		
		if (!this._focus) {
			var sheet = this._sheet,
				state = sheet.state;
			if (state == zss.SSheetCtrl.EDITING) {
				sheet.dp.stopEditing();
			} else if (state == zss.SSheetCtrl.NOFOCUS) {
				sheet.dp.gainFocus();
				return;
			}
			this._gainFocus();
		}
	},
	doMouseUp_: function (evt) {
		this._dragging = false;//clear dragging always when mouse up
	},
	isEditable: function () {
		var wgt = this._sheet._wgt;
		return !wgt.isProtect() || wgt.objectEditable;
	},
	_gainFocus: function(){
		var sheet = this._sheet,
		$n = jq(this.$n()),
		borderWidth = this._focusBorderSize,
		left = this._leftPos - borderWidth,
		top = this._topPos - borderWidth;
		$n.addClass('zswidget-focus');
		if (left > 0)
			$n.css({left: jq.px(left)});
		if (top > 0)
			$n.css({top: jq.px(top)});
		
		sheet.fire('onFocused', {ctrl: this});
		this._updateListenFocus(true);
	},
	afterKeyDown_: function (evt) {
		var data = evt.data;
		data.wgt=true;
		data.wgtType=this._type;
		data.wgtId=this._id;
		data.sheetId=this._sheet._wgt.getSheetId();
		this.$supers('afterKeyDown_', arguments);
	},
	/**
	 * Set focus attribute and register listener or unregister sheet onFocused event
	 * @param boolean 
	 */
	_updateListenFocus: function (b) {
		var curr = this._focus;
		if (curr != b) {
			this._sheet[curr ? 'unlisten' : 'listen']({onFocused: this.proxy(this._onSheetFocus)});
			this._focus = b;
			this.$n('fo')[b ? 'focus' : 'blur']();
		}
	},
	bind_: function () {
		this.$supers('bind_', arguments);
	},
	unbind_: function () {
		this._updateListenFocus(false);
		this.$supers('unbind_', arguments);
	},
	domClass_: function (no) {
		return 'zswidget';
	}
});
})();