!function (name, context, definition) { // Export ArgueJS as module, or __ as global, if not using module loaders. if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') { module.exports = definition(); } else if (typeof define === 'function' && typeof define.amd === 'object') { define(function () { return definition(); }); } else { var oldValue = context[name]; context[name] = definition(); context[name].noConflict = function(){ context[name] = oldValue; return this; } } }('__', this, function () { var isArray = Array.isArray || function(obj){ // Util function to check if an object is actually an Array. return toString.call(obj) == '[object Array]'; } var __ = function(signature, upperArguments) { // The ArgueJS constructor method. var input; if (!__.belongs(signature, Object)) // If 'signature' is not defined or is not an object, goes away. throw new Error("parameter 'signature' waiting for Object argument but received " + __.typeof(signature)); if (upperArguments !== undefined && !__.belongs(upperArguments, Array) && !__.belongs(upperArguments, 'Arguments')) // If 'upperArguments' is defined it must be a valid arguments list throw new Error("parameter 'upperArguments' waiting for Array or Arguments argument but received " + __.typeof(upperArguments)); try{ // Assumes upperArguments as the passed arguments or try to infer from the caller function. input = upperArguments || arguments.callee.caller.arguments; } catch(e){ throw new Error('It is not possible to infer arguments in strict mode. See http://github.com/zvictor/ArgueJs#propagating-arguments for alternatives.'); } // Makes a copy of the input, transforming Arguments (if is the case) into Array. // See more at https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array#Array_generic_methods input = Array.prototype.slice.call( input ); var paramSum = 0; for (var name in signature) { if (!signature.hasOwnProperty(name)) continue; // We just count how many parameters we have to evaluate... paramSum++; } if(input.length > paramSum) // Ops, someone really likes to talk here! throw new Error("Too many arguments"); // As we can have optional arguments, // there are many ways to consider the same call to a function. // A call (1, 3) to range([Number], Number, [Number]) // could be validated as (1, 3), (1, 3, undefined) or (undefined, 1, 3). // In order to determine which one has the best fit to the given signature, // we need to generate all of them. var expansion = [input]; // Here we start to expand the passed arguments. // We start with just the given input... var pivotIndex = -1; for (var name in signature) { if (!signature.hasOwnProperty(name)) continue; // ... and for each parameter... if(!isNaN(parseFloat(name)) && isFinite(name)) throw new Error("NameError: a parameter name can not be numeric"); pivotIndex++; var optional = isArray(signature[name]); var type = (optional) ? signature[name][0] : signature[name]; if(type === undefined) throw new TypeError("unsupported parameter type "+type); var copy = expansion.slice(0); // (we make a copy here to avoid infinity loop) for(var i=0; i<copy.length; i++){ var args = copy[i]; var value = args[pivotIndex]; // ... we evaluate the respective argument value of each possible argument list. if (type !== null && !__.belongs(value, type)){ // If the argument value does not pass through the type checking, // the argument list is not valid for the given signature... // ... and we delete the current argument list, entirely! // Note that in our library, the Null param allows ANY type for `value`, expansion.splice( expansion.indexOf(args), 1); if(!optional && !expansion.length) // If no more arguments list remains, the input is not compatible. Cheeky arguments, go play with the kids! throw new Error("parameter '" + name + "' waiting for " + (type.name || type) + " argument but received " + __.typeof(value)); } args = copy[i].slice(0); if(value !== undefined) // In case argument is undefined we do not pass it to the next param args.splice(pivotIndex, 0, undefined); if(optional && args.length <= paramSum) // In case the parameter is optional, // it means that the same argument list we are iterating now // would also be valid if we added a "undefined" value at this position, // passing the current value to the next parameter. expansion.push(args); } } if(!expansion.length){ // This Error happens when all the REQUIRED parameters are satisfied, // but is not possible to satisfy the type checking of any of the OPTIONAL parameters. var plainSignature = []; for (var key in signature) { if (!signature.hasOwnProperty(key)) continue; if(__.belongs(signature[key], Array)) plainSignature.push( "["+signature[key][0].name+"]" ); else plainSignature.push( signature[key].name ); } var plainInput= []; for (var i=0; i< input.length; i++) plainInput.push( __.typeof(input[i]) ); throw new Error("Incompatible type signature. Expecting ( "+(plainSignature.join(", "))+" ), given ( "+(plainInput.join(", "))+" )."); } // Now that we finished the expansion, // which of the arguments list are we supposed to choose? input = expansion[expansion.length-1]; // This way we are putting the required arguments in the leftmost valid side var result = {'doc':doc}; // Reference for the API doc generator. var paramIndex = 0; // Now, whith the input extended, // is time to pass through the arguments and parameters again... for (var name in signature) { if (!signature.hasOwnProperty(name)) continue; var value = input[paramIndex]; var definition = signature[name]; var optional = isArray(definition); var type = (optional) ? definition[0] : definition; if (optional && !__.belongs(value, type)) // ... replacing undefined optional values by their default values... value = definition[1]; paramIndex++; // and generating the final object. result[name] = value; } return result; }; var specialTypes = [ // Special types definition: function Arguments(){ // An Array-like object corresponding to the arguments passed to a function. // The arguments object is a local variable available within all functions; // `arguments` as a property of `Function` can no longer be used. throw new Error("It is not possible to create a new Arguments instance"); //return Array.apply(this, arguments); }, function global(){ // In strict mode `this` is never the global, // but also in strict mode `eval` operates in a separate context in which this is always the global. // In non-strict mode `this` is the current context. If there is no current context, it assumes the global. // An anonymous function has no context and hence in non-strict mode assumes the global. // See more at http://stackoverflow.com/questions/3277182/599991/how-to-get-the-global-object-in-javascript return Function('return this')(); } ]; __.type = {}; // Save a reference for each special type for(var i=0; i<specialTypes.length; i++) __.type[specialTypes[i].name] = specialTypes[i]; __.getType = function(obj) { // Util function that gives us the String representation of the type of the given object. if(obj === undefined || obj === null) return obj; if (obj === __.type.global()) // Host objects are browser-created objects that are not specified by the ES5 standard. // All DOM elements and global functions are host objects. // ES5 declines to specify a return value for typeof when applied to host objects, // neither does it suggest a value for the [[Class]] property of host objects. // The upshot is that cross-browser type-checking of host objects is generally not reliable. // See more at http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ return __.type.global; var name = ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1]; if (['Arguments'].indexOf( name ) != -1) return __.type[name]; return eval( name ); } __.typeof = function(obj) { // Util function that gives us the String representation of the type of the given object. var type = __.getType(obj); var toString = ({}).toString.call(type).match(/\s([a-z|A-Z]+)/)[1]; if(type === undefined) //workaround for the PhantomJS to avoid DOMWindow casting. //see more at http://stackoverflow.com/q/14218670/599991 return "Undefined"; else if(type === null) return "Null"; return (type && type.hasOwnProperty('name')) ? type.name : toString; } __.belongs = function(value, type) { // Util function that determines if a given instance belongs to the given type class. // code highly inspired on UnderscoreJS. var result = false; if(type === null || type === undefined) return value === type; else if (type == Function && typeof (/./) !== 'function') // Hack needed, as seen on UnderscoreJS' `isFunction`, reasons unknown result = typeof value === 'function'; else if (type == Boolean) result = value === true || value === false || __.getType(value) == Boolean; else if (type == Array) result = isArray(value); else if (type in __.type) result = __.typeof(value) === type; else if (type && type.hasOwnProperty('name') && ['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Object'].indexOf( type.name ) != -1) result = __.getType(value) === type; else try{ result = value instanceof type; } catch(e){ throw new TypeError("unsupported type "+type); } return result; } __.noConflict = function() { // Relinquish ArgueJS's control of the __ variable. throw new Error("noConflit is not implemented for module loaders"); } var doc = function() { // Coming soon. Here you will be able to generate JSDoc, JavaDoc, etc, for your functions. throw Error('Not implemented yet'); }; return __; });