├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── README.md
├── browsers.js
├── karma.conf.js
├── lib
└── routehandler.js
├── logo.png
├── package.json
├── routehandler-logo.svg
├── src
└── routehandler.tag
└── test
├── routehandler.coffee
└── samplepages.tag
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | .DS_Store
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4.2.3"
4 |
5 | branches:
6 | only:
7 | - master
8 |
9 | script:
10 | npm test
11 |
12 | after_success:
13 | - 'cat ./coverage/report-lcov/lcov.info | ./node_modules/.bin/coveralls'
14 |
15 | sudo: false
16 |
17 | before_install:
18 | npm install
19 |
20 | notifications:
21 | email: false
22 |
23 | addons:
24 | sauce_connect:true
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## [1.2.2]
4 | ### Bugfix
5 | * Tags not remounting after middlware (issue #4)
6 |
7 | ## [1.2.1]
8 | ### Added
9 | * Change Middleware callback parameters to (ctx,next,page)
10 | * Allowed `use` and `tag` to be used together on the same route declaration
11 |
12 | ## [1.2.0]
13 | ### Added
14 | * Middleware, tag can now be replace with use within route, which accepts a callback
15 |
16 | ## [1.1.0]
17 | ### Added
18 | * Default routes using '/', allowing subroutes to autoload when parent routehandler changes
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://travis-ci.org/crisward/riot-routehandler)
4 | [](https://coveralls.io/github/crisward/riot-routehandler?branch=master)
5 | [](https://www.npmjs.com/package/riot-routehandler)
6 | [](https://www.npmjs.com/package/riot-routehandler)
7 |
8 | ## Installing
9 |
10 | ```bash
11 | npm install riot-routehandler@1.2.3 # for riot 2
12 |
13 | npm install riot-routehandler # for riot 3
14 | ```
15 |
16 | ## Versions
17 |
18 | This branch is for Riot v3
19 | I've forked a branch for Riot v2 and will maintain that if neccessary going forward.
20 |
21 | ## Demo
22 |
23 | Basic demo [here](http://codepen.io/crisward/pen/xwGJpM?editors=101)
24 |
25 | ## Usage
26 |
27 | This has been designed to be used with a common js compiler. I mainly use 'Browserify', though it should work fine
28 | with webpack. You'll also need something like `riotify` to require your tags in.
29 |
30 | First you'll need to setup your routes and tag files. Each route can be assigned
31 | a tag. When the route is navigated to your tag will be loaded into the routehandler tag.
32 | Mount will be called when your tag is added and unmount will be called when it is navigated away from.
33 |
34 | ```javascript
35 | //app.js
36 | var riot = require('riot');
37 | require('riot-routehandler');
38 | require('home.tag')
39 | require('about.tag')
40 | require('settings.tag')
41 | require('settings1.tag')
42 | require('settings2.tag')
43 |
44 | var routes = [
45 | {route:"/",tag:"home"},
46 | {route:"/about/",tag:"about"},
47 | {route:"/settings/",tag:"settings",routes:[
48 | {route:"setting1/",tag:"settings1"},
49 | {route:"setting2/:name?",tag:"settings2"},
50 | ]}
51 | ];
52 |
53 | var app = riot.mount('routehandler',{routes:routes,options:{hashbang:true}});
54 | ```
55 |
56 | You'll also need to add the routehandler to your html file.
57 | If you add links to your page which match those in your router, they'll be
58 | intercepted to use your client side routes.
59 |
60 | ```html
61 |
62 |
63 |
64 | Sample Doc
65 |
66 |
67 | Home
68 | About
69 | Settings
70 |
71 |
72 |
73 |
74 |
75 | ```
76 |
77 | ### Subroutes
78 |
79 | In the example above the settings section has two sub-routes, `/settings/setting1/` and
80 | `/settings/setting2/:name?`.
81 |
82 | For these to work, the settings tag will also need a routehandler.
83 |
84 | ```html
85 |
86 |
Settings Panel
87 |
88 |
89 | ```
90 |
91 | ### Parameters
92 |
93 | Any parameters passed into routes are made available on your tag as opts.params.
94 | So in the example above, the name parameter would be accessed as...
95 |
96 | ```html
97 |
98 |
Hello {opts.params.name}
99 |
100 | ```
101 |
102 | ### Passing in data
103 |
104 | Any properties passed into the top level routehandler will be passed into
105 | all sub-routehandlers too. This can be useful for passing down 'stores'.
106 |
107 | `app = riot.mount('routehandler',{routes:routes,options:{hashbang:true},stores:stores})`
108 |
109 | ### Options
110 |
111 | Any options your want to pass into the [page.js](https://github.com/visionmedia/page.js)
112 | system can be done via `options`. The above example is using hashbang routing.
113 | Other options can be [found here](https://github.com/visionmedia/page.js#pageoptions)
114 |
115 | ### Navigation
116 |
117 | Navigation can be done programmatically via opts.page. Riot-routehandler
118 | uses [page.js](https://github.com/visionmedia/page.js), so naviating is done with this
119 | passed in function. ie to go to about page
120 |
121 | `opts.page('/about/')`
122 |
123 | ### Default Routes
124 |
125 | It is possible to have a default subroute. This can be done by simply using the `/`
126 | in your subroutes. With the following routes, `subpage` tag will be shown by default
127 | if you navigate to `/page/`.
128 |
129 | ```html
130 | routes = [
131 | {route:"/",tag:"home"},
132 | {route:"/page/",tag:"settings",routes:[
133 | {route:"/",tag:"subpage"},
134 | {route:"/another/",tag:"subpage2"},
135 | ]}
136 | ];
137 | ```
138 |
139 | ### Middleware
140 |
141 | It is sometimes useful to run code before a tag is displayed, perhaps for permission
142 | checking or even animation (ie changing classes on the body tag)
143 |
144 | This is possible by passing functions into the router in the form of middleware.
145 | Each middleware function will be passed a context object, a next callback and a reference
146 | to the `page` instance so you have access to the various page methods. eg.
147 |
148 | ```javascript
149 | var auth = function(ctx,next,page){
150 | if(loggedin) return next();
151 | page.redirect('/login')
152 | }
153 |
154 | routes = [
155 | {route:"/",tag:"home"},
156 | {route:"/admin/",use:auth},
157 | {route:"/admin/",tag:"adminpanel",routes:[
158 | {route:"/",tag:"mainadmin"},
159 | {route:"/user/",tag:"useradmin"},
160 | ]}
161 | ];
162 |
163 | ```
164 |
165 |
166 |
167 |
168 | ## About
169 |
170 | Couldn't find anything for riot which gave this ui-router type functionality.
171 | I also wanted a familiar path syntax, so used page.js as it uses the same route
172 | matching as express.js. Also thought this would aid in making this work server
173 | side eventually. Finally I wanted something to be small and simple in the spirit of riot.
174 | Angular ui-router does a ton of stuff I never use, so I've kept this to do
175 | exaclty what I need and no more. Page.js is apparently 1200bytes and this library
176 | is <60 lines of code.
177 |
178 |
179 | ## Running Tests
180 |
181 | ```
182 | npm install
183 | npm test
184 | ```
185 |
186 | ## Compatibility
187 |
188 | Internet Explorer versions 9 and below don't support the pushstate api, so require the addition of a browser shim to make this work. Page.js recommends . This is available via a cdn, and if
189 | included with a conditional comment, would only be loaded in IE9 and below.
190 |
191 | ```html
192 |
195 | ```
196 |
197 |
198 | ## Todo
199 |
200 | * Add an examples folder and a pretty site.
201 | * Maybe add a demo video.
202 |
203 |
204 | ## Credit
205 |
206 | Thanks to vision media for [page.js](https://github.com/visionmedia/page.js) which this depends on and the
207 | other routers which inspired this including [angular-ui-router](https://github.com/angular-ui/ui-router)
208 |
209 |
210 | Thanks also to [coderofsalvation](https://github.com/coderofsalvation) for his middleware suggestion
211 | and the [Noun Project](https://thenounproject.com/) for the logo.
212 |
213 | ## License
214 |
215 | (The MIT License)
216 |
217 | Copyright (c) 2015 Cris Ward
218 |
219 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
220 |
221 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
222 |
223 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
224 |
225 |
--------------------------------------------------------------------------------
/browsers.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | browsers: {
3 | slsafari: {
4 | base: 'SauceLabs',
5 | browserName: 'safari',
6 | platform: 'OS X 10.10',
7 | group: 0
8 | },
9 | //slIE9: {
10 | // base: 'SauceLabs',
11 | // browserName: 'internet explorer',
12 | // platform: 'Windows 7',
13 | // version: '9',
14 | // group: 1
15 | //},
16 | slIE10: {
17 | base: 'SauceLabs',
18 | browserName: 'internet explorer',
19 | platform: 'Windows 8',
20 | version: '10',
21 | group: 1
22 | },
23 | slIOS8: {
24 | base: 'SauceLabs',
25 | browserName: 'iphone',
26 | platform: 'OS X 10.10',
27 | version: '8.4',
28 | group: 1
29 | },
30 | slIE11: {
31 | base: 'SauceLabs',
32 | browserName: 'internet explorer',
33 | platform: 'Windows 8.1',
34 | version: '11',
35 | group: 1
36 | },
37 | slandroid5: {
38 | base: 'SauceLabs',
39 | browserName: 'android',
40 | version: '5.1',
41 | group: 2
42 | },
43 | slchrome: {
44 | base: 'SauceLabs',
45 | browserName: 'chrome',
46 | group: 2
47 | },
48 | slfirefox: {
49 | base: 'SauceLabs',
50 | browserName: 'firefox',
51 | group: 2
52 | },
53 | slandroid4: {
54 | base: 'SauceLabs',
55 | browserName: 'android',
56 | version: '4.0',
57 | group: 3
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | 'use strict';
3 | var saucelabsBrowsers = require('./browsers').browsers
4 | var browsers = ['PhantomJS']
5 | if (process.env.SAUCE_USERNAME) {
6 | browsers = Object.keys(saucelabsBrowsers)
7 | }
8 |
9 | config.set({
10 | basePath: '',
11 | frameworks: ['browserify','mocha', 'chai','sinon'],
12 | files: [
13 | './node_modules/simulant/dist/simulant.js',
14 | './test/*.coffee'
15 | ],
16 | preprocessors: {
17 | './test/*.coffee': [ 'browserify'],
18 | './lib/*.js': [ 'browserify']
19 | },
20 | "browserify": {
21 | "debug": true,
22 | "transform": ["browserify-istanbul"],
23 | extensions: ['.js', '.tag', '.coffee']
24 | },
25 | reporters: ['spec', "coverage",'saucelabs'],
26 | //hostname:'192.168.1.7',
27 | port: 9876,
28 | colors: true,
29 | autoWatch: true,
30 | singleRun: true,
31 |
32 | // level of logging
33 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
34 | logLevel: config.LOG_INFO,
35 |
36 | browsers: browsers,
37 | coverageReporter: {
38 | dir: 'coverage/',
39 | reporters: [{type:'lcov',subdir: 'report-lcov'},{type:'text-summary'}]
40 | },
41 | sauceLabs: {
42 | build: 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')',
43 | tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
44 | testName: 'riot-routehandler',
45 | connectOptions: {
46 | port: 5757,
47 | logfile: 'sauce_connect.log'
48 | },
49 | startConnect: true,
50 | recordVideo: false,
51 | recordScreenshots: false,
52 | },
53 | captureTimeout: 120000,// Increase timeout in case connection in CI is slow
54 | customLaunchers: saucelabsBrowsers
55 | });
56 | };
--------------------------------------------------------------------------------
/lib/routehandler.js:
--------------------------------------------------------------------------------
1 |
2 | riot.tag2('routehandler', '', '', '', function(opts) {
3 | var page;
4 |
5 | page = null;
6 |
7 | if (typeof exports === "object" && (typeof exports !== "undefined" && exports !== null)) {
8 | page = require('page');
9 | } else if (window.page == null) {
10 | return console.log('Page.js not found - please check it has been npm installed or included in your page');
11 | } else {
12 | page = window.page;
13 | }
14 |
15 | this.on('mount', (function(_this) {
16 | return function() {
17 | var basepath, ref;
18 | _this.tagstack = [];
19 | if (opts.routes) {
20 | _this.mountRoutes({
21 | handler: _this
22 | }, opts.routes);
23 | if ((ref = opts.options) != null ? ref.base : void 0) {
24 | basepath = opts.options.base;
25 | page.base(basepath);
26 | delete opts.options.base;
27 | }
28 | return page(opts.options);
29 | }
30 | };
31 | })(this));
32 |
33 | this.mountRoutes = (function(_this) {
34 | return function(parent, routes) {
35 | var route;
36 | return route = _this.findRoute(null, routes, function(tree, req) {
37 | var i, idx, len, nexttag, ref, ref1, ref2, ref3, removeTag, results, routeopts, tag;
38 | delete opts.routes;
39 | routeopts = opts;
40 | routeopts.page = page;
41 | routeopts.params = req.params;
42 | tag = _this;
43 | for (idx = i = 0, len = tree.length; i < len; idx = ++i) {
44 | route = tree[idx];
45 | if (_this.tagstack[idx] && _this.tagstack[idx].tagname === route.tag) {
46 | nexttag = _this.tagstack[idx].nexttag;
47 | riot.update();
48 | } else {
49 | if (tag && (route != null ? route.tag : void 0)) {
50 | nexttag = tag.setTag(route.tag, routeopts);
51 | }
52 | }
53 | _this.tagstack[idx] = {
54 | tagname: route.tag,
55 | nexttag: nexttag,
56 | tag: tag
57 | };
58 | tag = (nexttag != null ? (ref = nexttag[0]) != null ? ref.tags.routehandler : void 0 : void 0) || (nexttag != null ? (ref1 = nexttag[0]) != null ? (ref2 = ref1.root.querySelector('routehandler')) != null ? ref2._tag : void 0 : void 0 : void 0);
59 | }
60 | results = [];
61 | while (idx < _this.tagstack.length) {
62 | removeTag = _this.tagstack.pop();
63 | results.push((ref3 = removeTag.nexttag[0]) != null ? ref3.unmount(true) : void 0);
64 | }
65 | return results;
66 | });
67 | };
68 | })(this);
69 |
70 | this.setTag = (function(_this) {
71 | return function(tagname, routeopts) {
72 | var tag;
73 | _this.root.childNodes[0].setAttribute("data-is", tagname);
74 | _this.tags[tagname];
75 | tag = riot.mount(tagname, routeopts);
76 | tag[0].opts = routeopts;
77 | return tag;
78 | };
79 | })(this);
80 |
81 | this.findRoute = (function(_this) {
82 | return function(parents, routes, cback) {
83 | var i, len, parentpath, results, route, subparents;
84 | parentpath = parents ? parents.map(function(ob) {
85 | return ob.route;
86 | }).join("").replace(/\/\//g, '/') : "";
87 | results = [];
88 | for (i = 0, len = routes.length; i < len; i++) {
89 | route = routes[i];
90 | if ((route.use != null) && typeof route.use === "function") {
91 | (function(route) {
92 | var mainroute;
93 | mainroute = (parentpath + route.route).replace(/\/\//g, '/');
94 | return page(mainroute, function(ctx, next) {
95 | if (mainroute !== "*") {
96 | cback([route], ctx);
97 | }
98 | return route.use(ctx, next, page);
99 | });
100 | })(route);
101 | }
102 | if (route.tag != null) {
103 | subparents = parents ? parents.slice() : [];
104 | subparents.push(route);
105 | (function(subparents) {
106 | var mainroute, thisroute;
107 | thisroute = route;
108 | mainroute = (parentpath + route.route).replace(/\/\//g, '/');
109 | return page(mainroute, function(req, next) {
110 | var ref;
111 | cback(subparents, req);
112 | if ((ref = thisroute.routes) != null ? ref.filter(function(route) {
113 | return route.route === "/";
114 | }).length : void 0) {
115 | return next();
116 | }
117 | });
118 | })(subparents);
119 | }
120 | if (route.routes) {
121 | results.push(_this.findRoute(subparents, route.routes, cback));
122 | } else {
123 | results.push(void 0);
124 | }
125 | }
126 | return results;
127 | };
128 | })(this);
129 | });
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crisward/riot-routehandler/b61e2926a8bb2e2e2b2b3773f8cb91577915d503/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "riot-routehandler",
3 | "version": "3.0.2",
4 | "description": "A angular-ui-router inspired router handler for riot.js",
5 | "main": "lib/routehandler.js",
6 | "repository": "crisward/riot-routehandler",
7 | "scripts": {
8 | "test": "karma start",
9 | "tdd": "npm run watch & karma start --reporters=spec --browsers=Chrome --singleRun=false",
10 | "watch": "riot -w src lib --template pug",
11 | "build": "riot src lib --template pug"
12 | },
13 | "directories": {
14 | "lib": "lib"
15 | },
16 | "browserify": {
17 | "transform": [
18 | [
19 | "riotify",
20 | {
21 | "expr": false,
22 | "type": "coffee",
23 | "template": "pug",
24 | "extension": ".tag"
25 | }
26 | ],
27 | [
28 | "coffeeify",
29 | {
30 | "extension": ".coffee"
31 | }
32 | ]
33 | ]
34 | },
35 | "author": "cris ward",
36 | "license": "ISC",
37 | "dependencies": {
38 | "page": "^1.6.3"
39 | },
40 | "publishConfig": {
41 | "registry": "http://registry.npmjs.org"
42 | },
43 | "devDependencies": {
44 | "browserify-istanbul": "0.2.1",
45 | "chai": "3.0.0",
46 | "coffee-script": "1.9.3",
47 | "coffeeify": "1.1.0",
48 | "coveralls": "2.11.4",
49 | "istanbul": "0.3.20",
50 | "karma": "0.13.9",
51 | "karma-browserify": "4.3.0",
52 | "karma-chai": "0.1.0",
53 | "karma-chrome-launcher": "0.2.1",
54 | "karma-coverage": "0.5.0",
55 | "karma-mocha": "0.1",
56 | "karma-phantomjs-launcher": "0.1",
57 | "karma-sauce-launcher": "0.2.14",
58 | "karma-sinon": "1.0.4",
59 | "karma-spec-reporter": "0.0.20",
60 | "mocha": "2.0.1",
61 | "pug": "0.1.0",
62 | "riot": "3.0.2",
63 | "riotify": "0.1.2",
64 | "simulant": "0.1.5",
65 | "sinon": "1.12.2",
66 | "sinon-chai": "2.8.0",
67 | "watchify": "^3.2.1"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/routehandler-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
40 |
--------------------------------------------------------------------------------
/src/routehandler.tag:
--------------------------------------------------------------------------------
1 | routehandler
2 | div(data-is="{tagname}")
3 |
4 | script(type='text/coffeescript').
5 | page = null
6 | if typeof exports == "object" && exports?
7 | page = require 'page'
8 | else if !window.page?
9 | return console.log 'Page.js not found - please check it has been npm installed or included in your page'
10 | else
11 | page = window.page
12 | @on 'mount',=>
13 | @tagstack = []
14 | if opts.routes
15 | @mountRoutes({handler:@},opts.routes)
16 | if opts.options?.base
17 | basepath = opts.options.base
18 | page.base(basepath)
19 | delete opts.options.base
20 | page(opts.options)
21 |
22 | @mountRoutes = (parent,routes)=>
23 | route = @findRoute null,routes,(tree,req)=>
24 | delete opts.routes
25 | routeopts = opts
26 | routeopts.page = page
27 | routeopts.params = req.params
28 | tag = @
29 | for route,idx in tree
30 | if @tagstack[idx] && @tagstack[idx].tagname == route.tag
31 | nexttag = @tagstack[idx].nexttag
32 | riot.update()
33 | else
34 | nexttag = tag.setTag(route.tag,routeopts) if tag && route?.tag #dont mount middlware
35 | @tagstack[idx] = {tagname:route.tag,nexttag:nexttag,tag:tag}
36 | tag = nexttag?[0]?.tags.routehandler || nexttag?[0]?.root.querySelector('routehandler')?._tag
37 | while idx < @tagstack.length
38 | removeTag = @tagstack.pop()
39 | removeTag.nexttag[0]?.unmount(true)
40 |
41 | @setTag = (tagname,routeopts)=>
42 | @root.childNodes[0].setAttribute("data-is",tagname)
43 | @tags[tagname]
44 | tag = riot.mount(tagname,routeopts)
45 | tag[0].opts = routeopts
46 | tag
47 |
48 | @findRoute = (parents,routes,cback)=>
49 | parentpath = if parents then parents.map((ob)->ob.route).join("").replace(/\/\//g,'/') else ""
50 | for route in routes
51 | if route.use? && typeof route.use == "function"
52 | do (route)->
53 | mainroute = (parentpath+route.route).replace(/\/\//g,'/')
54 | page mainroute,(ctx,next)->
55 | cback([route],ctx) if mainroute !="*" #dont call unmount with wild
56 | route.use(ctx,next,page)
57 |
58 | if route.tag?
59 | subparents = if parents then parents.slice() else []
60 | subparents.push(route)
61 | do (subparents)->
62 | thisroute = route
63 | mainroute = (parentpath+route.route).replace(/\/\//g,'/')
64 | page mainroute, (req,next)->
65 | cback(subparents,req)
66 | next() if thisroute.routes?.filter((route)-> route.route=="/").length
67 |
68 |
69 | @findRoute(subparents,route.routes,cback) if route.routes
--------------------------------------------------------------------------------
/test/routehandler.coffee:
--------------------------------------------------------------------------------
1 | window.riot = require 'riot'
2 | routehandler = require '../lib/routehandler.js'
3 | simulant = require 'simulant'
4 | page = require 'page'
5 | require './samplepages.tag'
6 | spyclick = null
7 | test = {}
8 | test =
9 | middleware1:(ctx,next)->
10 | window.middleran1 = true
11 | next()
12 | middleware2:(ctx,next,page)->
13 | window.middleran2 = true
14 | page.redirect('/page1/')
15 | middleware3:(ctx,next,page)->
16 | window.middleran3 = true
17 | middleware4:(ctx,next,page)->
18 | window.middleran4 = true
19 | router = document.querySelector("routehandler");
20 | router._tag.setTag("middleware");
21 | middleware5:(ctx,next,page)->
22 | window.middleran5 = true
23 | next()
24 |
25 | routes = [
26 | {route:"*",use:test.middleware1}
27 | {route:"/",tag:"home"}
28 | {route:"/page100/",use:test.middleware2}
29 | {route:"/page101/",use:test.middleware3,tag:"page1"}
30 | {route:"/page102/",use:test.middleware4}
31 | {route:"/page1/",tag:"page1"}
32 | {route:"/page1/:name",tag:"page1"}
33 | {route:"/page2/",tag:"page2",routes:[
34 | {route:"/",tag:"page4"}
35 | {route:"/sub/:name?",tag:"page2sub"}
36 | ]}
37 | {route:"/page3/",tag:"page2",routes:[
38 | {route:"sub/",tag:"page3sub",routes:[
39 | {route:"three/",tag:"page3subsub"}
40 | {route:"/",tag:"page5"}
41 | {route:"four/",tag:"page2sub"}
42 | {route:"five/",use:test.middleware5}
43 | {route:"five/",tag:"page6"}
44 |
45 | ]}
46 | ]}
47 | {route:"/page103/",tag:"hiddensub",routes:[
48 | {route:"/page104",tag:"page4"}
49 | ]}
50 | ]
51 |
52 | describe 'routehandler',->
53 |
54 | before ->
55 | @domnode = document.createElement('app')
56 | @node = document.body.appendChild(@domnode)
57 | @tag = riot.mount(@domnode,'app',{options:{hashbang:false,base:'/test'},routes,test:'Cheese'})[0]
58 |
59 | after ->
60 | @domnode = ''
61 | @tag.unmount()
62 |
63 | afterEach ->
64 | delete window.middleran5
65 |
66 | it "should exist on the page",->
67 | expect(document.querySelectorAll('routehandler').length).to.equal(1)
68 |
69 | it "should show home page",->
70 | page('/')
71 | expect(document.body.textContent).to.contain('Home Page')
72 |
73 | it "should show page2 and default page",->
74 | page('/page2/')
75 | expect(document.body.textContent).to.contain('Page 2')
76 | expect(document.body.textContent).to.not.contain('subpage')
77 | expect(document.body.textContent).to.contain('Default Page')
78 |
79 | it "should show sub page",->
80 | page('/page2/sub/')
81 | expect(document.body.textContent).to.contain('subpage')
82 |
83 | it "should allow deep routes, event if previous route was not a parent",->
84 | page('/page2/sub/')
85 | expect(document.body.textContent).to.contain('subpage')
86 |
87 | it "should show sub page inside page 2",->
88 | page('/page2/sub/')
89 | expect(document.body.textContent).to.contain('Page 2')
90 | expect(document.body.textContent).to.contain('subpage')
91 |
92 | it "should show route with parameter",->
93 | page('/page1/cris')
94 | expect(document.body.textContent).to.contain('cris')
95 |
96 | it "should show subroute with parameter",->
97 | page('/page2/sub/cris/')
98 | expect(document.body.textContent).to.contain('cris')
99 |
100 | it "should have access to root routehandler opts at all levels",->
101 | page('/page2/sub/cris')
102 | expect(document.body.textContent).to.contain('Cheese')
103 |
104 | it "should load default page at the third level",->
105 | page('/page3/sub/')
106 | expect(document.body.textContent).to.contain('third level default')
107 |
108 | it "should not mount a tag if it's already mounted",->
109 | window.mountcount = 0
110 | page('/page1/')
111 | expect(window.mountcount).to.equal(1)
112 | page('/page1/')
113 | expect(window.mountcount).to.equal(1)
114 | page('/page1/')
115 | expect(window.mountcount).to.equal(1)
116 |
117 | it "should unmount a subtag, if it's unmounted",->
118 | window.submountcount = 0
119 | page('/page2/sub/test')
120 | expect(window.submountcount).to.equal(1)
121 | page('/page2/sub/test')
122 | expect(window.submountcount).to.equal(1)
123 | page('/page2/')
124 | expect(window.submountcount).to.equal(0)
125 |
126 | it "should not unmount a subtag, if it's child is unmounted",->
127 | window.submountcount = 0
128 | window.subsubmountcount = 0
129 | page('/page3/sub/three')
130 | expect(window.submountcount).to.equal(1)
131 | expect(window.subsubmountcount).to.equal(1)
132 | page('/page3/sub/three')
133 | expect(window.submountcount).to.equal(1)
134 | expect(window.subsubmountcount).to.equal(1)
135 | page('/page3/sub/')
136 | expect(window.submountcount).to.equal(1)
137 | expect(window.subsubmountcount).to.equal(0)
138 | page('/page3')
139 | expect(window.submountcount).to.equal(0)
140 | expect(window.subsubmountcount).to.equal(0)
141 |
142 | it "should unmount a tag when overwritten",->
143 | window.mountcount = 0
144 | page('/page1/')
145 | expect(window.mountcount).to.equal(1)
146 | page('/page2/')
147 | expect(window.mountcount).to.equal(0)
148 |
149 | it "should use routehandlers in yielded tags",->
150 | page('/page103/')
151 | expect(document.body.textContent).to.contain('hello hidden sub')
152 | page('/page103/page104')
153 | expect(document.body.textContent).to.contain('hello hidden sub')
154 | expect(document.body.textContent).to.contain('Default Page')
155 |
156 | it "should have access to sub sub sub document",->
157 | page('/page3/sub/three/')
158 | expect(document.body.textContent).to.contain('subsub')
159 |
160 | it "should show page five with submiddleware with next",->
161 | page('/page3/sub/five/')
162 | expect(@domnode.textContent).to.contain("run after submiddle")
163 |
164 |
165 | it "should switch subsubtags",->
166 | page('/page3/sub/three/')
167 | expect(document.body.textContent).to.contain('subsub')
168 | page('/page3/sub/four/')
169 | expect(document.body.textContent).to.contain("I'm a subpage")
170 |
171 | it "should update properties when path changes",->
172 | page('/page1/')
173 | expect(document.body.textContent).not.to.contain('cris')
174 | expect(document.body.textContent).to.contain("hello I'm page 1")
175 | page('/page1/cris')
176 | expect(document.body.textContent).to.contain('cris')
177 |
178 | describe "Middleware",->
179 |
180 | it "should call middleware on base route",->
181 | window.middleran1 = false
182 | page('/')
183 | expect(window.middleran1).to.be.true
184 |
185 | it "should redirect from middleware",(done)->
186 | window.middleran2 = false
187 | page('/page100/')
188 | expect(window.middleran2).to.be.true
189 | setTimeout ->
190 | expect(document.body.textContent).to.contain("hello I'm page 1")
191 | done()
192 |
193 | it "should change route after middleware",->
194 | # https://github.com/crisward/riot-routehandler/issues/4
195 | simulant.fire( document.querySelector('a[href="/test/"]'), 'click' )
196 | expect(@domnode.textContent).to.contain('Home Page')
197 |
198 | simulant.fire( document.querySelector('a[href="/test/page102/"]'), 'click' )
199 | expect(document.body.textContent).to.contain('hello middleware')
200 |
201 | simulant.fire( document.querySelector('a[href="/test/page1/"]'), 'click' )
202 | expect(document.body.textContent).to.contain("hello I'm page 1")
203 |
204 | simulant.fire( document.querySelector('a[href="/test/page102/"]'), 'click' )
205 | expect(document.body.textContent).to.contain('hello middleware')
206 |
207 | simulant.fire( document.querySelector('a[href="/test/page1/"]'), 'click' )
208 | expect(document.body.textContent).to.contain("hello I'm page 1")
209 |
210 | simulant.fire( document.querySelector('a[href="/test/page102/"]'), 'click' )
211 | expect(document.body.textContent).to.contain('hello middleware')
212 |
213 | simulant.fire( document.querySelector('a[href="/test/page1/"]'), 'click' )
214 | expect(document.body.textContent).to.contain("hello I'm page 1")
215 |
216 | it "should run middleware when declaired on same line as tag",->
217 | window.middleran3 = false
218 | page('/page101/')
219 | expect(window.middleran3).to.be.true
220 |
221 | it "should run middleware on subroute",->
222 | expect(window.middleran5).to.be.undefined
223 | page('/page3/sub/five/')
224 | expect(window.middleran5).to.be.true
225 |
226 |
--------------------------------------------------------------------------------
/test/samplepages.tag:
--------------------------------------------------------------------------------
1 | app
2 | a(href="/test/") home
3 | a(href="/test/page1/") page1
4 | a(href="/test/page102/") middleware
5 |
6 | routehandler(options="{opts.options}",routes="{opts.routes}",test="{opts.test}")
7 |
8 | hiddenhandler
9 |
10 |
11 | hiddensub
12 | p hello hidden sub
13 | hiddenhandler
14 | routehandler
15 |
16 | home
17 | p hello I'm a Home Page
18 |
19 | page4
20 | p Default Page
21 |
22 | page5
23 | p third level default
24 |
25 | page6
26 | p run after submiddle
27 |
28 | middleware
29 | h3 hello middleware
30 |
31 | page1
32 | p hello I'm page 1
33 | p your name is {opts.params ? opts.params.name : ''}
34 |
35 | script(type='text/coffeescript').
36 | @on 'mount',->
37 | window.mountcount++ if window.mountcount?
38 | @on 'unmount',->
39 | window.mountcount-- if window.mountcount?
40 |
41 | page2
42 | p hello I'm Page 2
43 |
44 | routehandler
45 |
46 | page2sub
47 | p(if="{opts.params}") I'm a subpage {opts.params.name}
48 | p Mmm {opts.test}
49 |
50 | script(type='text/coffeescript').
51 | @on 'mount',->
52 | window.submountcount++ if window.submountcount?
53 | @on 'unmount',->
54 | window.submountcount-- if window.submountcount?
55 |
56 | page3sub
57 | p hello I'm Page 3 sub
58 |
59 | script(type='text/coffeescript').
60 | @on 'mount',->
61 | window.submountcount++ if window.submountcount?
62 | @on 'unmount',->
63 | window.submountcount-- if window.submountcount?
64 |
65 | routehandler
66 |
67 | page3subsub
68 | p hello I'm Page 3 subsub
69 |
70 | script(type='text/coffeescript').
71 | @on 'mount',->
72 | window.subsubmountcount++ if window.subsubmountcount?
73 | @on 'unmount',->
74 | window.subsubmountcount-- if window.subsubmountcount?
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------