-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathFunction.js
More file actions
325 lines (300 loc) · 13.6 KB
/
Function.js
File metadata and controls
325 lines (300 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
#if COPYRIGHT
//------------------------------------------------------------------------------
// <copyright file="Function.js" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
#endif
Function.__typeName = 'Function';
Function.__class = true;
Function.createCallback = function(method, context) {
/// <summary>
/// Creates a callback function that retains the parameter initially used during its creation.
/// The callback is used without parameter but this will call the actual method with the parameter.
/// This is especially useful when setting up a handler for a DOM event that must retain a parameter
/// despite the DOM event handler needing to be a function with the event object as the only parameter.
/// In this case, the function will be called with the event as the first parameter and the context
/// as the second.
/// If the callback is called with an arbitrary list of parameters, the context is appended.
/// </summary>
/// <param name="method" type="Function">The function for which the callback is created.</param>
/// <param name="context" mayBeNull="true">The parameter for the function.</param>
/// <returns type="Function">The callback function.</returns>
// The method still makes sense for null context, but not if the context is omitted altogether
// (omitted context makes the callback equivalent to the method itself, with one more level of indirection).
return function() {
var l = arguments.length;
if (l > 0) {
// arguments is not a real array, need to build a real one from it so we can add
var args = [];
for (var i = 0; i < l; i++) {
args[i] = arguments[i];
}
args[l] = context;
return method.apply(this, args);
}
return method.call(this, context);
}
}
Function.createDelegate = function(instance, method) {
/// <summary>
/// Creates a delegate function that retains the context from its creation
/// (i.e. what 'this' means from within its scope).
/// This is especially useful when setting up an event handler to point to an object method
/// that needs to use the 'this' pointer from within its scope.
/// </summary>
/// <param name="instance" mayBeNull="true">
/// The object instance that will be the context for the function (i.e. what 'this' means from within its scope).
/// </param>
/// <param name="method" type="Function">The function from which the delegate is created.</param>
/// <returns type="Function">The delegate function.</returns>
// The method still makes some sense with a null instance, in the same way that createCallback still
// makes sense with a null context.
return function() {
return method.apply(instance, arguments);
}
}
Function.emptyFunction = Function.emptyMethod = function() {
/// <summary>A function that does nothing.</summary>
/// <validationOptions enabled="false"/>
}
Function.validateParameters = function(parameters, expectedParameters, validateParameterCount) {
/// <summary locid="M:J#Function.validateParameters">
/// Validates the parameters to a method are as expected.
/// </summary>
/// <param name="parameters"/>
/// <param name="expectedParameters"/>
/// <param name="validateParameterCount" type="Boolean" optional="true">True if extra parameters are prohibited, false if they should be ignored. The default is true.</param>
/// <returns type="Error" mayBeNull="true"/>
/// <example>
/// function foo(anyParam, stringParam, anyArrayParam, stringArrayParam,
/// interfaceParam, optionalStringParam) {
/// #if DEBUG
/// var e = Function.validateParameters(arguments, [
/// { name: "anyParam" },
/// { name: "mayBeNullParam", mayBeNull: true },
/// { name: "stringParam", type: String },
/// { name: "floatParam", type: Number },
/// { name: "intParam", type: Number, integer: true },
/// { name: "domParam", domElement: true },
/// { name: "anyArrayParam", type: Array },
/// { name: "mayBeNullArrayParam", type: Array, elementMayBeNull: true },
/// { name: "stringArrayParam", type: Array, elementType: String },
/// { name: "intArrayParam", type: Array, elementType: Number, elementInteger: true },
/// { name: "domElementArrayParam", type: Array, elementDomElement: true },
/// { name: "interfaceParam", type: Sys.IFoo }
/// { name: "optionalStringParam", type: String, optional: true }
/// { name: "stringParamArray", type: String, parameterArray: true }
/// { name: "mayBeNullParamArray", parameterArray: true, mayBeNull: true }
/// ]);
/// if (e) throw e;
/// #endif
/// }
/// </example>
// type info omitted from parameters so that the parameter-parameters are not enumerated and each entry validated
return Function._validateParams(parameters, expectedParameters, validateParameterCount);
}
Function._validateParams = function(params, expectedParams, validateParameterCount) {
// *DO NOT* triple-slash comment those. The double-slashes here are on purpose.
// We don't need to document private functions and those will induce infinite loops
// if the preprocessor generates validation code for these.
// <summary>
// Validates the parameters to a method.
// </summary>
// <example>
// function foo(anyParam, stringParam, anyArrayParam, stringArrayParam,
// interfaceParam, optionalStringParam) {
// #if DEBUG
// var e = Function._validateParams(arguments, [
// { name: "anyParam" },
// { name: "mayBeNullParam", mayBeNull: true },
// { name: "stringParam", type: String },
// { name: "floatParam", type: Number },
// { name: "intParam", type: Number, integer: true },
// { name: "domParam", domElement: true },
// { name: "anyArrayParam", type: Array },
// { name: "mayBeNullArrayParam", type: Array, elementMayBeNull: true },
// { name: "stringArrayParam", type: Array, elementType: String },
// { name: "intArrayParam", type: Array, elementType: Number, elementInteger: true },
// { name: "domElementArrayParam", type: Array, elementDomElement: true },
// { name: "interfaceParam", type: Sys.IFoo }
// { name: "optionalStringParam", type: String, optional: true }
// { name: "stringParamArray", type: String, parameterArray: true }
// { name: "mayBeNullParamArray", parameterArray: true, mayBeNull: true }
// ]);
// if (e) throw e;
// #endif
// }
// </example>
// <param name="params" type="Array">Array of parameter values passed to the method.</param>
// <param name="expectedParams" type="Array" optional="true">Array of JSON objects describing the expected parameters.</param>
var e, expectedLength = expectedParams.length;
validateParameterCount = validateParameterCount || (typeof(validateParameterCount) === "undefined");
e = Function._validateParameterCount(params, expectedParams, validateParameterCount);
if (e) {
e.popStackFrame();
return e;
}
for (var i = 0, l = params.length; i < l; i++) {
// If there are more params than expectedParams, then the last expectedParam
// must be a paramArray. Use the last expectedParam to validate the remaining
// params.
var expectedParam = expectedParams[Math.min(i, expectedLength - 1)],
paramName = expectedParam.name;
if (expectedParam.parameterArray) {
// Append index of parameter in parameterArray
paramName += "[" + (i - expectedLength + 1) + "]";
}
else if (!validateParameterCount && (i >= expectedLength)) {
// there were more params than expected params.
// count validation is disabled, and the last expected param
// is not a parameter array. Just ignore the rest of the parameters.
break;
}
e = Function._validateParameter(params[i], expectedParam, paramName);
if (e) {
e.popStackFrame();
return e;
}
}
return null;
}
Function._validateParameterCount = function(params, expectedParams, validateParameterCount) {
var i, error,
expectedLen = expectedParams.length,
actualLen = params.length;
if (actualLen < expectedLen) {
// this might be ok if some parameters are optional or if theres a parameter array
var minParams = expectedLen;
for (i = 0; i < expectedLen; i++) {
var param = expectedParams[i];
if (param.optional || param.parameterArray) {
minParams--;
}
}
if (actualLen < minParams) {
error = true;
}
}
else if (validateParameterCount && (actualLen > expectedLen)) {
// this might be ok if a parameter is a parameterArray
error = true;
for (i = 0; i < expectedLen; i++) {
if (expectedParams[i].parameterArray) {
error = false; // infinite parameters is ok
break;
}
}
}
if (error) {
var e = Error.parameterCount();
e.popStackFrame();
return e;
}
return null;
}
Function._validateParameter = function(param, expectedParam, paramName) {
var e,
expectedType = expectedParam.type,
expectedInteger = !!expectedParam.integer,
expectedDomElement = !!expectedParam.domElement,
mayBeNull = !!expectedParam.mayBeNull;
e = Function._validateParameterType(param, expectedType, expectedInteger, expectedDomElement, mayBeNull, paramName);
if (e) {
e.popStackFrame();
return e;
}
// If parameter is an array, and not undefined or null, validate the type of its elements
var expectedElementType = expectedParam.elementType,
elementMayBeNull = !!expectedParam.elementMayBeNull;
if (expectedType === Array && typeof(param) !== "undefined" && param !== null &&
(expectedElementType || !elementMayBeNull)) {
var expectedElementInteger = !!expectedParam.elementInteger,
expectedElementDomElement = !!expectedParam.elementDomElement;
for (var i=0; i < param.length; i++) {
var elem = param[i];
e = Function._validateParameterType(elem, expectedElementType,
expectedElementInteger, expectedElementDomElement, elementMayBeNull,
paramName + "[" + i + "]");
if (e) {
e.popStackFrame();
return e;
}
}
}
return null;
}
Function._validateParameterType = function(param, expectedType, expectedInteger, expectedDomElement, mayBeNull, paramName) {
var e, i;
if (typeof(param) === "undefined") {
if (mayBeNull) {
return null;
}
else {
e = Error.argumentUndefined(paramName);
e.popStackFrame();
return e;
}
}
if (param === null) {
if (mayBeNull) {
return null;
}
else {
e = Error.argumentNull(paramName);
e.popStackFrame();
return e;
}
}
if (expectedType && expectedType.__enum) {
if (typeof(param) !== 'number') {
e = Error.argumentType(paramName, Object.getType(param), expectedType);
e.popStackFrame();
return e;
}
if ((param % 1) === 0) {
var values = expectedType.prototype;
if (!expectedType.__flags || (param === 0)) {
for (i in values) {
if (values[i] === param) return null;
}
}
else {
var v = param;
for (i in values) {
var vali = values[i];
if (vali === 0) continue;
if ((vali & param) === vali) {
v -= vali;
}
if (v === 0) return null;
}
}
}
e = Error.argumentOutOfRange(paramName, param, String.format(Sys.Res.enumInvalidValue, param, expectedType.getName()));
e.popStackFrame();
return e;
}
// Text nodes are not considered DOM elements here.
if (expectedDomElement && (!Sys._isDomElement(param) || (param.nodeType === 3))) {
e = Error.argument(paramName, Sys.Res.argumentDomElement);
e.popStackFrame();
return e;
}
// If there is no expected type, any type is allowed.
if (expectedType && !Sys._isInstanceOfType(expectedType, param)) {
e = Error.argumentType(paramName, Object.getType(param), expectedType);
e.popStackFrame();
return e;
}
if (expectedType === Number && expectedInteger) {
// Modulo operator is 5x faster than Math.round().
// Modulo returns Number.NaN for Number.NaN, Number.POSITIVE_INFINITY, and Number.NEGATIVE_INFINITY.
if ((param % 1) !== 0) {
e = Error.argumentOutOfRange(paramName, param, Sys.Res.argumentInteger);
e.popStackFrame();
return e;
}
}
return null;
}