Permalink
Browse files

Updated the statechart framework to provide routing support

  • Loading branch information...
mlcohen committed Sep 21, 2011
1 parent 1680a73 commit c6f258a922f0ac80e7a461012bb0cbd2715d4f46
Showing with 1,445 additions and 54 deletions.
  1. +1 −1 Buildfile
  2. +12 −0 apps/statechart_routing/Buildfile
  3. +11 −0 apps/statechart_routing/controllers/login_controller.js
  4. +7 −0 apps/statechart_routing/controllers/main_controller.js
  5. +17 −0 apps/statechart_routing/controllers/statechart_controller.js
  6. +25 −0 apps/statechart_routing/core.js
  7. +15 −0 apps/statechart_routing/main.js
  8. +18 −0 apps/statechart_routing/resources/_theme.css
  9. +14 −0 apps/statechart_routing/resources/bar_page.js
  10. +14 −0 apps/statechart_routing/resources/foo_page.js
  11. +9 −0 apps/statechart_routing/resources/loading.rhtml
  12. +61 −0 apps/statechart_routing/resources/login_page.js
  13. +46 −0 apps/statechart_routing/resources/main_page.js
  14. +76 −0 apps/statechart_routing/statechart.js
  15. +27 −0 apps/statechart_routing/theme.js
  16. +113 −0 frameworks/statechart/mixins/statechart_delegate.js
  17. +194 −10 frameworks/statechart/system/state.js
  18. +78 −0 frameworks/statechart/system/state_route_handler_context.js
  19. +52 −10 frameworks/statechart/system/statechart.js
  20. +161 −0 frameworks/statechart/tests/state/methods/route_triggered.js
  21. +5 −33 .../statechart/tests/state_transitioning/history_state/standard/without_concurrent_states/context.js
  22. +213 −0 frameworks/statechart/tests/state_transitioning/routing/with_concurrent_states/basic.js
  23. +212 −0 frameworks/statechart/tests/state_transitioning/routing/without_concurrent_states/basic.js
  24. +64 −0 frameworks/statechart/tests/system/state_route_handler_context/methods/retry.js
