#if COPYRIGHT //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ #endif Function.__typeName = 'Function'; Function.__class = true; Function.createCallback = function(method, context) { /// /// 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. /// /// The function for which the callback is created. /// The parameter for the function. /// The callback function. // 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) { /// /// 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. /// /// /// The object instance that will be the context for the function (i.e. what 'this' means from within its scope). /// /// The function from which the delegate is created. /// The delegate function. // 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() { /// A function that does nothing. /// } Function.validateParameters = function(parameters, expectedParameters, validateParameterCount) { /// /// Validates the parameters to a method are as expected. /// /// /// /// True if extra parameters are prohibited, false if they should be ignored. The default is true. /// /// /// 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 /// } /// // 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. // // Validates the parameters to a method. // // // 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 // } // // Array of parameter values passed to the method. // Array of JSON objects describing the expected parameters. 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; }