raptor-amd
Version:
Module to support AMD with RaptorJS 3 (CommonJS)
374 lines (311 loc) • 11.6 kB
JavaScript
/* jshint strict:false */
require('raptor-polyfill/array/isArray');
var win = typeof window === 'undefined' ? exports : window;
var CLIENT_PATH = 'raptor-modules/client';
var raptorModulesClient = win.$rmod || require(CLIENT_PATH);
var defs = {}; //Registered module definitions are added to this object
var isArray = Array.isArray; //Helper function to check if an object is an Array object
var extend = require('raptor-util/extend');
var forEach = require('raptor-util/forEach');
var forEachEntry = require('raptor-util/forEachEntry');
var inherit = require('raptor-util/inherit');
var arrayFromArguments = require('raptor-util/arrayFromArguments');
var createError = require('raptor-util/createError');
function isString(s) {
return typeof s == 'string';
}
function isFunction(f) {
return typeof f == 'function';
}
function registerCommonJSModule(id) {
var firstSlash = id.indexOf('/');
var parentModule;
var subPath;
if (firstSlash === -1) {
parentModule = id;
subPath = '';
} else {
parentModule = id.substring(0, firstSlash);
subPath = id.substring(firstSlash);
}
var basePath = '/' + parentModule + '@AMD';
var realPath = basePath + subPath;
raptorModulesClient.dep('', parentModule, 'AMD');
raptorModulesClient.def(realPath, function(require, exports, module, __filename, __dirname) {
var def = defs[id];
var factory = def.factory;
var postCreate = def.postCreate;
var dependencies = def.dependencies;
var superclass = def.superclass;
function gather(dependencies) {
var out = [];
for (var i=0, len=dependencies.length; i<len; i++) {
var d = dependencies[i];
if (d === 'require') {
d = require;
} else if (d === 'exports') {
d = exports;
} else if (d === 'module') {
d = module;
} else if (d === 'super') {
d = typeof superclass === 'string' ? require(superclass) : superclass;
if (d) {
d = d.prototype;
}
} else if (d === 'raptor') {
d = win.raptor;
} else {
d = require(d);
}
out.push(d);
}
return out;
}
if (dependencies) {
dependencies = gather(dependencies);
} else{
dependencies = [];
}
var defaultDependencies = def.legacy ? [win.raptor, exports, module] : [require, exports, module];
var instance = isFunction(factory) ? factory.apply(this, dependencies.concat(defaultDependencies)) : factory;
var o;
if (postCreate) {
forEach(postCreate, function(postCreateFunc) {
if ((o = postCreateFunc(instance, require, gather))) { //Check if the postCreate function produced a new function...
instance = o; //if so, use that instead
}
});
}
if (instance === undefined) {
instance = module.exports;
} else {
module.exports = instance;
}
def.instance = instance;
});
}
function getOrCreateDef(id) { //Returns the module definition entry for the given ID or creates one of one does not exist
var def = defs[id];
if (!def) {
def = defs[id] = {postCreate: []};
registerCommonJSModule(id);
}
return def;
}
function _makeClass(clazz, superclass, name) {
if (!isFunction(clazz)) {
var o = clazz;
clazz = o.init || function() {};
extend(clazz.prototype, o);
}
if (superclass) {
inherit(clazz, superclass, true);
clazz.superclass = superclass.prototype;
}
clazz.getName = clazz.getName || function() {
return name;
};
var proto = clazz.prototype;
proto.constructor = clazz;
proto.getClass = function() {
return clazz;
};
return clazz;
}
function _enumValueOrdinal() {
return this._ordinal;
}
function _enumValueName() {
return this._name;
}
function _enumValueCompareTo(other) {
return this._ordinal - other._ordinal;
}
/**
* This functions takes in the arguments to define, define.Class and define.extend
* calls and does the hard work of handling optional arguments.
*
* @param {arguments} args The arguments object for the define, define.Class or define.extend
* @param {Boolean} isClass Should only be true if this is define.Class call
* @param {Boolean} isExtend Should only be true if this is a define.extend call
* @return {Object|undefined} If no id is provided then the anonymous object is immediately built and returned. Otherwise, undefined is returned.
* @private
*/
function _define(args, isExtend, isClass, isEnum, isLegacy) {
var i=0;
var last = args.length-1;
var arg;
var id; //The object id (optional)
var superclass; //The superclass (optional, should only be allowed for define.Class but that is not enforced currently...less code)
var enumValues;
var dependencies = []; //The dependencies arguments... defaults to an empty array
var postCreate; //A function that should be invoked after the object is created for the first time...Used to handle inheritance and to apply an extension
var factory; //The factory function or object definition (required, always the last argument)
/*
Loop through the arguments to sort things out...
*/
for (; i<last; i++) {
arg = args[i];
if (isString(arg)) { //We found a string argument
if (id) { //If we already found an "id" then this string must be the superclass
superclass = arg;
} else { //Otherwise it is the module ID
id = arg;
}
} else if (isArray(arg)) { //We found an array...The argument must be the array of dependency IDs
dependencies = arg;
} else if (isEnum) {
enumValues = arg;
} else {
superclass = arg.superclass;
}
}
factory = args[last]; //The factory function is always the last argument
if (isExtend) { //If define.extend then we need to register a "post create" function to modify the target module
var extendDependencies = dependencies;
var extendFactory = factory;
dependencies = null;
factory = null;
postCreate = function(target, require, gather) {
if (isFunction(extendFactory)) {
extendFactory = extendFactory.apply(this, gather(extendDependencies).concat([require, target]));
}
if (extendFactory) {
extend(isFunction(target) ? target.prototype : target, extendFactory);
}
};
} else {
if (isClass || superclass) {
postCreate = function(instance, require) {
superclass = isString(superclass) ? require(superclass) : superclass;
return _makeClass(instance, superclass, id);
};
} else if (isEnum) {
if (isArray(factory)) {
enumValues = factory;
factory = null;
}
postCreate = function(EnumClass) {
if (EnumClass) {
if (typeof EnumClass == 'object') {
EnumClass = _makeClass(EnumClass, 0, id); // Convert the class object definition to
// a class constructor function
}
} else {
EnumClass = function() {};
}
var proto = EnumClass.prototype,
count = 0,
_addEnumValue = function(name, EnumCtor) {
return extend(
EnumClass[name] = new EnumCtor(),
{
_ordinal: count++,
_name: name
});
};
if (isArray(enumValues)) {
forEach(enumValues, function(name) {
_addEnumValue(name, EnumClass);
});
}
else if (enumValues) {
var EnumCtor = function() {};
EnumCtor.prototype = proto;
forEachEntry(enumValues, function(name, args) {
EnumClass.apply(_addEnumValue(name, EnumCtor), args || []);
});
}
EnumClass.valueOf = function(name) {
return EnumClass[name];
};
extend(proto, {
name : _enumValueName,
ordinal : _enumValueOrdinal,
compareTo : _enumValueCompareTo
});
if (proto.toString == Object.prototype.toString) {
proto.toString = _enumValueName;
}
return EnumClass;
};
}
}
if (!id) {
throw new Error('"id" is required');
}
var def = getOrCreateDef(id);
if (factory) {
def.factory = factory;
}
if (dependencies) {
def.dependencies = dependencies;
}
if (superclass) {
def.superclass = superclass;
}
def.legacy = isLegacy === 1;
if (postCreate) {
def.postCreate.push(postCreate);
var instance = def.instance;
if (instance) {
postCreate(instance);
}
}
}
function raptorDefine() {
_define(arguments);
}
raptorDefine.extend = function() {
return _define(arguments, 1);
};
raptorDefine.Class = function() {
return _define(arguments, 0, 1);
};
raptorDefine.Enum = function() {
return _define(arguments, 0, 0, 1);
};
function raptorRequire(id) {
return raptorModulesClient.require(id, '');
}
if (!win.raptorDefine) {
// always export raptorDefine function to global scope (this should not cause a conflict)
win.raptorDefine = raptorDefine;
win.raptorRequire = raptorRequire;
if (win.raptorNoConflict !== true) {
// We are not in no-conflict mode so expose define and require.
// Put AMD-style define and require functions in the global window scope.
win.define = raptorDefine;
win.require = raptorRequire;
}
raptorDefine.amd = {};
raptorDefine('raptor', {
inherit: inherit,
extend: extend,
forEach: forEach,
arrayFromArguments: arrayFromArguments,
forEachEntry: forEachEntry,
createError: createError
});
win.raptor = {
require: function(id) {
return raptorRequire(id.replace(/\./g, '/'));
},
define: function(id) {
id = id.replace(/\./g, '/');
_define(arguments, 0, 0, 0, 1 /*legacy*/);
},
defineClass: function(id) {
id = id.replace(/\./g, '/');
_define(arguments, 0, 1, 0, 1 /*legacy*/);
},
extend: function(id) {
if (typeof id === 'string') {
id = id.replace(/\./g, '/');
_define(arguments, 1, 0, 0, 1 /*legacy*/);
} else {
extend.apply(this, arguments);
}
}
};
}