public/src/autocomplete/url_pattern_matcher.js (88 lines of code) (raw):
let _ = require('lodash');
let engine = require('./engine');
module.exports.URL_PATH_END_MARKER = "__url_path_end__";
function AcceptEndpointComponent(endpoint, parent) {
engine.SharedComponent.call(this, endpoint.id, parent);
this.endpoint = endpoint
}
AcceptEndpointComponent.prototype = _.create(engine.SharedComponent.prototype, {"constructor": AcceptEndpointComponent});
(function (cls) {
cls.match = function (token, context, editor) {
if (token !== module.exports.URL_PATH_END_MARKER) {
return null;
}
if (this.endpoint.methods && -1 === _.indexOf(this.endpoint.methods, context.method)) {
return null;
}
var r = Object.getPrototypeOf(cls).match.call(this, token, context, editor);
r.context_values = r.context_values || {};
r.context_values['endpoint'] = this.endpoint;
if (_.isNumber(this.endpoint.priority)) {
r.priority = this.endpoint.priority;
}
return r;
}
})(AcceptEndpointComponent.prototype);
/**
* @param parametrizedComponentFactories a dict of the following structure
* that will be used as a fall back for pattern parameters (i.e.: {indices})
* {
* indices: function (part, parent) {
* return new SharedComponent(part, parent)
* }
* }
* @constructor
*/
function UrlPatternMatcher(parametrizedComponentFactories) {
// This is not really a component, just a handy container to make iteration logic simpler
this.rootComponent = new engine.SharedComponent("ROOT");
this.parametrizedComponentFactories = parametrizedComponentFactories || {};
}
(function (cls) {
cls.addEndpoint = function (pattern, endpoint) {
var c,
active_component = this.rootComponent,
endpointComponents = endpoint.url_components || {};
var partList = pattern.split("/");
_.each(partList, function (part, partIndex) {
if (part.search(/^{.+}$/) >= 0) {
part = part.substr(1, part.length - 2);
if (active_component.getComponent(part)) {
// we already have something for this, reuse
active_component = active_component.getComponent(part);
return;
}
// a new path, resolve.
if ((c = endpointComponents[part])) {
// endpoint specific. Support list
if (_.isArray(c)) {
c = new engine.ListComponent(part, c, active_component);
}
else if (_.isObject(c) && c.type === "list") {
c = new engine.ListComponent(part, c.list, active_component, c.multi_valued, c.allow_non_valid);
}
else {
console.warn("incorrectly configured url component ", part, " in endpoint", endpoint);
c = new engine.SharedComponent(part);
}
}
else if ((c = this.parametrizedComponentFactories[part])) {
// c is a f
c = c(part, active_component);
}
else {
// just accept whatever with not suggestions
c = new engine.SimpleParamComponent(part, active_component);
}
active_component = c;
}
else {
// not pattern
var lookAhead = part, s;
for (partIndex++; partIndex < partList.length; partIndex++) {
s = partList[partIndex];
if (s.indexOf("{") >= 0) {
break;
}
lookAhead += "/" + s;
}
if (active_component.getComponent(part)) {
// we already have something for this, reuse
active_component = active_component.getComponent(part);
active_component.addOption(lookAhead);
}
else {
c = new engine.ConstantComponent(part, active_component, lookAhead);
active_component = c;
}
}
}, this);
// mark end of endpoint path
new AcceptEndpointComponent(endpoint, active_component);
};
cls.getTopLevelComponents = function () {
return this.rootComponent.next;
}
})(UrlPatternMatcher.prototype);
module.exports.UrlPatternMatcher = UrlPatternMatcher;