Last active
October 12, 2018 23:58
-
-
Save virtuald/ac2fabcc37aeb00ecfe7a94e29035c72 to your computer and use it in GitHub Desktop.
Rewrite of riot.js tag router
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (riot, route) { | |
'use strict'; | |
// | |
// Modified version of the riot-router tags | |
// | |
// - Intended to be used recursively. You can even use multiple routers in a tag and they'll work | |
// - Each 'route' event receives the parent container, and any arguments that it would normally get | |
// - Child tags receive an attribute 'route_path' which contains the current routing | |
// - Adding a '/@@' to the end of a route allows the route to have subordinate routers | |
// - Child routers use relative paths, not absolute paths (this makes tags more composable) | |
// | |
riot = riot && riot.hasOwnProperty('default') ? riot['default'] : riot; | |
// customizes the second parser | |
// - Returns the full path as the first arg | |
// - Adds @@ and makes it capturing | |
function SECOND_PARSER(path, filter) { | |
var f = filter | |
.replace(/\?/g, '\\?') | |
.replace(/\*/g, '([^/?#]+?)') | |
.replace(/\/@@$/, '/(.*)?') | |
.replace(/\.\./, '.*'); | |
var re = new RegExp(("^" + f + "$")); | |
var args = path.match(re); | |
if (args) { return args.slice(0) } | |
} | |
route.parser(null, SECOND_PARSER); | |
riot.tag2('router', '<yield></yield>', '', '', function(opts) { | |
var this$1 = this; | |
// This holds our child route tags as an object | |
this.$ = []; | |
// If this tag is mounted underneath another router, use that instead of | |
// creating our own router context | |
var parent = this.parent; | |
while (parent) { | |
if (parent.subroute) { | |
break; | |
} | |
parent = parent.parent; | |
} | |
if (parent) { | |
this.process = function(path) { | |
this$1.$.some(function(o) { | |
var args = SECOND_PARSER(path, o.f); | |
if (typeof args != 'undefined') { | |
o.a.apply(o, args) | |
return true; | |
} | |
}); | |
} | |
this.route = function(filter, action) { | |
this$1.$.push({f: filter, a: action}); | |
} | |
this.routestop = parent.subroute(this); | |
} else { | |
// no parent? create a brand new route context then! | |
this.route = route.create(); | |
this.routestop = this.route.stop; | |
this.process = function(){} | |
this.on('mount', function () { | |
// To avoid updating route tag before mount, we use setTimeout here | |
window.setTimeout(function () { return route.start(true); }, 0); | |
}); | |
} | |
this.select = function (target) { | |
[].concat(this$1.tags.route) | |
.forEach(function (r) { return r.show = (r === target); }); | |
}; | |
this.on('unmount', function () { | |
this$1.routestop(); | |
this.$ = []; | |
}); | |
}); | |
riot.tag2('route', '<virtual if="{show}"><yield></yield></virtual>', '', '', function(opts) { | |
var this$1 = this; | |
this$1.show = false; | |
// contains any routers in child tags | |
this$1.$ = []; | |
// The last trailing segment we were asked to process | |
this.trailer = ''; | |
var path = opts.path; | |
// if ends with /@@, add an additional path to handle the trailer | |
if (path.endsWith('/@@')) { | |
this.subroute = function(o) { | |
this.$.push(o); | |
return function() { | |
this.$.splice(this.$.indexOf(o), 1); | |
}; | |
} | |
this.parent.route(path, function() { | |
var args = [], len = arguments.length - 1; | |
var lastArg = arguments[len]; | |
while ( len-- ) args[ len ] = arguments[ len ]; | |
// The first argument is the current route's path, which doesn't include the trailer | |
args[0] = args[0].substring(0, args[0].length - (lastArg.length + 1)); | |
this$1.trailer = lastArg; | |
onroute(args); | |
}); | |
// register another route without the trailer | |
path = path.substring(0, path.length - 3); | |
} | |
this.parent.route(path, function () { | |
var args = [], len = arguments.length; | |
while ( len-- ) args[ len ] = arguments[ len ]; | |
this$1.trailer = ''; | |
onroute(args); | |
}); | |
function onroute(args) { | |
var container = this$1.parent.parent; | |
var route_path = (container.route_path ? container.route_path + '/' + args[0] : args[0]); | |
this$1.route_path = route_path; | |
function doupdate() { | |
flatten(this$1.tags).forEach(function (tag) { | |
tag.route_path = route_path; | |
tag.trigger.apply(tag, [ 'route', container ].concat( args.slice(1) )); | |
tag.update(); | |
}); | |
// send the trailer to any router children | |
this$1.$.forEach(function(router) { | |
router.process(this$1.trailer); | |
}); | |
} | |
this$1.parent.select(this$1); | |
this$1.parent.update(); | |
// If not mounted, there's no way to intercept the child tags while mounting. | |
// We need to wait the `updated` event to access them via `this.tags`. | |
if (this$1.isMounted) { | |
doupdate(); | |
} else { | |
this$1.one('updated', doupdate); | |
} | |
} | |
function flatten(tags) { | |
return Object.keys(tags) | |
.map(function (key) { return tags[key]; }) | |
.reduce(function (acc, tag) { return acc.concat(tag); }, []) | |
} | |
this.on('unmount', function() { | |
this$1.$ = []; | |
}) | |
}); | |
})(riot, route); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment