├── app ├── views │ └── .gitkeep ├── components │ ├── .gitkeep │ ├── route-displayer.js │ ├── code-editor.js │ ├── template-visualization.js │ └── route-tree.js ├── helpers │ └── .gitkeep ├── models │ └── .gitkeep ├── routes │ ├── .gitkeep │ └── route.js ├── styles │ ├── .gitkeep │ └── app.scss ├── templates │ ├── .gitkeep │ ├── components │ │ ├── .gitkeep │ │ ├── route-children.hbs │ │ ├── route-displayer.hbs │ │ ├── validation-hooks.hbs │ │ ├── setup-hooks.hbs │ │ └── template-visualization.hbs │ ├── index.hbs │ ├── route.hbs │ └── application.hbs ├── controllers │ ├── .gitkeep │ ├── route.js │ └── application.js ├── router.js ├── app.js └── lib │ └── route-debug.js ├── public ├── .gitkeep ├── robots.txt └── crossdomain.xml ├── vendor └── .gitkeep ├── tests ├── unit │ └── .gitkeep ├── helpers │ ├── resolver.js │ └── start-app.js ├── test-helper.js ├── .jshintrc └── index.html ├── .bowerrc ├── testem.json ├── .travis.yml ├── deploy ├── .gitignore ├── bower.json ├── .editorconfig ├── .jshintrc ├── Brocfile.js ├── package.json ├── config └── environment.js └── README.md /app/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/routes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /app/templates/index.hbs: -------------------------------------------------------------------------------- 1 |

Route information

2 |

Select a route from the route tree to see detailed information

