From dc988e0faaa028e4780d640eee40d8e6556e66a3 Mon Sep 17 00:00:00 2001 From: Buddy Sandidge Date: Sun, 15 Feb 2015 23:43:43 -0800 Subject: [PATCH] Add router to frontend --- .jshintrc | 2 +- assets/app/app.js | 16 +++++++++++----- assets/app/core/layout/module.js | 2 +- assets/app/core/model.js | 18 ++++++++++++++++++ assets/app/core/router.js | 29 +++++++++++++++++++++++++++++ assets/app/main.js | 15 ++++++++++++++- bin/deck | 11 +++++++++-- test/app/app.js | 18 +++++++++++++++++- test/app/core/model.js | 13 +++++++++++++ test/app/core/router.js | 26 ++++++++++++++++++++++++++ 10 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 assets/app/core/model.js create mode 100644 assets/app/core/router.js create mode 100644 test/app/core/model.js create mode 100644 test/app/core/router.js diff --git a/.jshintrc b/.jshintrc index 1c0f579..96c7d88 100644 --- a/.jshintrc +++ b/.jshintrc @@ -28,7 +28,7 @@ "maxdepth": 4, "maxerr": 50, "maxlen": 80, - "maxparams": 4, + "maxparams": 5, "indent": 2, "globals": { diff --git a/assets/app/app.js b/assets/app/app.js index a952311..60f7025 100644 --- a/assets/app/app.js +++ b/assets/app/app.js @@ -1,8 +1,10 @@ define([ 'underscore', + 'backbone', 'marionette', - 'core/layout/module' -], function AppDefine(_, Marionette, Layout) { + 'core/layout/module', + 'core/router' +], function AppDefine(_, Backbone, Marionette, Layout, Router) { 'use strict'; var Application = Marionette.Application; @@ -15,15 +17,19 @@ define([ } App.prototype = new Application(); + _.extend(App.prototype, { Layout: Layout, initialize: function initialize(config) { config = config || {}; - if (config.Layout) { - this.Layout = config.Layout; - } + this.Layout = config.Layout || this.Layout; this.module('layout', this.Layout); + + this.router = new Router(); + this.listenTo(this.router.model, 'change:slide', function (model, num) { + this.triggerMethod('slide:change', num); + }, this); } }); diff --git a/assets/app/core/layout/module.js b/assets/app/core/layout/module.js index a3484e1..6a3394d 100644 --- a/assets/app/core/layout/module.js +++ b/assets/app/core/layout/module.js @@ -10,7 +10,7 @@ define([ Model: LayoutModel, View: LayoutView, - onStart: function(options) { + onStart: function onStart(options) { var opts = _.defaults({}, options || {}, {layout: {}}); _.defaults(opts.layout, {view: {}, model: {}}); diff --git a/assets/app/core/model.js b/assets/app/core/model.js new file mode 100644 index 0000000..baebdfa --- /dev/null +++ b/assets/app/core/model.js @@ -0,0 +1,18 @@ +define([ + 'underscore', + 'backbone', + 'marionette' +], function (_, Backbone, Marionette) { + var Obj = Marionette.Object; + var Model = Backbone.Model; + + function BaseModel() { + Model.apply(this, arguments); + } + + BaseModel.prototype = new Model(); + + _.extend(BaseModel.prototype, Obj.prototype); + + return BaseModel; +}); diff --git a/assets/app/core/router.js b/assets/app/core/router.js new file mode 100644 index 0000000..00afa3f --- /dev/null +++ b/assets/app/core/router.js @@ -0,0 +1,29 @@ +define(['underscore', 'backbone', 'core/model'], function (_, Backbone, Model) { + var Router = Backbone.Router; + + function DeckRouter() { + Router.apply(this, arguments); + } + + DeckRouter.prototype = new Router(); + + _.extend(DeckRouter.prototype, { + Model: Model, + model: null, + + routes: { + 'slide/:id': 'slide' + }, + + initialize: function initialize(config) { + config = config || {}; + this.model = config.model || new this.Model(); + }, + + slide: function slide(id) { + this.model.set('slide', parseInt(id, 10)); + } + }); + + return DeckRouter; +}); diff --git a/assets/app/main.js b/assets/app/main.js index d06f467..c946a1c 100644 --- a/assets/app/main.js +++ b/assets/app/main.js @@ -1,10 +1,23 @@ -define('main', ['underscore', 'config', 'app'], function main(_, config, App) { +define('main', [ + 'underscore', + 'backbone', + 'config', + 'app' +], function main(_, Backbone, config, App) { 'use strict'; requirejs.config(config()); return function main(options) { var opts = _.defaults({}, options, {config: {}}); var app = new App(opts); + + app.on('start', function () { + if (!Backbone.history.start({pushState: true})) { + app.router.navigate('slide/1'); + } + }); + app.start(opts.config); + return app; }; }); diff --git a/bin/deck b/bin/deck index 6146530..f2492df 100755 --- a/bin/deck +++ b/bin/deck @@ -34,7 +34,7 @@ function pathToFile() { ); } -app.get('/', function index(req, res) { +function index(req, res) { var indexFile = pathToFile('assets', 'index.html'); function compileServe(err, text) { @@ -47,7 +47,9 @@ app.get('/', function index(req, res) { } fs.readFile(indexFile, {encoding: 'utf8'}, compileServe); -}); +} + +app.get('/', index); function serveIfExists(path) { return function _serveIfExists(cb) { @@ -98,6 +100,11 @@ app.get('/slides', function showHandler(req, res) { }); app.get(/slide\/(\d+)/, function showHandler(req, res, next) { + if (!req.xhr) { + index(req, res); + return; + } + var slideId = req.params[0]; var slidePath = pathToFile('data', 'slide', slideId + '.json'); diff --git a/test/app/app.js b/test/app/app.js index e2fb6a2..78afe57 100644 --- a/test/app/app.js +++ b/test/app/app.js @@ -1,4 +1,4 @@ -define(['marionette', 'app'], function (Marionette, App) { +define(['backbone', 'marionette', 'app'], function (Backbone, Marionette, App) { describe('App', function () { it('→ exits', function () { expect(new App()).not.toBeUndefined(); @@ -18,5 +18,21 @@ define(['marionette', 'app'], function (Marionette, App) { expect(new App().layout).not.toBeUndefined(); }); }); + + describe('→ has router', function () { + it('→ exits', function () { + expect(new App().router).not.toBeUndefined(); + }); + + describe('→ listens to events', function () { + it('→ exits', function () { + var app = new App(); + app.onSlideChange = app.onSlideChange || function () {}; + spyOn(app, 'onSlideChange'); + app.router.model.set('slide', 123); + expect(app.onSlideChange).toHaveBeenCalledWith(123); + }); + }); + }); }); }); diff --git a/test/app/core/model.js b/test/app/core/model.js new file mode 100644 index 0000000..cfdde4b --- /dev/null +++ b/test/app/core/model.js @@ -0,0 +1,13 @@ +define(['core/model'], function (Model) { + describe('Model', function () { + it('→ exits', function () { + expect(new Model()).not.toBeUndefined(); + }); + + describe('→ triggerMethod', function () { + it('→ exits', function () { + expect((new Model()).triggerMethod).not.toBeUndefined(); + }); + }); + }); +}); diff --git a/test/app/core/router.js b/test/app/core/router.js new file mode 100644 index 0000000..1c8d6b2 --- /dev/null +++ b/test/app/core/router.js @@ -0,0 +1,26 @@ +define(['backbone', 'core/router'], function (Backbone, Router) { + describe('Router', function () { + it('→ exits', function () { + expect(new Router()).not.toBeUndefined(); + }); + + describe('→ is passed model', function () { + it('→ exits', function () { + expect((new Router()).model).not.toBeUndefined(); + }); + + it('→ same as passed in', function () { + var model = new Backbone.Model(); + expect((new Router({model: model})).model).toBe(model); + }); + }); + + describe('→ navigate changes model', function () { + it('→ goes to the number for page', function () { + var router = new Router(); + router.slide('123'); + expect(router.model.get('slide')).toBe(123); + }); + }); + }); +});