discourse/app/assets/javascripts/discourse/mapping-router.js.es6
2018-06-15 17:03:24 +02:00

143 lines
3.4 KiB
JavaScript

import { defaultHomepage } from "discourse/lib/utilities";
import { rewritePath } from "discourse/lib/url";
const rootURL = Discourse.BaseUri;
const BareRouter = Ember.Router.extend({
rootURL,
location: Ember.testing ? "none" : "discourse-location",
handleURL(url) {
url = rewritePath(url);
const params = url.split("?");
if (params[0] === "/") {
url = defaultHomepage();
if (params[1] && params[1].length) {
url = `${url}?${params[1]}`;
}
}
return this._super(url);
}
});
// Ember's router can't be extended. We need to allow plugins to add routes to routes that were defined
// in the core app. This class has the same API as Ember's `Router.map` but saves the results in a tree.
// The tree is applied after all plugins are defined.
class RouteNode {
constructor(name, opts = {}, depth = 0) {
this.name = name;
this.opts = opts;
this.depth = depth;
this.children = [];
this.childrenByName = {};
this.paths = {};
this.site = Discourse.Site.current();
if (!opts.path) {
opts.path = name;
}
this.paths[opts.path] = true;
}
route(name, opts, fn) {
if (typeof opts === "function") {
fn = opts;
opts = {};
} else {
opts = opts || {};
}
const existing = this.childrenByName[name];
if (existing) {
if (opts.path) {
existing.paths[opts.path] = true;
}
existing.extract(fn);
} else {
const node = new RouteNode(name, opts, this.depth + 1);
node.extract(fn);
this.childrenByName[name] = node;
this.children.push(node);
}
}
extract(fn) {
if (!fn) {
return;
}
fn.call(this);
}
mapRoutes(router) {
const children = this.children;
if (this.name === "root") {
children.forEach(c => c.mapRoutes(router));
} else {
const builder =
children.length === 0
? undefined
: function() {
children.forEach(c => c.mapRoutes(this));
};
router.route(this.name, this.opts, builder);
}
}
findSegment(segments) {
if (segments && segments.length) {
const first = segments.shift();
const node = this.childrenByName[first];
if (node) {
return segments.length === 0 ? node : node.findSegment(segments);
}
}
}
findPath(path) {
if (path) {
return this.findSegment(path.split("."));
}
}
}
export function mapRoutes() {
const tree = new RouteNode("root");
const extras = [];
// If a module is defined as `route-map` in discourse or a plugin, its routes
// will be built automatically. You can supply a `resource` property to
// automatically put it in that resource, such as `admin`. That way plugins
// can define admin routes.
Object.keys(requirejs._eak_seen).forEach(function(key) {
if (/route-map$/.test(key)) {
var module = requirejs(key, null, null, true);
if (!module || !module.default) {
throw new Error(key + " must export a route map.");
}
const mapObj = module.default;
if (typeof mapObj === "function") {
tree.extract(mapObj);
} else {
extras.push(mapObj);
}
}
});
extras.forEach(extra => {
let node = tree.findPath(extra.resource);
if (node) {
node.extract(extra.map);
}
});
return BareRouter.extend().map(function() {
tree.mapRoutes(this);
this.route("unknown", { path: "*path" });
});
}
export default BareRouter;