Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ember

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ember - npm Package Compare versions

Comparing version 0.9.8 to 1.0.0-pre

routing.js

360

application.js

@@ -7,2 +7,3 @@ //

require("./states");
require("./routing");

@@ -96,21 +97,21 @@

Instantiate all controllers currently available on the namespace
and inject them onto a state manager.
and inject them onto a router.
Example:
App.PostsController = Ember.ArrayController.extend();
App.CommentsController = Ember.ArrayController.extend();
App.PostsController = Ember.ArrayController.extend();
App.CommentsController = Ember.ArrayController.extend();
var stateManager = Ember.StateManager.create({
...
});
var router = Ember.Router.create({
...
});
App.initialize(stateManager);
App.initialize(router);
stateManager.get('postsController') // <App.PostsController:ember1234>
stateManager.get('commentsController') // <App.CommentsController:ember1235>
router.get('postsController') // <App.PostsController:ember1234>
router.get('commentsController') // <App.CommentsController:ember1235>
stateManager.getPath('postsController.stateManager') // stateManager
router.get('postsController.router') // router
*/
initialize: function(stateManager) {
initialize: function(router) {
var properties = Ember.A(Ember.keys(this)),

@@ -120,9 +121,30 @@ injections = get(this.constructor, 'injections'),

if (!router && Ember.Router.detect(namespace['Router'])) {
router = namespace['Router'].create();
this._createdRouter = router;
}
if (router) {
set(this, 'router', router);
// By default, the router's namespace is the current application.
//
// This allows it to find model classes when a state has a
// route like `/posts/:post_id`. In that case, it would first
// convert `post_id` into `Post`, and then look it up on its
// namespace.
set(router, 'namespace', this);
}
Ember.runLoadHooks('application', this);
properties.forEach(function(property) {
injections.forEach(function(injection) {
injection(namespace, stateManager, property);
injections.forEach(function(injection) {
properties.forEach(function(property) {
injection[1](namespace, router, property);
});
});
if (router && router instanceof Ember.Router) {
this.startRouting(router);
}
},

@@ -133,3 +155,2 @@

var eventDispatcher = get(this, 'eventDispatcher'),
stateManager = get(this, 'stateManager'),
customEvents = get(this, 'customEvents');

@@ -140,6 +161,2 @@

this.ready();
if (stateManager) {
this.setupStateManager(stateManager);
}
},

@@ -150,12 +167,22 @@

If the application has a state manager, use it to route
to the current URL, and trigger a new call to `route`
whenever the URL changes.
If the application has a router, use it to route to the current URL, and
trigger a new call to `route` whenever the URL changes.
*/
setupStateManager: function(stateManager) {
var location = get(stateManager, 'location');
startRouting: function(router) {
var location = get(router, 'location'),
rootElement = get(this, 'rootElement'),
applicationController = get(router, 'applicationController');
stateManager.route(location.getURL());
Ember.assert("ApplicationView and ApplicationController must be defined on your application", (this.ApplicationView && applicationController) );
var applicationView = this.ApplicationView.create({
controller: applicationController
});
this._createdApplicationView = applicationView;
applicationView.appendTo(rootElement);
router.route(location.getURL());
location.onUpdateURL(function(url) {
stateManager.route(url);
router.route(url);
});

@@ -171,9 +198,10 @@ },

/** @private */
destroy: function() {
willDestroy: function() {
get(this, 'eventDispatcher').destroy();
return this._super();
if (this._createdRouter) { this._createdRouter.destroy(); }
if (this._createdApplicationView) { this._createdApplicationView.destroy(); }
},
registerInjection: function(callback) {
this.constructor.registerInjection(callback);
registerInjection: function(options) {
this.constructor.registerInjection(options);
}

@@ -185,15 +213,38 @@ });

injections: Ember.A(),
registerInjection: function(callback) {
get(this, 'injections').pushObject(callback);
registerInjection: function(options) {
var injections = get(this, 'injections'),
before = options.before,
name = options.name,
injection = options.injection,
location;
if (before) {
location = injections.find(function(item) {
if (item[0] === before) { return true; }
});
location = injections.indexOf(location);
} else {
location = get(injections, 'length');
}
injections.splice(location, 0, [name, injection]);
}
});
Ember.Application.registerInjection(function(app, stateManager, property) {
if (!/^[A-Z].*Controller$/.test(property)) { return; }
Ember.Application.registerInjection({
name: 'controllers',
injection: function(app, router, property) {
if (!/^[A-Z].*Controller$/.test(property)) { return; }
var name = property[0].toLowerCase() + property.substr(1),
controller = app[property].create();
var name = property.charAt(0).toLowerCase() + property.substr(1),
controller = app[property].create();
stateManager.set(name, controller);
controller.set('target', stateManager);
router.set(name, controller);
controller.setProperties({
target: router,
controllers: router,
namespace: app
});
}
});

@@ -216,15 +267,58 @@

onUpdateURL(callback): triggers the callback when the URL changes
formatURL(url): formats `url` to be placed into `href` attribute
Calling setURL will not trigger onUpdateURL callbacks.
TODO: This, as well as the Ember.Location documentation below, should
perhaps be moved so that it's visible in the JsDoc output.
*/
/**
@class
Ember.Location returns an instance of the correct implementation of
the `location` API.
You can pass it a `implementation` ('hash', 'history', 'none') to force a
particular implementation.
*/
Ember.Location = {
create: function(options) {
var implementation = options && options.implementation;
Ember.assert("Ember.Location.create: you must specify a 'implementation' option", !!implementation);
var implementationClass = this.implementations[implementation];
Ember.assert("Ember.Location.create: " + implementation + " is not a valid implementation", !!implementationClass);
return implementationClass.create.apply(implementationClass, arguments);
},
registerImplementation: function(name, implementation) {
this.implementations[name] = implementation;
},
implementations: {}
};
})();
(function() {
var get = Ember.get, set = Ember.set;
/**
@class
Ember.HashLocation implements the location API using the browser's
hash. At present, it relies on a hashchange event existing in the
browser.
@extends Ember.Object
*/
Ember.HashLocation = Ember.Object.extend({
Ember.HashLocation = Ember.Object.extend(
/** @scope Ember.HashLocation.prototype */ {
/** @private */
init: function() {
set(this, 'location', get(this, 'location') || window.location);
set(this, 'callbacks', Ember.A());
},

@@ -262,4 +356,5 @@

var self = this;
var guid = Ember.guidFor(this);
var hashchange = function() {
Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
var path = location.hash.substr(1);

@@ -271,34 +366,138 @@ if (get(self, 'lastSetURL') === path) { return; }

callback(location.hash.substr(1));
};
});
},
get(this, 'callbacks').pushObject(hashchange);
window.addEventListener('hashchange', hashchange);
/**
@private
Given a URL, formats it to be placed into the page as part
of an element's `href` attribute.
This is used, for example, when using the {{action}} helper
to generate a URL based on an event.
*/
formatURL: function(url) {
return '#'+url;
},
/** @private */
willDestroy: function() {
get(this, 'callbacks').forEach(function(callback) {
window.removeEventListener('hashchange', callback);
});
set(this, 'callbacks', null);
var guid = Ember.guidFor(this);
Ember.$(window).unbind('hashchange.ember-location-'+guid);
}
});
Ember.Location.registerImplementation('hash', Ember.HashLocation);
})();
(function() {
var get = Ember.get, set = Ember.set;
/**
Ember.Location returns an instance of the correct implementation of
the `location` API.
@class
You can pass it a `style` ('hash', 'html5', 'none') to force a
particular implementation.
Ember.HistoryLocation implements the location API using the browser's
history.pushState API.
@extends Ember.Object
*/
Ember.Location = {
create: function(options) {
var style = options && options.style;
Ember.assert("you must provide a style to Ember.Location.create", !!style);
Ember.HistoryLocation = Ember.Object.extend(
/** @scope Ember.HistoryLocation.prototype */ {
if (style === "hash") {
return Ember.HashLocation.create.apply(Ember.HashLocation, arguments);
/** @private */
init: function() {
set(this, 'location', get(this, 'location') || window.location);
set(this, '_initialURL', get(this, 'location').pathname);
},
/**
Will be pre-pended to path upon state change
*/
rootURL: '/',
/**
@private
Used to give history a starting reference
*/
_initialURL: null,
/**
@private
Returns the current `location.pathname`.
*/
getURL: function() {
return get(this, 'location').pathname;
},
/**
@private
Uses `history.pushState` to update the url without a page reload.
*/
setURL: function(path) {
var state = window.history.state,
initialURL = get(this, '_initialURL');
path = this.formatPath(path);
if ((initialURL !== path && !state) || (state && state.path !== path)) {
window.history.pushState({ path: path }, null, path);
}
},
/**
@private
Register a callback to be invoked whenever the browser
history changes, including using forward and back buttons.
*/
onUpdateURL: function(callback) {
var guid = Ember.guidFor(this);
Ember.$(window).bind('popstate.ember-location-'+guid, function(e) {
callback(location.pathname);
});
},
/**
@private
returns the given path appended to rootURL
*/
formatPath: function(path) {
var rootURL = get(this, 'rootURL');
if (path !== '') {
rootURL = rootURL.replace(/\/$/, '');
}
return rootURL + path;
},
/**
@private
Used when using {{action}} helper. Since no formatting
is required we just return the url given.
*/
formatURL: function(url) {
return url;
},
/** @private */
willDestroy: function() {
var guid = Ember.guidFor(this);
Ember.$(window).unbind('popstate.ember-location-'+guid);
}
};
});
Ember.Location.registerImplementation('history', Ember.HistoryLocation);
})();

@@ -309,3 +508,40 @@

(function() {
var get = Ember.get, set = Ember.set;
/**
@class
Ember.NoneLocation does not interact with the browser. It is useful for
testing, or when you need to manage state with your Router, but temporarily
don't want it to muck with the URL (for example when you embed your
application in a larger page).
@extends Ember.Object
*/
Ember.NoneLocation = Ember.Object.extend(
/** @scope Ember.NoneLocation.prototype */ {
path: '',
getURL: function() {
return get(this, 'path');
},
setURL: function(path) {
set(this, 'path', path);
},
onUpdateURL: function(callback) {
// We are not wired up to the browser, so we'll never trigger the callback.
},
formatURL: function(url) {
// The return value is not overly meaningful, but we do not want to throw
// errors when test code renders templates containing {{action href=true}}
// helpers.
return url;
}
});
Ember.Location.registerImplementation('none', Ember.NoneLocation);
})();

@@ -319,1 +555,7 @@

(function() {
})();

54

debug.js

@@ -19,22 +19,18 @@ //

Ember.ENV = 'undefined' === typeof ENV ? {} : ENV;
if (!('MANDATORY_SETTER' in Ember.ENV)) {
Ember.ENV.MANDATORY_SETTER = true; // default to true for debug dist
}
/**
Define an assertion that will throw an exception if the condition is not
met. Ember build tools will remove any calls to Ember.assert() when
doing a production build.
doing a production build. Example:
## Examples
// Test for truthiness
Ember.assert('Must pass a valid object', obj);
// Fail unconditionally
Ember.assert('This code path should never be run')
#js:
// pass a simple Boolean value
Ember.assert('must pass a valid object', !!obj);
// pass a function. If the function returns false the assertion fails
// any other return value (including void) will pass.
Ember.assert('a passed record must have a firstName', function() {
if (obj instanceof Ember.Record) {
return !Ember.empty(obj.firstName);
}
});
@static

@@ -47,8 +43,6 @@ @function

@param {Boolean} test
Must return true for the assertion to pass. If you pass a function it
will be executed. If the function returns false an exception will be
Must be truthy for the assertion to pass. If falsy, an exception will be
thrown.
*/
Ember.assert = function(desc, test) {
if ('function' === typeof test) test = test()!==false;
if (!test) throw new Error("assertion failed: "+desc);

@@ -68,9 +62,9 @@ };

@param {Boolean} test
An optional boolean or function. If the test returns false, the warning
will be displayed.
An optional boolean. If falsy, the warning will be displayed.
*/
Ember.warn = function(message, test) {
if (arguments.length === 1) { test = false; }
if ('function' === typeof test) test = test()!==false;
if (!test) Ember.Logger.warn("WARNING: "+message);
if (!test) {
Ember.Logger.warn("WARNING: "+message);
if ('trace' in Ember.Logger) Ember.Logger.trace();
}
};

@@ -89,4 +83,3 @@

@param {Boolean} test
An optional boolean or function. If the test returns false, the deprecation
will be displayed.
An optional boolean. If falsy, the deprecation will be displayed.
*/

@@ -97,3 +90,2 @@ Ember.deprecate = function(message, test) {

if (arguments.length === 1) { test = false; }
if ('function' === typeof test) { test = test()!==false; }
if (test) { return; }

@@ -103,3 +95,3 @@

var error, stackStr = '';
var error;

@@ -109,5 +101,4 @@ // When using new Error, we can't do the arguments check for Chrome. Alternatives are welcome

if (error.stack) {
var stack;
if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) {
var stack, stackStr = '';
if (error['arguments']) {

@@ -126,5 +117,6 @@ // Chrome

stackStr = "\n " + stack.slice(2).join("\n ");
message = message + stackStr;
}
Ember.Logger.warn("DEPRECATION: "+message+stackStr);
Ember.Logger.warn("DEPRECATION: "+message);
};

@@ -131,0 +123,0 @@

@@ -8,2 +8,3 @@ //

require("./states");
require("./routing");
require("./handlebars");

@@ -10,0 +11,0 @@ require("./application");

@@ -7,7 +7,7 @@ {

"author": "Charles Jolley",
"version": "v0.9.8",
"version": "v1.0.0-pre",
"dependencies": {
"handlebars": "~> 1.0.0-beta.6",
"window": "git://github.com/charlesjolley/node-window.git#master",
"jquery": "~> 1.7",
"jquery": "~> 1.7.0",
"convoy": "~0.3"

@@ -14,0 +14,0 @@ },

@@ -9,11 +9,36 @@ //

(function() {
var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
var get = Ember.get, set = Ember.set;
Ember.State = Ember.Object.extend(Ember.Evented, {
/**
@class
@extends Ember.Object
*/
Ember.State = Ember.Object.extend(Ember.Evented,
/** @scope Ember.State.prototype */{
isState: true,
/**
A reference to the parent state.
@type Ember.State
*/
parentState: null,
start: null,
/**
The name of this state.
@type String
*/
name: null,
/**
The full path to this state.
@type String
@readOnly
*/
path: Ember.computed(function() {
var parentPath = getPath(this, 'parentState.path'),
var parentPath = get(this, 'parentState.path'),
path = get(this, 'name');

@@ -34,3 +59,3 @@

*/
fire: function(name) {
trigger: function(name) {
if (this[name]) {

@@ -42,7 +67,9 @@ this[name].apply(this, [].slice.call(arguments, 1));

/** @private */
init: function() {
var states = get(this, 'states'), foundStates;
set(this, 'childStates', Ember.A());
set(this, 'eventTransitions', get(this, 'eventTransitions') || {});
var name;
var name, value, transitionTarget;

@@ -60,3 +87,10 @@ // As a convenience, loop over the properties

if (name === "constructor") { continue; }
this.setupChild(states, name, this[name]);
if (value = this[name]) {
if (transitionTarget = value.transitionTarget) {
this.eventTransitions[name] = transitionTarget;
}
this.setupChild(states, name, value);
}
}

@@ -71,14 +105,16 @@

set(this, 'routes', {});
set(this, 'pathsCache', {});
set(this, 'pathsCacheNoContext', {});
},
/** @private */
setupChild: function(states, name, value) {
if (!value) { return false; }
if (Ember.State.detect(value)) {
if (value.isState) {
set(value, 'name', name);
} else if (Ember.State.detect(value)) {
value = value.create({
name: name
});
} else if (value.isState) {
set(value, 'name', name);
}

@@ -93,2 +129,13 @@

lookupEventTransition: function(name) {
var path, state = this;
while(state && !path) {
path = state.eventTransitions[name];
state = state.get('parentState');
}
return path;
},
/**

@@ -99,3 +146,3 @@ A Boolean value indicating whether the state is a leaf state

@property {Boolean}
@type Boolean
*/

@@ -106,7 +153,87 @@ isLeaf: Ember.computed(function() {

setupControllers: Ember.K,
/**
A boolean value indicating whether the state takes a context.
By default we assume all states take contexts.
*/
hasContext: true,
/**
This is the default transition event.
@event
@param {Ember.StateManager} manager
@param context
@see Ember.StateManager#transitionEvent
*/
setup: Ember.K,
/**
This event fires when the state is entered.
@event
@param {Ember.StateManager} manager
*/
enter: Ember.K,
/**
This event fires when the state is exited.
@event
@param {Ember.StateManager} manager
*/
exit: Ember.K
});
var Event = Ember.$ && Ember.$.Event;
Ember.State.reopenClass(
/** @scope Ember.State */{
/**
@static
Creates an action function for transitioning to the named state while preserving context.
The following example StateManagers are equivalent:
aManager = Ember.StateManager.create({
stateOne: Ember.State.create({
changeToStateTwo: Ember.State.transitionTo('stateTwo')
}),
stateTwo: Ember.State.create({})
})
bManager = Ember.StateManager.create({
stateOne: Ember.State.create({
changeToStateTwo: function(manager, context){
manager.transitionTo('stateTwo', context)
}
}),
stateTwo: Ember.State.create({})
})
@param {String} target
*/
transitionTo: function(target) {
var event = function(stateManager, context) {
if (Event && context instanceof Event) {
if (context.hasOwnProperty('context')) {
context = context.context;
} else {
// If we received an event and it doesn't contain
// a context, don't pass along a superfluous
// context to the target of the event.
return stateManager.transitionTo(target);
}
}
stateManager.transitionTo(target, context);
};
event.transitionTarget = target;
return event;
}
});
})();

@@ -117,6 +244,168 @@

(function() {
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
var arrayForEach = Ember.ArrayPolyfills.forEach;
/**
@private
A Transition takes the enter, exit and resolve states and normalizes
them:
* takes any passed in contexts into consideration
* adds in `initialState`s
*/
var Transition = function(raw) {
this.enterStates = raw.enterStates.slice();
this.exitStates = raw.exitStates.slice();
this.resolveState = raw.resolveState;
this.finalState = raw.enterStates[raw.enterStates.length - 1] || raw.resolveState;
};
Transition.prototype = {
/**
@private
Normalize the passed in enter, exit and resolve states.
This process also adds `finalState` and `contexts` to the Transition object.
@param {Ember.StateManager} manager the state manager running the transition
@param {Array} contexts a list of contexts passed into `transitionTo`
*/
normalize: function(manager, contexts) {
this.matchContextsToStates(contexts);
this.addInitialStates();
this.removeUnchangedContexts(manager);
return this;
},
/**
@private
Match each of the contexts passed to `transitionTo` to a state.
This process may also require adding additional enter and exit
states if there are more contexts than enter states.
@param {Array} contexts a list of contexts passed into `transitionTo`
*/
matchContextsToStates: function(contexts) {
var stateIdx = this.enterStates.length - 1,
matchedContexts = [],
state,
context;
// Next, we will match the passed in contexts to the states they
// represent.
//
// First, assign a context to each enter state in reverse order. If
// any contexts are left, add a parent state to the list of states
// to enter and exit, and assign a context to the parent state.
//
// If there are still contexts left when the state manager is
// reached, raise an exception.
//
// This allows the following:
//
// |- root
// | |- post
// | | |- comments
// | |- about (* current state)
//
// For `transitionTo('post.comments', post, post.get('comments')`,
// the first context (`post`) will be assigned to `root.post`, and
// the second context (`post.get('comments')`) will be assigned
// to `root.post.comments`.
//
// For the following:
//
// |- root
// | |- post
// | | |- index (* current state)
// | | |- comments
//
// For `transitionTo('post.comments', otherPost, otherPost.get('comments')`,
// the `<root.post>` state will be added to the list of enter and exit
// states because its context has changed.
while (contexts.length > 0) {
if (stateIdx >= 0) {
state = this.enterStates[stateIdx--];
} else {
if (this.enterStates.length) {
state = get(this.enterStates[0], 'parentState');
if (!state) { throw "Cannot match all contexts to states"; }
} else {
// If re-entering the current state with a context, the resolve
// state will be the current state.
state = this.resolveState;
}
this.enterStates.unshift(state);
this.exitStates.unshift(state);
}
// in routers, only states with dynamic segments have a context
if (get(state, 'hasContext')) {
context = contexts.pop();
} else {
context = null;
}
matchedContexts.unshift(context);
}
this.contexts = matchedContexts;
},
/**
@private
Add any `initialState`s to the list of enter states.
*/
addInitialStates: function() {
var finalState = this.finalState, initialState;
while(true) {
initialState = get(finalState, 'initialState') || 'start';
finalState = get(finalState, 'states.' + initialState);
if (!finalState) { break; }
this.finalState = finalState;
this.enterStates.push(finalState);
this.contexts.push(undefined);
}
},
/**
@private
Remove any states that were added because the number of contexts
exceeded the number of explicit enter states, but the context has
not changed since the last time the state was entered.
@param {Ember.StateManager} manager passed in to look up the last
context for a states
*/
removeUnchangedContexts: function(manager) {
// Start from the beginning of the enter states. If the state was added
// to the list during the context matching phase, make sure the context
// has actually changed since the last time the state was entered.
while (this.enterStates.length > 0) {
if (this.enterStates[0] !== this.exitStates[0]) { break; }
if (this.enterStates.length === this.contexts.length) {
if (manager.getStateMeta(this.enterStates[0], 'context') !== this.contexts[0]) { break; }
this.contexts.shift();
}
this.resolveState = this.enterStates.shift();
this.exitStates.shift();
}
}
};
/**
@class
StateManager is part of Ember's implementation of a finite state machine. A StateManager

@@ -160,3 +449,3 @@ instance manages a number of properties that are instances of `Ember.State`,

When created a StateManager instance will immediately enter into the state
defined as its `start` property or the state referenced by name in its
defined as its `start` property or the state referenced by name in its
`initialState` property:

@@ -168,3 +457,3 @@

managerA.getPath('currentState.name') // 'start'
managerA.get('currentState.name') // 'start'

@@ -176,5 +465,5 @@ managerB = Ember.StateManager.create({

managerB.getPath('currentState.name') // 'beginHere'
managerB.get('currentState.name') // 'beginHere'
Because it is a property you may also provided a computed function if you wish to derive
Because it is a property you may also provide a computed function if you wish to derive
an `initialState` programmatically:

@@ -198,20 +487,20 @@

Calling `goToState` transitions between states:
Calling `transitionTo` transitions between states:
robotManager = Ember.StateManager.create({
initialState: 'poweredDown',
poweredDown: Ember.State.create({}),
poweredUp: Ember.State.create({})
})
robotManager = Ember.StateManager.create({
initialState: 'poweredDown',
poweredDown: Ember.State.create({}),
poweredUp: Ember.State.create({})
})
robotManager.getPath('currentState.name') // 'poweredDown'
robotManager.goToState('poweredUp')
robotManager.getPath('currentState.name') // 'poweredUp'
robotManager.get('currentState.name') // 'poweredDown'
robotManager.transitionTo('poweredUp')
robotManager.get('currentState.name') // 'poweredUp'
Before transitioning into a new state the existing `currentState` will have its
`exit` method called with the StateManager instance as its first argument and
`exit` method called with the StateManager instance as its first argument and
an object representing the transition as its second argument.
After transitioning into a new state the new `currentState` will have its
`enter` method called with the StateManager instance as its first argument and
`enter` method called with the StateManager instance as its first argument and
an object representing the transition as its second argument.

@@ -222,3 +511,3 @@

poweredDown: Ember.State.create({
exit: function(stateManager, transition){
exit: function(stateManager){
console.log("exiting the poweredDown state")

@@ -228,3 +517,3 @@ }

poweredUp: Ember.State.create({
enter: function(stateManager, transition){
enter: function(stateManager){
console.log("entering the poweredUp state. Destroy all humans.")

@@ -235,4 +524,4 @@ }

robotManager.getPath('currentState.name') // 'poweredDown'
robotManager.goToState('poweredUp')
robotManager.get('currentState.name') // 'poweredDown'
robotManager.transitionTo('poweredUp')
// will log

@@ -244,3 +533,3 @@ // 'exiting the poweredDown state'

Once a StateManager is already in a state, subsequent attempts to enter that state will
not trigger enter or exit method calls. Attempts to transition into a state that the
not trigger enter or exit method calls. Attempts to transition into a state that the
manager does not have will result in no changes in the StateManager's current state:

@@ -251,3 +540,3 @@

poweredDown: Ember.State.create({
exit: function(stateManager, transition){
exit: function(stateManager){
console.log("exiting the poweredDown state")

@@ -257,3 +546,3 @@ }

poweredUp: Ember.State.create({
enter: function(stateManager, transition){
enter: function(stateManager){
console.log("entering the poweredUp state. Destroy all humans.")

@@ -264,103 +553,102 @@ }

robotManager.getPath('currentState.name') // 'poweredDown'
robotManager.goToState('poweredUp')
robotManager.get('currentState.name') // 'poweredDown'
robotManager.transitionTo('poweredUp')
// will log
// 'exiting the poweredDown state'
// 'entering the poweredUp state. Destroy all humans.'
robotManager.goToState('poweredUp') // no logging, no state change
robotManager.transitionTo('poweredUp') // no logging, no state change
robotManager.goToState('someUnknownState') // silently fails
robotManager.getPath('currentState.name') // 'poweredUp'
robotManager.transitionTo('someUnknownState') // silently fails
robotManager.get('currentState.name') // 'poweredUp'
Each state property may itself contain properties that are instances of Ember.State.
The StateManager can transition to specific sub-states in a series of goToState method calls or
via a single goToState with the full path to the specific state. The StateManager will also
Each state property may itself contain properties that are instances of Ember.State.
The StateManager can transition to specific sub-states in a series of transitionTo method calls or
via a single transitionTo with the full path to the specific state. The StateManager will also
keep track of the full path to its currentState
robotManager = Ember.StateManager.create({
initialState: 'poweredDown',
poweredDown: Ember.State.create({
charging: Ember.State.create(),
charged: Ember.State.create()
}),
poweredUp: Ember.State.create({
mobile: Ember.State.create(),
stationary: Ember.State.create()
})
})
initialState: 'poweredDown',
poweredDown: Ember.State.create({
charging: Ember.State.create(),
charged: Ember.State.create()
}),
poweredUp: Ember.State.create({
mobile: Ember.State.create(),
stationary: Ember.State.create()
})
})
robotManager.getPath('currentState.name') // 'poweredDown'
robotManager.get('currentState.name') // 'poweredDown'
robotManager.goToState('poweredUp')
robotManager.getPath('currentState.name') // 'poweredUp'
robotManager.transitionTo('poweredUp')
robotManager.get('currentState.name') // 'poweredUp'
robotManager.goToState('mobile')
robotManager.getPath('currentState.name') // 'mobile'
robotManager.transitionTo('mobile')
robotManager.get('currentState.name') // 'mobile'
// transition via a state path
robotManager.goToState('poweredDown.charging')
robotManager.getPath('currentState.name') // 'charging'
// transition via a state path
robotManager.transitionTo('poweredDown.charging')
robotManager.get('currentState.name') // 'charging'
robotManager.getPath('currentState.get.path') // 'poweredDown.charging'
robotManager.get('currentState.path') // 'poweredDown.charging'
Enter transition methods will be called for each state and nested child state in their
hierarchical order. Exit methods will be called for each state and its nested states in
reverse hierarchical order.
Enter transition methods will be called for each state and nested child state in their
hierarchical order. Exit methods will be called for each state and its nested states in
reverse hierarchical order.
Exit transitions for a parent state are not called when entering into one of its child states,
only when transitioning to a new section of possible states in the hierarchy.
Exit transitions for a parent state are not called when entering into one of its child states,
only when transitioning to a new section of possible states in the hierarchy.
robotManager = Ember.StateManager.create({
initialState: 'poweredDown',
poweredDown: Ember.State.create({
robotManager = Ember.StateManager.create({
initialState: 'poweredDown',
poweredDown: Ember.State.create({
enter: function(){},
exit: function(){
console.log("exited poweredDown state")
},
charging: Ember.State.create({
enter: function(){},
exit: function(){
console.log("exited poweredDown state")
},
charging: Ember.State.create({
enter: function(){},
exit: function(){}
}),
charged: Ember.State.create({
enter: function(){
console.log("entered charged state")
},
exit: function(){
console.log("exited charged state")
}
})
exit: function(){}
}),
poweredUp: Ember.State.create({
charged: Ember.State.create({
enter: function(){
console.log("entered poweredUp state")
console.log("entered charged state")
},
exit: function(){},
mobile: Ember.State.create({
enter: function(){
console.log("entered mobile state")
},
exit: function(){}
}),
stationary: Ember.State.create({
enter: function(){},
exit: function(){}
})
exit: function(){
console.log("exited charged state")
}
})
}),
poweredUp: Ember.State.create({
enter: function(){
console.log("entered poweredUp state")
},
exit: function(){},
mobile: Ember.State.create({
enter: function(){
console.log("entered mobile state")
},
exit: function(){}
}),
stationary: Ember.State.create({
enter: function(){},
exit: function(){}
})
})
})
robotManager.get('currentState.get.path') // 'poweredDown'
robotManager.goToState('charged')
// logs 'entered charged state'
// but does *not* log 'exited poweredDown state'
robotManager.getPath('currentState.name') // 'charged
robotManager.get('currentState.path') // 'poweredDown'
robotManager.transitionTo('charged')
// logs 'entered charged state'
// but does *not* log 'exited poweredDown state'
robotManager.get('currentState.name') // 'charged
robotManager.goToState('poweredUp.mobile')
// logs
// 'exited charged state'
// 'exited poweredDown state'
// 'entered poweredUp state'
// 'entered mobile state'
robotManager.transitionTo('poweredUp.mobile')
// logs
// 'exited charged state'
// 'exited poweredDown state'
// 'entered poweredUp state'
// 'entered mobile state'

@@ -370,5 +658,5 @@ During development you can set a StateManager's `enableLogging` property to `true` to

robotManager = Ember.StateManager.create({
enableLogging: true
})
robotManager = Ember.StateManager.create({
enableLogging: true
})

@@ -380,3 +668,3 @@ ## Managing currentState with Actions

and moving up through the parent states in a state hierarchy until an appropriate method is found
or the StateManager instance itself is reached.
or the StateManager instance itself is reached.

@@ -398,3 +686,3 @@ If an appropriately named method is found it will be called with the state manager as the first

managerA.getPath('currentState.name') // 'subsubstateOne'
managerA.get('currentState.name') // 'subsubstateOne'
managerA.send('anAction')

@@ -407,7 +695,7 @@ // 'stateOne.substateOne.subsubstateOne' has no anAction method

someObject = {}
managerA.send('anAction', someObject)
// the 'anAction' method of 'stateOne.substateOne' is called again
// with managerA as the first argument and
// someObject as the second argument.
someObject = {}
managerA.send('anAction', someObject)
// the 'anAction' method of 'stateOne.substateOne' is called again
// with managerA as the first argument and
// someObject as the second argument.

@@ -420,65 +708,83 @@

managerB = Ember.StateManager.create({
initialState: 'stateOne.substateOne.subsubstateOne',
stateOne: Ember.State.create({
substateOne: Ember.State.create({
subsubstateOne: Ember.State.create({})
})
}),
stateTwo: Ember.State.create({
anAction: function(manager, context){
// will not be called below because it is
// not a parent of the current state
}
managerB = Ember.StateManager.create({
initialState: 'stateOne.substateOne.subsubstateOne',
stateOne: Ember.State.create({
substateOne: Ember.State.create({
subsubstateOne: Ember.State.create({})
})
}),
stateTwo: Ember.State.create({
anAction: function(manager, context){
// will not be called below because it is
// not a parent of the current state
}
})
})
managerB.getPath('currentState.name') // 'subsubstateOne'
managerB.send('anAction')
// Error: <Ember.StateManager:ember132> could not
// respond to event anAction in state stateOne.substateOne.subsubstateOne.
managerB.get('currentState.name') // 'subsubstateOne'
managerB.send('anAction')
// Error: <Ember.StateManager:ember132> could not
// respond to event anAction in state stateOne.substateOne.subsubstateOne.
Inside of an action method the given state should delegate `goToState` calls on its
Inside of an action method the given state should delegate `transitionTo` calls on its
StateManager.
robotManager = Ember.StateManager.create({
initialState: 'poweredDown.charging',
poweredDown: Ember.State.create({
charging: Ember.State.create({
chargeComplete: function(manager, context){
manager.goToState('charged')
}
}),
charged: Ember.State.create({
boot: function(manager, context){
manager.goToState('poweredUp')
}
})
}),
poweredUp: Ember.State.create({
beginExtermination: function(manager, context){
manager.goToState('rampaging')
},
rampaging: Ember.State.create()
})
})
robotManager = Ember.StateManager.create({
initialState: 'poweredDown.charging',
poweredDown: Ember.State.create({
charging: Ember.State.create({
chargeComplete: function(manager, context){
manager.transitionTo('charged')
}
}),
charged: Ember.State.create({
boot: function(manager, context){
manager.transitionTo('poweredUp')
}
})
}),
poweredUp: Ember.State.create({
beginExtermination: function(manager, context){
manager.transitionTo('rampaging')
},
rampaging: Ember.State.create()
})
})
robotManager.getPath('currentState.name') // 'charging'
robotManager.send('boot') // throws error, no boot action
// in current hierarchy
robotManager.getPath('currentState.name') // remains 'charging'
robotManager.get('currentState.name') // 'charging'
robotManager.send('boot') // throws error, no boot action
// in current hierarchy
robotManager.get('currentState.name') // remains 'charging'
robotManager.send('beginExtermination') // throws error, no beginExtermination
// action in current hierarchy
robotManager.getPath('currentState.name') // remains 'charging'
robotManager.send('beginExtermination') // throws error, no beginExtermination
// action in current hierarchy
robotManager.get('currentState.name') // remains 'charging'
robotManager.send('chargeComplete')
robotManager.getPath('currentState.name') // 'charged'
robotManager.send('chargeComplete')
robotManager.get('currentState.name') // 'charged'
robotManager.send('boot')
robotManager.getPath('currentState.name') // 'poweredUp'
robotManager.send('boot')
robotManager.get('currentState.name') // 'poweredUp'
robotManager.send('beginExtermination', allHumans)
robotManager.getPath('currentState.name') // 'rampaging'
robotManager.send('beginExtermination', allHumans)
robotManager.get('currentState.name') // 'rampaging'
Transition actions can also be created using the `transitionTo` method of the Ember.State class. The
following example StateManagers are equivalent:
aManager = Ember.StateManager.create({
stateOne: Ember.State.create({
changeToStateTwo: Ember.State.transitionTo('stateTwo')
}),
stateTwo: Ember.State.create({})
})
bManager = Ember.StateManager.create({
stateOne: Ember.State.create({
changeToStateTwo: function(manager, context){
manager.transitionTo('stateTwo', context)
}
}),
stateTwo: Ember.State.create({})
})
**/

@@ -500,3 +806,3 @@ Ember.StateManager = Ember.State.extend(

if (!initialState && getPath(this, 'states.start')) {
if (!initialState && get(this, 'states.start')) {
initialState = 'start';

@@ -506,9 +812,45 @@ }

if (initialState) {
this.goToState(initialState);
this.transitionTo(initialState);
Ember.assert('Failed to transition to initial state "' + initialState + '"', !!get(this, 'currentState'));
}
},
stateMetaFor: function(state) {
var meta = get(this, 'stateMeta'),
stateMeta = meta.get(state);
if (!stateMeta) {
stateMeta = {};
meta.set(state, stateMeta);
}
return stateMeta;
},
setStateMeta: function(state, key, value) {
return set(this.stateMetaFor(state), key, value);
},
getStateMeta: function(state, key) {
return get(this.stateMetaFor(state), key);
},
/**
The current state from among the manager's possible states. This property should
not be set directly. Use `transitionTo` to move between states by name.
@type Ember.State
@readOnly
*/
currentState: null,
/**
The name of transitionEvent that this stateManager will dispatch
@property {String}
@default 'setup'
*/
transitionEvent: 'setup',
/**
If set to true, `errorOnUnhandledEvents` will cause an exception to be

@@ -518,3 +860,4 @@ raised if you attempt to send an event to a state manager that is not

@property {Boolean}
@type Boolean
@default true
*/

@@ -524,10 +867,10 @@ errorOnUnhandledEvent: true,

send: function(event, context) {
this.sendRecursively(event, get(this, 'currentState'), context);
Ember.assert('Cannot send event "' + event + '" while currentState is ' + get(this, 'currentState'), get(this, 'currentState'));
return this.sendRecursively(event, get(this, 'currentState'), context);
},
sendRecursively: function(event, currentState, context) {
var log = this.enableLogging;
var log = this.enableLogging,
action = currentState[event];
var action = currentState[event];
// Test to see if the action is a method that

@@ -541,9 +884,9 @@ // can be invoked. Don't blindly check just for

if (log) { Ember.Logger.log(fmt("STATEMANAGER: Sending event '%@' to state %@.", [event, get(currentState, 'path')])); }
action.call(currentState, this, context);
return action.call(currentState, this, context);
} else {
var parentState = get(currentState, 'parentState');
if (parentState) {
this.sendRecursively(event, parentState, context);
return this.sendRecursively(event, parentState, context);
} else if (get(this, 'errorOnUnhandledEvent')) {
throw new Ember.Error(this.toString() + " could not respond to event " + event + " in state " + getPath(this, 'currentState.path') + ".");
throw new Ember.Error(this.toString() + " could not respond to event " + event + " in state " + get(this, 'currentState.path') + ".");
}

@@ -553,118 +896,205 @@ }

findStatesByRoute: function(state, route) {
if (!route || route === "") { return undefined; }
var r = route.split('.'), ret = [];
/**
Finds a state by its state path.
for (var i=0, len = r.length; i < len; i += 1) {
var states = get(state, 'states') ;
Example:
if (!states) { return undefined; }
manager = Ember.StateManager.create({
root: Ember.State.create({
dashboard: Ember.State.create()
})
});
var s = get(states, r[i]);
if (s) { state = s; ret.push(s); }
else { return undefined; }
manager.getStateByPath(manager, "root.dashboard")
// returns the dashboard state
@param {Ember.State} root the state to start searching from
@param {String} path the state path to follow
@returns {Ember.State} the state at the end of the path
*/
getStateByPath: function(root, path) {
var parts = path.split('.'),
state = root;
for (var i=0, l=parts.length; i<l; i++) {
state = get(get(state, 'states'), parts[i]);
if (!state) { break; }
}
return ret;
return state;
},
goToState: function() {
// not deprecating this yet so people don't constantly need to
// make trivial changes for little reason.
return this.transitionTo.apply(this, arguments);
},
findStateByPath: function(state, path) {
var possible;
pathForSegments: function(array) {
return Ember.ArrayUtils.map(array, function(tuple) {
Ember.assert("A segment passed to transitionTo must be an Array", Ember.typeOf(tuple) === "array");
return tuple[0];
}).join(".");
while (!possible && state) {
possible = this.getStateByPath(state, path);
state = get(state, 'parentState');
}
return possible;
},
transitionTo: function(name, context) {
// 1. Normalize arguments
// 2. Ensure that we are in the correct state
// 3. Map provided path to context objects and send
// appropriate setupControllers events
/**
@private
if (Ember.empty(name)) { return; }
A state stores its child states in its `states` hash.
This code takes a path like `posts.show` and looks
up `origin.states.posts.states.show`.
var segments;
It returns a list of all of the states from the
origin, which is the list of states to call `enter`
on.
*/
findStatesByPath: function(origin, path) {
if (!path || path === "") { return undefined; }
var r = path.split('.'),
ret = [];
if (Ember.typeOf(name) === "array") {
segments = Array.prototype.slice.call(arguments);
} else {
segments = [[name, context]];
}
for (var i=0, len = r.length; i < len; i++) {
var states = get(origin, 'states');
var path = this.pathForSegments(segments);
if (!states) { return undefined; }
var currentState = get(this, 'currentState') || this, state, newState;
var s = get(states, r[i]);
if (s) { origin = s; ret.push(s); }
else { return undefined; }
}
var exitStates = [], enterStates, resolveState;
return ret;
},
state = currentState;
goToState: function() {
// not deprecating this yet so people don't constantly need to
// make trivial changes for little reason.
return this.transitionTo.apply(this, arguments);
},
if (state.routes[path]) {
// cache hit
transitionTo: function(path, context) {
// XXX When is transitionTo called with no path
if (Ember.empty(path)) { return; }
var route = state.routes[path];
exitStates = route.exitStates;
enterStates = route.enterStates;
state = route.futureState;
resolveState = route.resolveState;
} else {
// cache miss
// The ES6 signature of this function is `path, ...contexts`
var contexts = context ? Array.prototype.slice.call(arguments, 1) : [],
currentState = get(this, 'currentState') || this;
newState = this.findStatesByRoute(currentState, path);
// First, get the enter, exit and resolve states for the current state
// and specified path. If possible, use an existing cache.
var hash = this.contextFreeTransition(currentState, path);
while (state && !newState) {
exitStates.unshift(state);
// Next, process the raw state information for the contexts passed in.
var transition = new Transition(hash).normalize(this, contexts);
state = get(state, 'parentState');
if (!state) {
newState = this.findStatesByRoute(this, path);
if (!newState) { return; }
}
newState = this.findStatesByRoute(state, path);
}
this.enterState(transition);
this.triggerSetupContext(transition);
},
resolveState = state;
contextFreeTransition: function(currentState, path) {
var cache = currentState.pathsCache[path];
if (cache) { return cache; }
enterStates = newState.slice(0);
exitStates = exitStates.slice(0);
var enterStates = this.findStatesByPath(currentState, path),
exitStates = [],
resolveState = currentState;
if (enterStates.length > 0) {
state = enterStates[enterStates.length - 1];
// Walk up the states. For each state, check whether a state matching
// the `path` is nested underneath. This will find the closest
// parent state containing `path`.
//
// This allows the user to pass in a relative path. For example, for
// the following state hierarchy:
//
// | |root
// | |- posts
// | | |- show (* current)
// | |- comments
// | | |- show
//
// If the current state is `<root.posts.show>`, an attempt to
// transition to `comments.show` will match `<root.comments.show>`.
//
// First, this code will look for root.posts.show.comments.show.
// Next, it will look for root.posts.comments.show. Finally,
// it will look for `root.comments.show`, and find the state.
//
// After this process, the following variables will exist:
//
// * resolveState: a common parent state between the current
// and target state. In the above example, `<root>` is the
// `resolveState`.
// * enterStates: a list of all of the states represented
// by the path from the `resolveState`. For example, for
// the path `root.comments.show`, `enterStates` would have
// `[<root.comments>, <root.comments.show>]`
// * exitStates: a list of all of the states from the
// `resolveState` to the `currentState`. In the above
// example, `exitStates` would have
// `[<root.posts>`, `<root.posts.show>]`.
while (resolveState && !enterStates) {
exitStates.unshift(resolveState);
while (enterStates.length > 0 && enterStates[0] === exitStates[0]) {
enterStates.shift();
exitStates.shift();
resolveState = get(resolveState, 'parentState');
if (!resolveState) {
enterStates = this.findStatesByPath(this, path);
if (!enterStates) {
Ember.assert('Could not find state for path: "'+path+'"');
return;
}
}
enterStates = this.findStatesByPath(resolveState, path);
}
currentState.routes[path] = {
exitStates: exitStates,
enterStates: enterStates,
futureState: state,
resolveState: resolveState
};
// If the path contains some states that are parents of both the
// current state and the target state, remove them.
//
// For example, in the following hierarchy:
//
// |- root
// | |- post
// | | |- index (* current)
// | | |- show
//
// If the `path` is `root.post.show`, the three variables will
// be:
//
// * resolveState: `<state manager>`
// * enterStates: `[<root>, <root.post>, <root.post.show>]`
// * exitStates: `[<root>, <root.post>, <root.post.index>]`
//
// The goal of this code is to remove the common states, so we
// have:
//
// * resolveState: `<root.post>`
// * enterStates: `[<root.post.show>]`
// * exitStates: `[<root.post.index>]`
//
// This avoid unnecessary calls to the enter and exit transitions.
while (enterStates.length > 0 && enterStates[0] === exitStates[0]) {
resolveState = enterStates.shift();
exitStates.shift();
}
this.enterState(exitStates, enterStates, state);
this.triggerSetupContext(resolveState, segments);
// Cache the enterStates, exitStates, and resolveState for the
// current state and the `path`.
var transitions = currentState.pathsCache[path] = {
exitStates: exitStates,
enterStates: enterStates,
resolveState: resolveState
};
return transitions;
},
triggerSetupContext: function(root, segments) {
var state = root;
triggerSetupContext: function(transitions) {
var contexts = transitions.contexts,
offset = transitions.enterStates.length - contexts.length,
enterStates = transitions.enterStates,
transitionEvent = get(this, 'transitionEvent');
Ember.ArrayUtils.forEach(segments, function(tuple) {
var path = tuple[0], context = tuple[1];
Ember.assert("More contexts provided than states", offset >= 0);
state = this.findStatesByRoute(state, path);
state = state[state.length-1];
state.fire('setupControllers', this, context);
arrayForEach.call(enterStates, function(state, idx) {
state.trigger(transitionEvent, this, contexts[idx-offset]);
}, this);
//getPath(root, path).setupControllers(this, context);
},

@@ -683,63 +1113,16 @@

asyncEach: function(list, callback, doneCallback) {
var async = false, self = this;
if (!list.length) {
if (doneCallback) { doneCallback.call(this); }
return;
}
var head = list[0];
var tail = list.slice(1);
var transition = {
async: function() { async = true; },
resume: function() {
self.asyncEach(tail, callback, doneCallback);
}
};
callback.call(this, head, transition);
if (!async) { transition.resume(); }
},
enterState: function(exitStates, enterStates, state) {
enterState: function(transition) {
var log = this.enableLogging;
var stateManager = this;
var exitStates = transition.exitStates.slice(0).reverse();
arrayForEach.call(exitStates, function(state) {
state.trigger('exit', this);
}, this);
exitStates = exitStates.slice(0).reverse();
this.asyncEach(exitStates, function(state, transition) {
state.fire('exit', stateManager, transition);
}, function() {
this.asyncEach(enterStates, function(state, transition) {
if (log) { Ember.Logger.log("STATEMANAGER: Entering " + get(state, 'path')); }
state.fire('enter', stateManager, transition);
}, function() {
var startState = state, enteredState, initialState;
arrayForEach.call(transition.enterStates, function(state) {
if (log) { Ember.Logger.log("STATEMANAGER: Entering " + get(state, 'path')); }
state.trigger('enter', this);
}, this);
initialState = get(startState, 'initialState');
if (!initialState) {
initialState = 'start';
}
// right now, start states cannot be entered asynchronously
while (startState = get(get(startState, 'states'), initialState)) {
enteredState = startState;
if (log) { Ember.Logger.log("STATEMANAGER: Entering " + get(startState, 'path')); }
startState.fire('enter', stateManager);
initialState = get(startState, 'initialState');
if (!initialState) {
initialState = 'start';
}
}
set(this, 'currentState', enteredState || state);
});
});
set(this, 'currentState', transition.finalState);
}

@@ -753,180 +1136,2 @@ });

(function() {
var escapeForRegex = function(text) {
return text.replace(/[\-\[\]{}()*+?.,\\\^\$|#\s]/g, "\\$&");
};
Ember._RouteMatcher = Ember.Object.extend({
state: null,
init: function() {
var route = this.route,
identifiers = [],
count = 1,
escaped;
// Strip off leading slash if present
if (route.charAt(0) === '/') {
route = this.route = route.substr(1);
}
escaped = escapeForRegex(route);
var regex = escaped.replace(/:([a-z_]+)(?=$|\/)/gi, function(match, id) {
identifiers[count++] = id;
return "([^/]+)";
});
this.identifiers = identifiers;
this.regex = new RegExp("^/?" + regex);
},
match: function(path) {
var match = path.match(this.regex);
if (match) {
var identifiers = this.identifiers,
hash = {};
for (var i=1, l=identifiers.length; i<l; i++) {
hash[identifiers[i]] = match[i];
}
return {
remaining: path.substr(match[0].length),
hash: hash
};
}
},
generate: function(hash) {
var identifiers = this.identifiers, route = this.route, id;
for (var i=1, l=identifiers.length; i<l; i++) {
id = identifiers[i];
route = route.replace(new RegExp(":" + id), hash[id]);
}
return route;
}
});
})();
(function() {
var get = Ember.get, getPath = Ember.getPath;
// The Ember Routable mixin assumes the existance of a simple
// routing shim that supports the following three behaviors:
//
// * .getURL() - this is called when the page loads
// * .setURL(newURL) - this is called from within the state
// manager when the state changes to a routable state
// * .onURLChange(callback) - this happens when the user presses
// the back or forward button
Ember.Routable = Ember.Mixin.create({
init: function() {
this.on('setupControllers', this, this.stashContext);
this._super();
},
stashContext: function(manager, context) {
var meta = get(manager, 'stateMeta'),
serialized = this.serialize(manager, context);
meta.set(this, serialized);
if (get(this, 'isRoutable')) {
this.updateRoute(manager, get(manager, 'location'));
}
},
updateRoute: function(manager, location) {
if (location && get(this, 'isLeaf')) {
var path = this.absoluteRoute(manager);
location.setURL(path);
}
},
absoluteRoute: function(manager) {
var parentState = get(this, 'parentState');
var path = '';
if (get(parentState, 'isRoutable')) {
path = parentState.absoluteRoute(manager);
}
var matcher = get(this, 'routeMatcher'),
hash = get(manager, 'stateMeta').get(this);
var generated = matcher.generate(hash);
if (generated !== "") {
return path + '/' + matcher.generate(hash);
} else {
return path;
}
},
isRoutable: Ember.computed(function() {
return typeof this.route === "string";
}).cacheable(),
routeMatcher: Ember.computed(function() {
return Ember._RouteMatcher.create({ route: get(this, 'route') });
}).cacheable(),
deserialize: function(manager, context) {
return context;
},
serialize: function(manager, context) {
return context;
},
routePath: function(manager, path) {
if (get(this, 'isLeaf')) { return; }
var childStates = get(this, 'childStates'), match;
childStates = childStates.sort(function(a, b) {
return getPath(b, 'route.length') - getPath(a, 'route.length');
});
var state = childStates.find(function(state) {
var matcher = get(state, 'routeMatcher');
if (match = matcher.match(path)) { return true; }
});
Ember.assert("Could not find state for path " + path, !!state);
var object = state.deserialize(manager, match.hash) || {};
manager.transitionTo(get(state, 'path'), object);
manager.send('routePath', match.remaining);
}
});
Ember.State.reopen(Ember.Routable);
})();
(function() {
Ember.Router = Ember.StateManager.extend({
route: function(path) {
if (path.charAt(0) === '/') {
path = path.substr(1);
}
this.send('routePath', path);
}
});
})();
(function() {
// ==========================================================================

@@ -933,0 +1138,0 @@ // Project: Ember Statecharts

@@ -12,14 +12,53 @@ //

(function() {
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
var get = Ember.get;
Ember.StateManager.reopen(
/** @scope Ember.StateManager.prototype */ {
/**
If the current state is a view state or the descendent of a view state,
this property will be the view associated with it. If there is no
view state active in this state manager, this value will be null.
@type Ember.View
*/
currentView: Ember.computed(function() {
var currentState = get(this, 'currentState'),
view;
while (currentState) {
// TODO: Remove this when view state is removed
if (get(currentState, 'isViewState')) {
view = get(currentState, 'view');
if (view) { return view; }
}
currentState = get(currentState, 'parentState');
}
return null;
}).property('currentState').cacheable()
});
})();
(function() {
var get = Ember.get, set = Ember.set;
/**
@class
@deprecated
Ember.ViewState extends Ember.State to control the presence of a childView within a
container based on the current state of the ViewState's StateManager.
## Interactions with Ember's View System.
When combined with instances of `Ember.ViewState`, StateManager is designed to
interact with Ember's view system to control which views are added to
When combined with instances of `Ember.StateManager`, ViewState is designed to
interact with Ember's view system to control which views are added to
and removed from the DOM based on the manager's current state.
By default, a StateManager will manage views inside the 'body' element. This can be
customized by setting the `rootElement` property to a CSS selector of an existing
customized by setting the `rootElement` property to a CSS selector of an existing
HTML element you would prefer to receive view rendering.

@@ -39,3 +78,3 @@

// make sure this view instance is added to the browser
aLayoutView.appendTo('body')
aLayoutView.appendTo('body')

@@ -73,3 +112,3 @@ App.viewStates = Ember.StateManager.create({

viewStates.goToState('showingPeople')
viewStates.transitionTo('showingPeople')

@@ -88,6 +127,6 @@ The above code will change the rendered HTML from

Changing the current state via `goToState` from `showingPeople` to
Changing the current state via `transitionTo` from `showingPeople` to
`showingPhotos` will remove the `showingPeople` view and add the `showingPhotos` view:
viewStates.goToState('showingPhotos')
viewStates.transitionTo('showingPhotos')

@@ -128,3 +167,3 @@ will change the rendered HTML to

viewStates.goToState('showingPeople.withEditingPanel')
viewStates.transitionTo('showingPeople.withEditingPanel')

@@ -152,6 +191,6 @@

view: Ember.View.extend({}),
enter: function(manager, transition){
enter: function(manager){
// calling _super ensures this view will be
// properly inserted
this._super(manager, transition);
this._super(manager);

@@ -198,4 +237,4 @@ // now you can do other things

If you prefer to start with an empty body and manage state programmatically you
can also take advantage of StateManager's `rootView` property and the ability of
`Ember.ContainerView`s to manually manage their child views.
can also take advantage of StateManager's `rootView` property and the ability of
`Ember.ContainerView`s to manually manage their child views.

@@ -232,3 +271,3 @@

## User Manipulation of State via `{{action}}` Helpers
The Handlebars `{{action}}` helper is StateManager-aware and will use StateManager action sending
The Handlebars `{{action}}` helper is StateManager-aware and will use StateManager action sending
to connect user interaction to action-based state transitions.

@@ -259,42 +298,13 @@

`view` that references the `Ember.View` object that was interacted with.
**/
Ember.StateManager.reopen(
/** @scope Ember.StateManager.prototype */ {
Ember.ViewState = Ember.State.extend(
/** @scope Ember.ViewState.prototype */ {
isViewState: true,
/**
If the current state is a view state or the descendent of a view state,
this property will be the view associated with it. If there is no
view state active in this state manager, this value will be null.
init: function() {
Ember.deprecate("Ember.ViewState is deprecated and will be removed from future releases. Consider using the outlet pattern to display nested views instead. For more information, see http://emberjs.com/guides/outlets/.");
return this._super();
},
@property
*/
currentView: Ember.computed(function() {
var currentState = get(this, 'currentState'),
view;
while (currentState) {
if (get(currentState, 'isViewState')) {
view = get(currentState, 'view');
if (view) { return view; }
}
currentState = get(currentState, 'parentState');
}
return null;
}).property('currentState').cacheable()
});
})();
(function() {
var get = Ember.get, set = Ember.set;
Ember.ViewState = Ember.State.extend({
isViewState: true,
enter: function(stateManager) {

@@ -301,0 +311,0 @@ var view = get(this, 'view'), root, childViews;

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc