1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2009, 2010, 2012, 2013, 2014, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2005, 2006, 2007, 2009, 2010, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 
 25 /**
 26 * @class
 27 * This static class enables entities (for example, {@link DwtDialog}s) to be dragged around within
 28 * an application window. The code is basically the same as in dom-drag.js from www.youngpup.net
 29 *
 30 * @author Ross Dargahi
 31 * 
 32 * @private
 33 */
 34 DwtDraggable = function() {
 35 }
 36 
 37 DwtDraggable.dragEl = null;
 38 
 39 /**
 40  * Initializes dragging for <code>dragEl</code>
 41  * 
 42  * @param {HTMLElement} dragEl 	the element being dragged, can also be a handle e.g. the
 43  * 		title bar in a dialog
 44  * @param {HTMLElement} [rootEl]	the actual element that will be moved. This will be a
 45  * 		parent element of <i>dragEl</i>
 46  * @param {number} [minX] 	the minimum x coord to which we can drag
 47  * @param {number} [maxX] 	the maximum x coord to which we can drag
 48  * @param {number} [minY] 	the minimum y coord to which we can drag
 49  * @param {number} [maxY] 	the maximum x coord to which we can drag
 50  * @param {AjxCallback} dragStartCB	the callback that is called when dragging is started
 51  * @param {AjxCallback}dragCB		the callback that is called when dragging
 52  * @param {AjxCallback}dragEndCB	the callback that is called when dragging is ended
 53  * @param {boolean} [swapHorizRef]	if <code>true</code>, then mouse motion to the right will move element left
 54  * @param {boolean} [swapVertRef]		if <code>true</code>, then mouse motion to the bottom will move element up
 55  * @param {function} [fXMapper] 		the function that overrides this classes x coordinate transformations
 56  * @param {function} [fYMapper] 		the function that overrides this classes y coordinate transformations
 57  *
 58  */
 59 DwtDraggable.init = 
 60 function(dragEl, rootEl, minX, maxX, minY, maxY, dragStartCB, dragCB, dragEndCB, 
 61 		 swapHorizRef, swapVertRef, fXMapper, fYMapper) {
 62 	dragEl.onmousedown = DwtDraggable.__start;
 63 
 64 	dragEl.__hMode = swapHorizRef ? false : true;
 65 	dragEl.__vMode = swapVertRef ? false : true;
 66 
 67 	dragEl.__root = (rootEl && rootEl != null) ? rootEl : dragEl ;
 68 
 69 	if (dragEl.__hMode && isNaN(parseInt(dragEl.__root.style.left))) 
 70 		dragEl.__root.style.left = "0px";
 71 	if (dragEl.__vMode && isNaN(parseInt(dragEl.__root.style.top))) 
 72 		dragEl.__root.style.top = "0px";
 73 		
 74 	if (!dragEl.__hMode && isNaN(parseInt(dragEl.__root.style.right))) 
 75 		dragEl.__root.style.right = "0px";
 76 	if (!dragEl.__vMode && isNaN(parseInt(dragEl.__root.style.bottom))) 
 77 		dragEl.__root.style.bottom = "0px";
 78 
 79 	dragEl.__minX = (typeof minX != 'undefined') ? minX : null;
 80 	dragEl.__minY = (typeof minY != 'undefined') ? minY : null;
 81 	dragEl.__maxX = (typeof maxX != 'undefined') ? maxX : null;
 82 	dragEl.__maxY = (typeof maxY != 'undefined') ? maxY : null;
 83 
 84 	dragEl.__xMapper = fXMapper ? fXMapper : null;
 85 	dragEl.__yMapper = fYMapper ? fYMapper : null;
 86 
 87 	dragEl.__root.onDragStart = dragStartCB
 88 	dragEl.__root.onDragEnd = dragEndCB
 89 	dragEl.__root.onDrag = dragCB;
 90 };
 91 
 92 /**
 93  * Sets the minimum and maximum drag boundries
 94  * 
 95  * @param {HTMLElement} dragEl Element being dragged, can also be a handle e.g. the
 96  * 		title bar in a dialog
 97  * @param {number} minX 	the minimum x coordinate
 98  * @param {number} maxX 	the maximum x coordinate
 99  * @param {number} minY 	the minimum y coordinate
100  * @param {number} maxY 	the maximum y coordinate
101  */
102 DwtDraggable.setDragBoundaries =
103 function (dragEl ,minX, maxX, minY, maxY) {
104 	if (dragEl != null) {
105 		if (minX != null) dragEl.__minX = minX;
106 		if (maxX != null) dragEl.__maxX = maxX;
107 		if (minY != null) dragEl.__minY = minY;
108 		if (maxY != null) dragEl.__maxY = maxY;
109 	}
110 };
111 
112 /** @private */
113 DwtDraggable.__start =
114 function(e)	{
115 	var dragEl = DwtDraggable.dragEl = this;
116 	e = DwtDraggable.__fixE(e);
117 	var x = parseInt(dragEl.__hMode ? dragEl.__root.style.left : dragEl.__root.style.right );
118 	var y = parseInt(dragEl.__vMode ? dragEl.__root.style.top  : dragEl.__root.style.bottom);
119 	if (dragEl.__root.onDragStart)
120 		dragEl.__root.onDragStart.run([x, y]);
121 
122 	dragEl.__lastMouseX = e.clientX;
123 	dragEl.__lastMouseY = e.clientY;
124 
125 	if (dragEl.__hMode) {
126 		if (dragEl.__minX != null)	
127 			dragEl.__minMouseX = e.clientX - x + dragEl.__minX;
128 		if (dragEl.__maxX != null)
129 			dragEl.__maxMouseX = dragEl.__minMouseX + dragEl.__maxX - dragEl.__minX;
130 	} else {
131 		if (dragEl.__minX != null)
132 			dragEl.__maxMouseX = -dragEl.__minX + e.clientX + x;
133 		if (dragEl.__maxX != null)
134 			dragEl.__minMouseX = -dragEl.__maxX + e.clientX + x;
135 	}
136 
137 	if (dragEl.__vMode) {
138 		if (dragEl.__minY != null)
139 			dragEl.__minMouseY = e.clientY - y + dragEl.__minY;
140 		if (dragEl.__maxY != null)
141 			dragEl.__maxMouseY = dragEl.__minMouseY + dragEl.__maxY - dragEl.__minY;
142 	} else {
143 		if (dragEl.__minY != null)
144 			dragEl.__maxMouseY = -dragEl.__minY + e.clientY + y;
145 		if (dragEl.__maxY != null)
146 			dragEl.__minMouseY = -dragEl.__maxY + e.clientY + y;
147 	}
148 
149 	document.onmousemove = DwtDraggable.__drag;
150 	document.onmouseup = DwtDraggable.__end;
151 
152 	return false;
153 };
154 
155 /** @private */
156 DwtDraggable.__drag =
157 function(e)	{
158 	e = DwtDraggable.__fixE(e);
159 	var dragEl = DwtDraggable.dragEl;
160 
161 	var ey	= e.clientY;
162 	var ex	= e.clientX;
163 	var x = parseInt(dragEl.__hMode ? dragEl.__root.style.left : dragEl.__root.style.right );
164 	var y = parseInt(dragEl.__vMode ? dragEl.__root.style.top  : dragEl.__root.style.bottom);
165 	var nx, ny;
166 
167 	if (!dragEl.__xMapper) {
168 		if (dragEl.__minX != null)
169 			ex = dragEl.__hMode ? Math.max(ex, dragEl.__minMouseX) : Math.min(ex, dragEl.__maxMouseX);
170 		if (dragEl.__maxX != null)
171 			ex = dragEl.__hMode ? Math.min(ex, dragEl.__maxMouseX) : Math.max(ex, dragEl.__minMouseX);
172 		nx = x + ((ex - dragEl.__lastMouseX) * (dragEl.__hMode ? 1 : -1));
173 	} else {
174 		nx = dragEl.__xMapper(x, ex);
175 	}
176 
177 	if (!dragEl.__yMapper) {
178 		if (dragEl.__minY != null)
179 			ey = dragEl.__vMode ? Math.max(ey, dragEl.__minMouseY) : Math.min(ey, dragEl.__maxMouseY);
180 		if (dragEl.__maxY != null)
181 			ey = dragEl.__vMode ? Math.min(ey, dragEl.__maxMouseY) : Math.max(ey, dragEl.__minMouseY);
182 		ny = y + ((ey - dragEl.__lastMouseY) * (dragEl.__vMode ? 1 : -1));
183 	} else {
184 		ny = dragEl.__yMapper(y, ey);
185 	}
186 
187 	DwtDraggable.dragEl.__root.style[dragEl.__hMode ? "left" : "right"] = nx + "px";
188 	DwtDraggable.dragEl.__root.style[dragEl.__vMode ? "top" : "bottom"] = ny + "px";
189 	DwtDraggable.dragEl.__lastMouseX = ex;
190 	DwtDraggable.dragEl.__lastMouseY = ey;
191 
192 	if (DwtDraggable.dragEl.__root.onDrag)
193 		DwtDraggable.dragEl.__root.onDrag.run([nx, ny]);
194 		
195 	return false;
196 };
197 
198 /** @private */
199 DwtDraggable.__end =
200 function() {
201 	document.onmousemove = null;
202 	document.onmouseup   = null;
203 	if (DwtDraggable.dragEl.__root.onDragEnd)
204 		DwtDraggable.dragEl.__root.onDragEnd.run([parseInt(DwtDraggable.dragEl.__root.style[DwtDraggable.dragEl.__hMode ? "left" : "right"]), 
205 											 	  parseInt(DwtDraggable.dragEl.__root.style[DwtDraggable.dragEl.__vMode ? "top" : "bottom"])]);
206 	DwtDraggable.dragEl = null;
207 };
208 
209 /** @private */
210 DwtDraggable.__fixE =
211 function(e) {
212 	if (typeof e == 'undefined')
213 		e = window.event;
214 	if (!AjxEnv.isWebKitBased) {
215 		if (typeof e.layerX == 'undefined')
216 			e.layerX = e.offsetX;
217 		if (typeof e.layerY == 'undefined')
218 			e.layerY = e.offsetY;
219 	}
220 	return e;
221 };
222