0 && array[len - 1] && array[len - 1].hasOwnProperty('queryParams')) {
+ queryParams = array[len - 1].queryParams;
+ head = slice.call(array, 0, len - 1);
+ return [head, queryParams];
+ } else {
+ return [array, null];
+ }
+ }
+
+ function performIntermediateTransition(router, recogHandlers, matchPointResults) {
+
+ var handlerInfos = generateHandlerInfos(router, recogHandlers);
+ for (var i = 0; i < handlerInfos.length; ++i) {
+ var handlerInfo = handlerInfos[i];
+ handlerInfo.context = matchPointResults.providedModels[handlerInfo.name];
+ }
+
+ var stubbedTransition = {
+ router: router,
+ isAborted: false
+ };
+
+ setupContexts(stubbedTransition, handlerInfos);
+ }
+
/**
@private
Creates, begins, and returns a Transition.
*/
- function performTransition(router, recogHandlers, providedModelsArray, params, data) {
+ function performTransition(router, recogHandlers, providedModelsArray, params, queryParams, data, isIntermediate) {
- var matchPointResults = getMatchPoint(router, recogHandlers, providedModelsArray, params),
+ var matchPointResults = getMatchPoint(router, recogHandlers, providedModelsArray, params, queryParams),
targetName = recogHandlers[recogHandlers.length - 1].handler,
wasTransitioning = false,
currentHandlerInfos = router.currentHandlerInfos;
+ if (isIntermediate) {
+ return performIntermediateTransition(router, recogHandlers, matchPointResults);
+ }
+
// Check if there's already a transition underway.
if (router.activeTransition) {
- if (transitionsIdentical(router.activeTransition, targetName, providedModelsArray)) {
+ if (transitionsIdentical(router.activeTransition, targetName, providedModelsArray, queryParams)) {
return router.activeTransition;
}
router.activeTransition.abort();
@@ -29850,9 +30657,12 @@ define("router",
transition.providedModelsArray = providedModelsArray;
transition.params = matchPointResults.params;
transition.data = data || {};
+ transition.queryParams = queryParams;
+ transition.pivotHandler = matchPointResults.pivotHandler;
router.activeTransition = transition;
var handlerInfos = generateHandlerInfos(router, recogHandlers);
+ transition.handlerInfos = handlerInfos;
// Fire 'willTransition' event on current handlers, but don't fire it
// if a transition was already underway.
@@ -29861,7 +30671,7 @@ define("router",
}
log(router, transition.sequence, "Beginning validation for transition to " + transition.targetName);
- validateEntry(transition, handlerInfos, 0, matchPointResults.matchPoint, matchPointResults.handlerParams)
+ validateEntry(transition, matchPointResults.matchPoint, matchPointResults.handlerParams)
.then(transitionSuccess, transitionFailure);
return transition;
@@ -29870,14 +30680,10 @@ define("router",
checkAbort(transition);
try {
- log(router, transition.sequence, "Validation succeeded, finalizing transition;");
+ finalizeTransition(transition, handlerInfos);
- // Don't overwrite contexts / update URL if this was a noop transition.
- if (!currentHandlerInfos || !currentHandlerInfos.length ||
- !router.recognizer.hasRoute(currentHandlerInfos[currentHandlerInfos.length - 1].name) ||
- currentHandlerInfos.length !== matchPointResults.matchPoint) {
- finalizeTransition(transition, handlerInfos);
- }
+ // currentHandlerInfos was updated in finalizeTransition
+ trigger(router, router.currentHandlerInfos, true, ['didTransition']);
if (router.didTransition) {
router.didTransition(handlerInfos);
@@ -29886,6 +30692,7 @@ define("router",
log(router, transition.sequence, "TRANSITION COMPLETE.");
// Resolve with the final handler.
+ transition.isActive = false;
deferred.resolve(handlerInfos[handlerInfos.length - 1].handler);
} catch(e) {
deferred.reject(e);
@@ -29916,11 +30723,15 @@ define("router",
var handlerObj = recogHandlers[i],
isDynamic = handlerObj.isDynamic || (handlerObj.names && handlerObj.names.length);
- handlerInfos.push({
+ var handlerInfo = {
isDynamic: !!isDynamic,
name: handlerObj.handler,
handler: router.getHandler(handlerObj.handler)
- });
+ };
+ if(handlerObj.queryParams) {
+ handlerInfo.queryParams = handlerObj.queryParams;
+ }
+ handlerInfos.push(handlerInfo);
}
return handlerInfos;
}
@@ -29928,7 +30739,7 @@ define("router",
/**
@private
*/
- function transitionsIdentical(oldTransition, targetName, providedModelsArray) {
+ function transitionsIdentical(oldTransition, targetName, providedModelsArray, queryParams) {
if (oldTransition.targetName !== targetName) { return false; }
@@ -29938,6 +30749,11 @@ define("router",
for (var i = 0, len = oldModels.length; i < len; ++i) {
if (oldModels[i] !== providedModelsArray[i]) { return false; }
}
+
+ if(!queryParamsEqual(oldTransition.queryParams, queryParams)) {
+ return false;
+ }
+
return true;
}
@@ -29949,25 +30765,39 @@ define("router",
*/
function finalizeTransition(transition, handlerInfos) {
+ log(transition.router, transition.sequence, "Validation succeeded, finalizing transition;");
+
var router = transition.router,
seq = transition.sequence,
- handlerName = handlerInfos[handlerInfos.length - 1].name;
+ handlerName = handlerInfos[handlerInfos.length - 1].name,
+ urlMethod = transition.urlMethod,
+ i;
// Collect params for URL.
var objects = [], providedModels = transition.providedModelsArray.slice();
- for (var i = handlerInfos.length - 1; i>=0; --i) {
+ for (i = handlerInfos.length - 1; i>=0; --i) {
var handlerInfo = handlerInfos[i];
if (handlerInfo.isDynamic) {
var providedModel = providedModels.pop();
objects.unshift(isParam(providedModel) ? providedModel.toString() : handlerInfo.context);
}
+
+ if (handlerInfo.handler.inaccessibleByURL) {
+ urlMethod = null;
+ }
}
- var params = paramsForHandler(router, handlerName, objects);
+ var newQueryParams = {};
+ for (i = handlerInfos.length - 1; i>=0; --i) {
+ merge(newQueryParams, handlerInfos[i].queryParams);
+ }
+ router.currentQueryParams = newQueryParams;
+
+
+ var params = paramsForHandler(router, handlerName, objects, transition.queryParams);
router.currentParams = params;
- var urlMethod = transition.urlMethod;
if (urlMethod) {
var url = router.recognizer.generate(handlerName, params);
@@ -29990,7 +30820,10 @@ define("router",
and `afterModel` in promises, and checks for redirects/aborts
between each.
*/
- function validateEntry(transition, handlerInfos, index, matchPoint, handlerParams) {
+ function validateEntry(transition, matchPoint, handlerParams) {
+
+ var handlerInfos = transition.handlerInfos,
+ index = transition.resolveIndex;
if (index === handlerInfos.length) {
// No more contexts to resolve.
@@ -30014,6 +30847,8 @@ define("router",
return proceed();
}
+ transition.trigger(true, 'willResolveModel', transition, handler);
+
return RSVP.resolve().then(handleAbort)
.then(beforeModel)
.then(handleAbort)
@@ -30034,7 +30869,7 @@ define("router",
}
function handleError(reason) {
- if (reason instanceof Router.TransitionAborted) {
+ if (reason instanceof Router.TransitionAborted || transition.isAborted) {
// if the transition was aborted and *no additional* error was thrown,
// reject with the Router.TransitionAborted instance
return RSVP.reject(reason);
@@ -30047,7 +30882,7 @@ define("router",
// An error was thrown / promise rejected, so fire an
// `error` event from this handler info up to root.
- trigger(router, handlerInfos.slice(0, index + 1), true, ['error', reason, transition]);
+ transition.trigger(true, 'error', reason, transition, handlerInfo.handler);
// Propagate the original error.
return RSVP.reject(reason);
@@ -30057,13 +30892,20 @@ define("router",
log(router, seq, handlerName + ": calling beforeModel hook");
- var p = handler.beforeModel && handler.beforeModel(transition);
+ var args;
+
+ if (handlerInfo.queryParams) {
+ args = [handlerInfo.queryParams, transition];
+ } else {
+ args = [transition];
+ }
+
+ var p = handler.beforeModel && handler.beforeModel.apply(handler, args);
return (p instanceof Transition) ? null : p;
}
function model() {
log(router, seq, handlerName + ": resolving model");
-
var p = getModel(handlerInfo, transition, handlerParams[handlerName], index >= matchPoint);
return (p instanceof Transition) ? null : p;
}
@@ -30078,7 +30920,15 @@ define("router",
transition.resolvedModels[handlerInfo.name] = context;
- var p = handler.afterModel && handler.afterModel(context, transition);
+ var args;
+
+ if (handlerInfo.queryParams) {
+ args = [context, handlerInfo.queryParams, transition];
+ } else {
+ args = [context, transition];
+ }
+
+ var p = handler.afterModel && handler.afterModel.apply(handler, args);
return (p instanceof Transition) ? null : p;
}
@@ -30086,7 +30936,8 @@ define("router",
log(router, seq, handlerName + ": validation succeeded, proceeding");
handlerInfo.context = transition.resolvedModels[handlerInfo.name];
- return validateEntry(transition, handlerInfos, index + 1, matchPoint, handlerParams);
+ transition.resolveIndex++;
+ return validateEntry(transition, matchPoint, handlerParams);
}
}
@@ -30109,9 +30960,8 @@ define("router",
or use one of the models provided to `transitionTo`.
*/
function getModel(handlerInfo, transition, handlerParams, needsUpdate) {
-
var handler = handlerInfo.handler,
- handlerName = handlerInfo.name;
+ handlerName = handlerInfo.name, args;
if (!needsUpdate && handler.hasOwnProperty('context')) {
return handler.context;
@@ -30122,7 +30972,13 @@ define("router",
return typeof providedModel === 'function' ? providedModel() : providedModel;
}
- return handler.model && handler.model(handlerParams || {}, transition);
+ if (handlerInfo.queryParams) {
+ args = [handlerParams || {}, handlerInfo.queryParams, transition];
+ } else {
+ args = [handlerParams || {}, transition, handlerInfo.queryParams];
+ }
+
+ return handler.model && handler.model.apply(handler, args);
}
/**
@@ -30151,14 +31007,16 @@ define("router",
@param {Array[Object]} args arguments passed to transitionTo,
replaceWith, or handleURL
*/
- function doTransition(router, args) {
+ function doTransition(router, args, isIntermediate) {
// Normalize blank transitions to root URL transitions.
var name = args[0] || '/';
- if (name.charAt(0) === '/') {
- return createURLTransition(router, name);
+ if(args.length === 1 && args[0].hasOwnProperty('queryParams')) {
+ return createQueryParamTransition(router, args[0], isIntermediate);
+ } else if (name.charAt(0) === '/') {
+ return createURLTransition(router, name, isIntermediate);
} else {
- return createNamedTransition(router, args);
+ return createNamedTransition(router, slice.call(args), isIntermediate);
}
}
@@ -30198,9 +31056,6 @@ define("router",
}
return object;
}
-
-
- return Router;
});
})();
@@ -30235,34 +31090,25 @@ DSL.prototype = {
if (callback) {
var dsl = new DSL(name);
+ route(dsl, 'loading');
+ route(dsl, 'error', { path: "/_unused_dummy_error_path_route_" + name + "/:error" });
callback.call(dsl);
- this.push(options.path, name, dsl.generate());
+ this.push(options.path, name, dsl.generate(), options.queryParams);
} else {
- this.push(options.path, name);
+ this.push(options.path, name, null, options.queryParams);
}
+
},
- push: function(url, name, callback) {
+ push: function(url, name, callback, queryParams) {
var parts = name.split('.');
if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; }
- this.matches.push([url, name, callback]);
+ this.matches.push([url, name, callback, queryParams]);
},
route: function(name, options) {
-
-
- options = options || {};
-
- if (typeof options.path !== 'string') {
- options.path = "/" + name;
- }
-
- if (this.parent && this.parent !== 'application') {
- name = this.parent + "." + name;
- }
-
- this.push(options.path, name);
+ route(this, name, options);
},
generate: function() {
@@ -30281,6 +31127,22 @@ DSL.prototype = {
}
};
+function route(dsl, name, options) {
+
+
+ options = options || {};
+
+ if (typeof options.path !== 'string') {
+ options.path = "/" + name;
+ }
+
+ if (dsl.parent && dsl.parent !== 'application') {
+ name = dsl.parent + "." + name;
+ }
+
+ dsl.push(options.path, name, null, options.queryParams);
+}
+
DSL.map = function(callback) {
var dsl = new DSL();
callback.call(dsl);
@@ -30366,7 +31228,7 @@ Ember.generateController = function(container, controllerName, context) {
@submodule ember-routing
*/
-var Router = requireModule("router");
+var Router = requireModule("router")['default'];
var get = Ember.get, set = Ember.set;
var defineProperty = Ember.defineProperty;
@@ -30379,7 +31241,7 @@ var DefaultView = Ember._MetamorphView;
@namespace Ember
@extends Ember.Object
*/
-Ember.Router = Ember.Object.extend({
+Ember.Router = Ember.Object.extend(Ember.Evented, {
location: 'hash',
init: function() {
@@ -30413,19 +31275,18 @@ Ember.Router = Ember.Object.extend({
},
didTransition: function(infos) {
- var appController = this.container.lookup('controller:application'),
- path = Ember.Router._routePath(infos);
+ updatePaths(this);
- if (!('currentPath' in appController)) { defineProperty(appController, 'currentPath'); }
- set(appController, 'currentPath', path);
-
- if (!('currentRouteName' in appController)) { defineProperty(appController, 'currentRouteName'); }
- set(appController, 'currentRouteName', infos[infos.length - 1].name);
+ this._cancelLoadingEvent();
this.notifyPropertyChange('url');
+ // Put this in the runloop so url will be accurate. Seems
+ // less surprising than didTransition being out of sync.
+ Ember.run.once(this, this.trigger, 'didTransition');
+
if (get(this, 'namespace').LOG_TRANSITIONS) {
- Ember.Logger.log("Transitioned into '" + path + "'");
+ Ember.Logger.log("Transitioned into '" + Ember.Router._routePath(infos) + "'");
}
},
@@ -30437,6 +31298,17 @@ Ember.Router = Ember.Object.extend({
return this._doTransition('transitionTo', arguments);
},
+ intermediateTransitionTo: function() {
+ this.router.intermediateTransitionTo.apply(this.router, arguments);
+
+ updatePaths(this);
+
+ var infos = this.router.currentHandlerInfos;
+ if (get(this, 'namespace').LOG_TRANSITIONS) {
+ Ember.Logger.log("Intermediate-transitioned into '" + Ember.Router._routePath(infos) + "'");
+ }
+ },
+
replaceWith: function() {
return this._doTransition('replaceWith', arguments);
},
@@ -30471,6 +31343,13 @@ Ember.Router = Ember.Object.extend({
this.router.reset();
},
+ willDestroy: function(){
+ var location = get(this, 'location');
+ location.destroy();
+
+ this._super.apply(this, arguments);
+ },
+
_lookupActiveView: function(templateName) {
var active = this._activeViews[templateName];
return active && active[0];
@@ -30520,8 +31399,6 @@ Ember.Router = Ember.Object.extend({
seen[name] = true;
if (!handler) {
- if (name === 'loading') { return {}; }
-
container.register(routeName, DefaultRoute.extend());
handler = container.lookup(routeName);
@@ -30530,16 +31407,6 @@ Ember.Router = Ember.Object.extend({
}
}
- if (name === 'application') {
- // Inject default `error` handler.
- // Note: `events` is deprecated, but we'll let the
- // deprecation warnings be handled at event-handling time rather
- // than duplicating that logic here.
- var actions = handler._actions || handler.events;
- if (!actions) { actions = handler._actions = {}; }
- actions.error = actions.error || Ember.Router._defaultErrorHandler;
- }
-
handler.routeName = name;
return handler;
};
@@ -30580,12 +31447,12 @@ Ember.Router = Ember.Object.extend({
args = [].slice.call(args);
args[0] = args[0] || '/';
- var passedName = args[0], name, self = this;
+ var passedName = args[0], name, self = this,
+ isQueryParamsOnly = false;
- if (passedName.charAt(0) === '/') {
+ if (!isQueryParamsOnly && passedName.charAt(0) === '/') {
name = passedName;
- } else {
-
+ } else if (!isQueryParamsOnly) {
if (!this.router.hasRoute(passedName)) {
name = args[0] = passedName + '.index';
} else {
@@ -30596,14 +31463,7 @@ Ember.Router = Ember.Object.extend({
var transitionPromise = this.router[method].apply(this.router, args);
- // Don't schedule loading state entry if user has already aborted the transition.
- if (this.router.activeTransition) {
- this._scheduleLoadingStateEntry();
- }
-
- transitionPromise.then(function(route) {
- self._transitionCompleted(route);
- }, function(error) {
+ transitionPromise.then(null, function(error) {
if (error.name === "UnrecognizedURLError") {
}
@@ -30615,49 +31475,152 @@ Ember.Router = Ember.Object.extend({
return transitionPromise;
},
- _scheduleLoadingStateEntry: function() {
- if (this._loadingStateActive) { return; }
- this._shouldEnterLoadingState = true;
- Ember.run.scheduleOnce('routerTransitions', this, this._enterLoadingState);
+ _scheduleLoadingEvent: function(transition, originRoute) {
+ this._cancelLoadingEvent();
+ this._loadingStateTimer = Ember.run.scheduleOnce('routerTransitions', this, '_fireLoadingEvent', transition, originRoute);
},
- _enterLoadingState: function() {
- if (this._loadingStateActive || !this._shouldEnterLoadingState) { return; }
-
- var loadingRoute = this.router.getHandler('loading');
- if (loadingRoute) {
- if (loadingRoute.enter) { loadingRoute.enter(); }
- if (loadingRoute.setup) { loadingRoute.setup(); }
- this._loadingStateActive = true;
+ _fireLoadingEvent: function(transition, originRoute) {
+ if (!this.router.activeTransition) {
+ // Don't fire an event if we've since moved on from
+ // the transition that put us in a loading state.
+ return;
}
+
+ transition.trigger(true, 'loading', transition, originRoute);
},
- _exitLoadingState: function () {
- this._shouldEnterLoadingState = false;
- if (!this._loadingStateActive) { return; }
-
- var loadingRoute = this.router.getHandler('loading');
- if (loadingRoute && loadingRoute.exit) { loadingRoute.exit(); }
- this._loadingStateActive = false;
- },
-
- _transitionCompleted: function(route) {
- this.notifyPropertyChange('url');
- this._exitLoadingState();
+ _cancelLoadingEvent: function () {
+ if (this._loadingStateTimer) {
+ Ember.run.cancel(this._loadingStateTimer);
+ }
+ this._loadingStateTimer = null;
}
});
+/**
+ @private
+
+ Helper function for iterating root-ward, starting
+ from (but not including) the provided `originRoute`.
+
+ Returns true if the last callback fired requested
+ to bubble upward.
+ */
+function forEachRouteAbove(originRoute, transition, callback) {
+ var handlerInfos = transition.handlerInfos,
+ originRouteFound = false;
+
+ for (var i = handlerInfos.length - 1; i >= 0; --i) {
+ var handlerInfo = handlerInfos[i],
+ route = handlerInfo.handler;
+
+ if (!originRouteFound) {
+ if (originRoute === route) {
+ originRouteFound = true;
+ }
+ continue;
+ }
+
+ if (callback(route, handlerInfos[i + 1].handler) !== true) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// These get invoked when an action bubbles above ApplicationRoute
+// and are not meant to be overridable.
+var defaultActionHandlers = {
+
+ willResolveModel: function(transition, originRoute) {
+ originRoute.router._scheduleLoadingEvent(transition, originRoute);
+ },
+
+ error: function(error, transition, originRoute) {
+ // Attempt to find an appropriate error substate to enter.
+ var router = originRoute.router;
+
+ var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) {
+ var childErrorRouteName = findChildRouteName(route, childRoute, 'error');
+ if (childErrorRouteName) {
+ router.intermediateTransitionTo(childErrorRouteName, error);
+ return;
+ }
+ return true;
+ });
+
+ if (tryTopLevel) {
+ // Check for top-level error state to enter.
+ if (routeHasBeenDefined(originRoute.router, 'application_error')) {
+ router.intermediateTransitionTo('application_error', error);
+ return;
+ }
+ } else {
+ // Don't fire an assertion if we found an error substate.
+ return;
+ }
+
+ Ember.Logger.assert(false, 'Error while loading route: ' + Ember.inspect(error));
+ },
+
+ loading: function(transition, originRoute) {
+ // Attempt to find an appropriate loading substate to enter.
+ var router = originRoute.router;
+
+ var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) {
+ var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading');
+
+ if (childLoadingRouteName) {
+ router.intermediateTransitionTo(childLoadingRouteName);
+ return;
+ }
+
+ // Don't bubble above pivot route.
+ if (transition.pivotHandler !== route) {
+ return true;
+ }
+ });
+
+ if (tryTopLevel) {
+ // Check for top-level loading state to enter.
+ if (routeHasBeenDefined(originRoute.router, 'application_loading')) {
+ router.intermediateTransitionTo('application_loading');
+ return;
+ }
+ }
+ }
+};
+
+function findChildRouteName(parentRoute, originatingChildRoute, name) {
+ var router = parentRoute.router,
+ childName,
+ targetChildRouteName = originatingChildRoute.routeName.split('.').pop(),
+ namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.';
+
+ childName = namespace + name;
+ if (routeHasBeenDefined(router, childName)) {
+ return childName;
+ }
+}
+
+function routeHasBeenDefined(router, name) {
+ var container = router.container;
+ return router.hasRoute(name) &&
+ (container.has('template:' + name) || container.has('route:' + name));
+}
+
function triggerEvent(handlerInfos, ignoreFailure, args) {
var name = args.shift();
if (!handlerInfos) {
if (ignoreFailure) { return; }
- throw new Ember.Error("Could not trigger event '" + name + "'. There are no active handlers");
+ throw new Ember.Error("Can't trigger action '" + name + "' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call `.send()` on the `Transition` object passed to the `model/beforeModel/afterModel` hooks.");
}
var eventWasHandled = false;
- for (var i=handlerInfos.length-1; i>=0; i--) {
+ for (var i = handlerInfos.length - 1; i >= 0; i--) {
var handlerInfo = handlerInfos[i],
handler = handlerInfo.handler;
@@ -30667,21 +31630,46 @@ function triggerEvent(handlerInfos, ignoreFailure, args) {
} else {
return;
}
- } else if (handler.events && handler.events[name]) {
-
- if (handler.events[name].apply(handler, args) === true) {
- eventWasHandled = true;
- } else {
- return;
- }
}
}
+ if (defaultActionHandlers[name]) {
+ defaultActionHandlers[name].apply(null, args);
+ return;
+ }
+
if (!eventWasHandled && !ignoreFailure) {
- throw new Ember.Error("Nothing handled the event '" + name + "'.");
+ throw new Ember.Error("Nothing handled the action '" + name + "'.");
}
}
+function updatePaths(router) {
+ var appController = router.container.lookup('controller:application');
+
+ if (!appController) {
+ // appController might not exist when top-level loading/error
+ // substates have been entered since ApplicationRoute hasn't
+ // actually been entered at that point.
+ return;
+ }
+
+ var infos = router.router.currentHandlerInfos,
+ path = Ember.Router._routePath(infos);
+
+ if (!('currentPath' in appController)) {
+ defineProperty(appController, 'currentPath');
+ }
+
+ set(appController, 'currentPath', path);
+
+ if (!('currentRouteName' in appController)) {
+ defineProperty(appController, 'currentRouteName');
+ }
+
+ set(appController, 'currentRouteName', infos[infos.length - 1].name);
+}
+
+
Ember.Router.reopenClass({
router: null,
map: function(callback) {
@@ -30711,13 +31699,6 @@ Ember.Router.reopenClass({
return router;
},
- _defaultErrorHandler: function(error, transition) {
- Ember.Logger.error('Error while loading route:', error);
-
- // Using setTimeout allows us to escape from the Promise's try/catch block
- setTimeout(function() { throw error; });
- },
-
_routePath: function(handlerInfos) {
var path = [];
@@ -30732,6 +31713,9 @@ Ember.Router.reopenClass({
}
});
+Router.Transition.prototype.send = Router.Transition.prototype.trigger;
+
+
})();
@@ -30750,6 +31734,7 @@ var get = Ember.get, set = Ember.set,
a_forEach = Ember.EnumerableUtils.forEach,
a_replace = Ember.EnumerableUtils.replace;
+
/**
The `Ember.Route` class is used to define individual routes. Refer to
the [routing guide](http://emberjs.com/guides/routing/) for documentation.
@@ -30759,6 +31744,7 @@ var get = Ember.get, set = Ember.set,
@extends Ember.Object
*/
Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
+
/**
@private
@@ -31023,7 +32009,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
Transition into another route. Optionally supply model(s) for the
route in question. If multiple models are supplied they will be applied
last to first recursively up the resource tree (see Multiple Models Example
- below). The model(s) will be serialized into the URL using the appropriate
+ below). The model(s) will be serialized into the URL using the appropriate
route's `serialize` hook. See also 'replaceWith'.
Simple Transition Example
@@ -31047,6 +32033,24 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
});
```
+ Transition to a nested route
+
+ ```javascript
+ App.Router.map(function() {
+ this.resource('articles', { path: '/articles' }, function() {
+ this.route('new');
+ });
+ });
+
+ App.IndexRoute = Ember.Route.extend({
+ actions: {
+ transitionToNewArticle: function() {
+ this.transitionTo('articles.new');
+ }
+ }
+ });
+ ```
+
Multiple Models Example
```javascript
@@ -31078,9 +32082,29 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
return router.transitionTo.apply(router, arguments);
},
+ /**
+ Perform a synchronous transition into another route with out attempting
+ to resolve promises, update the URL, or abort any currently active
+ asynchronous transitions (i.e. regular transitions caused by
+ `transitionTo` or URL changes).
+
+ This method is handy for performing intermediate transitions on the
+ way to a final destination route, and is called internally by the
+ default implementations of the `error` and `loading` handlers.
+
+ @method intermediateTransitionTo
+ @param {String} name the name of the route
+ @param {...Object} models the model(s) to be used while transitioning
+ to the route.
+ */
+ intermediateTransitionTo: function() {
+ var router = this.router;
+ router.intermediateTransitionTo.apply(router, arguments);
+ },
+
/**
Transition into another route while replacing the current URL, if possible.
- This will replace the current history entry instead of adding a new one.
+ This will replace the current history entry instead of adding a new one.
Beside that, it is identical to `transitionTo` in all other respects. See
'transitionTo' for additional information regarding multiple models.
@@ -31108,7 +32132,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
*/
replaceWith: function() {
var router = this.router;
- return this.router.replaceWith.apply(this.router, arguments);
+ return router.replaceWith.apply(router, arguments);
},
/**
@@ -31156,7 +32180,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@method setup
*/
- setup: function(context) {
+ setup: function(context, queryParams) {
var controllerName = this.controllerName || this.routeName,
controller = this.controllerFor(controllerName, true);
if (!controller) {
@@ -31167,18 +32191,20 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
// referenced in action handlers
this.controller = controller;
+ var args = [controller, context];
+
if (this.setupControllers) {
this.setupControllers(controller, context);
} else {
- this.setupController(controller, context);
+ this.setupController.apply(this, args);
}
if (this.renderTemplates) {
this.renderTemplates(context);
} else {
- this.renderTemplate(controller, context);
+ this.renderTemplate.apply(this, args);
}
},
@@ -31269,6 +32295,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@method beforeModel
@param {Transition} transition
+ @param {Object} queryParams the active query params for this route
@return {Promise} if the value returned from this hook is
a promise, the transition will pause until the transition
resolves. Otherwise, non-promise return values are not
@@ -31302,12 +32329,13 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@param {Object} resolvedModel the value returned from `model`,
or its resolved value if it was a promise
@param {Transition} transition
+ @param {Object} queryParams the active query params for this handler
@return {Promise} if the value returned from this hook is
a promise, the transition will pause until the transition
resolves. Otherwise, non-promise return values are not
utilized in any way.
*/
- afterModel: function(resolvedModel, transition) {
+ afterModel: function(resolvedModel, transition, queryParams) {
this.redirect(resolvedModel, transition);
},
@@ -31367,6 +32395,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@method model
@param {Object} params the parameters extracted from the URL
@param {Transition} transition
+ @param {Object} queryParams the query params for this route
@return {Object|Promise} the model for this route. If
a promise is returned, the transition will pause until
the promise resolves, and the resolved value of the promise
@@ -31513,6 +32542,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
instance would be used.
Example
+
```js
App.PostRoute = Ember.Route.extend({
setupController: function(controller, model) {
@@ -31737,9 +32767,18 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
}
options = options || {};
- name = name ? name.replace(/\//g, '.') : this.routeName;
+
+ var templateName;
+
+ if (name) {
+ name = name.replace(/\//g, '.');
+ templateName = name;
+ } else {
+ name = this.routeName;
+ templateName = this.templateName || name;
+ }
+
var viewName = options.view || this.viewName || name;
- var templateName = this.templateName || name;
var container = this.container,
view = container.lookup('view:' + viewName),
@@ -32181,7 +33220,13 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
templateContext = helperParameters.context,
paths = getResolvedPaths(helperParameters),
length = paths.length,
- path, i;
+ path, i, normalizedPath;
+
+ var linkTextPath = helperParameters.options.linkTextPath;
+ if (linkTextPath) {
+ normalizedPath = Ember.Handlebars.normalizePath(templateContext, linkTextPath, helperParameters.options.data);
+ this.registerObserver(normalizedPath.root, normalizedPath.path, this, this.rerender);
+ }
for(i=0; i < length; i++) {
path = paths[i];
@@ -32190,8 +33235,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
continue;
}
- var normalizedPath =
- Ember.Handlebars.normalizePath(templateContext, path, helperParameters.options.data);
+ normalizedPath = Ember.Handlebars.normalizePath(templateContext, path, helperParameters.options.data);
this.registerObserver(normalizedPath.root, normalizedPath.path, this, this._paramsChanged);
}
},
@@ -32200,13 +33244,25 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
@private
This method is invoked by observers installed during `init` that fire
- whenever the helpers
+ whenever the params change
@method _paramsChanged
*/
_paramsChanged: function() {
this.notifyPropertyChange('resolvedParams');
},
+
+ /**
+ @private
+
+ This method is invoked by observers installed during `init` that fire
+ whenever the query params change
+ */
+ _queryParamsChanged: function (object, path) {
+ this.notifyPropertyChange('queryParams');
+ },
+
+
/**
@private
@@ -32329,6 +33385,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
types = options.types,
data = options.data;
+ // Original implementation if query params not enabled
return resolveParams(parameters.context, parameters.params, { types: types, data: data });
}).property(),
@@ -32342,7 +33399,6 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
@return {Array} An array with the route name and any dynamic segments
*/
routeArgs: Ember.computed(function() {
-
var resolvedParams = get(this, 'resolvedParams').slice(0),
router = get(this, 'router'),
namedRoute = resolvedParams[0];
@@ -32362,8 +33418,37 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
}
return resolvedParams;
+ }).property('resolvedParams', 'queryParams', 'router.url'),
+
+
+ _potentialQueryParams: Ember.computed(function () {
+ var namedRoute = get(this, 'resolvedParams')[0];
+ if (!namedRoute) { return null; }
+ var router = get(this, 'router');
+
+ namedRoute = fullRouteName(router, namedRoute);
+
+ return router.router.queryParamsForHandler(namedRoute);
}).property('resolvedParams'),
+ queryParams: Ember.computed(function () {
+ var self = this,
+ queryParams = null,
+ allowedQueryParams = get(this, '_potentialQueryParams');
+
+ if (!allowedQueryParams) { return null; }
+ allowedQueryParams.forEach(function (param) {
+ var value = get(self, param);
+ if (typeof value !== 'undefined') {
+ queryParams = queryParams || {};
+ queryParams[param] = value;
+ }
+ });
+
+
+ return queryParams;
+ }).property('_potentialQueryParams.[]'),
+
/**
Sets the element's `href` attribute to the url for
the `LinkView`'s targeted route.
@@ -32433,44 +33518,44 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
To override this option for your entire application, see
"Overriding Application-wide Defaults".
-
+
### Disabling the `link-to` helper
- By default `{{link-to}}` is enabled.
+ By default `{{link-to}}` is enabled.
any passed value to `disabled` helper property will disable the `link-to` helper.
-
+
static use: the `disabled` option:
-
+
```handlebars
{{#link-to 'photoGallery' disabled=true}}
Great Hamster Photos
{{/link-to}}
```
-
+
dynamic use: the `disabledWhen` option:
-
+
```handlebars
{{#link-to 'photoGallery' disabledWhen=controller.someProperty}}
Great Hamster Photos
{{/link-to}}
```
-
+
any passed value to `disabled` will disable it except `undefined`.
to ensure that only `true` disable the `link-to` helper you can
override the global behaviour of `Ember.LinkView`.
-
- ```javascript
+
+ ```javascript
Ember.LinkView.reopen({
disabled: Ember.computed(function(key, value) {
- if (value !== undefined) {
- this.set('_isDisabled', value === true);
+ if (value !== undefined) {
+ this.set('_isDisabled', value === true);
}
return value === true ? get(this, 'disabledClass') : false;
})
});
```
-
+
see "Overriding Application-wide Defaults" for more.
-
+
### Handling `href`
`{{link-to}}` will use your application's Router to
fill the element's `href` property with a url that
@@ -32648,6 +33733,22 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
hash.disabledBinding = hash.disabledWhen;
+ if (!options.fn) {
+ var linkTitle = params.shift();
+ var linkType = options.types.shift();
+ var context = this;
+ if (linkType === 'ID') {
+ options.linkTextPath = linkTitle;
+ options.fn = function() {
+ return Ember.Handlebars.get(context, linkTitle, options);
+ };
+ } else {
+ options.fn = function() {
+ return linkTitle;
+ };
+ }
+ }
+
hash.parameters = {
context: this,
options: options,
@@ -32790,13 +33891,13 @@ var get = Ember.get, set = Ember.set;
Ember.onLoad('Ember.Handlebars', function(Handlebars) {
/**
- Calling ``{{render}}`` from within a template will insert another
+ Calling ``{{render}}`` from within a template will insert another
template that matches the provided name. The inserted template will
access its properties on its own controller (rather than the controller
of the parent template).
If a view class with the same name exists, the view class also will be used.
-
+
Note: A given controller may only be used *once* in your app in this manner.
A singleton instance of the controller will be created for you.
@@ -32808,17 +33909,17 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
});
```
- ```handelbars
+ ```handlebars
Hello, {{who}}.
```
```handelbars
-
+
My great app
- {{render navigaton}}
+ {{render navigation}}
```
-
+
```html
My great app
@@ -32826,7 +33927,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
```
- Optionally you may provide a second argument: a property path
+ Optionally you may provide a second argument: a property path
that will be bound to the `model` property of the controller.
If a `model` property path is specified, then a new instance of the
@@ -32956,9 +34057,15 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
var keys = ["alt", "shift", "meta", "ctrl"];
- var isAllowedClick = function(event, allowedKeys) {
+ var POINTER_EVENT_TYPE_REGEX = /^click|mouse|touch/;
+
+ var isAllowedEvent = function(event, allowedKeys) {
if (typeof allowedKeys === "undefined") {
- return isSimpleClick(event);
+ if (POINTER_EVENT_TYPE_REGEX.test(event.type)) {
+ return isSimpleClick(event);
+ } else {
+ allowedKeys = [];
+ }
}
if (allowedKeys.indexOf("any") >= 0) {
@@ -32982,7 +34089,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
ActionHelper.registeredActions[actionId] = {
eventName: options.eventName,
handler: function(event) {
- if (!isAllowedClick(event, allowedKeys)) { return true; }
+ if (!isAllowedEvent(event, allowedKeys)) { return true; }
event.preventDefault();
@@ -33988,10 +35095,11 @@ Ember.HistoryLocation = Ember.Object.extend({
*/
getURL: function() {
var rootURL = get(this, 'rootURL'),
- url = get(this, 'location').pathname;
+ location = get(this, 'location'),
+ path = location.pathname;
rootURL = rootURL.replace(/\/$/, '');
- url = url.replace(rootURL, '');
+ var url = path.replace(rootURL, '');
return url;
},
@@ -34519,6 +35627,10 @@ Ember.DefaultResolver = Ember.Object.extend({
return this.resolveOther(parsedName);
},
+ resolveHelper: function(parsedName) {
+ return this.resolveOther(parsedName) || Ember.Handlebars.helpers[parsedName.fullNameWithoutType];
+ },
+
/**
Lookup the model on the Application namespace
@@ -34812,8 +35924,7 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin
if (!this.$) { this.$ = Ember.$; }
this.__container__ = this.buildContainer();
- this.Router = this.Router || this.defaultRouter();
- if (this.Router) { this.Router.namespace = this; }
+ this.Router = this.defaultRouter();
this._super();
@@ -34871,13 +35982,17 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin
@method defaultRouter
@return {Ember.Router} the default router
*/
+
defaultRouter: function() {
- // Create a default App.Router if one was not supplied to make
- // it possible to do App.Router.map(...) without explicitly
- // creating a router first.
- if (this.router === undefined) {
- return Ember.Router.extend();
+ if (this.Router === false) { return; }
+ var container = this.__container__;
+
+ if (this.Router) {
+ container.unregister('router:main');
+ container.register('router:main', this.Router);
}
+
+ return container.lookupFactory('router:main');
},
/**
@@ -34942,7 +36057,7 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin
Call `advanceReadiness` after any asynchronous setup logic has completed.
Each call to `deferReadiness` must be matched by a call to `advanceReadiness`
or the application will never become ready and routing will not begin.
-
+
@method advanceReadiness
@see {Ember.Application#deferReadiness}
*/
@@ -35034,7 +36149,9 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin
// At this point, the App.Router must already be assigned
if (this.Router) {
- this.register('router:main', this.Router);
+ var container = this.__container__;
+ container.unregister('router:main');
+ container.register('router:main', this.Router);
}
this.runInitializers();
@@ -35311,6 +36428,9 @@ Ember.Application.reopenClass({
container.optionsForType('component', { singleton: false });
container.optionsForType('view', { singleton: false });
container.optionsForType('template', { instantiate: false });
+
+ container.optionsForType('helper', { instantiate: false });
+
container.register('application:main', namespace, { instantiate: false });
container.register('controller:basic', Ember.Controller, { instantiate: false });
@@ -35319,6 +36439,7 @@ Ember.Application.reopenClass({
container.register('route:basic', Ember.Route, { instantiate: false });
container.register('event_dispatcher:main', Ember.EventDispatcher);
+ container.register('router:main', Ember.Router);
container.injection('router:main', 'namespace', 'application:main');
container.injection('controller', 'target', 'router:main');