3 | -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "framework": "qunit", 3 | "test_page": "tests/index.html", 4 | "launch_in_ci": ["PhantomJS"], 5 | "launch_in_dev": ["PhantomJS", "Chrome"] 6 | } 7 | -------------------------------------------------------------------------------- /app/templates/components/route-children.hbs: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember/resolver'; 2 | 3 | var resolver = Resolver.create(); 4 | 5 | resolver.namespace = { 6 | modulePrefix: 'diagonal' 7 | }; 8 | 9 | export default resolver; 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | 4 | sudo: false 5 | 6 | cache: 7 | directories: 8 | - node_modules 9 | 10 | install: 11 | - npm install -g bower 12 | - npm install 13 | - bower install 14 | 15 | script: 16 | - npm test 17 | -------------------------------------------------------------------------------- /app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | var Router = Ember.Router.extend({ 4 | location: DiagonalENV.locationType 5 | }); 6 | 7 | Router.map(function() { 8 | this.resource('route', {path: "/route/:route_name"}) 9 | }); 10 | 11 | export default Router; 12 | -------------------------------------------------------------------------------- /app/templates/components/route-displayer.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 |

    {{link-to route.value.name 'route' route}}

    3 | {{#if route.value.url}} 4 | {{route.value.url}} 5 | {{/if}} 6 | 7 | {{route-children children=route.children isShowingSubStates=isShowingSubStates}} 8 |
  • 9 | -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -x 5 | 6 | ember build --environment=production 7 | ssh blog@alexspeller.com "rm -rf diagonal-upload" 8 | scp -r -C dist blog@alexspeller.com:diagonal-upload 9 | ssh blog@alexspeller.com 'mv diagonal "diagonal-bu-`date`" && mv diagonal-upload diagonal && chmod +x diagonal' 10 | -------------------------------------------------------------------------------- /app/components/route-displayer.js: -------------------------------------------------------------------------------- 1 | export default Ember.Component.extend({ 2 | isVisible: function () { 3 | if(!this.get('route')) { return false; } 4 | if(this.get('isShowingSubStates')) { 5 | return true; 6 | } 7 | 8 | return !this.get('route').isSubState; 9 | }.property('isShowingSubStates', 'isSubState') 10 | }); 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components/* 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log 17 | testem.log 18 | 19 | .DS_Store 20 | -------------------------------------------------------------------------------- /app/routes/route.js: -------------------------------------------------------------------------------- 1 | export default Ember.Route.extend({ 2 | model: function (params) { 3 | var route = this.controllerFor('application') 4 | .routeFor(params.route_name); 5 | if(!route) { 6 | this.transitionTo('index'); 7 | } 8 | return route; 9 | }, 10 | 11 | serialize: function (route) { 12 | return {route_name: route.value.name}; 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from 'ember/resolver'; 3 | import loadInitializers from 'ember/load-initializers'; 4 | 5 | Ember.MODEL_FACTORY_INJECTIONS = true; 6 | 7 | var App = Ember.Application.extend({ 8 | modulePrefix: 'diagonal', // TODO: loaded via config 9 | Resolver: Resolver, 10 | rootElement: '#main-app' 11 | }); 12 | 13 | loadInitializers(App, 'diagonal'); 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /app/templates/components/validation-hooks.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 | {{route.value.name}} → beforeModel() 3 |
  • 4 |
  • 5 | {{route.value.name}} → model() 6 |
  • 7 |
  • 8 | {{route.value.name}} → afterModel() 9 |
  • 10 | -------------------------------------------------------------------------------- /app/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import 'vendor/bootstrap-sass-official/assets/stylesheets/bootstrap'; 2 | 3 | 4 | html, body { 5 | margin: 20px; 6 | textarea { 7 | font-family: monospace; 8 | } 9 | } 10 | 11 | 12 | .node circle { 13 | fill: #fff; 14 | stroke: steelblue; 15 | stroke-width: 3px; 16 | } 17 | 18 | .node text { font: 12px sans-serif; } 19 | 20 | .link { 21 | fill: none; 22 | stroke: #ccc; 23 | stroke-width: 2px; 24 | } 25 | 26 | 27 | a.active { 28 | color: #870009; 29 | } 30 | -------------------------------------------------------------------------------- /app/templates/components/setup-hooks.hbs: -------------------------------------------------------------------------------- 1 |
  • 2 | {{route.value.name}} → activate() 3 |
  • 4 |
  • 5 | {{route.value.name}} → setupController() 6 |
  • 7 |
  • 8 | {{route.value.name}} → renderTemplate() 9 |
  • 10 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { setResolver } from 'ember-qunit'; 3 | 4 | setResolver(resolver); 5 | 6 | document.write('
    '); 7 | 8 | QUnit.config.urlConfig.push({ id: 'nocontainer', label: 'Hide container'}); 9 | if (QUnit.urlParams.nocontainer) { 10 | document.getElementById('ember-testing-container').style.visibility = 'hidden'; 11 | } else { 12 | document.getElementById('ember-testing-container').style.visibility = 'visible'; 13 | } 14 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diagonal", 3 | "dependencies": { 4 | "handlebars": "~1.3.0", 5 | "jquery": "^1.11.1", 6 | "ember": "1.7.0", 7 | "ember-resolver": "~0.1.7", 8 | "loader": "stefanpenner/loader.js#1.0.1", 9 | "ember-cli-shims": "stefanpenner/ember-cli-shims#0.0.3", 10 | "ember-cli-test-loader": "rwjblue/ember-cli-test-loader#0.0.4", 11 | "ember-load-initializers": "stefanpenner/ember-load-initializers#0.0.2", 12 | "bootstrap-sass-official": "~3.2.0+1", 13 | "d3": "~3.4.11", 14 | "codemirror": "~4.5.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.hbs] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.css] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | [*.html] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | [*.md] 33 | trim_trailing_whitespace = false 34 | -------------------------------------------------------------------------------- /public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": { 3 | "document": true, 4 | "window": true, 5 | "-Promise": true, 6 | "DiagonalENV": true 7 | }, 8 | "browser" : true, 9 | "boss" : true, 10 | "curly": true, 11 | "debug": false, 12 | "devel": true, 13 | "eqeqeq": true, 14 | "evil": true, 15 | "forin": false, 16 | "immed": false, 17 | "laxbreak": false, 18 | "newcap": true, 19 | "noarg": true, 20 | "noempty": false, 21 | "nonew": false, 22 | "nomen": false, 23 | "onevar": false, 24 | "plusplus": false, 25 | "regexp": false, 26 | "undef": true, 27 | "sub": true, 28 | "strict": false, 29 | "white": false, 30 | "eqnull": true, 31 | "esnext": true, 32 | "unused": true 33 | } 34 | -------------------------------------------------------------------------------- /Brocfile.js: -------------------------------------------------------------------------------- 1 | /* global require, module */ 2 | 3 | var EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | 5 | var app = new EmberApp(); 6 | 7 | // Use `app.import` to add additional libraries to the generated 8 | // output files. 9 | // 10 | // If you need to use different assets in different 11 | // environments, specify an object as the first parameter. That 12 | // object's keys should be the environment name and the values 13 | // should be the asset to use in that environment. 14 | // 15 | // If the library that you are including contains AMD or ES6 16 | // modules that you would like to import into your application 17 | // please specify an object with the list of modules as keys 18 | // along with the exports of each module as its value. 19 | 20 | module.exports = app.toTree(); 21 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | /* global require */ 2 | 3 | var Application = require('diagonal/app')['default']; 4 | var Router = require('diagonal/router')['default']; 5 | import Ember from 'ember'; 6 | 7 | export default function startApp(attrs) { 8 | var App; 9 | 10 | var attributes = Ember.merge({ 11 | // useful Test defaults 12 | rootElement: '#ember-testing', 13 | LOG_ACTIVE_GENERATION: false, 14 | LOG_VIEW_LOOKUPS: false 15 | }, attrs); // but you can override; 16 | 17 | Router.reopen({ 18 | location: 'none' 19 | }); 20 | 21 | Ember.run(function() { 22 | App = Application.create(attributes); 23 | App.setupForTesting(); 24 | App.injectTestHelpers(); 25 | }); 26 | 27 | App.reset(); // this shouldn't be needed, i want to be able to "start an app at a specific URL" 28 | 29 | return App; 30 | } 31 | -------------------------------------------------------------------------------- /app/components/code-editor.js: -------------------------------------------------------------------------------- 1 | export default Ember.Component.extend({ 2 | tagName: "textarea", 3 | _initializeEditor: function() { 4 | var self = this; 5 | this.$().val(this.get('value')); 6 | var codemirror = CodeMirror.fromTextArea(self.$().get(0), { 7 | lineWrapping: true, 8 | lineNumbers: true, 9 | mode: 'javascript', 10 | theme: 'solarized', 11 | viewportMargin: Infinity 12 | }); 13 | 14 | codemirror.on("change", function(instance){ 15 | Ember.run(function(){ 16 | self.set("value", instance.getValue()); 17 | }); 18 | }); 19 | 20 | this.set("editor", codemirror); 21 | 22 | }.on('didInsertElement'), 23 | 24 | updateValue: function(){ 25 | if(this.get("editor").getValue() !== this.get("value")){ 26 | this.get("editor").setValue(this.get("value")); 27 | } 28 | }.observes("value") 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diagonal", 3 | "version": "0.0.0", 4 | "private": true, 5 | "directories": { 6 | "doc": "doc", 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "start": "ember server", 11 | "build": "ember build", 12 | "test": "ember test" 13 | }, 14 | "repository": "https://github.com/stefanpenner/ember-cli", 15 | "engines": { 16 | "node": ">= 0.10.0" 17 | }, 18 | "author": "", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "body-parser": "^1.2.0", 22 | "broccoli-asset-rev": "0.0.17", 23 | "broccoli-ember-hbs-template-compiler": "^1.6.1", 24 | "ember-cli": "0.0.42", 25 | "ember-cli-ember-data": "0.1.0", 26 | "ember-cli-inject-live-reload": "^1.0.2", 27 | "ember-cli-ic-ajax": "0.1.1", 28 | "ember-cli-qunit": "0.0.5", 29 | "express": "^4.8.5", 30 | "glob": "^4.0.5", 31 | "broccoli-sass": "^0.2.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/components/template-visualization.js: -------------------------------------------------------------------------------- 1 | export default Em.Component.extend({ 2 | tail: function () { 3 | return this.get('parents').slice(1); 4 | }.property('parents.[]'), 5 | 6 | route: Em.computed.alias('parents.firstObject'), 7 | name: Em.computed.alias('route.value.name'), 8 | hasChildren: Em.computed.alias('route.children.length'), 9 | baseDir: Ember.computed('name', function () { 10 | return this.get('name').replace(/\./g, '/'); 11 | }), 12 | 13 | templateFile: Ember.computed('baseDir', 'usePods', 'podModulePrefix', function () { 14 | return this.genPath('template', 'templates', 'hbs'); 15 | }), 16 | 17 | genPath: function (singular, plural, ext) { 18 | if (this.get('usePods')) { 19 | return 'app/' + this.get('podModulePrefix') + '/' + this.get('baseDir') + '/' + singular + '.' + ext; 20 | } else { 21 | return 'app/' + plural + '/' + this.get('baseDir') + '.' + ext; 22 | } 23 | } 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | environment: environment, 6 | baseURL: '/', 7 | locationType: 'auto', 8 | EmberENV: { 9 | FEATURES: { 10 | // Here you can enable experimental features on an ember canary build 11 | // e.g. 'with-controller': true 12 | } 13 | }, 14 | 15 | APP: { 16 | // Here you can pass flags/options to your application instance 17 | // when it is created 18 | } 19 | }; 20 | 21 | if (environment === 'development') { 22 | // ENV.APP.LOG_RESOLVER = true; 23 | ENV.APP.LOG_ACTIVE_GENERATION = true; 24 | // ENV.APP.LOG_TRANSITIONS = true; 25 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 26 | ENV.APP.LOG_VIEW_LOOKUPS = true; 27 | } 28 | 29 | if (environment === 'test') { 30 | ENV.baseURL = '/'; // Testem prefers this... 31 | } 32 | 33 | if (environment === 'production') { 34 | ENV.baseURL = '/ember-diagonal'; // Testem prefers this... 35 | } 36 | 37 | return ENV; 38 | }; 39 | -------------------------------------------------------------------------------- /app/templates/components/template-visualization.hbs: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | {{#link-to 'route' name}} 4 | {{route.value.template.name}}.hbs 5 | {{/link-to}} 6 |
    7 |
    8 | {{#if tail.length}} 9 | {{template-visualization parents=tail isShowingSubStates=isShowingSubStates usePods=usePods podModulePrefix=podModulePrefix}} 10 | {{else}} 11 | {{#if hasChildren}} 12 | \{{outlet}} contains one of: 13 | 24 | {{else}} 25 | No \{{outlet}} in {{templateFile}} because there are no child routes. 26 | {{/if}} 27 | {{/if}} 28 |
    29 |
    30 | 31 | 32 | -------------------------------------------------------------------------------- /app/controllers/route.js: -------------------------------------------------------------------------------- 1 | export default Em.ObjectController.extend({ 2 | usePods: true, 3 | podModulePrefix: 'pods', 4 | needs: ['application'], 5 | isShowingSubStates: Em.computed.alias('controllers.application.isShowingSubStates'), 6 | parentsAndSelf: function () { 7 | return this.get('parents').concat(this.get('model')); 8 | }.property('parents.[]', 'model'), 9 | 10 | genPath: function (singular, plural, ext) { 11 | if (this.get('usePods')) { 12 | return 'app/' + this.get('podModulePrefix') + '/' + this.get('baseDir') + '/' + singular + '.' + ext; 13 | } else { 14 | return 'app/' + plural + '/' + this.get('baseDir') + '.' + ext; 15 | } 16 | }, 17 | 18 | baseDir: Ember.computed('value.name', function () { 19 | return this.get('value.name').replace(/\./g, '/'); 20 | }), 21 | 22 | routeFile: Ember.computed('baseDir', 'usePods', 'podModulePrefix', function () { 23 | return this.genPath('route', 'routes', 'js'); 24 | }), 25 | 26 | templateFile: Ember.computed('baseDir', 'usePods', 'podModulePrefix', function () { 27 | return this.genPath('template', 'templates', 'hbs'); 28 | }), 29 | 30 | controllerFile: Ember.computed('baseDir', 'usePods', 'podModulePrefix', function () { 31 | return this.genPath('controller', 'controllers', 'js'); 32 | }) 33 | }); 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Diagonal 2 | 3 | This README outlines the details of collaborating on this Ember application. 4 | 5 | A short introduction of this app could easily go here. 6 | 7 | ## Prerequisites 8 | 9 | You will need the following things properly installed on your computer. 10 | 11 | * [Git](http://git-scm.com/) 12 | * [Node.js](http://nodejs.org/) (with NPM) and [Bower](http://bower.io/) 13 | 14 | ## Installation 15 | 16 | * `git clone ` this repository 17 | * change into the new directory 18 | * `npm install` 19 | * `bower install` 20 | 21 | ## Running / Development 22 | 23 | * `ember server` 24 | * Visit your app at http://localhost:4200. 25 | 26 | ### Code Generators 27 | 28 | Make use of the many generators for code, try `ember help generate` for more details 29 | 30 | ### Running Tests 31 | 32 | * `ember test` 33 | * `ember test --server` 34 | 35 | ### Building 36 | 37 | * `ember build` (development) 38 | * `ember build --environment production` (production) 39 | 40 | ### Deploying 41 | 42 | Specify what it takes to deploy your app. 43 | 44 | ## Further Reading / Useful Links 45 | 46 | * ember: http://emberjs.com/ 47 | * ember-cli: http://www.ember-cli.com/ 48 | * Development Browser Extensions 49 | * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) 50 | * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) 51 | 52 | -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "QUnit", 10 | "define", 11 | "console", 12 | "equal", 13 | "notEqual", 14 | "notStrictEqual", 15 | "test", 16 | "asyncTest", 17 | "testBoth", 18 | "testWithDefault", 19 | "raises", 20 | "throws", 21 | "deepEqual", 22 | "start", 23 | "stop", 24 | "ok", 25 | "strictEqual", 26 | "module", 27 | "moduleFor", 28 | "moduleForComponent", 29 | "moduleForModel", 30 | "process", 31 | "expect", 32 | "visit", 33 | "exists", 34 | "fillIn", 35 | "click", 36 | "keyEvent", 37 | "find", 38 | "findWithAssert", 39 | "wait", 40 | "DS", 41 | "keyEvent", 42 | "isolatedContainer", 43 | "startApp", 44 | "andThen", 45 | "currentURL", 46 | "currentPath", 47 | "currentRouteName" 48 | ], 49 | "node": false, 50 | "browser": false, 51 | "boss": true, 52 | "curly": false, 53 | "debug": false, 54 | "devel": false, 55 | "eqeqeq": true, 56 | "evil": true, 57 | "forin": false, 58 | "immed": false, 59 | "laxbreak": false, 60 | "newcap": true, 61 | "noarg": true, 62 | "noempty": false, 63 | "nonew": false, 64 | "nomen": false, 65 | "onevar": false, 66 | "plusplus": false, 67 | "regexp": false, 68 | "undef": true, 69 | "sub": true, 70 | "strict": false, 71 | "white": false, 72 | "eqnull": true, 73 | "esnext": true 74 | } 75 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Diagonal Tests 7 | 8 | 9 | 10 | {{BASE_TAG}} 11 | 12 | 13 | 14 | 15 | 31 | 32 | 33 |
    34 |
    35 | 36 | 37 | 38 | 39 | 40 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/templates/route.hbs: -------------------------------------------------------------------------------- 1 |

    Showing route {{value.name}}

    2 | 6 | 7 | {{#if usePods}} 8 |
    9 | 10 | {{input type='text' value=podModulePrefix class='form-control'}} 11 |
    12 | {{/if}} 13 |
    14 |
    Url
    15 |
    16 | {{#if value.url}} 17 | {{value.url}} 18 | {{else}} 19 |

    This route has no URL, as it is a parent route. You will always be in a child state of this route (e.g. {{value.name}}.index)

    20 | {{/if}} 21 |
    22 |
    Route
    23 |
    {{routeFile}}
    24 |
    Controller
    25 |
    {{controllerFile}}
    26 |
    Template
    27 |
    {{templateFile}}
    28 |
    Template Outline
    29 |
    30 |

    This is what the nested templates look like for this route. All templates shown below will be visible at the same time when you are in this route:

    31 | {{template-visualization parents=parentsAndSelf isShowingSubStates=isShowingSubStates usePods=usePods podModulePrefix=podModulePrefix}} 32 |
    33 | {{#if value.url}} 34 |
    Route Hooks
    35 |
    36 |

    When you visit {{value.url}}, these are the route hooks that will be called:

    37 |

    1. Validation Phase

    38 |

    First all of these model hooks will be called in this order:

    39 |
      40 | {{#each parentsAndSelf}} 41 | {{validation-hooks route=this}} 42 | {{/each}} 43 |
    44 |

    2. Setup Phase

    45 |

    If none of the model hooks abort the transition, either by calling transition.abort() or this.transitionTo('someOtherRoute'), then these setup hooks will be called in this order after all the validation hooks have run:

    46 |
      47 | {{#each parentsAndSelf}} 48 | {{setup-hooks route=this}} 49 | {{/each}} 50 |
    51 |
    52 | {{/if}} 53 |
    54 | -------------------------------------------------------------------------------- /app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | Fork me on GitHub 2 | 3 |
    4 |

    Like this? Also check out Ember Data Model Maker

    5 |
    6 | 7 |
    8 |
    9 |
    10 |

    Diagonal Routes

    11 |

    See what route structure, templates and route hooks are for a given ember route definition. Enter your router code below to see the structure:

    12 | {{code-editor value=routesInput}} 13 |
    14 |
    15 | 16 | 17 | 18 | {{#if isError}} 19 |

    20 |

    21 |

    22 | There was an error: {{theApplication.message}} 23 |

    24 |

    25 | Line {{errorLineNumber}} Column {{errorColumn}} 26 |

    27 |
    28 |

    29 | {{else}} 30 |

    31 |

    32 | 35 |
    36 |

    37 |
    38 |
    39 | 43 | 44 | {{#if isShowingTree}} 45 | {{route-tree routeTree=routeTree isShowingSubStates=isShowingSubStates action='showRoute'}} 46 | {{else}} 47 | {{route-displayer route=routeTree isShowingSubStates=isShowingSubStates}} 48 | {{/if}} 49 |
    50 |
    51 | {{outlet}} 52 |
    53 |
    54 | {{/if}} 55 | 56 |
    57 | -------------------------------------------------------------------------------- /app/components/route-tree.js: -------------------------------------------------------------------------------- 1 | export default Em.Component.extend({ 2 | buildVisualization: function () { 3 | if(!this.get('routeTree')) { 4 | return; 5 | } 6 | 7 | this.$().empty(); 8 | 9 | var margin = {top: 20, right: 120, bottom: 20, left: 120}, 10 | width = 600 - margin.right - margin.left, 11 | height = 500 - margin.top - margin.bottom; 12 | var component = this; 13 | var i = 0; 14 | 15 | var tree = d3.layout.tree() 16 | .size([height, width]); 17 | 18 | var diagonal = d3.svg.diagonal() 19 | .projection(function(d) { return [d.y, d.x]; }); 20 | 21 | var svg = d3.select(this.get('element')).append("svg") 22 | .attr("width", width + margin.right + margin.left) 23 | .attr("height", height + margin.top + margin.bottom) 24 | .append("g") 25 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 26 | 27 | var visFunc = function (route) { 28 | if (!component.isVisible(route)) { 29 | return 'none'; 30 | } 31 | }; 32 | var root = this.get('routeTree'); 33 | 34 | // Compute the new tree layout. 35 | var nodes = tree.nodes(root).reverse(), 36 | links = tree.links(nodes); 37 | 38 | // Normalize for fixed-depth. 39 | nodes.forEach(function(d) { d.y = d.depth * 120; }); 40 | 41 | // Declare the nodes… 42 | var node = svg.selectAll("g.node") 43 | .data(nodes, function(d) { return d.id || (d.id = ++i); }); 44 | 45 | // Enter the nodes. 46 | var nodeEnter = node.enter().append("g") 47 | .attr("class", "node") 48 | .style('display', visFunc) 49 | .style('cursor', 'pointer') 50 | .on('click', function (route) { 51 | component.sendAction('action', route); 52 | }) 53 | .attr("transform", function(d) { 54 | return "translate(" + d.y + "," + d.x + ")"; }); 55 | 56 | nodeEnter.append("circle") 57 | .attr("r", 10) 58 | .style("fill", "#fff") 59 | 60 | 61 | nodeEnter.append("text") 62 | .attr("x", function(d) { 63 | return d.children || d._children ? -13 : 13; }) 64 | .attr("dy", ".35em") 65 | .on('click', function (route) { 66 | component.sendAction('action', route); 67 | }) 68 | .style('cursor', 'pointer') 69 | .attr("text-anchor", function(d) { 70 | return d.children || d._children ? "end" : "start"; }) 71 | .text(function(d) { return d.name; }) 72 | .style("fill-opacity", 1) 73 | .style('display', visFunc); 74 | 75 | // Declare the links… 76 | var link = svg.selectAll("path.link") 77 | .data(links, function(d) { return d.target.id; }); 78 | 79 | // Enter the links. 80 | link.enter().insert("path", "g") 81 | .attr("class", "link") 82 | .attr("d", diagonal) 83 | .style('display', function (link) { 84 | return visFunc(link.target); 85 | }); 86 | 87 | 88 | }.on('didInsertElement').observes('routeTree', 'isShowingSubStates'), 89 | 90 | isVisible: function (route) { 91 | if(!route) { return false; } 92 | if(this.get('isShowingSubStates')) { 93 | return true; 94 | } 95 | 96 | return !route.isSubState; 97 | } 98 | }); 99 | -------------------------------------------------------------------------------- /app/controllers/application.js: -------------------------------------------------------------------------------- 1 | import Ember from "ember"; 2 | import RouteDebug from "diagonal/lib/route-debug"; 3 | 4 | export default Ember.Controller.extend({ 5 | isShowingSubStates: false, 6 | isShowingTree: false, 7 | isShowingList: Em.computed.not('isShowingTree'), 8 | 9 | queryParams: ['routesInput'], 10 | 11 | routesInput: Em.computed( function() { 12 | return "this.route('posts');\n" + 13 | "this.route('post', {path: '/posts/:post_id'}, function () {\n" + 14 | " this.route('new');\n" + 15 | " this.route('edit');\n" + 16 | " this.route('comments', function() {\n" + 17 | " this.route('new');\n" + 18 | " });\n" + 19 | "});\n" + 20 | "this.route('signup');" 21 | }), 22 | 23 | routeTree: Em.computed('theApplication', 'isError', function () { 24 | if (this.get('isError')) { 25 | return null; 26 | } 27 | var rd = RouteDebug.create({ 28 | application: this.get('theApplication') 29 | }); 30 | 31 | return this.mungeTree(rd.get('routeTree')); 32 | }), 33 | 34 | routeFor: function (name, start) { 35 | var tree = start || this.get('routeTree'); 36 | if (tree.value.name == name) { 37 | return tree; 38 | } 39 | 40 | if(tree.children) { 41 | var children = tree.children.map(function (child) { 42 | return this.routeFor(name, child); 43 | }, this).compact(); 44 | 45 | if (children.length) { 46 | return children[0]; 47 | } 48 | } 49 | }, 50 | 51 | mungeTree: function (node, parent, parents) { 52 | node.name = node.value.name; 53 | node.isSubState = node.name.match(/(^|\.)(loading|error)$/); 54 | parents = (parents && parents.slice()) || []; 55 | if(parent) { 56 | node.parent = parent; 57 | parents.push(parent); 58 | } 59 | node.parents = parents; 60 | if(node.children && node.children.length) { 61 | node.children.forEach(function (child) { 62 | this.mungeTree(child, node, parents); 63 | }, this); 64 | } 65 | return node; 66 | }, 67 | 68 | isError: Em.computed('theApplication', function () { 69 | return (this.get('theApplication') instanceof Error); 70 | }), 71 | 72 | errorLineNumber: function () { 73 | try { 74 | return this.get('errorMatch')[1] 75 | } catch (e) { 76 | return 'unknown'; 77 | } 78 | }.property('errorMatch'), 79 | 80 | errorColumn: function () { 81 | try { 82 | return this.get('errorMatch')[2] 83 | } catch (e) { 84 | return 'unknown'; 85 | } 86 | }.property('errorMatch'), 87 | 88 | errorMatch: function () { 89 | return this.get('theApplication').stack.match(/:(\d+):(\d+)/) 90 | }.property('theApplication'), 91 | 92 | transitionOnChange: function() { 93 | this.transitionToRoute('index'); 94 | }.observes('theApplication'), 95 | 96 | theApplication: function() { 97 | if(this.oldApp) { 98 | Em.run(this.oldApp, 'destroy'); 99 | } 100 | var App = Ember.Application.create({ 101 | name: "App", 102 | rootElement: '#demo-app' 103 | }); 104 | 105 | 106 | var container = App.__container__; 107 | 108 | App.Router.reopen({ 109 | location: 'none' 110 | }); 111 | App.deferReadiness(); 112 | 113 | var Router = App.Router; 114 | try { 115 | var evalStr = "Router.map(function(){ " + this.get('routesInput') + " })"; 116 | eval(evalStr); 117 | } catch (e) { 118 | return e; 119 | } 120 | Ember.run(App, 'advanceReadiness'); 121 | container.lookup('router:main').startRouting(); 122 | this.oldApp = App; 123 | return App; 124 | }.property('routesInput'), 125 | 126 | actions: { 127 | showRoute: function(route) { 128 | this.transitionToRoute('route', route.name); 129 | }, 130 | showTree: function () { 131 | this.set('isShowingTree', true); 132 | }, 133 | showList: function () { 134 | this.set('isShowingTree', false); 135 | } 136 | } 137 | }); 138 | -------------------------------------------------------------------------------- /app/lib/route-debug.js: -------------------------------------------------------------------------------- 1 | var classify = Ember.String.classify; 2 | 3 | var RouteDebug = Ember.Object.extend({ 4 | 5 | router: Ember.computed('application', function() { 6 | return this.get('application.__container__').lookup('router:main'); 7 | }), 8 | 9 | applicationController: Ember.computed('application', function() { 10 | var container = this.get('application.__container__'); 11 | return container.lookup('controller:application'); 12 | }), 13 | 14 | routeTree: Ember.computed('router', function() { 15 | var routeNames = this.get('router.router.recognizer.names'); 16 | var routeTree = {}; 17 | 18 | for(var routeName in routeNames) { 19 | if (!routeNames.hasOwnProperty(routeName)) { 20 | continue; 21 | } 22 | var route = routeNames[routeName]; 23 | var handlers = Ember.A(route.handlers); 24 | buildSubTree.call(this, routeTree, route); 25 | } 26 | 27 | return arrayizeChildren({ children: routeTree }).children[0]; 28 | }) 29 | }); 30 | 31 | 32 | var buildSubTree = function(routeTree, route) { 33 | var handlers = route.handlers; 34 | var subTree = routeTree, item, 35 | routeClassName, routeHandler, controllerName, 36 | controllerClassName, container, templateName, 37 | controllerFactory; 38 | for (var i = 0; i < handlers.length; i++) { 39 | item = handlers[i]; 40 | var handler = item.handler; 41 | if (subTree[handler] === undefined) { 42 | routeClassName = classify(handler.replace('.', '_')) + 'Route'; 43 | container = this.get('application.__container__'); 44 | routeHandler = container.lookup('router:main').router.getHandler(handler); 45 | controllerName = routeHandler.get('controllerName') || routeHandler.get('routeName'); 46 | controllerClassName = classify(controllerName.replace('.', '_')) + 'Controller'; 47 | controllerFactory = container.lookupFactory('controller:' + controllerName); 48 | templateName = handler.replace('.', '/'); 49 | 50 | subTree[handler] = { 51 | value: { 52 | name: handler, 53 | routeHandler: { 54 | className: routeClassName, 55 | name: handler 56 | }, 57 | controller: { 58 | className: controllerClassName, 59 | name: controllerName, 60 | exists: controllerFactory ? true : false 61 | }, 62 | template: { 63 | name: templateName 64 | } 65 | } 66 | }; 67 | 68 | if (i === handlers.length - 1) { 69 | // it is a route, get url 70 | subTree[handler].value.url = getURL(container, route.segments); 71 | subTree[handler].value.type = 'route'; 72 | } else { 73 | // it is a resource, set children object 74 | subTree[handler].children = {}; 75 | subTree[handler].value.type = 'resource'; 76 | } 77 | 78 | } 79 | subTree = subTree[handler].children; 80 | } 81 | }; 82 | 83 | function arrayizeChildren(routeTree) { 84 | var obj = { value: routeTree.value }; 85 | 86 | if (routeTree.children) { 87 | var childrenArray = []; 88 | for(var i in routeTree.children) { 89 | var route = routeTree.children[i]; 90 | childrenArray.push(arrayizeChildren(route)); 91 | } 92 | obj.children = childrenArray; 93 | } 94 | 95 | return obj; 96 | } 97 | 98 | function getURL(container, segments) { 99 | var locationImplementation = container.lookup('router:main').location; 100 | var url = []; 101 | for (var i = 0; i < segments.length; i++) { 102 | var name = null; 103 | 104 | try { 105 | name = segments[i].generate(); 106 | } catch (e) { 107 | // is dynamic 108 | name = ':' + segments[i].name; 109 | } 110 | if (name) { 111 | url.push(name); 112 | } 113 | } 114 | 115 | url = url.join('/'); 116 | 117 | if (url.match(/_unused_dummy_/)) { 118 | url = ''; 119 | } else { 120 | url = '/' + url; 121 | url = locationImplementation.formatURL(url); 122 | } 123 | 124 | return url; 125 | } 126 | 127 | export default RouteDebug; 128 | --------------------------------------------------------------------------------