View
@@ -40,7 +40,7 @@ config :foundation, :required => [:routing, :core_foundation, :datetime, :'
config :datastore, :required => [:runtime, :datetime]
config :desktop, :required => [:foundation]
config :media, :required => [:desktop]
-config :statechart, :required => [:core_foundation], :test_required => [:core_foundation, :desktop]
+config :statechart, :required => [:core_foundation], :test_required => [:core_foundation, :desktop, :routing]
config :ajax, :required => [:runtime, :core_foundation]
config :"experimental/split_view", :test_required => [:desktop]
@@ -0,0 +1,12 @@
+# ==========================================================================
+# Project: Test
+# Copyright: @2011 My Company, Inc.
+# ==========================================================================
+
+# This is your Buildfile for your app, Test. This tells SproutCore
+# how to build your app. These settings override those in your project
+# Buildfile, which contains default settings for all apps in your project.
+
+# It is better to add :required targets here than in the global Buildfile.
+config :test, :required => :sproutcore
+
@@ -0,0 +1,11 @@
+/*globals Test */
+
+Test.loginController = SC.Object.create({
+
+ username: null,
+
+ password: null,
+
+ loggedIn: NO
+
+});
@@ -0,0 +1,7 @@
+/*globals Test */
+
+Test.mainController = SC.Object.create({
+
+ mode: null
+
+});
@@ -0,0 +1,17 @@
+/*globals Test */
+
+Test.statechartController = SC.Object.create(SC.StatechartDelegate, {
+
+ lastRouteContext: null,
+
+ statechartShouldStateHandleTriggeredRoute: function(statechart, state, context) {
+ return Test.loginController.get('loggedIn');
+ },
+
+ statechartStateCancelledHandlingTriggeredRoute: function(statechart, state, context) {
+ this.set('lastRouteContext', context);
+ SC.routes.set('location', null);
+ statechart.gotoState('loggedOutState');
+ }
+
+});
@@ -0,0 +1,25 @@
+// ==========================================================================
+// Project: Test
+// Copyright: @2011 My Company, Inc.
+// ==========================================================================
+/*globals Test */
+
+/** @namespace
+
+ My cool new app. Describe your application.
+
+ @extends SC.Object
+*/
+Test = SC.Application.create(
+ /** @scope Test.prototype */ {
+
+ NAMESPACE: 'Test',
+ VERSION: '0.1.0',
+
+ store: SC.Store.create().from(SC.Record.fixtures),
+
+ MODE_FOO: 0,
+
+ MODE_BAR: 1
+
+}) ;
@@ -0,0 +1,15 @@
+// ==========================================================================
+// Project: Test
+// Copyright: @2011 My Company, Inc.
+// ==========================================================================
+/*globals Test */
+
+Test.main = function main() {
+
+ var sc = Test.statechart;
+ SC.RootResponder.responder.set('defaultResponder', sc);
+ sc.initStatechart();
+
+} ;
+
+function main() { Test.main(); }
@@ -0,0 +1,18 @@
+/*
+ This defines the global $theme variable for use inside your CSS.
+ The $theme variable holds the CSS class names for your theme.
+
+ You can then theme your app by using CSS like this:
+
+ $theme.button {
+ color: blue;
+ @include slices('white-button.png', $left: 3, $right: 3);
+ }
+
+ Any _theme.css file is prepended to all files inside its directory,
+ and any subdirectories that don't define their own _theme.css file.
+
+ This allows you to give different directories different values for
+ $theme if you wish.
+*/
+$theme: '.ace.test';
@@ -0,0 +1,14 @@
+/*globals Test */
+
+Test.barPage = SC.Page.create({
+
+ mainView: SC.View.design({
+ layout: { top: 0, bottom: 0, left: 0, right: 0 },
+ childViews: 'labelView'.w(),
+ labelView: SC.LabelView.design({
+ layout: { centerX: 0, centerY: 0, height: 24, width: 100 },
+ value: 'In Bar Mode'
+ })
+ })
+
+});
@@ -0,0 +1,14 @@
+/*globals Test */
+
+Test.fooPage = SC.Page.create({
+
+ mainView: SC.View.design({
+ layout: { top: 0, bottom: 0, left: 0, right: 0 },
+ childViews: 'labelView'.w(),
+ labelView: SC.LabelView.design({
+ layout: { centerX: 0, centerY: 0, height: 24, width: 100 },
+ value: 'In Foo Mode'
+ })
+ })
+
+});
@@ -0,0 +1,9 @@
+<% content_for :loading do %>
+<% # Any HTML in this file will be visible on screen while your page loads
+ # its application JavaScript. SproutCore applications are optimized for
+ # caching and startup very fast, so your users will often only see this
+ # content for a brief moment on their first app load, if at all.
+%>
+<p class="loading">Loading...<p>
+
+<% end %>
@@ -0,0 +1,61 @@
+/*globals Test */
+
+Test.loginPage = SC.Page.create({
+
+ mainPane: SC.MainPane.design({
+
+ childViews: 'mainView'.w(),
+
+ mainView: SC.View.design({
+
+ childViews: 'headerView usernameView passwordView footerView'.w(),
+
+ layout: { centerX: 0, centerY: 0, width: 300, height: 150 },
+
+ headerView: SC.LabelView.design({
+ layout: { top: 0, left: 0, right: 0, height: 30 },
+ value: 'Login',
+ textAlign: SC.ALIGN_CENTER
+ }),
+
+ usernameView: SC.View.design({
+ childViews: 'labelView textfieldView'.w(),
+ layout: { top: 30, left: 0, right: 0, height: 30 },
+ labelView: SC.LabelView.design({
+ layout: { centerX: 0, left: 0, height: 24, width: 80 },
+ value: "Username:"
+ }),
+ textfieldView: SC.TextFieldView.design({
+ layout: { centerX: 0, left: 90, right: 0, height: 24 },
+ valueBinding: 'Test.loginController.username'
+ })
+ }),
+
+ passwordView: SC.View.design({
+ childViews: 'labelView textfieldView'.w(),
+ layout: { top: 60, left: 0, right: 0, height: 30 },
+ labelView: SC.LabelView.design({
+ layout: { centerX: 0, left: 0, height: 24, width: 80 },
+ value: "Password:"
+ }),
+ textfieldView: SC.TextFieldView.design({
+ layout: { centerX: 0, left: 90, right: 0, height: 24 },
+ valueBinding: 'Test.loginController.password'
+ })
+ }),
+
+ footerView: SC.View.design({
+ childViews: 'buttonView'.w(),
+ layout: { top: 120, left: 0, right: 0, bottom: 0 },
+ buttonView: SC.ButtonView.design({
+ layout: { height: 24, width: 80, right: 0, bottom: 0 },
+ title: 'Login',
+ action: 'login'
+ })
+ })
+
+ })
+
+ })
+
+});
@@ -0,0 +1,46 @@
+// ==========================================================================
+// Project: Test - mainPage
+// Copyright: @2011 My Company, Inc.
+// ==========================================================================
+/*globals Test */
+
+Test.mainPage = SC.Page.create({
+
+ fooView: SC.outlet('Test.fooPage.mainView'),
+
+ barView: SC.outlet('Test.barPage.mainView'),
+
+ emptyView: SC.View.design(),
+
+ mainPane: SC.MainPane.design({
+
+ childViews: 'headerView containerView'.w(),
+
+ headerView: SC.View.design({
+ layout: { top: 0, left: 0, right: 0, height: 50 },
+ childViews: 'switchModeView'.w(),
+ switchModeView: SC.SegmentedView.design({
+ layout: { centerX: 0, centerY: 0, height: 24, width: 100 },
+ items: [
+ { title: 'Foo', value: Test.MODE_FOO, action: 'switchToFooMode' },
+ { title: 'Bar', value: Test.MODE_BAR, action: 'switchToBarMode' }
+ ],
+ itemTitleKey: 'title',
+ itemValueKey: 'value',
+ itemActionKey: 'action',
+ valueBinding: SC.Binding.oneWay('Test.mainController.mode')
+ })
+ }),
+
+ containerView: SC.ContainerView.design({
+ layout: { top: 50, left: 0, right: 0, bottom: 0 },
+ contentViewBinding: SC.Binding.transform(function(mode) {
+ if (mode === Test.MODE_FOO) return Test.fooPage.get('mainView');
+ if (mode === Test.MODE_BAR) return Test.barPage.get('mainView');
+ return Test.mainPage.get('emptyView');
+ }).oneWay('Test.mainController.mode')
+ })
+
+ })
+
+});
@@ -0,0 +1,76 @@
+/*globals Test */
+
+sc_require('controllers/statechart_controller');
+
+Test.statechart = SC.Statechart.create({
+
+ trace: YES,
+
+ initialState: 'loggedOutState',
+
+ delegate: Test.statechartController,
+
+ loggedOutState: SC.State.design({
+
+ enterState: function() {
+ Test.loginPage.get('mainPane').append();
+ },
+
+ exitState: function() {
+ Test.loginPage.get('mainPane').remove();
+ },
+
+ login: function() {
+ Test.loginController.set('loggedIn', YES);
+ var del = this.get('statechartDelegate');
+ var ctx = del.get('lastRouteContext');
+ if (ctx) {
+ ctx.retry();
+ } else {
+ this.gotoState('loggedInState');
+ }
+ }
+
+ }),
+
+ loggedInState: SC.State.design({
+
+ enterState: function() {
+ Test.mainPage.get('mainPane').append();
+ },
+
+ switchToFooMode: function() {
+ this.gotoState('fooState');
+ },
+
+ switchToBarMode: function() {
+ this.gotoState('barState');
+ },
+
+ initialSubstate: 'fooState',
+
+ fooState: SC.State.design({
+
+ representRoute: 'foo',
+
+ enterState: function() {
+ this.set('location', 'foo');
+ Test.mainController.set('mode', Test.MODE_FOO);
+ }
+
+ }),
+
+ barState: SC.State.design({
+
+ representRoute: 'bar/:id',
+
+ enterState: function() {
+ this.set('location', 'bar/4?blah=xml');
+ Test.mainController.set('mode', Test.MODE_BAR);
+ }
+
+ })
+
+ })
+
+});
@@ -0,0 +1,27 @@
+// ==========================================================================
+// Project: Test
+// Copyright: @2011 My Company, Inc.
+// ==========================================================================
+/*globals Test */
+
+// This is the theme that defines how your app renders.
+//
+// Your app is given its own theme so it is easier and less
+// messy for you to override specific things just for your
+// app.
+//
+// You don't have to create the whole theme on your own, though:
+// your app's theme is based on SproutCore's Ace theme.
+//
+// NOTE: if you want to change the theme this one is based on, don't
+// forget to change the :css_theme property in your buildfile.
+Test.Theme = SC.AceTheme.create({
+ name: 'test'
+});
+
+// SproutCore needs to know that your app's theme exists
+SC.Theme.addTheme(Test.Theme);
+
+// Setting it as the default theme makes every pane SproutCore
+// creates default to this theme unless otherwise specified.
+SC.defaultTheme = 'test';
Oops, something went wrong.

0 comments on commit c6f258a

Please sign in to comment.