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;