Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adds basic application and statechart structure

  • Loading branch information...
commit bd6571e4bfdcaeafd0e35dd9cea3b4ddff9b69a9 1 parent d954f9d
Dominik Guzei authored
View
1  .gitignore
@@ -14,3 +14,4 @@
/log/*.log
/tmp
.idea
+.DS_Store
View
19 app/assets/javascripts/app/App.js.coffee
@@ -0,0 +1,19 @@
+
+App = SC.Application.create {
+
+ ready: ->
+ App.statechart.initStatechart();
+ @_super()
+
+}
+
+# create namespaces
+App.models = {}
+App.views = {}
+App.controllers = {}
+App.mediators = {}
+App.states = {}
+App.templates = {}
+
+# export App to global namespace
+this.App = App
View
26 app/assets/javascripts/app/controllers/statechartController.js.coffee
@@ -0,0 +1,26 @@
+
+App = this.App
+
+App.controllers.statechartController = SC.Object.create(SC.StatechartDelegate, {
+
+ lastRouteContext: null
+
+ statechartShouldStateHandleTriggeredRoute: (statechart, state, context) ->
+ @set 'lastRouteContext', context
+
+ lastCheckedState = state
+ shouldTrigger = true
+
+ while shouldTrigger and lastCheckedState?
+
+ if lastCheckedState.beforeFilter
+ shouldTrigger = lastCheckedState.beforeFilter()
+ if not shouldTrigger
+ SC.routes.set 'location', null
+ break
+
+ lastCheckedState = lastCheckedState.get 'parentState'
+
+ return shouldTrigger
+
+})
View
11 app/assets/javascripts/app/mediators/authenticationMediator.js.coffee
@@ -0,0 +1,11 @@
+
+App = this.App
+
+App.mediators.authenticationMediator = SC.Object.create {
+
+ userName: null,
+ password: null,
+
+ loggedIn: NO
+
+}
View
19 app/assets/javascripts/app/statechart.js.coffee
@@ -0,0 +1,19 @@
+
+#= require app/utils/stateToString
+#= require app/utils/statechartAsActionTarget
+#= require app/controllers/statechartController
+#= require app/states/BrowsingState
+#= require app/states/AuthenticationState
+
+App = this.App
+
+App.statechart = SC.Statechart.create {
+
+ statesAreConcurrent: YES
+ trace: YES
+
+ delegate: App.controllers.statechartController
+
+ Browsing: App.states.BrowsingState
+ Authentication: App.states.AuthenticationState
+}
View
58 app/assets/javascripts/app/states/AuthenticationState.js.coffee
@@ -0,0 +1,58 @@
+
+#= require app/mediators/authenticationMediator
+
+App = this.App
+
+App.states.AuthenticationState = SC.State.extend {
+
+ initialSubstate: 'Login',
+
+ Login: SC.State.extend {
+
+ initialSubstate: 'LoggedOut'
+
+ LoggedOut: SC.State.extend {
+
+ enterState: ->
+ App.mediators.authenticationMediator.set 'loggedIn', NO
+ App.mediators.authenticationMediator.set 'userName', null
+ App.mediators.authenticationMediator.set 'password', null
+
+ login: ->
+ @gotoState 'Authentication.Login.Authenticate'
+ }
+
+ LoggedIn: SC.State.extend {
+ enterState: ->
+ App.mediators.authenticationMediator.set 'loggedIn', YES
+
+ context = App.controllers.statechartController.get 'lastRouteContext'
+ if context then context.retry()
+
+ logout: ->
+ @gotoState 'Authentication.Login.LoggedOut'
+ }
+
+ Authenticate: SC.State.extend {
+ enterState: ->
+ App.mediators.authenticationMediator.set 'userName', 'DominikGuzei'
+ App.mediators.authenticationMediator.set 'password', 'xyz'
+
+ @gotoState 'Authentication.Login.LoggedIn'
+ }
+ }
+
+ Registration: SC.State.extend {
+
+ initialSubstate: 'Intern'
+
+ Intern: SC.State.extend {
+
+ }
+
+ Facebook: SC.State.extend {
+
+ }
+ }
+
+}
View
41 app/assets/javascripts/app/states/BrowsingState.js.coffee
@@ -0,0 +1,41 @@
+#= require app/mediators/authenticationMediator
+
+App = this.App
+
+App.states.BrowsingState = SC.State.extend {
+
+ initialSubstate: 'Public'
+
+ Public: SC.State.extend {
+
+ initialSubstate: 'ListNotes'
+
+ ListNotes: SC.State.extend {
+
+ representRoute: 'notes'
+
+ createNote: ->
+ @gotoState 'Browsing.Restricted.CreateNote'
+ }
+ }
+
+ Restricted: SC.State.extend {
+
+ initialSubstate: 'CreateNote'
+
+ beforeFilter: (context) ->
+ unless App.mediators.authenticationMediator.get('loggedIn')
+ @get('statechart').send('login')
+ return false
+
+ return true
+
+ CreateNote: SC.State.extend {
+ representRoute: 'notes/create'
+ }
+
+ logout: ->
+ @gotoState 'Browsing.Public.ListNotes'
+ }
+
+}
View
5 app/assets/javascripts/app/utils/stateToString.js.coffee
@@ -0,0 +1,5 @@
+
+SC.State.reopen {
+ toString: ->
+ return @get 'fullPath'
+}
View
5 app/assets/javascripts/app/utils/statechartAsActionTarget.js.coffee
@@ -0,0 +1,5 @@
+
+SC.Statechart.reopen {
+ send: ->
+ @sendAction.apply(this, arguments)
+}
View
7 app/assets/javascripts/application.js
@@ -12,4 +12,9 @@
//
//= require jquery
//= require jquery_ujs
-//= require_tree .
+//= require lib/ember
+//= require lib/sproutcore-routing
+//= require lib/ember-statechart
+
+//= require app/App
+//= require app/statechart
View
3,806 app/assets/javascripts/lib/ember-statechart.js
3,806 additions, 0 deletions not shown
View
15,839 app/assets/javascripts/lib/ember.js
15,839 additions, 0 deletions not shown
View
570 app/assets/javascripts/lib/sproutcore-routing.js
@@ -0,0 +1,570 @@
+// ==========================================================================
+// Project: SproutCore - JavaScript Application Framework
+// Copyright: ©2006-2011 Strobe Inc. and contributors.
+// Portions ©2008-2011 Apple Inc. All rights reserved.
+// License: Licensed under MIT license (see license.js)
+// ==========================================================================
+
+var get = SC.get, set = SC.set;
+
+/**
+ Wether the browser supports HTML5 history.
+*/
+var supportsHistory = !!(window.history && window.history.pushState);
+
+/**
+ Wether the browser supports the hashchange event.
+*/
+var supportsHashChange = ('onhashchange' in window) && (document.documentMode === undefined || document.documentMode > 7);
+
+/**
+ @class
+
+ Route is a class used internally by SC.routes. The routes defined by your
+ application are stored in a tree structure, and this is the class for the
+ nodes.
+*/
+var Route = SC.Object.extend(
+/** @scope Route.prototype */ {
+
+ target: null,
+
+ method: null,
+
+ staticRoutes: null,
+
+ dynamicRoutes: null,
+
+ wildcardRoutes: null,
+
+ add: function(parts, target, method) {
+ var part, nextRoute;
+
+ // clone the parts array because we are going to alter it
+ parts = SC.copy(parts);
+
+ if (!parts || parts.length === 0) {
+ this.target = target;
+ this.method = method;
+
+ } else {
+ part = parts.shift();
+
+ // there are 3 types of routes
+ switch (part.slice(0, 1)) {
+
+ // 1. dynamic routes
+ case ':':
+ part = part.slice(1, part.length);
+ if (!this.dynamicRoutes) this.dynamicRoutes = {};
+ if (!this.dynamicRoutes[part]) this.dynamicRoutes[part] = this.constructor.create();
+ nextRoute = this.dynamicRoutes[part];
+ break;
+
+ // 2. wildcard routes
+ case '*':
+ part = part.slice(1, part.length);
+ if (!this.wildcardRoutes) this.wildcardRoutes = {};
+ nextRoute = this.wildcardRoutes[part] = this.constructor.create();
+ break;
+
+ // 3. static routes
+ default:
+ if (!this.staticRoutes) this.staticRoutes = {};
+ if (!this.staticRoutes[part]) this.staticRoutes[part] = this.constructor.create();
+ nextRoute = this.staticRoutes[part];
+ }
+
+ // recursively add the rest of the route
+ if (nextRoute) nextRoute.add(parts, target, method);
+ }
+ },
+
+ routeForParts: function(parts, params) {
+ var part, key, route;
+
+ // clone the parts array because we are going to alter it
+ parts = SC.copy(parts);
+
+ // if parts is empty, we are done
+ if (!parts || parts.length === 0) {
+ return this.method ? this : null;
+
+ } else {
+ part = parts.shift();
+
+ // try to match a static route
+ if (this.staticRoutes && this.staticRoutes[part]) {
+ route = this.staticRoutes[part].routeForParts(parts, params);
+ if (route) {
+ return route;
+ }
+ }
+
+ // else, try to match a dynamic route
+ for (key in this.dynamicRoutes) {
+ route = this.dynamicRoutes[key].routeForParts(parts, params);
+ if (route) {
+ params[key] = part;
+ return route;
+ }
+ }
+
+ // else, try to match a wilcard route
+ for (key in this.wildcardRoutes) {
+ parts.unshift(part);
+ params[key] = parts.join('/');
+ return this.wildcardRoutes[key].routeForParts(null, params);
+ }
+
+ // if nothing was found, it means that there is no match
+ return null;
+ }
+ }
+
+});
+
+/**
+ @class
+
+ SC.routes manages the browser location. You can change the hash part of the
+ current location. The following code
+
+ SC.routes.set('location', 'notes/edit/4');
+
+ will change the location to http://domain.tld/my_app#notes/edit/4. Adding
+ routes will register a handler that will be called whenever the location
+ changes and matches the route:
+
+ SC.routes.add(':controller/:action/:id', MyApp, MyApp.route);
+
+ You can pass additional parameters in the location hash that will be relayed
+ to the route handler:
+
+ SC.routes.set('location', 'notes/show/4?format=xml&language=fr');
+
+ The syntax for the location hash is described in the location property
+ documentation, and the syntax for adding handlers is described in the
+ add method documentation.
+
+ Browsers keep track of the locations in their history, so when the user
+ presses the 'back' or 'forward' button, the location is changed, SC.route
+ catches it and calls your handler. Except for Internet Explorer versions 7
+ and earlier, which do not modify the history stack when the location hash
+ changes.
+
+ SC.routes also supports HTML5 history, which uses a '/' instead of a '#'
+ in the URLs, so that all your website's URLs are consistent.
+*/
+var routes = SC.routes = SC.Object.create(
+ /** @scope SC.routes.prototype */{
+
+ /**
+ Set this property to true if you want to use HTML5 history, if available on
+ the browser, instead of the location hash.
+
+ HTML 5 history uses the history.pushState method and the window's popstate
+ event.
+
+ By default it is false, so your URLs will look like:
+
+ http://domain.tld/my_app#notes/edit/4
+
+ If set to true and the browser supports pushState(), your URLs will look
+ like:
+
+ http://domain.tld/my_app/notes/edit/4
+
+ You will also need to make sure that baseURI is properly configured, as
+ well as your server so that your routes are properly pointing to your
+ SproutCore application.
+
+ @see http://dev.w3.org/html5/spec/history.html#the-history-interface
+ @property
+ @type {Boolean}
+ */
+ wantsHistory: false,
+
+ /**
+ A read-only boolean indicating whether or not HTML5 history is used. Based
+ on the value of wantsHistory and the browser's support for pushState.
+
+ @see wantsHistory
+ @property
+ @type {Boolean}
+ */
+ usesHistory: null,
+
+ /**
+ The base URI used to resolve routes (which are relative URLs). Only used
+ when usesHistory is equal to true.
+
+ The build tools automatically configure this value if you have the
+ html5_history option activated in the Buildfile:
+
+ config :my_app, :html5_history => true
+
+ Alternatively, it uses by default the value of the href attribute of the
+ <base> tag of the HTML document. For example:
+
+ <base href="http://domain.tld/my_app">
+
+ The value can also be customized before or during the exectution of the
+ main() method.
+
+ @see http://www.w3.org/TR/html5/semantics.html#the-base-element
+ @property
+ @type {String}
+ */
+ baseURI: document.baseURI,
+
+ /** @private
+ A boolean value indicating whether or not the ping method has been called
+ to setup the SC.routes.
+
+ @property
+ @type {Boolean}
+ */
+ _didSetup: false,
+
+ /** @private
+ Internal representation of the current location hash.
+
+ @property
+ @type {String}
+ */
+ _location: null,
+
+ /** @private
+ Routes are stored in a tree structure, this is the root node.
+
+ @property
+ @type {Route}
+ */
+ _firstRoute: null,
+
+ /** @private
+ An internal reference to the Route class.
+
+ @property
+ */
+ _Route: Route,
+
+ /** @private
+ Internal method used to extract and merge the parameters of a URL.
+
+ @returns {Hash}
+ */
+ _extractParametersAndRoute: function(obj) {
+ var params = {},
+ route = obj.route || '',
+ separator, parts, i, len, crumbs, key;
+
+ separator = (route.indexOf('?') < 0 && route.indexOf('&') >= 0) ? '&' : '?';
+ parts = route.split(separator);
+ route = parts[0];
+ if (parts.length === 1) {
+ parts = [];
+ } else if (parts.length === 2) {
+ parts = parts[1].split('&');
+ } else if (parts.length > 2) {
+ parts.shift();
+ }
+
+ // extract the parameters from the route string
+ len = parts.length;
+ for (i = 0; i < len; ++i) {
+ crumbs = parts[i].split('=');
+ params[crumbs[0]] = crumbs[1];
+ }
+
+ // overlay any parameter passed in obj
+ for (key in obj) {
+ if (obj.hasOwnProperty(key) && key !== 'route') {
+ params[key] = '' + obj[key];
+ }
+ }
+
+ // build the route
+ parts = [];
+ for (key in params) {
+ parts.push([key, params[key]].join('='));
+ }
+ params.params = separator + parts.join('&');
+ params.route = route;
+
+ return params;
+ },
+
+ /**
+ The current location hash. It is the part in the browser's location after
+ the '#' mark.
+
+ The following code
+
+ SC.routes.set('location', 'notes/edit/4');
+
+ will change the location to http://domain.tld/my_app#notes/edit/4 and call
+ the correct route handler if it has been registered with the add method.
+
+ You can also pass additional parameters. They will be relayed to the route
+ handler. For example, the following code
+
+ SC.routes.add(':controller/:action/:id', MyApp, MyApp.route);
+ SC.routes.set('location', 'notes/show/4?format=xml&language=fr');
+
+ will change the location to
+ http://domain.tld/my_app#notes/show/4?format=xml&language=fr and call the
+ MyApp.route method with the following argument:
+
+ { route: 'notes/show/4',
+ params: '?format=xml&language=fr',
+ controller: 'notes',
+ action: 'show',
+ id: '4',
+ format: 'xml',
+ language: 'fr' }
+
+ The location can also be set with a hash, the following code
+
+ SC.routes.set('location',
+ { route: 'notes/edit/4', format: 'xml', language: 'fr' });
+
+ will change the location to
+ http://domain.tld/my_app#notes/show/4?format=xml&language=fr.
+
+ The 'notes/show/4&format=xml&language=fr' syntax for passing parameters,
+ using a '&' instead of a '?', as used in SproutCore 1.0 is still supported.
+
+ @property
+ @type {String}
+ */
+ location: function(key, value) {
+ this._skipRoute = false;
+ return this._extractLocation(key, value);
+ }.property(),
+
+ _extractLocation: function(key, value) {
+ var crumbs, encodedValue;
+
+ if (value !== undefined) {
+ if (value === null) {
+ value = '';
+ }
+
+ if (typeof(value) === 'object') {
+ crumbs = this._extractParametersAndRoute(value);
+ value = crumbs.route + crumbs.params;
+ }
+
+ if (!this._skipPush && (!SC.empty(value) || (this._location && this._location !== value))) {
+ encodedValue = encodeURI(value);
+
+ if (this.usesHistory) {
+ if (encodedValue.length > 0) {
+ encodedValue = '/' + encodedValue;
+ }
+ window.history.pushState(null, null, get(this, 'baseURI') + encodedValue);
+ } else if (encodedValue.length > 0 || window.location.hash.length > 0) {
+ window.location.hash = encodedValue;
+ }
+ }
+
+ this._location = value;
+ }
+
+ return this._location;
+ },
+
+ updateLocation: function(loc){
+ this._skipRoute = true;
+ return this._extractLocation('location', loc);
+ },
+
+ /**
+ You usually don't need to call this method. It is done automatically after
+ the application has been initialized.
+
+ It registers for the hashchange event if available. If not, it creates a
+ timer that looks for location changes every 150ms.
+ */
+ ping: function() {
+ if (!this._didSetup) {
+ this._didSetup = true;
+ var state;
+
+ if (get(this, 'wantsHistory') && supportsHistory) {
+ this.usesHistory = true;
+
+ // Move any hash state to url state
+ // TODO: Make sure we have a hash before adding slash
+ state = window.location.hash.slice(1);
+ if (state.length > 0) {
+ state = '/' + state;
+ window.history.replaceState(null, null, get(this, 'baseURI')+state);
+ }
+
+ popState();
+ jQuery(window).bind('popstate', popState);
+
+ } else {
+ this.usesHistory = false;
+
+ if (get(this, 'wantsHistory')) {
+ // Move any url state to hash
+ var base = get(this, 'baseURI');
+ var loc = (base.charAt(0) === '/') ? document.location.pathname : document.location.href.replace(document.location.hash, '');
+ state = loc.slice(base.length+1);
+ if (state.length > 0) {
+ window.location.href = base+'#'+state;
+ }
+ }
+
+ if (supportsHashChange) {
+ hashChange();
+ jQuery(window).bind('hashchange', hashChange);
+
+ } else {
+ // we don't use a SC.Timer because we don't want
+ // a run loop to be triggered at each ping
+ var invokeHashChange = function() {
+ hashChange();
+ setTimeout(invokeHashChange, 100);
+ };
+ invokeHashChange();
+ }
+ }
+ }
+ },
+
+ /**
+ Adds a route handler. Routes have the following format:
+
+ - 'users/show/5' is a static route and only matches this exact string,
+ - ':action/:controller/:id' is a dynamic route and the handler will be
+ called with the 'action', 'controller' and 'id' parameters passed in a
+ hash,
+ - '*url' is a wildcard route, it matches the whole route and the handler
+ will be called with the 'url' parameter passed in a hash.
+
+ Route types can be combined, the following are valid routes:
+
+ - 'users/:action/:id'
+ - ':controller/show/:id'
+ - ':controller/ *url' (ignore the space, because of jslint)
+
+ @param {String} route the route to be registered
+ @param {Object} target the object on which the method will be called, or
+ directly the function to be called to handle the route
+ @param {Function} method the method to be called on target to handle the
+ route, can be a function or a string
+ */
+ add: function(route, target, method) {
+ if (!this._didSetup) {
+ SC.run.once(this, 'ping');
+ }
+
+ if (method === undefined && SC.typeOf(target) === 'function') {
+ method = target;
+ target = null;
+ } else if (SC.typeOf(method) === 'string') {
+ method = target[method];
+ }
+
+ if (!this._firstRoute) this._firstRoute = Route.create();
+ this._firstRoute.add(route.split('/'), target, method);
+
+ return this;
+ },
+
+ /**
+ Observer of the 'location' property that calls the correct route handler
+ when the location changes.
+ */
+ locationDidChange: function() {
+ this.trigger();
+ }.observes('location'),
+
+ /**
+ Triggers a route even if already in that route (does change the location, if it
+ is not already changed, as well).
+
+ If the location is not the same as the supplied location, this simply lets "location"
+ handle it (which ends up coming back to here).
+ */
+ trigger: function() {
+ var location = get(this, 'location'),
+ params, route;
+
+ if (this._firstRoute) {
+ params = this._extractParametersAndRoute({ route: location });
+ location = params.route;
+ delete params.route;
+ delete params.params;
+
+ route = this.getRoute(location, params);
+ if (route && route.method) {
+ route.method.call(route.target || this, params);
+ }
+ }
+ },
+
+ getRoute: function(route, params) {
+ var firstRoute = this._firstRoute;
+ if (params === null) {
+ params = {};
+ }
+
+ return firstRoute.routeForParts(route.split('/'), params);
+ },
+
+ exists: function(route, params) {
+ route = this.getRoute(route, params);
+ return route !== null && route.method !== null;
+ }
+
+});
+
+/**
+ Event handler for the hashchange event. Called automatically by the browser
+ if it supports the hashchange event, or by our timer if not.
+*/
+function hashChange(event) {
+ var loc = window.location.hash;
+
+ // Remove the '#' prefix
+ loc = (loc && loc.length > 0) ? loc.slice(1, loc.length) : '';
+
+ if (!jQuery.browser.mozilla) {
+ // because of bug https://bugzilla.mozilla.org/show_bug.cgi?id=483304
+ loc = decodeURI(loc);
+ }
+
+ if (get(routes, 'location') !== loc && !routes._skipRoute) {
+ SC.run.once(function() {
+ routes._skipPush = true;
+ set(routes, 'location', loc);
+ routes._skipPush = false;
+ });
+ }
+ routes._skipRoute = false;
+}
+
+function popState(event) {
+ var base = get(routes, 'baseURI'),
+ loc = (base.charAt(0) === '/') ? document.location.pathname : document.location.href;
+
+ if (loc.slice(0, base.length) === base) {
+ // Remove the base prefix and the extra '/'
+ loc = loc.slice(base.length + 1, loc.length);
+
+ if (get(routes, 'location') !== loc && !routes._skipRoute) {
+ SC.run.once(function() {
+ routes._skipPush = true;
+ set(routes, 'location', loc);
+ routes._skipPush = false;
+ });
+ }
+ }
+ routes._skipRoute = false;
+}
View
4 app/controllers/notes_controller.rb
@@ -0,0 +1,4 @@
+class NotesController < ApplicationController
+ def index
+ end
+end
View
2  app/views/notes/index.html.erb
@@ -0,0 +1,2 @@
+<h1>Notes#index</h1>
+<p>Find me in app/views/notes/index.html.erb</p>
View
55 config/routes.rb
@@ -1,58 +1,5 @@
RoutingStatechartExample::Application.routes.draw do
- # The priority is based upon order of creation:
- # first created -> highest priority.
- # Sample of regular route:
- # match 'products/:id' => 'catalog#view'
- # Keep in mind you can assign values other than :controller and :action
+ root :to => 'notes#index'
- # Sample of named route:
- # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
- # This route can be invoked with purchase_url(:id => product.id)
-
- # Sample resource route (maps HTTP verbs to controller actions automatically):
- # resources :products
-
- # Sample resource route with options:
- # resources :products do
- # member do
- # get 'short'
- # post 'toggle'
- # end
- #
- # collection do
- # get 'sold'
- # end
- # end
-
- # Sample resource route with sub-resources:
- # resources :products do
- # resources :comments, :sales
- # resource :seller
- # end
-
- # Sample resource route with more complex sub-resources
- # resources :products do
- # resources :comments
- # resources :sales do
- # get 'recent', :on => :collection
- # end
- # end
-
- # Sample resource route within a namespace:
- # namespace :admin do
- # # Directs /admin/products/* to Admin::ProductsController
- # # (app/controllers/admin/products_controller.rb)
- # resources :products
- # end
-
- # You can have the root of your site routed with "root"
- # just remember to delete public/index.html.
- # root :to => 'welcome#index'
-
- # See how all your routes lay out with "rake routes"
-
- # This is a legacy wild controller route that's not recommended for RESTful applications.
- # Note: This route will make all actions in every controller accessible via GET requests.
- # match ':controller(/:action(/:id))(.:format)'
end
View
241 public/index.html
@@ -1,241 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>Ruby on Rails: Welcome aboard</title>
- <style type="text/css" media="screen">
- body {
- margin: 0;
- margin-bottom: 25px;
- padding: 0;
- background-color: #f0f0f0;
- font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
- font-size: 13px;
- color: #333;
- }
-
- h1 {
- font-size: 28px;
- color: #000;
- }
-
- a {color: #03c}
- a:hover {
- background-color: #03c;
- color: white;
- text-decoration: none;
- }
-
-
- #page {
- background-color: #f0f0f0;
- width: 750px;
- margin: 0;
- margin-left: auto;
- margin-right: auto;
- }
-
- #content {
- float: left;
- background-color: white;
- border: 3px solid #aaa;
- border-top: none;
- padding: 25px;
- width: 500px;
- }
-
- #sidebar {
- float: right;
- width: 175px;
- }
-
- #footer {
- clear: both;
- }
-
- #header, #about, #getting-started {
- padding-left: 75px;
- padding-right: 30px;
- }
-
-
- #header {
- background-image: url("assets/rails.png");
- background-repeat: no-repeat;
- background-position: top left;
- height: 64px;
- }
- #header h1, #header h2 {margin: 0}
- #header h2 {
- color: #888;
- font-weight: normal;
- font-size: 16px;
- }
-
-
- #about h3 {
- margin: 0;
- margin-bottom: 10px;
- font-size: 14px;
- }
-
- #about-content {
- background-color: #ffd;
- border: 1px solid #fc0;
- margin-left: -55px;
- margin-right: -10px;
- }
- #about-content table {
- margin-top: 10px;
- margin-bottom: 10px;
- font-size: 11px;
- border-collapse: collapse;
- }
- #about-content td {
- padding: 10px;
- padding-top: 3px;
- padding-bottom: 3px;
- }
- #about-content td.name {color: #555}
- #about-content td.value {color: #000}
-
- #about-content ul {
- padding: 0;
- list-style-type: none;
- }
-
- #about-content.failure {
- background-color: #fcc;
- border: 1px solid #f00;
- }
- #about-content.failure p {
- margin: 0;
- padding: 10px;
- }
-
-
- #getting-started {
- border-top: 1px solid #ccc;
- margin-top: 25px;
- padding-top: 15px;
- }
- #getting-started h1 {
- margin: 0;
- font-size: 20px;
- }
- #getting-started h2 {
- margin: 0;
- font-size: 14px;
- font-weight: normal;
- color: #333;
- margin-bottom: 25px;
- }
- #getting-started ol {
- margin-left: 0;
- padding-left: 0;
- }
- #getting-started li {
- font-size: 18px;
- color: #888;
- margin-bottom: 25px;
- }
- #getting-started li h2 {
- margin: 0;
- font-weight: normal;
- font-size: 18px;
- color: #333;
- }
- #getting-started li p {
- color: #555;
- font-size: 13px;
- }
-
-
- #sidebar ul {
- margin-left: 0;
- padding-left: 0;
- }
- #sidebar ul h3 {
- margin-top: 25px;
- font-size: 16px;
- padding-bottom: 10px;
- border-bottom: 1px solid #ccc;
- }
- #sidebar li {
- list-style-type: none;
- }
- #sidebar ul.links li {
- margin-bottom: 5px;
- }
-
- .filename {
- font-style: italic;
- }
- </style>
- <script type="text/javascript">
- function about() {
- info = document.getElementById('about-content');
- if (window.XMLHttpRequest)
- { xhr = new XMLHttpRequest(); }
- else
- { xhr = new ActiveXObject("Microsoft.XMLHTTP"); }
- xhr.open("GET","rails/info/properties",false);
- xhr.send("");
- info.innerHTML = xhr.responseText;
- info.style.display = 'block'
- }
- </script>
- </head>
- <body>
- <div id="page">
- <div id="sidebar">
- <ul id="sidebar-items">
- <li>
- <h3>Browse the documentation</h3>
- <ul class="links">
- <li><a href="http://guides.rubyonrails.org/">Rails Guides</a></li>
- <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
- <li><a href="http://www.ruby-doc.org/core/">Ruby core</a></li>
- <li><a href="http://www.ruby-doc.org/stdlib/">Ruby standard library</a></li>
- </ul>
- </li>
- </ul>
- </div>
-
- <div id="content">
- <div id="header">
- <h1>Welcome aboard</h1>
- <h2>You&rsquo;re riding Ruby on Rails!</h2>
- </div>
-
- <div id="about">
- <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
- <div id="about-content" style="display: none"></div>
- </div>
-
- <div id="getting-started">
- <h1>Getting started</h1>
- <h2>Here&rsquo;s how to get rolling:</h2>
-
- <ol>
- <li>
- <h2>Use <code>rails generate</code> to create your models and controllers</h2>
- <p>To see all available options, run it without parameters.</p>
- </li>
-
- <li>
- <h2>Set up a default route and remove <span class="filename">public/index.html</span></h2>
- <p>Routes are set up in <span class="filename">config/routes.rb</span>.</p>
- </li>
-
- <li>
- <h2>Create your database</h2>
- <p>Run <code>rake db:create</code> to create your database. If you're not using SQLite (the default), edit <span class="filename">config/database.yml</span> with your username and password.</p>
- </li>
- </ol>
- </div>
- </div>
-
- <div id="footer">&nbsp;</div>
- </div>
- </body>
-</html>
View
BIN  spec/architecture/ember-app-statechart.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Please sign in to comment.
Something went wrong with that request. Please try again.