js/plugins/router.js
define(['director', 'logger'], function(director, log) {
'use strict';
return function(Vue, options) {
options = options || {};
var Router = Vue.extend({
name: 'Router',
data: function() {
return {
prefix: options.prefix || '',
router: new director.Router(),
current_path: null,
};
},
created: function() {
this.router.configure({
strict: false,
html5history: true,
convert_hash_in_init: false,
// recurse: 'forward',
before: this.on_before_event.bind(this),
on: this.on_on_event.bind(this),
after: this.on_after_event.bind(this),
notfound: this.on_notfound.bind(this)
});
// Register some common parameter type
this.router.param('oid', /([0-9a-fA-F\\-]+)/);
},
computed: {
current_route: function() {
if (this.current_path) {
return this.current_path.replace(this.prefix, '/').replace('//', '/');
}
},
parameters: function() {
/* Load GET parameters if any once and for all.
Inspired by http://stackoverflow.com/a/979995 */
let parameters = {};
if (window.location.search) {
for (let param of window.location.search.substring(1).split("&")) {
let [name, value] = param.split("=");
value = decodeURIComponent(value);
if (typeof parameters[name] === "undefined") {
parameters[name] = value;
} else if (typeof parameters[name] === "string") {
// If second entry with this name
parameters[name] = [parameters[name], value];
} else {
// If third or later entry with this name
parameters[name].push(value);
}
}
}
return parameters;
}
},
methods: {
/**
* Route to the given route
*/
go: function(route) {
log.debug('Go to', route);
this.router.setRoute(this.to_path(route));
},
to_path: function(route) {
var path = (this.prefix + route).replace('//', '/');
if (path[0] !== '/') {
path = '/' + path;
}
return path;
},
update: function() {
this.current_path = '/' + this.router.getRoute().join('/');
},
on_before_event: function() {
this.callback('before');
this.$dispatch('route:changed:before', this);
},
on_on_event: function() {
this.update();
this.callback('on');
this.$dispatch('route:changed', this);
},
on_after_event: function() {
this.callback('after');
this.$dispatch('route:changed:after', this);
},
on: function(route, func) {
route = this.to_path(route);
this.router.on.apply(this.router, [route, func]);
},
on_notfound: function() {
this.update();
log.debug('Route not found', this.current_route);
},
callback: function(name) {
var option = this.$options[name],
callbacks;
if (!option) { return; }
callbacks = Vue.util.isArray(option) ? option : [option];
for (var i=0; i < callbacks.length; i++) {
callbacks[i].apply(this);
}
},
init: function() {
this.router.init();
},
/**
* Recursively bind routes to a given scope
* @param {Object} routes a routing table to bind
* @param {Object} scope The scope to bind callbacks on
* @return {Object} The binded routing table
*/
bind: function(routes, scope) {
for (var prop in routes){
if (Vue.util.isObject(routes[prop])) {
routes[prop] = this.bind(routes[prop], scope);
} else if (Vue.util.isFunction(routes[prop])) {
routes[prop] = Vue.util.bind(routes[prop], scope);
}
}
return routes;
},
mount: function(obj) {
// Bind routes
if (Object.getOwnPropertyNames(obj.$options.routes || {}).length > 0) {
var routes = this.bind(obj.$options.routes, obj);
this.router.mount(routes, this.prefix);
}
}
}
});
/**
* Make the router object available globaly on Vue.route
* and as instance property on this.$router.
*/
Vue.prototype.$router = Vue.router = new Router();
/**
* Bind routes on init
*/
var super_scope = Vue.prototype._initScope;
Vue.prototype._initScope = function() {
super_scope.apply(this);
Vue.router.mount(this);
};
/**
* Allow to route from everywhere:
* - on global scope with Vue.go
* - on instance scope with this.$go
*/
Vue.prototype.$go = Vue.go = function(route) {
return Vue.router.go(route);
};
/**
* Allow to declare routes as component options
*/
Vue.options.routes = {};
/**
* Allow to declare route for click event
*/
Vue.directive('route', {
isLiteral: true,
bind: function() {
this.route = this.expression;
this.handler = function() {
Vue.go(this.route);
}.bind(this);
Vue.util.on(this.el, 'click', this.handler);
if (!this.el.className.indexOf('pointer') >= 0) {
this.el.className += ' pointer';
}
},
update: function(value) {
this.route = value;
},
unbind: function() {
Vue.util.off(this.el, 'click', this.handler);
}
});
};
});