Created
October 31, 2014 17:38
-
-
Save nicovalencia/4f2787ad4c9b95b75bf2 to your computer and use it in GitHub Desktop.
injector angular
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function createInjector(modulesToLoad) { | |
var INSTANTIATING = {}, | |
providerSuffix = 'Provider', | |
path = [], | |
loadedModules = new HashMap(), | |
providerCache = { | |
$provide: { | |
provider: supportObject(provider), | |
factory: supportObject(factory), | |
service: supportObject(service), | |
value: supportObject(value), | |
constant: supportObject(constant), | |
decorator: decorator | |
} | |
}, | |
providerInjector = (providerCache.$injector = | |
createInternalInjector(providerCache, function() { | |
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); | |
})), | |
instanceCache = {}, | |
instanceInjector = (instanceCache.$injector = | |
createInternalInjector(instanceCache, function(servicename) { | |
var provider = providerInjector.get(servicename + providerSuffix); | |
return instanceInjector.invoke(provider.$get, provider); | |
})); | |
forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); | |
return instanceInjector; | |
//////////////////////////////////// | |
// $provider | |
//////////////////////////////////// | |
function supportObject(delegate) { | |
return function(key, value) { | |
if (isObject(key)) { | |
forEach(key, reverseParams(delegate)); | |
} else { | |
return delegate(key, value); | |
} | |
}; | |
} | |
function provider(name, provider_) { | |
assertNotHasOwnProperty(name, 'service'); | |
if (isFunction(provider_) || isArray(provider_)) { | |
provider_ = providerInjector.instantiate(provider_); | |
} | |
if (!provider_.$get) { | |
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); | |
} | |
return providerCache[name + providerSuffix] = provider_; | |
} | |
function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } | |
function service(name, constructor) { | |
return factory(name, ['$injector', function($injector) { | |
return $injector.instantiate(constructor); | |
}]); | |
} | |
function value(name, val) { return factory(name, valueFn(val)); } | |
function constant(name, value) { | |
assertNotHasOwnProperty(name, 'constant'); | |
providerCache[name] = value; | |
instanceCache[name] = value; | |
} | |
function decorator(serviceName, decorFn) { | |
var origProvider = providerInjector.get(serviceName + providerSuffix), | |
orig$get = origProvider.$get; | |
origProvider.$get = function() { | |
var origInstance = instanceInjector.invoke(orig$get, origProvider); | |
return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); | |
}; | |
} | |
//////////////////////////////////// | |
// Module Loading | |
//////////////////////////////////// | |
function loadModules(modulesToLoad){ | |
var runBlocks = [], moduleFn, invokeQueue, i, ii; | |
forEach(modulesToLoad, function(module) { | |
if (loadedModules.get(module)) return; | |
loadedModules.put(module, true); | |
try { | |
if (isString(module)) { | |
moduleFn = angularModule(module); | |
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); | |
for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { | |
var invokeArgs = invokeQueue[i], | |
provider = providerInjector.get(invokeArgs[0]); | |
provider[invokeArgs[1]].apply(provider, invokeArgs[2]); | |
} | |
} else if (isFunction(module)) { | |
runBlocks.push(providerInjector.invoke(module)); | |
} else if (isArray(module)) { | |
runBlocks.push(providerInjector.invoke(module)); | |
} else { | |
assertArgFn(module, 'module'); | |
} | |
} catch (e) { | |
if (isArray(module)) { | |
module = module[module.length - 1]; | |
} | |
if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { | |
// Safari & FF's stack traces don't contain error.message content | |
// unlike those of Chrome and IE | |
// So if stack doesn't contain message, we create a new string that contains both. | |
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. | |
/* jshint -W022 */ | |
e = e.message + '\n' + e.stack; | |
} | |
throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", | |
module, e.stack || e.message || e); | |
} | |
}); | |
return runBlocks; | |
} | |
//////////////////////////////////// | |
// internal Injector | |
//////////////////////////////////// | |
function createInternalInjector(cache, factory) { | |
function getService(serviceName) { | |
if (cache.hasOwnProperty(serviceName)) { | |
if (cache[serviceName] === INSTANTIATING) { | |
throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- ')); | |
} | |
return cache[serviceName]; | |
} else { | |
try { | |
path.unshift(serviceName); | |
cache[serviceName] = INSTANTIATING; | |
return cache[serviceName] = factory(serviceName); | |
} catch (err) { | |
if (cache[serviceName] === INSTANTIATING) { | |
delete cache[serviceName]; | |
} | |
throw err; | |
} finally { | |
path.shift(); | |
} | |
} | |
} | |
function invoke(fn, self, locals){ | |
var args = [], | |
$inject = annotate(fn), | |
length, i, | |
key; | |
for(i = 0, length = $inject.length; i < length; i++) { | |
key = $inject[i]; | |
if (typeof key !== 'string') { | |
throw $injectorMinErr('itkn', | |
'Incorrect injection token! Expected service name as string, got {0}', key); | |
} | |
args.push( | |
locals && locals.hasOwnProperty(key) | |
? locals[key] | |
: getService(key) | |
); | |
} | |
if (!fn.$inject) { | |
// this means that we must be an array. | |
fn = fn[length]; | |
} | |
// http://jsperf.com/angularjs-invoke-apply-vs-switch | |
// #5388 | |
return fn.apply(self, args); | |
} | |
function instantiate(Type, locals) { | |
var Constructor = function() {}, | |
instance, returnedValue; | |
// Check if Type is annotated and use just the given function at n-1 as parameter | |
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); | |
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; | |
instance = new Constructor(); | |
returnedValue = invoke(Type, instance, locals); | |
return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; | |
} | |
return { | |
invoke: invoke, | |
instantiate: instantiate, | |
get: getService, | |
annotate: annotate, | |
has: function(name) { | |
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); | |
} | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment