1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2009, 2010, 2011, 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, 2011, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Creates a callback which consists of at least a function reference, and possibly also
 26  * an object to call it from.
 27  * @constructor
 28  * @class
 29  * This class represents a callback function which can be called standalone, or from a
 30  * given object. What the callback takes as arguments and what it returns are left to the
 31  * client.
 32  *
 33  * @author Conrad Damon
 34  * 
 35  * @param {object}	obj		the object to call the function from
 36  * @param {function}	func	the callback function
 37  * @param {array}	args   the default arguments
 38  * 
 39  */
 40 AjxCallback = function(obj, func, args) {
 41 	if (arguments.length == 0) return;
 42 
 43     if (typeof arguments[0] == "function") {
 44         this.obj = null;
 45         this.func = arguments[0];
 46         this.args = arguments[1];
 47     }
 48     else {
 49         this.obj = obj;
 50         this.func = func;
 51         this.args = args;
 52     }
 53 };
 54 
 55 AjxCallback.prototype.isAjxCallback = true;
 56 AjxCallback.prototype.toString = function() { return "AjxCallback"; }
 57 
 58 AjxCallback.NOP = new AjxCallback(function(){});
 59 
 60 /**
 61  * Runs the callback function, from within the object if there is one. The
 62  * called function passed arguments are the concatenation of the argument
 63  * array passed to this object's constructor and the argument array passed
 64  * to the <code>run</code> method. Whatever the called function returns is
 65  * returned to the caller.
 66  *
 67  * @param {array}	[arg1..argN]	the first argument which will be appended to the argument
 68  *				array passed to this object's constructor. Any number of
 69  *				arguments may be passed to the <code>run</code> method.
 70  */
 71 AjxCallback.prototype.run =
 72 function(/* arg1 ... argN */) {
 73 	// combine original args with new ones
 74 	var args = [];
 75 
 76 	// sometimes we want to pass a null or false argument, so simply
 77 	// checking for if (this.args) won't do.
 78 	if (typeof this.args != "undefined") {
 79 		if (this.args!==null && this.args instanceof Array) { // IE mysteriously screws up the instanceof test if this.args is null
 80 			// NOTE: We must NOT use this.args directly if this method's
 81 			//       params are gonna be pushed onto the array because it
 82 			//       will change the original args!
 83 			args = arguments.length > 0 ? args.concat(this.args) : this.args;
 84 		} else {
 85 			args.push(this.args);
 86 		}
 87 	}
 88 
 89 	for (var i = 0; i < arguments.length; ++i) {
 90 		args.push(arguments[i]);
 91 	}
 92 
 93 	// invoke function
 94 	if (this.func) {
 95         return this.func.apply(this.obj || window, args);
 96 	}
 97 };
 98 
 99 /**
100  * This version of {@link AjxCallback.run} is here for {@link AjxDispatcher}, because it has a <code>run()</code>
101  * method in which it marshals arguments into an array. That leads to a problem
102  * in which the arguments are marshalled twice, so that by the time AjxDispatcher
103  * calls <code>callback.run(args)</code>, the args have already been collected into an array.
104  * Then when the function is invoked, it gets passed an actual array instead of the
105  * intended arg list. Calling <code>callback.run.apply(callback, args)</code> works on Firefox,
106  * but IE throws the error "Object expected", so we do this instead.
107  *
108  * @param	{array}	argList	 an array of arguments and treats them as an argument list, instead of as a single argument
109  * 
110  * @private
111  */
112 AjxCallback.prototype.run1 =
113 function(argList) {
114 	// combine original args with new ones
115 	var args = [];
116 
117 	// sometimes we want to pass a null or false argument, so simply
118 	// checking for if (this.args) won't do.
119 	if (typeof this.args != "undefined") {
120 		if (this.args!==null && this.args instanceof Array) {
121 			// NOTE: We must NOT use this.args directly if this method's
122 			//       params are gonna be pushed onto the array because it
123 			//       will change the original args!
124 			args = arguments.length > 0 ? args.concat(this.args) : this.args;
125 		} else {
126 			args.push(this.args);
127 		}
128 	}
129 
130 	if (argList && argList.length) {
131 		for (var i = 0; i < argList.length; ++i) {
132 			args.push(argList[i]);
133 		}
134 	}
135 
136 	// invoke function
137 	if (this.func) {
138 		return this.func.apply(this.obj || window, args);
139 	}
140 };
141 
142 /**
143  * This method returns a plain function that will call your supplied "func" in the context
144  * of "obj" and pass to it, in this order, any additional arguments that you
145  * pass to <code>simpleClosure</code> and the arguments that were passed to it at the call
146  * time.
147  *
148  * <p>
149  * An example should do:
150  *
151  * <pre>
152  *   div.onclick = AjxCallback.simpleClosure(this.handler, this, "some data");
153  *   ...
154  *   this.handler = function(data, event) {
155  *      // event will be passed for DOM2 compliant browsers
156  *      // and data is "some data"
157  *   };
158  * </pre>
159  * 
160  * @param	{function}	func		the function
161  * @param	{object}	obj			the object to call the function from
162  * @param	{array}	[arg1...argN]		any number of arguments
163  */
164 AjxCallback.simpleClosure = function(func, obj) {
165 	var args = [];
166 	for (var i = 2; i < arguments.length; ++i)
167 		args.push(arguments[i]);
168 	return function() {
169 		var args2 = [];
170 		for (var i = 0; i < arguments.length; ++i)
171 			args2.push(arguments[i]);
172 		return func.apply(obj || this, args.concat(args2));
173 	};
174 };
175 
176 AjxCallback.returnFalse = function() { return false; };
177 
178 AjxCallback.isNull = function(x) { return x == null; };
179