Created
May 20, 2015 11:55
-
-
Save Dynalon/c2143ebab219536e3761 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
'use strict'; | |
/* | |
* A module for adding new a routing system Angular 1. | |
*/ | |
angular.module('ngNewRouter', []) | |
.factory('$router', routerFactory) | |
.value('$routeParams', {}) | |
.factory('$componentMapper', $componentMapperFactory) | |
.provider('$pipeline', pipelineProvider) | |
.factory('$$pipeline', privatePipelineFactory) | |
.factory('$setupRoutersStep', setupRoutersStepFactory) | |
.factory('$initLocalsStep', initLocalsStepFactory) | |
.factory('$runCanDeactivateHookStep', runCanDeactivateHookStepFactory) | |
.factory('$runCanActivateHookStep', runCanActivateHookStepFactory) | |
.factory('$loadTemplatesStep', loadTemplatesStepFactory) | |
.value('$activateStep', activateStepValue) | |
.directive('ngOutlet', ngOutletDirective) | |
.directive('ngOutlet', ngOutletFillContentDirective) | |
.directive('ngLink', ngLinkDirective) | |
.directive('a', anchorLinkDirective) | |
var NOOP_CONTROLLER = function(){}; | |
/* | |
* A module for inspecting controller constructors | |
*/ | |
angular.module('ng') | |
.provider('$controllerIntrospector', $controllerIntrospectorProvider) | |
.config(controllerProviderDecorator); | |
/* | |
* decorates with routing info | |
*/ | |
function controllerProviderDecorator($controllerProvider, $controllerIntrospectorProvider) { | |
var register = $controllerProvider.register; | |
$controllerProvider.register = function (name, ctrl) { | |
$controllerIntrospectorProvider.register(name, ctrl); | |
return register.apply(this, arguments); | |
}; | |
} | |
controllerProviderDecorator.$inject = ["$controllerProvider", "$controllerIntrospectorProvider"]; | |
/* | |
* private service that holds route mappings for each controller | |
*/ | |
function $controllerIntrospectorProvider() { | |
var controllers = []; | |
var constructorsByName = {}; | |
var onControllerRegistered = null; | |
function getController(constructor) { | |
return angular.isArray(constructor) ? constructor[constructor.length - 1] : constructor; | |
} | |
return { | |
register: function (name, constructor) { | |
var controller = getController(constructor); | |
constructorsByName[name] = constructor; | |
if (controller.$routeConfig) { | |
if (onControllerRegistered) { | |
onControllerRegistered(name, controller.$routeConfig); | |
} else { | |
controllers.push({name: name, config: controller.$routeConfig}); | |
} | |
} | |
}, | |
$get: ['$componentMapper', function ($componentMapper) { | |
var fn = function (newOnControllerRegistered) { | |
onControllerRegistered = function (name, constructor) { | |
name = $componentMapper.component(name); | |
return newOnControllerRegistered(name, constructor); | |
}; | |
while(controllers.length > 0) { | |
var rule = controllers.pop(); | |
onControllerRegistered(rule.name, rule.config); | |
} | |
}; | |
fn.getTypeByName = function (name) { | |
return constructorsByName[name]; | |
}; | |
return fn; | |
}] | |
} | |
} | |
function routerFactory($$rootRouter, $rootScope, $location, $$grammar, $controllerIntrospector) { | |
$controllerIntrospector(function (name, config) { | |
$$grammar.config(name, config); | |
}); | |
$rootScope.$watch(function () { | |
return $location.path(); | |
}, function (newUrl) { | |
$$rootRouter.navigate(newUrl); | |
}); | |
var nav = $$rootRouter.navigate; | |
$$rootRouter.navigate = function (url) { | |
return nav.call(this, url).then(function (newUrl) { | |
if (newUrl) { | |
$location.path(newUrl); | |
} | |
}); | |
} | |
return $$rootRouter; | |
} | |
routerFactory.$inject = ["$$rootRouter", "$rootScope", "$location", "$$grammar", "$controllerIntrospector"]; | |
/** | |
* @name ngOutlet | |
* | |
* @description | |
* An ngOutlet is where resolved content goes. | |
* | |
* ## Use | |
* | |
* ```html | |
* <div ng-outlet="name"></div> | |
* ``` | |
* | |
* The value for the `ngOutlet` attribute is optional. | |
*/ | |
function ngOutletDirective($animate, $injector, $q, $router, $componentMapper, $controller) { | |
var rootRouter = $router; | |
return { | |
restrict: 'AE', | |
transclude: 'element', | |
terminal: true, | |
priority: 400, | |
require: ['?^^ngOutlet', 'ngOutlet'], | |
link: outletLink, | |
controller: function() {}, | |
controllerAs: '$$ngOutlet' | |
}; | |
function invoke(method, context, instruction) { | |
return $injector.invoke(method, context, instruction.locals); | |
} | |
function outletLink(scope, $element, attrs, ctrls, $transclude) { | |
var outletName = attrs.ngOutlet || 'default', | |
parentCtrl = ctrls[0], | |
myCtrl = ctrls[1], | |
router = (parentCtrl && parentCtrl.$$router) || rootRouter; | |
var currentScope, | |
newScope, | |
currentController, | |
currentElement, | |
previousLeaveAnimation, | |
previousInstruction; | |
function cleanupLastView() { | |
if (previousLeaveAnimation) { | |
$animate.cancel(previousLeaveAnimation); | |
previousLeaveAnimation = null; | |
} | |
if (currentScope) { | |
currentScope.$destroy(); | |
currentScope = null; | |
} | |
if (currentElement) { | |
previousLeaveAnimation = $animate.leave(currentElement); | |
previousLeaveAnimation.then(function() { | |
previousLeaveAnimation = null; | |
}); | |
currentElement = null; | |
} | |
} | |
router.registerOutlet({ | |
canDeactivate: function(instruction) { | |
if (currentController && currentController.canDeactivate) { | |
return invoke(currentController.canDeactivate, currentController, instruction); | |
} | |
return true; | |
}, | |
activate: function(instruction) { | |
var nextInstruction = serializeInstruction(instruction); | |
if (nextInstruction === previousInstruction) { | |
return; | |
} | |
var controllerConstructor = instruction.controllerConstructor; | |
if (!instruction.locals.$scope) { | |
instruction.locals.$scope = scope.$new(); | |
} | |
newScope = instruction.locals.$scope; | |
if (controllerConstructor === NOOP_CONTROLLER) { | |
console.warn && console.warn('Could not find controller for', $componentMapper.controllerName(instruction.component)); | |
} | |
var ctrl = $controller(controllerConstructor, instruction.locals); | |
instruction.controllerAs = $componentMapper.controllerAs(instruction.component); | |
instruction.controller = ctrl; | |
myCtrl.$$router = instruction.router; | |
myCtrl.$$template = instruction.template; | |
var controllerAs = instruction.controllerAs || instruction.component; | |
var clone = $transclude(newScope, function(clone) { | |
$animate.enter(clone, null, currentElement || $element); | |
cleanupLastView(); | |
}); | |
var newController = instruction.controller; | |
newScope[controllerAs] = newController; | |
var result; | |
if (currentController && currentController.deactivate) { | |
result = $q.when(invoke(currentController.deactivate, currentController, instruction)); | |
} | |
currentController = newController; | |
currentElement = clone; | |
currentScope = newScope; | |
previousInstruction = nextInstruction; | |
// finally, run the hook | |
if (newController.activate) { | |
var activationResult = $q.when(invoke(newController.activate, newController, instruction)); | |
if (result) { | |
return result.then(activationResult); | |
} else { | |
return activationResult; | |
} | |
} | |
return result; | |
} | |
}, outletName); | |
} | |
// TODO: how best to serialize? | |
function serializeInstruction(instruction) { | |
return JSON.stringify({ | |
path: instruction.path, | |
component: instruction.component, | |
params: Object.keys(instruction.params).reduce(function (acc, key) { | |
return (key !== 'childRoute' && (acc[key] = instruction.params[key])), acc; | |
}, {}) | |
}); | |
} | |
} | |
ngOutletDirective.$inject = ["$animate", "$injector", "$q", "$router", "$componentMapper", "$controller"]; | |
function ngOutletFillContentDirective($compile) { | |
return { | |
restrict: 'EA', | |
priority: -400, | |
require: 'ngOutlet', | |
link: function(scope, $element, attrs, ctrl) { | |
var template = ctrl.$$template; | |
$element.html(template); | |
var link = $compile($element.contents()); | |
link(scope); | |
} | |
}; | |
} | |
ngOutletFillContentDirective.$inject = ["$compile"]; | |
function makeComponentString(name) { | |
return [ | |
'<router-component component-name="', name, '">', | |
'</router-component>' | |
].join(''); | |
} | |
var LINK_MICROSYNTAX_RE = /^(.+?)(?:\((.*)\))?$/; | |
/** | |
* @name ngLink | |
* @description | |
* Lets you link to different parts of the app, and automatically generates hrefs. | |
* | |
* ## Use | |
* The directive uses a simple syntax: `ng-link="componentName({ param: paramValue })"` | |
* | |
* ## Example | |
* | |
* ```js | |
* angular.module('myApp', ['ngFuturisticRouter']) | |
* .controller('AppController', ['$router', function($router) { | |
* $router.config({ path: '/user/:id' component: 'user' }); | |
* this.user = { name: 'Brian', id: 123 }; | |
* }); | |
* ``` | |
* | |
* ```html | |
* <div ng-controller="AppController as app"> | |
* <a ng-link="user({id: app.user.id})">{{app.user.name}}</a> | |
* </div> | |
* ``` | |
*/ | |
function ngLinkDirective($router, $location, $parse) { | |
var rootRouter = $router; | |
return { | |
require: '?^^ngOutlet', | |
restrict: 'A', | |
link: ngLinkDirectiveLinkFn | |
}; | |
function ngLinkDirectiveLinkFn(scope, elt, attrs, ctrl) { | |
var router = (ctrl && ctrl.$$router) || rootRouter; | |
if (!router) { | |
return; | |
} | |
var link = attrs.ngLink || ''; | |
var parts = link.match(LINK_MICROSYNTAX_RE); | |
var routeName = parts[1]; | |
var routeParams = parts[2]; | |
var url; | |
if (routeParams) { | |
var routeParamsGetter = $parse(routeParams); | |
// we can avoid adding a watcher if it's a literal | |
if (routeParamsGetter.constant) { | |
var params = routeParamsGetter(); | |
url = '.' + router.generate(routeName, params); | |
elt.attr('href', url); | |
} else { | |
scope.$watch(function() { | |
return routeParamsGetter(scope); | |
}, function(params) { | |
url = '.' + router.generate(routeName, params); | |
elt.attr('href', url); | |
}, true); | |
} | |
} else { | |
url = '.' + router.generate(routeName); | |
elt.attr('href', url); | |
} | |
} | |
} | |
ngLinkDirective.$inject = ["$router", "$location", "$parse"]; | |
function anchorLinkDirective($router) { | |
return { | |
restrict: 'E', | |
link: function(scope, element) { | |
// If the linked element is not an anchor tag anymore, do nothing | |
if (element[0].nodeName.toLowerCase() !== 'a') return; | |
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. | |
var hrefAttrName = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? | |
'xlink:href' : 'href'; | |
element.on('click', function(event) { | |
if (event.which !== 1) | |
return; | |
var href = element.attr(hrefAttrName); | |
if (!href) { | |
event.preventDefault(); | |
} | |
if ($router.recognize(href)) { | |
$router.navigate(href); | |
event.preventDefault(); | |
} | |
}); | |
} | |
} | |
} | |
anchorLinkDirective.$inject = ["$router"]; | |
function setupRoutersStepFactory() { | |
return function (instruction) { | |
return instruction.router.makeDescendantRouters(instruction); | |
} | |
} | |
//TODO: rename to "normalize" step | |
/* | |
* $initLocalsStep | |
*/ | |
function initLocalsStepFactory($componentMapper, $controllerIntrospector) { | |
return function initLocals(instruction) { | |
return instruction.router.traverseInstruction(instruction, function(instruction) { | |
if (typeof instruction.component === 'function') { | |
instruction.controllerConstructor = instruction.component; | |
} else { | |
var controllerName = $componentMapper.controllerName(instruction.component); | |
if (typeof controllerName === 'function') { | |
instruction.controllerConstructor = controllerName; | |
} else { | |
instruction.controllerConstructor = $controllerIntrospector.getTypeByName(controllerName) || NOOP_CONTROLLER; | |
} | |
} | |
return instruction.locals = { | |
$router: instruction.router, | |
$routeParams: (instruction.params || {}) | |
}; | |
}); | |
} | |
} | |
initLocalsStepFactory.$inject = ["$componentMapper", "$controllerIntrospector"]; | |
function runCanDeactivateHookStepFactory() { | |
return function runCanDeactivateHook(instruction) { | |
return instruction.router.canDeactivateOutlets(instruction); | |
}; | |
} | |
function runCanActivateHookStepFactory($injector) { | |
function invoke(method, context, instruction) { | |
return $injector.invoke(method, context, { | |
$routeParams: instruction.params | |
}); | |
} | |
return function runCanActivateHook(instruction) { | |
return instruction.router.traverseInstruction(instruction, function(instruction) { | |
var controllerConstructor = instruction.controllerConstructor; | |
return !controllerConstructor.canActivate || invoke(controllerConstructor.canActivate, null, instruction); | |
}); | |
} | |
} | |
runCanActivateHookStepFactory.$inject = ["$injector"]; | |
function loadTemplatesStepFactory($componentMapper, $templateRequest) { | |
return function loadTemplates(instruction) { | |
return instruction.router.traverseInstruction(instruction, function(instruction) { | |
var componentTemplateUrl = $componentMapper.template(instruction.component); | |
return $templateRequest(componentTemplateUrl).then(function (templateHtml) { | |
return instruction.template = templateHtml; | |
}); | |
}); | |
}; | |
} | |
loadTemplatesStepFactory.$inject = ["$componentMapper", "$templateRequest"]; | |
function activateStepValue(instruction) { | |
return instruction.router.activateOutlets(instruction); | |
} | |
function pipelineProvider() { | |
var stepConfiguration; | |
var protoStepConfiguration = [ | |
'$setupRoutersStep', | |
'$initLocalsStep', | |
'$runCanDeactivateHookStep', | |
'$runCanActivateHookStep', | |
'$loadTemplatesStep', | |
'$activateStep' | |
]; | |
return { | |
steps: protoStepConfiguration.slice(0), | |
config: function (newConfig) { | |
protoStepConfiguration = newConfig; | |
}, | |
$get: ["$injector", "$q", 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 $componentMapper | |
* @description | |
* | |
* This lets you configure conventions for what controllers are named and where to load templates from. | |
* | |
* The default behavior is to dasherize and serve from `./components`. A component called `myWidget` | |
* uses a controller named `MyWidgetController` and a template loaded from `./components/my-widget/my-widget.html`. | |
* | |
* A component is: | |
* - a controller | |
* - a template | |
* - an optional router | |
* | |
* This service makes it easy to group all of them into a single concept. | |
*/ | |
function $componentMapperFactory() { | |
var DEFAULT_SUFFIX = 'Controller'; | |
var componentToCtrl = function componentToCtrlDefault(name) { | |
return name[0].toUpperCase() + name.substr(1) + DEFAULT_SUFFIX; | |
}; | |
var componentToTemplate = function componentToTemplateDefault(name) { | |
var dashName = dashCase(name); | |
return './components/' + dashName + '/' + dashName + '.html'; | |
}; | |
var ctrlToComponent = function ctrlToComponentDefault(name) { | |
return name[0].toLowerCase() + name.substr(1, name.length - DEFAULT_SUFFIX.length - 1); | |
}; | |
var componentToControllerAs = function componentToControllerAsDefault(name) { | |
return name; | |
}; | |
return { | |
controllerName: function (name) { | |
return componentToCtrl(name); | |
}, | |
controllerAs: function (name) { | |
return componentToControllerAs(name); | |
}, | |
template: function (name) { | |
return componentToTemplate(name); | |
}, | |
component: function (name) { | |
return ctrlToComponent(name); | |
}, | |
/** | |
* @name $componentMapper#setCtrlNameMapping | |
* @description takes a function for mapping component names to component controller names | |
*/ | |
setCtrlNameMapping: function(newFn) { | |
componentToCtrl = newFn; | |
return this; | |
}, | |
/** | |
* @name $componentMapper#setCtrlAsMapping | |
* @description takes a function for mapping component names to controllerAs name in the template | |
*/ | |
setCtrlAsMapping: function(newFn) { | |
componentToControllerAs = newFn; | |
return this; | |
}, | |
/** | |
* @name $componentMapper#setComponentFromCtrlMapping | |
* @description takes a function for mapping component controller names to component names | |
*/ | |
setComponentFromCtrlMapping: function (newFn) { | |
ctrlToComponent = newFn; | |
return this; | |
}, | |
/** | |
* @name $componentMapper#setTemplateMapping | |
* @description takes a function for mapping component names to component template URLs | |
*/ | |
setTemplateMapping: function(newFn) { | |
componentToTemplate = newFn; | |
return this; | |
} | |
}; | |
} | |
// this is a hack as a result of the build system used to transpile | |
function privatePipelineFactory($pipeline) { | |
return $pipeline; | |
} | |
privatePipelineFactory.$inject = ["$pipeline"]; | |
function dashCase(str) { | |
return str.replace(/([A-Z])/g, function ($1) { | |
return '-' + $1.toLowerCase(); | |
}); | |
} | |
angular.module('ngNewRouter').factory('$$rootRouter', ['$q', '$$grammar', '$$pipeline', function ($q, $$grammar, $$pipeline) { | |
/* | |
* artisinal, handcrafted subset of the traceur runtime for picky webdevs | |
*/ | |
var $defineProperty = Object.defineProperty, | |
$defineProperties = Object.defineProperties, | |
$create = Object.create, | |
$getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, | |
$getOwnPropertyNames = Object.getOwnPropertyNames, | |
$getPrototypeOf = Object.getPrototypeOf; | |
function createClass(ctor, object, staticObject, superClass) { | |
$defineProperty(object, 'constructor', { | |
value: ctor, | |
configurable: true, | |
enumerable: false, | |
writable: true | |
}); | |
if (arguments.length > 3) { | |
if (typeof superClass === 'function') | |
ctor.__proto__ = superClass; | |
ctor.prototype = $create(getProtoParent(superClass), getDescriptors(object)); | |
} else { | |
ctor.prototype = object; | |
} | |
$defineProperty(ctor, 'prototype', { | |
configurable: false, | |
writable: false | |
}); | |
return $defineProperties(ctor, getDescriptors(staticObject)); | |
} | |
function getProtoParent(superClass) { | |
if (typeof superClass === 'function') { | |
var prototype = superClass.prototype; | |
if (Object(prototype) === prototype || prototype === null) | |
return superClass.prototype; | |
throw new TypeError('super prototype must be an Object or null'); | |
} | |
if (superClass === null) | |
return null; | |
throw new TypeError(("Super expression must either be null or a function, not " + typeof superClass + ".")); | |
} | |
function getDescriptors(object) { | |
var descriptors = {}; | |
var names = $getOwnPropertyNames(object); | |
for (var i = 0; i < names.length; i++) { | |
var name = names[i]; | |
descriptors[name] = $getOwnPropertyDescriptor(object, name); | |
} | |
// TODO: someday you might use symbols and you'll have to re-evaluate | |
// your life choices that led to the creation of this file | |
// var symbols = getOwnPropertySymbols(object); | |
// for (var i = 0; i < symbols.length; i++) { | |
// var symbol = symbols[i]; | |
// descriptors[$traceurRuntime.toProperty(symbol)] = $getOwnPropertyDescriptor(object, $traceurRuntime.toProperty(symbol)); | |
// } | |
return descriptors; | |
} | |
function superDescriptor(homeObject, name) { | |
var proto = $getPrototypeOf(homeObject); | |
do { | |
var result = $getOwnPropertyDescriptor(proto, name); | |
if (result) | |
return result; | |
proto = $getPrototypeOf(proto); | |
} while (proto); | |
return undefined; | |
} | |
function superCall(self, homeObject, name, args) { | |
return superGet(self, homeObject, name).apply(self, args); | |
} | |
function superGet(self, homeObject, name) { | |
var descriptor = superDescriptor(homeObject, name); | |
if (descriptor) { | |
if (!descriptor.get) | |
return descriptor.value; | |
return descriptor.get.call(self); | |
} | |
return undefined; | |
} | |
"use strict"; | |
var Router = function Router(grammar, pipeline, parent, name) { | |
this.name = name; | |
this.parent = parent || null; | |
this.navigating = false; | |
this.outlets = {}; | |
this.children = {}; | |
this.registry = grammar; | |
this.pipeline = pipeline; | |
}; | |
(createClass)(Router, { | |
childRouter: function() { | |
var name = arguments[0] !== (void 0) ? arguments[0] : 'default'; | |
if (!this.children[name]) { | |
this.children[name] = new ChildRouter(this, name); | |
} | |
return this.children[name]; | |
}, | |
registerOutlet: function(view) { | |
var name = arguments[1] !== (void 0) ? arguments[1] : 'default'; | |
this.outlets[name] = view; | |
return this.renavigate(); | |
}, | |
config: function(mapping) { | |
this.registry.config(this.name, mapping); | |
return this.renavigate(); | |
}, | |
navigate: function(url, force) { | |
var $__0 = this; | |
if (this.navigating || (!force && url === this.lastCanonicalUrl)) { | |
return $q.when(); | |
} | |
this.lastNavigationAttempt = url; | |
var instruction = this.recognize(url); | |
if (!instruction) { | |
return $q.reject(); | |
} | |
this._startNavigating(); | |
instruction.router = this; | |
return this.pipeline.process(instruction).then((function() { | |
return $__0._finishNavigating(); | |
}), (function() { | |
return $__0._finishNavigating(); | |
})).then((function() { | |
return $__0.lastCanonicalUrl = instruction.canonicalUrl; | |
})); | |
}, | |
_startNavigating: function() { | |
this.navigating = true; | |
}, | |
_finishNavigating: function() { | |
this.navigating = false; | |
}, | |
makeDescendantRouters: function(instruction) { | |
this.traverseInstructionSync(instruction, (function(instruction, childInstruction) { | |
childInstruction.router = instruction.router.childRouter(childInstruction.component); | |
})); | |
}, | |
traverseInstructionSync: function(instruction, fn) { | |
var $__0 = this; | |
forEach(instruction.outlets, (function(childInstruction, outletName) { | |
return fn(instruction, childInstruction); | |
})); | |
forEach(instruction.outlets, (function(childInstruction) { | |
return $__0.traverseInstructionSync(childInstruction, fn); | |
})); | |
}, | |
traverseInstruction: function(instruction, fn) { | |
if (!instruction) { | |
return $q.when(); | |
} | |
return mapObjAsync(instruction.outlets, (function(childInstruction, outletName) { | |
return boolToPromise(fn(childInstruction, outletName)); | |
})).then((function() { | |
return mapObjAsync(instruction.outlets, (function(childInstruction, outletName) { | |
return childInstruction.router.traverseInstruction(childInstruction, fn); | |
})); | |
})); | |
}, | |
activateOutlets: function(instruction) { | |
return this.queryOutlets((function(outlet, name) { | |
if (!instruction.outlets[name]) { | |
return $q.when(); | |
} | |
return outlet.activate(instruction.outlets[name]); | |
})).then((function() { | |
return mapObjAsync(instruction.outlets, (function(instruction) { | |
return instruction.router.activateOutlets(instruction); | |
})); | |
})); | |
}, | |
canDeactivateOutlets: function(instruction) { | |
return this.traverseOutlets((function(outlet, name) { | |
return boolToPromise(outlet.canDeactivate(instruction.outlets[name])); | |
})); | |
}, | |
traverseOutlets: function(fn) { | |
var $__0 = this; | |
return this.queryOutlets(fn).then((function() { | |
return mapObjAsync($__0.children, (function(child) { | |
return child.traverseOutlets(fn); | |
})); | |
})); | |
}, | |
queryOutlets: function(fn) { | |
return mapObjAsync(this.outlets, fn); | |
}, | |
recognize: function(url) { | |
return this.registry.recognize(url); | |
}, | |
renavigate: function() { | |
var renavigateDestination = this.previousUrl || this.lastNavigationAttempt; | |
if (!this.navigating && renavigateDestination) { | |
return this.navigate(renavigateDestination, true); | |
} else { | |
return $q.when(); | |
} | |
}, | |
generate: function(name, params) { | |
return this.registry.generate(name, params, this._buildRouterChain()); | |
}, | |
_buildRouterChain: function() { | |
var routerChain = [], | |
current = this; | |
while (current) { | |
routerChain.push(current.name); | |
current = current.parent; | |
} | |
return routerChain; | |
} | |
}, {}); | |
Object.defineProperty(Router, "parameters", {get: function() { | |
return [[Grammar], [Pipeline], [], []]; | |
}}); | |
Object.defineProperty(Router.prototype.generate, "parameters", {get: function() { | |
return [[$traceurRuntime.type.string], []]; | |
}}); | |
var RootRouter = function RootRouter(grammar, pipeline) { | |
superCall(this, $RootRouter.prototype, "constructor", [grammar, pipeline, null, '/']); | |
}; | |
var $RootRouter = RootRouter; | |
(createClass)(RootRouter, {}, {}, Router); | |
Object.defineProperty(RootRouter, "parameters", {get: function() { | |
return [[Grammar], [Pipeline]]; | |
}}); | |
var ChildRouter = function ChildRouter(parent, name) { | |
superCall(this, $ChildRouter.prototype, "constructor", [parent.registry, parent.pipeline, parent, name]); | |
this.parent = parent; | |
}; | |
var $ChildRouter = ChildRouter; | |
(createClass)(ChildRouter, {}, {}, Router); | |
function forEach(obj, fn) { | |
Object.keys(obj).forEach((function(key) { | |
return fn(obj[key], key); | |
})); | |
} | |
function mapObjAsync(obj, fn) { | |
return $q.all(mapObj(obj, fn)); | |
} | |
function mapObj(obj, fn) { | |
var result = []; | |
Object.keys(obj).forEach((function(key) { | |
return result.push(fn(obj[key], key)); | |
})); | |
return result; | |
} | |
function boolToPromise(value) { | |
return value ? $q.when(value) : $q.reject(); | |
} | |
return new RootRouter($$grammar, $$pipeline); | |
}]); | |
angular.module('ngNewRouter').factory('$$grammar', ['$q', function ($q) { | |
/* | |
* artisinal, handcrafted subset of the traceur runtime for picky webdevs | |
*/ | |
var $defineProperty = Object.defineProperty, | |
$defineProperties = Object.defineProperties, | |
$create = Object.create, | |
$getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, | |
$getOwnPropertyNames = Object.getOwnPropertyNames, | |
$getPrototypeOf = Object.getPrototypeOf; | |
function createClass(ctor, object, staticObject, superClass) { | |
$defineProperty(object, 'constructor', { | |
value: ctor, | |
configurable: true, | |
enumerable: false, | |
writable: true | |
}); | |
if (arguments.length > 3) { | |
if (typeof superClass === 'function') | |
ctor.__proto__ = superClass; | |
ctor.prototype = $create(getProtoParent(superClass), getDescriptors(object)); | |
} else { | |
ctor.prototype = object; | |
} | |
$defineProperty(ctor, 'prototype', { | |
configurable: false, | |
writable: false | |
}); | |
return $defineProperties(ctor, getDescriptors(staticObject)); | |
} | |
function getProtoParent(superClass) { | |
if (typeof superClass === 'function') { | |
var prototype = superClass.prototype; | |
if (Object(prototype) === prototype || prototype === null) | |
return superClass.prototype; | |
throw new TypeError('super prototype must be an Object or null'); | |
} | |
if (superClass === null) | |
return null; | |
throw new TypeError(("Super expression must either be null or a function, not " + typeof superClass + ".")); | |
} | |
function getDescriptors(object) { | |
var descriptors = {}; | |
var names = $getOwnPropertyNames(object); | |
for (var i = 0; i < names.length; i++) { | |
var name = names[i]; | |
descriptors[name] = $getOwnPropertyDescriptor(object, name); | |
} | |
// TODO: someday you might use symbols and you'll have to re-evaluate | |
// your life choices that led to the creation of this file | |
// var symbols = getOwnPropertySymbols(object); | |
// for (var i = 0; i < symbols.length; i++) { | |
// var symbol = symbols[i]; | |
// descriptors[$traceurRuntime.toProperty(symbol)] = $getOwnPropertyDescriptor(object, $traceurRuntime.toProperty(symbol)); | |
// } | |
return descriptors; | |
} | |
function superDescriptor(homeObject, name) { | |
var proto = $getPrototypeOf(homeObject); | |
do { | |
var result = $getOwnPropertyDescriptor(proto, name); | |
if (result) | |
return result; | |
proto = $getPrototypeOf(proto); | |
} while (proto); | |
return undefined; | |
} | |
function superCall(self, homeObject, name, args) { | |
return superGet(self, homeObject, name).apply(self, args); | |
} | |
function superGet(self, homeObject, name) { | |
var descriptor = superDescriptor(homeObject, name); | |
if (descriptor) { | |
if (!descriptor.get) | |
return descriptor.value; | |
return descriptor.get.call(self); | |
} | |
return undefined; | |
} | |
"use strict"; | |
var RouteRecognizer = (function() { | |
var map = (function() { | |
function Target(path, matcher, delegate) { | |
this.path = path; | |
this.matcher = matcher; | |
this.delegate = delegate; | |
} | |
Target.prototype = {to: function(target, callback) { | |
var delegate = this.delegate; | |
if (delegate && delegate.willAddRoute) { | |
target = delegate.willAddRoute(this.matcher.target, target); | |
} | |
this.matcher.add(this.path, target); | |
if (callback) { | |
if (callback.length === 0) { | |
throw new Error("You must have an argument in the function passed to `to`"); | |
} | |
this.matcher.addChild(this.path, target, callback, this.delegate); | |
} | |
return this; | |
}}; | |
function Matcher(target) { | |
this.routes = {}; | |
this.children = {}; | |
this.target = target; | |
} | |
Matcher.prototype = { | |
add: function(path, handler) { | |
this.routes[path] = handler; | |
}, | |
addChild: function(path, target, callback, delegate) { | |
var matcher = new Matcher(target); | |
this.children[path] = matcher; | |
var match = generateMatch(path, matcher, delegate); | |
if (delegate && delegate.contextEntered) { | |
delegate.contextEntered(target, match); | |
} | |
callback(match); | |
} | |
}; | |
function generateMatch(startingPath, matcher, delegate) { | |
return function(path, nestedCallback) { | |
var fullPath = startingPath + path; | |
if (nestedCallback) { | |
nestedCallback(generateMatch(fullPath, matcher, delegate)); | |
} else { | |
return new Target(startingPath + path, matcher, delegate); | |
} | |
}; | |
} | |
function addRoute(routeArray, path, handler) { | |
var len = 0; | |
for (var i = 0, | |
l = routeArray.length; i < l; i++) { | |
len += routeArray[i].path.length; | |
} | |
path = path.substr(len); | |
var route = { | |
path: path, | |
handler: handler | |
}; | |
routeArray.push(route); | |
} | |
function eachRoute(baseRoute, matcher, callback, binding) { | |
var routes = matcher.routes; | |
for (var path in routes) { | |
if (routes.hasOwnProperty(path)) { | |
var routeArray = baseRoute.slice(); | |
addRoute(routeArray, path, routes[path]); | |
if (matcher.children[path]) { | |
eachRoute(routeArray, matcher.children[path], callback, binding); | |
} else { | |
callback.call(binding, routeArray); | |
} | |
} | |
} | |
} | |
return function(callback, addRouteCallback) { | |
var matcher = new Matcher(); | |
callback(generateMatch("", matcher, this.delegate)); | |
eachRoute([], matcher, function(route) { | |
if (addRouteCallback) { | |
addRouteCallback(this, route); | |
} else { | |
this.add(route); | |
} | |
}, this); | |
}; | |
}()); | |
var specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\']; | |
var escapeRegex = new RegExp('(\\' + specials.join('|\\') + ')', 'g'); | |
function isArray(test) { | |
return Object.prototype.toString.call(test) === "[object Array]"; | |
} | |
function StaticSegment(string) { | |
this.string = string; | |
} | |
StaticSegment.prototype = { | |
eachChar: function(callback) { | |
var string = this.string, | |
ch; | |
for (var i = 0, | |
l = string.length; i < l; i++) { | |
ch = string.charAt(i); | |
callback({validChars: ch}); | |
} | |
}, | |
regex: function() { | |
return this.string.replace(escapeRegex, '\\$1'); | |
}, | |
generate: function() { | |
return this.string; | |
} | |
}; | |
function DynamicSegment(name) { | |
this.name = name; | |
} | |
DynamicSegment.prototype = { | |
eachChar: function(callback) { | |
callback({ | |
invalidChars: "/", | |
repeat: true | |
}); | |
}, | |
regex: function() { | |
return "([^/]+)"; | |
}, | |
generate: function(params) { | |
return params[this.name]; | |
} | |
}; | |
function StarSegment(name) { | |
this.name = name; | |
} | |
StarSegment.prototype = { | |
eachChar: function(callback) { | |
callback({ | |
invalidChars: "", | |
repeat: true | |
}); | |
}, | |
regex: function() { | |
return "(.+)"; | |
}, | |
generate: function(params) { | |
return params[this.name]; | |
} | |
}; | |
function EpsilonSegment() {} | |
EpsilonSegment.prototype = { | |
eachChar: function() {}, | |
regex: function() { | |
return ""; | |
}, | |
generate: function() { | |
return ""; | |
} | |
}; | |
function parse(route, names, types) { | |
if (route.charAt(0) === "/") { | |
route = route.substr(1); | |
} | |
var segments = route.split("/"), | |
results = []; | |
for (var i = 0, | |
l = segments.length; i < l; i++) { | |
var segment = segments[i], | |
match; | |
if (match = segment.match(/^:([^\/]+)$/)) { | |
results.push(new DynamicSegment(match[1])); | |
names.push(match[1]); | |
types.dynamics++; | |
} else if (match = segment.match(/^\*([^\/]+)$/)) { | |
results.push(new StarSegment(match[1])); | |
names.push(match[1]); | |
types.stars++; | |
} else if (segment === "") { | |
results.push(new EpsilonSegment()); | |
} else { | |
results.push(new StaticSegment(segment)); | |
types.statics++; | |
} | |
} | |
return results; | |
} | |
function State(charSpec) { | |
this.charSpec = charSpec; | |
this.nextStates = []; | |
} | |
State.prototype = { | |
get: function(charSpec) { | |
var nextStates = this.nextStates; | |
for (var i = 0, | |
l = nextStates.length; i < l; i++) { | |
var child = nextStates[i]; | |
var isEqual = child.charSpec.validChars === charSpec.validChars; | |
isEqual = isEqual && child.charSpec.invalidChars === charSpec.invalidChars; | |
if (isEqual) { | |
return child; | |
} | |
} | |
}, | |
put: function(charSpec) { | |
var state; | |
if (state = this.get(charSpec)) { | |
return state; | |
} | |
state = new State(charSpec); | |
this.nextStates.push(state); | |
if (charSpec.repeat) { | |
state.nextStates.push(state); | |
} | |
return state; | |
}, | |
match: function(ch) { | |
var nextStates = this.nextStates, | |
child, | |
charSpec, | |
chars; | |
var returned = []; | |
for (var i = 0, | |
l = nextStates.length; i < l; i++) { | |
child = nextStates[i]; | |
charSpec = child.charSpec; | |
if (typeof(chars = charSpec.validChars) !== 'undefined') { | |
if (chars.indexOf(ch) !== -1) { | |
returned.push(child); | |
} | |
} else if (typeof(chars = charSpec.invalidChars) !== 'undefined') { | |
if (chars.indexOf(ch) === -1) { | |
returned.push(child); | |
} | |
} | |
} | |
return returned; | |
} | |
}; | |
function sortSolutions(states) { | |
return states.sort(function(a, b) { | |
if (a.types.stars !== b.types.stars) { | |
return a.types.stars - b.types.stars; | |
} | |
if (a.types.stars) { | |
if (a.types.statics !== b.types.statics) { | |
return b.types.statics - a.types.statics; | |
} | |
if (a.types.dynamics !== b.types.dynamics) { | |
return b.types.dynamics - a.types.dynamics; | |
} | |
} | |
if (a.types.dynamics !== b.types.dynamics) { | |
return a.types.dynamics - b.types.dynamics; | |
} | |
if (a.types.statics !== b.types.statics) { | |
return b.types.statics - a.types.statics; | |
} | |
return 0; | |
}); | |
} | |
function recognizeChar(states, ch) { | |
var nextStates = []; | |
for (var i = 0, | |
l = states.length; i < l; i++) { | |
var state = states[i]; | |
nextStates = nextStates.concat(state.match(ch)); | |
} | |
return nextStates; | |
} | |
var oCreate = Object.create || function(proto) { | |
function F() {} | |
F.prototype = proto; | |
return new F(); | |
}; | |
function RecognizeResults(queryParams) { | |
this.queryParams = queryParams || {}; | |
} | |
RecognizeResults.prototype = oCreate({ | |
splice: Array.prototype.splice, | |
slice: Array.prototype.slice, | |
push: Array.prototype.push, | |
length: 0, | |
queryParams: null | |
}); | |
function findHandler(state, path, queryParams) { | |
var handlers = state.handlers, | |
regex = state.regex; | |
var captures = path.match(regex), | |
currentCapture = 1; | |
var result = new RecognizeResults(queryParams); | |
for (var i = 0, | |
l = handlers.length; i < l; i++) { | |
var handler = handlers[i], | |
names = handler.names, | |
params = {}; | |
for (var j = 0, | |
m = names.length; j < m; j++) { | |
params[names[j]] = captures[currentCapture++]; | |
} | |
result.push({ | |
handler: handler.handler, | |
params: params, | |
isDynamic: !!names.length | |
}); | |
} | |
return result; | |
} | |
function addSegment(currentState, segment) { | |
segment.eachChar(function(ch) { | |
var state; | |
currentState = currentState.put(ch); | |
}); | |
return currentState; | |
} | |
var RouteRecognizer = function() { | |
this.rootState = new State(); | |
this.names = {}; | |
}; | |
RouteRecognizer.prototype = { | |
add: function(routes, options) { | |
var currentState = this.rootState, | |
regex = "^", | |
types = { | |
statics: 0, | |
dynamics: 0, | |
stars: 0 | |
}, | |
handlers = [], | |
allSegments = [], | |
name; | |
var isEmpty = true; | |
for (var i = 0, | |
l = routes.length; i < l; i++) { | |
var route = routes[i], | |
names = []; | |
var segments = parse(route.path, names, types); | |
allSegments = allSegments.concat(segments); | |
for (var j = 0, | |
m = segments.length; j < m; j++) { | |
var segment = segments[j]; | |
if (segment instanceof EpsilonSegment) { | |
continue; | |
} | |
isEmpty = false; | |
currentState = currentState.put({validChars: "/"}); | |
regex += "/"; | |
currentState = addSegment(currentState, segment); | |
regex += segment.regex(); | |
} | |
var handler = { | |
handler: route.handler, | |
names: names | |
}; | |
handlers.push(handler); | |
} | |
if (isEmpty) { | |
currentState = currentState.put({validChars: "/"}); | |
regex += "/"; | |
} | |
currentState.handlers = handlers; | |
currentState.regex = new RegExp(regex + "$"); | |
currentState.types = types; | |
if (name = options && options.as) { | |
this.names[name] = { | |
segments: allSegments, | |
handlers: handlers | |
}; | |
} | |
}, | |
handlersFor: function(name) { | |
var route = this.names[name], | |
result = []; | |
if (!route) { | |
throw new Error("There is no route named " + name); | |
} | |
for (var i = 0, | |
l = route.handlers.length; i < l; i++) { | |
result.push(route.handlers[i]); | |
} | |
return result; | |
}, | |
hasRoute: function(name) { | |
return !!this.names[name]; | |
}, | |
generate: function(name, params) { | |
var route = this.names[name], | |
output = ""; | |
if (!route) { | |
throw new Error("There is no route named " + name); | |
} | |
var segments = route.segments; | |
for (var i = 0, | |
l = segments.length; i < l; i++) { | |
var segment = segments[i]; | |
if (segment instanceof EpsilonSegment) { | |
continue; | |
} | |
output += "/"; | |
output += segment.generate(params); | |
} | |
if (output.charAt(0) !== '/') { | |
output = '/' + output; | |
} | |
if (params && params.queryParams) { | |
output += this.generateQueryString(params.queryParams, route.handlers); | |
} | |
return output; | |
}, | |
generateQueryString: function(params, handlers) { | |
var pairs = []; | |
var keys = []; | |
for (var key in params) { | |
if (params.hasOwnProperty(key)) { | |
keys.push(key); | |
} | |
} | |
keys.sort(); | |
for (var i = 0, | |
len = keys.length; i < len; i++) { | |
key = keys[i]; | |
var value = params[key]; | |
if (value == null) { | |
continue; | |
} | |
var pair = encodeURIComponent(key); | |
if (isArray(value)) { | |
for (var j = 0, | |
l = value.length; j < l; j++) { | |
var arrayPair = key + '[]' + '=' + encodeURIComponent(value[j]); | |
pairs.push(arrayPair); | |
} | |
} else { | |
pair += "=" + encodeURIComponent(value); | |
pairs.push(pair); | |
} | |
} | |
if (pairs.length === 0) { | |
return ''; | |
} | |
return "?" + pairs.join("&"); | |
}, | |
parseQueryString: function(queryString) { | |
var pairs = queryString.split("&"), | |
queryParams = {}; | |
for (var i = 0; i < pairs.length; i++) { | |
var pair = pairs[i].split('='), | |
key = decodeURIComponent(pair[0]), | |
keyLength = key.length, | |
isArray = false, | |
value; | |
if (pair.length === 1) { | |
value = 'true'; | |
} else { | |
if (keyLength > 2 && key.slice(keyLength - 2) === '[]') { | |
isArray = true; | |
key = key.slice(0, keyLength - 2); | |
if (!queryParams[key]) { | |
queryParams[key] = []; | |
} | |
} | |
value = pair[1] ? decodeURIComponent(pair[1]) : ''; | |
} | |
if (isArray) { | |
queryParams[key].push(value); | |
} else { | |
queryParams[key] = value; | |
} | |
} | |
return queryParams; | |
}, | |
recognize: function(path) { | |
var states = [this.rootState], | |
pathLen, | |
i, | |
l, | |
queryStart, | |
queryParams = {}, | |
isSlashDropped = false; | |
queryStart = path.indexOf('?'); | |
if (queryStart !== -1) { | |
var queryString = path.substr(queryStart + 1, path.length); | |
path = path.substr(0, queryStart); | |
queryParams = this.parseQueryString(queryString); | |
} | |
path = decodeURI(path); | |
if (path.charAt(0) !== "/") { | |
path = "/" + path; | |
} | |
pathLen = path.length; | |
if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { | |
path = path.substr(0, pathLen - 1); | |
isSlashDropped = true; | |
} | |
for (i = 0, l = path.length; i < l; i++) { | |
states = recognizeChar(states, path.charAt(i)); | |
if (!states.length) { | |
break; | |
} | |
} | |
var solutions = []; | |
for (i = 0, l = states.length; i < l; i++) { | |
if (states[i].handlers) { | |
solutions.push(states[i]); | |
} | |
} | |
states = sortSolutions(solutions); | |
var state = solutions[0]; | |
if (state && state.handlers) { | |
if (isSlashDropped && state.regex.source.slice(-5) === "(.+)$") { | |
path = path + "/"; | |
} | |
return findHandler(state, path, queryParams); | |
} | |
} | |
}; | |
RouteRecognizer.prototype.map = map; | |
RouteRecognizer.VERSION = 'VERSION_STRING_PLACEHOLDER'; | |
return RouteRecognizer; | |
}()); | |
var CHILD_ROUTE_SUFFIX = '/*childRoute'; | |
var Grammar = function Grammar() { | |
this.rules = {}; | |
}; | |
(createClass)(Grammar, { | |
config: function(name, config) { | |
if (name === 'app') { | |
name = '/'; | |
} | |
if (!this.rules[name]) { | |
this.rules[name] = new CanonicalRecognizer(name); | |
} | |
this.rules[name].config(config); | |
}, | |
recognize: function(url) { | |
var componentName = arguments[1] !== (void 0) ? arguments[1] : '/'; | |
var $__0 = this; | |
if (typeof url === 'undefined') { | |
return; | |
} | |
var componentRecognizer = this.rules[componentName]; | |
if (!componentRecognizer) { | |
return; | |
} | |
var context = componentRecognizer.recognize(url); | |
if (!context) { | |
return; | |
} | |
var lastContextChunk = context[context.length - 1]; | |
var lastHandler = lastContextChunk.handler; | |
var lastParams = lastContextChunk.params; | |
var instruction = { | |
outlets: {}, | |
params: lastParams | |
}; | |
if (lastParams && lastParams.childRoute) { | |
var childUrl = '/' + lastParams.childRoute; | |
instruction.canonicalUrl = lastHandler.rewroteUrl.substr(0, lastHandler.rewroteUrl.length - (lastParams.childRoute.length + 1)); | |
forEach(lastHandler.components, (function(componentName, outletName) { | |
instruction.outlets[outletName] = $__0.recognize(childUrl, componentName); | |
})); | |
var firstChildOutlet = instruction.outlets[Object.keys(instruction.outlets)[0]]; | |
if (firstChildOutlet) { | |
instruction.canonicalUrl += firstChildOutlet.canonicalUrl; | |
} | |
} else { | |
instruction.canonicalUrl = lastHandler.rewroteUrl; | |
forEach(lastHandler.components, (function(componentName, outletName) { | |
instruction.outlets[outletName] = {outlets: {}}; | |
})); | |
} | |
forEach(instruction.outlets, (function(instruction, componentName) { | |
instruction.component = lastHandler.components[componentName]; | |
instruction.params = lastParams; | |
})); | |
return instruction; | |
}, | |
generate: function(name, params) { | |
var recognizerChain = arguments[2] !== (void 0) ? arguments[2] : []; | |
var $__0 = this; | |
var path = ''; | |
var solution; | |
do { | |
solution = null; | |
recognizerChain.some((function(recognizerName) { | |
var recognizer = $__0.rules[recognizerName]; | |
if (recognizer && recognizer.hasRoute(name)) { | |
solution = recognizer; | |
return true; | |
} | |
})); | |
if (solution) { | |
path = solution.generate(name, params) + path; | |
} | |
if (!solution) { | |
return ''; | |
} | |
name = solution.name; | |
} while (solution.name !== '/'); | |
return path; | |
} | |
}, {}); | |
Object.defineProperty(Grammar.prototype.recognize, "parameters", {get: function() { | |
return [[$traceurRuntime.type.string], []]; | |
}}); | |
var CanonicalRecognizer = function CanonicalRecognizer(name) { | |
this.name = name; | |
this.rewrites = {}; | |
this.recognizer = new RouteRecognizer(); | |
}; | |
(createClass)(CanonicalRecognizer, { | |
config: function(mapping) { | |
var $__0 = this; | |
if (mapping instanceof Array) { | |
mapping.forEach((function(nav) { | |
return $__0.configOne(nav); | |
})); | |
} else { | |
this.configOne(mapping); | |
} | |
}, | |
getCanonicalUrl: function(url) { | |
if (url[0] === '.') { | |
url = url.substr(1); | |
} | |
if (url === '' || url[0] !== '/') { | |
url = '/' + url; | |
} | |
forEach(this.rewrites, function(toUrl, fromUrl) { | |
if (fromUrl === '/') { | |
if (url === '/') { | |
url = toUrl; | |
} | |
} else if (url.indexOf(fromUrl) === 0) { | |
url = url.replace(fromUrl, toUrl); | |
} | |
}); | |
return url; | |
}, | |
configOne: function(mapping) { | |
var $__0 = this; | |
if (mapping.redirectTo) { | |
if (this.rewrites[mapping.path]) { | |
throw new Error('"' + mapping.path + '" already maps to "' + this.rewrites[mapping.path] + '"'); | |
} | |
this.rewrites[mapping.path] = mapping.redirectTo; | |
return; | |
} | |
if (mapping.component) { | |
if (mapping.components) { | |
throw new Error('A route config should have either a "component" or "components" property, but not both.'); | |
} | |
mapping.components = mapping.component; | |
delete mapping.component; | |
} | |
if (typeof mapping.components === 'string') { | |
mapping.components = {default: mapping.components}; | |
} | |
var aliases; | |
if (mapping.as) { | |
aliases = [mapping.as]; | |
} else { | |
aliases = mapObj(mapping.components, (function(componentName, outletName) { | |
return outletName + ':' + componentName; | |
})); | |
if (mapping.components.default) { | |
aliases.push(mapping.components.default); | |
} | |
} | |
aliases.forEach((function(alias) { | |
return $__0.recognizer.add([{ | |
path: mapping.path, | |
handler: mapping | |
}], {as: alias}); | |
})); | |
var withChild = copy(mapping); | |
withChild.path += CHILD_ROUTE_SUFFIX; | |
this.recognizer.add([{ | |
path: withChild.path, | |
handler: withChild | |
}]); | |
}, | |
recognize: function(url) { | |
var canonicalUrl = this.getCanonicalUrl(url); | |
var context = this.recognizer.recognize(canonicalUrl); | |
if (context) { | |
context[0].handler.rewroteUrl = canonicalUrl; | |
} | |
return context; | |
}, | |
generate: function(name, params) { | |
return this.recognizer.generate(name, params); | |
}, | |
hasRoute: function(name) { | |
return this.recognizer.hasRoute(name); | |
} | |
}, {}); | |
function copy(obj) { | |
return JSON.parse(JSON.stringify(obj)); | |
} | |
function forEach(obj, fn) { | |
Object.keys(obj).forEach((function(key) { | |
return fn(obj[key], key); | |
})); | |
} | |
function mapObj(obj, fn) { | |
var result = []; | |
Object.keys(obj).forEach((function(key) { | |
return result.push(fn(obj[key], key)); | |
})); | |
return result; | |
} | |
return new Grammar(); | |
}]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment