1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 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, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 
 25 /**
 26  * 
 27  * 
 28  * @private
 29  */
 30 DwtUiEvent = function(init) {
 31 	if (arguments.length == 0) return;
 32 	DwtEvent.call(this, true);
 33 	this.reset();
 34 }
 35 
 36 DwtUiEvent.prototype = new DwtEvent;
 37 DwtUiEvent.prototype.constructor = DwtUiEvent;
 38 
 39 DwtUiEvent.prototype.isDwtUiEvent = true;
 40 DwtUiEvent.prototype.toString = function() { return "DwtUiEvent"; }
 41 
 42 DwtUiEvent.prototype.reset =
 43 function() {
 44 	this.dwtObj = null
 45 	this.altKey = false;
 46 	this.ctrlKey = false;
 47 	this.metaKey = false;
 48 	this.shiftKey = false;
 49 	this.target = null;
 50 	this.type = null;
 51 	this.docX = -1;
 52 	this.docY = -1;
 53 	this.elementX = -1;
 54 	this.elementY = -1;
 55 	this.ersatz = false; // True means this event was manufactured
 56 	this._stopPropagation = false;
 57 	this._returnValue = true;
 58 	this._dontCallPreventDefault = false; // True means to allow the the (unusual) situation in Firefox where we
 59 	                                      // want the event handler to return false without calling preventDefault().
 60 }
 61 
 62 /**
 63  * Pass caller's "this" as 'target' if using IE and the ev may have come from another window. The target
 64  * will be used to get to the window that generated the event, so the event can be found.
 65  */
 66 DwtUiEvent.getEvent =
 67 function(ev, target) {
 68 	ev = ev || window.event;
 69 	if (ev) { return ev; }
 70 
 71 	// get event from iframe in IE; see http://www.outofhanwell.com/blog/index.php?cat=25
 72 	if (target) {
 73 		DBG.println(AjxDebug.DBG3, "getEvent: Checking other window for event");
 74 		var pw = (target.ownerDocument || target.document || target).parentWindow;
 75 		return pw ? pw.event : null;
 76 	}
 77 }
 78 
 79 /**
 80  * Returns the target element of the event.
 81  * 
 82  * @param ev				[Event]		DHTML event
 83  * @param useRelatedTarget	[boolean]*	if true, return element that was related to this event;
 84  * 										for a MOUSEOVER or MOUSEOUT event, that's the element
 85  * 										moved from/to.
 86  */
 87 DwtUiEvent.getTarget =
 88 function(ev, useRelatedTarget)  {
 89 	ev = DwtUiEvent.getEvent(ev);
 90 	if (!ev) { return null; }
 91 	if (!useRelatedTarget) {
 92 		if (ev.target) {
 93 			// if text node (like on Safari) return parent
 94 			return (ev.target.nodeType == 3) ? ev.target.parentNode : ev.target;
 95 		} else if (ev.srcElement) {		// IE
 96 			return ev.srcElement;
 97 		}
 98 	} else {
 99 		if (ev.relatedTarget) {
100 			return ev.relatedTarget;
101 		} else if (ev.toElement) {		// IE
102 			return ev.toElement;
103 		} else if (ev.fromElement) {	// IE
104 			return ev.fromElement;
105 		}
106 	}
107 	return null;
108 }
109 
110 /**
111  * Returns the first element with a value for the given property, working its way up the element chain.
112  *
113  * @param ev				[Event]		DHTML event
114  * @param prop				[string]	the name of a property
115  * @param useRelatedTarget	[boolean]*	if true, return element that was related to this event;
116  * @param value				[string]*	expected value of given property
117  */
118 DwtUiEvent.getTargetWithProp =
119 function(ev, prop, useRelatedTarget, value)  {
120 	var htmlEl = DwtUiEvent.getTarget(ev, useRelatedTarget);
121 	while (htmlEl) {
122 		var elValue = Dwt.getAttr(htmlEl, prop);
123 		if (elValue != null && elValue !== "" && (!value || (elValue == value))) {
124 			return htmlEl;
125 		}
126 		htmlEl = htmlEl.parentNode;
127 	}
128 	return null;
129 }
130 
131 /**
132  * Returns the first element with the given class name, working its way up the element chain.
133  *
134  * @param ev				[Event]		DHTML event
135  * @param className			[string]	the requested class name
136  * @param useRelatedTarget	[boolean]*	if true, return element that was related to this event;
137  */
138 DwtUiEvent.getTargetWithClass =
139 function(ev, className, useRelatedTarget)  {
140 	var htmlEl = DwtUiEvent.getTarget(ev, useRelatedTarget);
141 	while (htmlEl && htmlEl.nodeType === 1) {
142 		if (Dwt.hasClass(htmlEl, className)) {
143 			return htmlEl;
144 		}
145 		htmlEl = htmlEl.parentNode;
146 	}
147 	return null;
148 }
149 
150 /**
151  * Returns the first element with values for all of the given properties, working its way up the element chain.
152  *
153  * @param ev				[Event]		DHTML event
154  * @param props				[array]		a list of property names (strings)
155  */
156 DwtUiEvent.getTargetWithProps =
157 function(ev, props)  {
158 	var htmlEl = DwtUiEvent.getTarget(ev);
159 	while (htmlEl) {
160 		var okay = true;
161 		for (var i in props) {
162 			var val = Dwt.getAttr(htmlEl, props[i]);
163 			if (val == null || val === "") {
164 				htmlEl = htmlEl.parentNode;
165 				okay = false;
166 				break;
167 			}
168 		}
169 		if (okay)
170 			return htmlEl;
171 	}
172 	return null;
173 }
174 
175 DwtUiEvent.copy = 
176 function(dest, src) {
177 	dest.altKey = src.altKey;
178 	dest.ctrlKey = src.ctrlKey;
179 	dest.metaKey = src.metaKey;
180 	dest.shiftKey = src.shiftKey;
181 	dest.target = src.target;
182 	dest.type = src.type;
183 	dest.dwtObj = src.dwtObj;
184 	dest.docX = src.docX;
185 	dest.docY = src.docY;
186 	dest.elementX = src.elementX;
187 	dest.elementY = src.elementY;
188 	dest.ersatz = src.ersatz;
189 	dest._stopPropagation = src._stopPropagation;
190 	dest._returnValue = src._returnValue;
191 }
192 
193 /**
194  * Copies properties from the native DHTML event to this DWT event object. The target
195  * control can be optionally fetched by providing true as the second argument.
196  * 
197  * @param ev	[Event]				DHTML event
198  * @param obj	[DwtControl|true]	if true, the target object will be fetched; otherwise
199  * 									used to set target object if present
200  */
201 DwtUiEvent.prototype.setFromDhtmlEvent =
202 function(ev, obj) {
203 	ev = DwtUiEvent.getEvent(ev);
204 	if (!ev) { return; }
205 	this.altKey = ev.altKey;
206 	this.ctrlKey = ev.ctrlKey;
207 	this.metaKey = ev.metaKey;
208 	this.shiftKey = ev.shiftKey;
209 	this.type = ev.type;
210 	this.target = DwtUiEvent.getTarget(ev);
211 	this.dwtObj = (obj === true) ? DwtControl.getTargetControl(ev) : obj;
212 
213 	// Compute document coordinates
214 	if (ev.pageX != null) {
215 		this.docX = ev.pageX;
216 		this.docY = ev.pageY;
217 	} else if (ev.clientX != null) {
218 		this.docX = ev.clientX + document.body.scrollLeft - document.body.clientLeft;
219 		this.docY = ev.clientY + document.body.scrollTop - document.body.clientTop;
220 		if (document.body.parentElement) {
221 				var bodParent = document.body.parentElement;
222 				this.docX += bodParent.scrollLeft - bodParent.clientLeft;
223 				this.docY += bodParent.scrollTop - bodParent.clientTop;
224 		}
225 	}
226 	// Compute Element coordinates
227 	if (ev.offsetX != null) {
228 		this.elementX = ev.offsetX;
229 		this.elementY = ev.offsetY;
230 	} else if (!AjxEnv.isWebKitBased && ev.layerX != null) {
231 		this.elementX = ev.layerX;
232 		this.elementY = ev.layerY;
233 	} else { // fail hard for others
234 		this.elementX = Dwt.DEFAULT;
235 		this.elementY = Dwt.DEFAULT;
236 	}
237 	
238 	this.ersatz = false;
239 	return ev;
240 }
241 
242 DwtUiEvent.prototype.setToDhtmlEvent =
243 function(ev) {
244 	DwtUiEvent.setBehaviour(ev, this._stopPropagation, this._returnValue, this._dontCallPreventDefault);
245 }
246 
247 DwtUiEvent.setBehaviour =
248 function(ev, stopPropagation, allowDefault, dontCallPreventDefault) {
249 	var dhtmlEv = DwtUiEvent.getEvent(ev);
250 	DwtUiEvent.setDhtmlBehaviour(dhtmlEv, stopPropagation, allowDefault, dontCallPreventDefault);
251 };
252 
253 DwtUiEvent.setDhtmlBehaviour =
254 function(dhtmlEv, stopPropagation, allowDefault, dontCallPreventDefault) {
255 
256 	dhtmlEv = DwtUiEvent.getEvent(dhtmlEv);
257 	if (!dhtmlEv) { return; }
258 
259 	// stopPropagation is referring to the function found in Mozilla's event object
260 	if (dhtmlEv.stopPropagation != null) {
261 		if (stopPropagation)
262 			dhtmlEv.stopPropagation();
263 		if (!allowDefault && !dontCallPreventDefault)
264 			dhtmlEv.preventDefault();
265 	} else {
266 		// IE only..
267 		dhtmlEv.returnValue = allowDefault;
268 		dhtmlEv.cancelBubble = stopPropagation;
269 	}
270 };
271 
272 /**
273  * @deprecated
274  * Use DwtControl.getTargetControl() instead.
275  * 
276  * Returns a control (DWT object) based on the event, by finding the event target and using
277  * its reference to a DWT object in the element's "dwtObj" expando property.
278  * 
279  * @param ev				[Event]		DHTML event
280  * @param useRelatedTarget	[boolean]*	if true, return element that was related to this event;
281  */
282 DwtUiEvent.getDwtObjFromEvent =
283 function(ev, useRelatedTarget) {
284 	var htmlEl = DwtUiEvent.getTargetWithProp(ev, "dwtObj", useRelatedTarget);
285 	return htmlEl ? Dwt.getObjectFromElement(htmlEl) : null;
286 };
287 
288 /**
289  * @deprecated
290  * Instead, do something like this:
291  * 		var htmlEl = DwtUiEvent.getTargetWithProp(ev, "myProp");
292  * 		var obj = DwtControl.findControl(htmlEl);
293  * 
294  * Returns a control (DWT object) based on the event, by finding the event target with the
295  * given property and using its reference to a DWT object.
296  * 
297  * @param ev				[Event]		DHTML event
298  * @param useRelatedTarget	[boolean]*	if true, return element that was related to this event;
299  */
300 DwtUiEvent.getDwtObjWithProp =
301 function(ev, prop) {
302 	var htmlEl = DwtUiEvent.getTargetWithProps(ev, ["dwtObj", prop]);
303 	return htmlEl ? Dwt.getObjectFromElement(htmlEl) : null;
304 };
305