1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2009, 2010, 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, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @constructor
 26  * @class
 27  * A drop target is registered with a control to indicate that the control is 
 28  * a drop target. The drop target is the mechanism by which the DnD framework provides 
 29  * the binding between the UI components and the application.
 30  * <p>
 31  * Application developers instantiate {@link DwtDropTarget} and register it with the control
 32  * which is to be a drop target (via {@link DwtControl.setDropTarget}). The
 33  * application should then register a listener with the {@link DwtDropTarget}. This way
 34  * when drop events occur the application will be notified and may act on them 
 35  * accordingly
 36  * </p>
 37  * 
 38  * @author Ross Dargahi
 39  * 
 40  * @param {array} transferType	a list of supported object types that may be dropped onto
 41  * 		this drop target. Typically the items represent classes (i.e. functions) whose 
 42  * 		instances may be dropped on this drop target e.g. 
 43  * 		<code>new DwtDropTarget(MailItem, AppointmentItme)</code>
 44  * 
 45  * @see DwtDropEvent
 46  * @see DwtControl
 47  * @see DwtControl#setDropTarget
 48  */
 49 DwtDropTarget = function(types) {
 50 	/** @private */
 51 	this._evtMgr = new AjxEventMgr();
 52 
 53 	/** @private */
 54 	this.__hasMultiple = false;
 55 	
 56 	this._types = {};
 57 	if (typeof types == "string") {
 58 		types = [types];
 59 	}
 60 	if (types && types.length) {
 61 		for (var i = 0; i < types.length; i++) {
 62 			this.addTransferType(types[i]);
 63 		}
 64 	}
 65 }
 66 
 67 /** @private */
 68 DwtDropTarget.__DROP_LISTENER = "DwtDropTarget.__DROP_LISTENER";
 69 
 70 /** @private */
 71 DwtDropTarget.__dropEvent = new DwtDropEvent();
 72 
 73 /**
 74  * Returns a string representation of this object.
 75  * 
 76  * @return {string}	a string representation of this object
 77  */
 78 DwtDropTarget.prototype.toString = 
 79 function() {
 80 	return "DwtDropTarget";
 81 }
 82 
 83 /**
 84  * Registers a listener for {@link DwtDragEvent} events.
 85  *
 86  * @param {AjxListener} dropTargetListener Listener to be registered 
 87  * 
 88  * @see DwtDropEvent
 89  * @see AjxListener
 90  * @see #removeDropListener
 91  */
 92 DwtDropTarget.prototype.addDropListener =
 93 function(dropTargetListener) {
 94 	this._evtMgr.addListener(DwtDropTarget.__DROP_LISTENER, dropTargetListener);
 95 }
 96 
 97 /**
 98  * Removes a registered event listener.
 99  * 
100  * @param {AjxListener} dropTargetListener Listener to be removed
101  * 
102  * @see AjxListener
103  * @see #addDropListener
104  */
105 DwtDropTarget.prototype.removeDropListener =
106 function(dropTargetListener) {
107 	this._evtMgr.removeListener(DwtDropTarget.__DROP_LISTENER, dropTargetListener);
108 }
109 
110 /**
111  *  Check to see if the types in <code>items</code> can be dropped on this drop target
112  *
113  * @param {object|array} items an array of objects or single object whose types are
114  * 		to be checked against the set of transfer types supported by this drop target
115  * 
116  * @return true if all of the objects in <code>items</code> may legally be dropped on 
117  * 		this drop target
118  * @type boolean
119  */
120 DwtDropTarget.prototype.isValidTarget =
121 function(items) {
122 	if (items instanceof Array) {
123 		var len = items.length;
124 		for (var i = 0; i < len; i++) {
125 			if (!this.__checkTarget(items[i])) {
126 				return false;
127 			}
128 		}
129 		return true;
130 	} else {
131 		return this.__checkTarget(items);
132 	}
133 }
134 
135 /**
136  * Calling this method indicates that the UI component backing this drop target has multiple 
137  * sub-components
138  */
139 DwtDropTarget.prototype.markAsMultiple = 
140 function() {
141 	this.__hasMultiple = true;
142 };
143 
144 /**
145  * Checks if the UI component backing this drop target has multiple sub-components.
146  * 
147  * @return	{boolean}		<code>true</code> if the UI component has multiple sub-components
148  */
149 DwtDropTarget.prototype.hasMultipleTargets = 
150 function () {
151 	return this.__hasMultiple;
152 };
153 
154 /**
155  * Gets the transfer types.
156  * 
157  * @return {array}	the list of transfer types supported by this drop target
158  * 
159  * @see #setTransferTypes
160  */
161 DwtDropTarget.prototype.getTransferTypes =
162 function() {
163 	return this._types;
164 }
165 
166 /**
167  * Declares a type of object as valid for being dropped onto this target. The type is provided
168  * as a string, since the corresponding class may not yet be defined. The type is eval'ed before
169  * it is used for any validation, since the check is done with <code>instanceof</code>.
170  * 
171  * @param {string}	type		the name of class
172  */
173 DwtDropTarget.prototype.addTransferType =
174 function(type) {
175 	this._types[type] = null;
176 };
177 
178 // The following methods are called by DwtControl during the Drag lifecycle 
179 
180 /** @private */
181 DwtDropTarget.prototype._dragEnter =
182 function(operation, targetControl, srcData, ev, dndProxy) {
183 	DwtDropTarget.__dropEvent.operation = operation;
184 	DwtDropTarget.__dropEvent.targetControl = targetControl;
185 	DwtDropTarget.__dropEvent.action = DwtDropEvent.DRAG_ENTER;
186 	DwtDropTarget.__dropEvent.srcData = srcData;
187 	DwtDropTarget.__dropEvent.uiEvent = ev;
188 	DwtDropTarget.__dropEvent.doIt = true;
189 	DwtDropTarget.__dropEvent.dndProxy = dndProxy;
190 	this._evtMgr.notifyListeners(DwtDropTarget.__DROP_LISTENER, DwtDropTarget.__dropEvent);
191 	return DwtDropTarget.__dropEvent.doIt;
192 }
193 
194 /** @private */
195 DwtDropTarget.prototype._dragLeave =
196 function() {
197 	DwtDropTarget.__dropEvent.action = DwtDropEvent.DRAG_LEAVE;
198 	this._evtMgr.notifyListeners(DwtDropTarget.__DROP_LISTENER, DwtDropTarget.__dropEvent);
199 }
200 
201 /** @private */
202 DwtDropTarget.prototype._dragOpChanged =
203 function(newOperation) {
204 	DwtDropTarget.__dropEvent.operation = newOperation;
205 	DwtDropTarget.__dropEvent.action = DwtDropEvent.DRAG_OP_CHANGED;
206 	this._evtMgr.notifyListeners(DwtDropTarget.__DROP_LISTENER, DwtDropTarget.__dropEvent);
207 	return DwtDropTarget.__dropEvent.doIt;
208 };
209 
210 /** @private */
211 DwtDropTarget.prototype._drop =
212 function(srcData, ev) {
213 	DwtDropTarget.__dropEvent.action = DwtDropEvent.DRAG_DROP;
214 	DwtDropTarget.__dropEvent.srcData = srcData;
215 	DwtDropTarget.__dropEvent.uiEvent = ev;
216 	this._evtMgr.notifyListeners(DwtDropTarget.__DROP_LISTENER, DwtDropTarget.__dropEvent);
217 	return DwtDropTarget.__dropEvent.doIt;
218 };
219 
220 
221 // Private methods
222 
223 /**@private*/
224 DwtDropTarget.prototype.__checkTarget =
225 function(item) {
226 	if (this._types) {
227 		for (var i in this._types) {
228 			var ctor;
229 			if (this._types[i]) {
230 				ctor = this._types[i];
231 			} else {
232 				ctor = this._types[i] = eval(i);
233 			}
234 			if (ctor && (typeof ctor == "function") && (item instanceof ctor)) {
235 				return true;
236 			}
237 		}
238 		return false;
239 	}
240 };
241