diff --git a/karma.es5.conf.js b/karma.es5.conf.js index f626426..4722ad9 100644 --- a/karma.es5.conf.js +++ b/karma.es5.conf.js @@ -15,6 +15,7 @@ module.exports = function(config) { buildDir + '/*.es5.js', + 'test/*.es5.js', 'test/*.es5.spec.js' ], diff --git a/src/router-directive.es5.js b/src/router-directive.es5.js index d1ced55..1083395 100644 --- a/src/router-directive.es5.js +++ b/src/router-directive.es5.js @@ -7,11 +7,21 @@ angular.module('ngNewRouter', []) .factory('$router', routerFactory) .value('$routeParams', {}) .provider('$componentLoader', $componentLoaderProvider) - .factory('$$pipeline', pipelineFactory) + .provider('$pipeline', pipelineProvider) + .factory('$$pipeline', privatePipelineFactory) + .factory('$setupRoutersStep', setupRoutersStepFactory) + .factory('$initLocalsStep', initLocalsStepFactory) + .factory('$initControllersStep', initControllersStepFactory) + .factory('$runCanDeactivateHookStep', runCanDeactivateHookStepFactory) + .factory('$runCanActivateHookStep', runCanActivateHookStepFactory) + .factory('$loadTemplatesStep', loadTemplatesStepFactory) + .value('$activateStep', activateStepValue) .directive('ngViewport', ngViewportDirective) .directive('ngViewport', ngViewportFillContentDirective) .directive('ngLink', ngLinkDirective) - .directive('a', anchorLinkDirective); + .directive('a', anchorLinkDirective) + + /* @@ -333,48 +343,17 @@ function anchorLinkDirective($router) { } } - -function pipelineFactory($controller, $componentLoader, $q, $injector, $templateRequest) { - - function invoke(method, context, instruction) { - return $injector.invoke(method, context, { - $routeParams: instruction.params - }); - } - - var STEPS = [ - setupRouters, - initLocals, - initControllers, - runCanDeactivateHook, - runCanActivateHook, - loadTemplates, - activate - ]; - - return { - process: function(instruction) { - // make a copy - var steps = STEPS.slice(0); - - function processOne(result) { - if (steps.length === 0) { - return result; - } - var step = steps.shift(); - return $q.when(step(instruction)).then(processOne); - } - - return processOne(); - } - } - - - function setupRouters(instruction) { +function setupRoutersStepFactory() { + return function (instruction) { return instruction.router.makeDescendantRouters(instruction); } +} - function initLocals(instruction) { +/* + * $initLocalsStep + */ +function initLocalsStepFactory() { + return function initLocals(instruction) { return instruction.router.traverseInstruction(instruction, function(instruction) { return instruction.locals = { $router: instruction.router, @@ -382,8 +361,13 @@ function pipelineFactory($controller, $componentLoader, $q, $injector, $template }; }); } +} - function initControllers(instruction) { +/* + * $initControllersStep + */ +function initControllersStepFactory($controller, $componentLoader) { + return function initControllers(instruction) { return instruction.router.traverseInstruction(instruction, function(instruction) { var controllerName = $componentLoader.controllerName(instruction.component); var locals = instruction.locals; @@ -397,32 +381,90 @@ function pipelineFactory($controller, $componentLoader, $q, $injector, $template return instruction.controller = ctrl; }); } +} - function runCanDeactivateHook(instruction) { +function runCanDeactivateHookStepFactory() { + return function runCanDeactivateHook(instruction) { return instruction.router.canDeactivatePorts(instruction); + }; +} + +function runCanActivateHookStepFactory($injector) { + + function invoke(method, context, instruction) { + return $injector.invoke(method, context, { + $routeParams: instruction.params + }); } - function runCanActivateHook(instruction) { + return function runCanActivateHook(instruction) { return instruction.router.traverseInstruction(instruction, function(instruction) { var controller = instruction.controller; return !controller.canActivate || invoke(controller.canActivate, controller, instruction); }); } +} - function loadTemplates(instruction) { +function loadTemplatesStepFactory($componentLoader, $templateRequest) { + return function loadTemplates(instruction) { return instruction.router.traverseInstruction(instruction, function(instruction) { var componentTemplateUrl = $componentLoader.template(instruction.component); return $templateRequest(componentTemplateUrl).then(function (templateHtml) { return instruction.template = templateHtml; }); }); - } + }; +} - function activate(instruction) { - return instruction.router.activatePorts(instruction); - } + +function activateStepValue(instruction) { + return instruction.router.activatePorts(instruction); } + +function pipelineProvider() { + var stepConfiguration; + + var protoStepConfiguration = [ + '$setupRoutersStep', + '$initLocalsStep', + '$initControllersStep', + '$runCanDeactivateHookStep', + '$runCanActivateHookStep', + '$loadTemplatesStep', + '$activateStep' + ]; + + return { + steps: protoStepConfiguration.slice(0), + config: function (newConfig) { + protoStepConfiguration = newConfig; + }, + $get: function ($injector, $q) { + stepConfiguration = protoStepConfiguration.map(function (step) { + return $injector.get(step); + }); + return { + process: function(instruction) { + // make a copy + var steps = stepConfiguration.slice(0); + + function processOne(result) { + if (steps.length === 0) { + return result; + } + var step = steps.shift(); + return $q.when(step(instruction)).then(processOne); + } + + return processOne(); + } + } + } + }; +} + + /** * @name $componentLoaderProvider * @description @@ -494,6 +536,12 @@ function $componentLoaderProvider() { }; } +// this is a hack as a result of the build system used to transpile +function privatePipelineFactory($pipeline) { + return $pipeline; +} + + function dashCase(str) { return str.replace(/([A-Z])/g, function ($1) { return '-' + $1.toLowerCase(); diff --git a/test/pipeline.es5.spec.js b/test/pipeline.es5.spec.js new file mode 100644 index 0000000..e3bbd0a --- /dev/null +++ b/test/pipeline.es5.spec.js @@ -0,0 +1,71 @@ +describe('$pipeline', function () { + + var elt, + $compile, + $rootScope, + $router, + $templateCache, + $controllerProvider; + + beforeEach(function() { + module('ng'); + module('ngNewRouter'); + module(function(_$controllerProvider_) { + $controllerProvider = _$controllerProvider_; + }); + }); + + it('should allow reconfiguration', function () { + module(function($pipelineProvider, $provide) { + $pipelineProvider.config([ + '$setupRoutersStep', + '$initLocalsStep', + 'myCustomStep', + '$initControllersStep', + '$runCanDeactivateHookStep', + '$runCanActivateHookStep', + '$loadTemplatesStep', + '$activateStep' + ]); + + $provide.value('myCustomStep', function (instruction) { + return instruction.router.traverseInstruction(instruction, function (instruction) { + return instruction.locals.custom = 'hello!' + }); + }); + }); + + inject(function(_$compile_, _$rootScope_, _$router_, _$templateCache_) { + $compile = _$compile_; + $rootScope = _$rootScope_; + $router = _$router_; + $templateCache = _$templateCache_; + }); + + put('one', '
{{one.custom}}
'); + $controllerProvider.register('OneController', function (custom) { + this.custom = custom; + }); + + $router.config([ + { path: '/', component: 'one' } + ]); + + compile(''); + + $router.navigate('/'); + $rootScope.$digest(); + + expect(elt.text()).toBe('hello!'); + }); + + function put (name, template) { + $templateCache.put(componentTemplatePath(name), [200, template, {}]); + } + + function compile(template) { + elt = $compile('
' + template + '
')($rootScope); + $rootScope.$digest(); + return elt; + } +}); diff --git a/test/router-viewport.es5.spec.js b/test/router-viewport.es5.spec.js index 8940898..22bb892 100644 --- a/test/router-viewport.es5.spec.js +++ b/test/router-viewport.es5.spec.js @@ -731,17 +731,3 @@ describe('ngViewport animations', function () { return elt; } }); - -function componentTemplatePath(name) { - return './components/' + dashCase(name) + '/' + dashCase(name) + '.html'; -} - -function componentControllerName(name) { - return name[0].toUpperCase() + name.substr(1) + 'Controller'; -} - -function dashCase(str) { - return str.replace(/([A-Z])/g, function ($1) { - return '-' + $1.toLowerCase(); - }); -} diff --git a/test/util.es5.js b/test/util.es5.js new file mode 100644 index 0000000..c12ed8d --- /dev/null +++ b/test/util.es5.js @@ -0,0 +1,85 @@ +/* + * Helpers to keep tests DRY + */ + +function componentTemplatePath(name) { + return './components/' + dashCase(name) + '/' + dashCase(name) + '.html'; +} + +function componentControllerName(name) { + return name[0].toUpperCase() + name.substr(1) + 'Controller'; +} + +function dashCase(str) { + return str.replace(/([A-Z])/g, function ($1) { + return '-' + $1.toLowerCase(); + }); +} + +function boringController (model, value) { + return function () { + this[model] = value; + }; +} + +function provideHelpers(fn, preInject) { + return function () { + var elt, + $compile, + $rootScope, + $router, + $templateCache, + $controllerProvider; + + module('ng'); + module('ngNewRouter'); + module(function(_$controllerProvider_) { + $controllerProvider = _$controllerProvider_; + }); + + inject(function(_$compile_, _$rootScope_, _$router_, _$templateCache_) { + $compile = _$compile_; + $rootScope = _$rootScope_; + $router = _$router_; + $templateCache = _$templateCache_; + }); + + function registerComponent(name, template, config) { + if (!template) { + template = ''; + } + var ctrl; + if (!config) { + ctrl = function () {}; + } else if (angular.isArray(config)) { + ctrl = function () {}; + ctrl.$routeConfig = config; + } else if (typeof config === 'function') { + ctrl = config; + } else { + ctrl = function () {}; + ctrl.prototype = config; + } + $controllerProvider.register(componentControllerName(name), ctrl); + put(name, template); + } + + + function put (name, template) { + $templateCache.put(componentTemplatePath(name), [200, template, {}]); + } + + function compile(template) { + var elt = $compile('
' + template + '
')($rootScope); + $rootScope.$digest(); + return elt; + } + + fn({ + registerComponent: registerComponent, + $router: $router, + put: put, + compile: compile + }) + } +}