diff options
Diffstat (limited to 'polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js')
-rw-r--r-- | polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js | 310 |
1 files changed, 282 insertions, 28 deletions
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js index b3ae649151..ac4129f430 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js @@ -14,36 +14,103 @@ (function(window) { 'use strict'; - var warnNotSupported = function(opt_name) { + const warnNotSupported = function(opt_name) { console.warn('Plugin API method ' + (opt_name || '') + ' is not supported'); }; - var stubbedMethods = ['_loadedGwt', 'screen', 'settingsScreen', 'panel']; - var GWT_PLUGIN_STUB = {}; - stubbedMethods.forEach(function(name) { + /** + * Hash of loaded and installed plugins, name to Plugin object. + */ + const plugins = {}; + + const stubbedMethods = ['_loadedGwt', 'screen', 'settingsScreen', 'panel']; + const GWT_PLUGIN_STUB = {}; + for (const name of stubbedMethods) { GWT_PLUGIN_STUB[name] = warnNotSupported.bind(null, name); - }); + } + + let _restAPI; + const getRestAPI = () => { + if (!_restAPI) { + _restAPI = document.createElement('gr-rest-api-interface'); + } + return _restAPI; + }; + + // TODO (viktard): deprecate in favor of GrPluginRestApi. + function send(method, url, opt_callback, opt_payload) { + return getRestAPI().send(method, url, opt_payload).then(response => { + if (response.status < 200 || response.status >= 300) { + return response.text().then(text => { + if (text) { + return Promise.reject(text); + } else { + return Promise.reject(response.status); + } + }); + } else { + return getRestAPI().getResponseObject(response); + } + }).then(response => { + if (opt_callback) { + opt_callback(response); + } + return response; + }); + } + + const API_VERSION = '0.1'; - var API_VERSION = '0.1'; + /** + * Plugin-provided custom components can affect content in extension + * points using one of following methods: + * - DECORATE: custom component is set with `content` attribute and may + * decorate (e.g. style) DOM element. + * - REPLACE: contents of extension point are replaced with the custom + * component. + * - STYLE: custom component is a shared styles module that is inserted + * into the extension point. + */ + const EndpointType = { + DECORATE: 'decorate', + REPLACE: 'replace', + STYLE: 'style', + }; // GWT JSNI uses $wnd to refer to window. // http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html window.$wnd = window; + function getPluginNameFromUrl(url) { + const base = Gerrit.BaseUrlBehavior.getBaseUrl(); + const pathname = url.pathname.replace(base, ''); + // Site theme is server from predefined path. + if (pathname === '/static/gerrit-theme.html') { + return 'gerrit-theme'; + } else if (!pathname.startsWith('/plugins')) { + console.warn('Plugin not being loaded from /plugins base path:', + url.href, '— Unable to determine name.'); + return; + } + return pathname.split('/')[2]; + } + function Plugin(opt_url) { + this._domHooks = new GrDomHooksManager(this); + if (!opt_url) { console.warn('Plugin not being loaded from /plugins base path.', 'Unable to determine name.'); return; } + this.deprecated = { + install: deprecatedAPI.install.bind(this), + popup: deprecatedAPI.popup.bind(this), + onAction: deprecatedAPI.onAction.bind(this), + }; this._url = new URL(opt_url); - if (this._url.pathname.indexOf('/plugins') !== 0) { - console.warn('Plugin not being loaded from /plugins base path:', - this._url.href, '— Unable to determine name.'); - return; - } - this._name = this._url.pathname.split('/')[2]; + this._name = getPluginNameFromUrl(this._url); } Plugin._sharedAPIElement = document.createElement('gr-js-api-interface'); @@ -54,6 +121,30 @@ return this._name; }; + Plugin.prototype.registerStyleModule = function(endpointName, moduleName) { + Gerrit._endpoints.registerModule( + this, endpointName, EndpointType.STYLE, moduleName); + }; + + Plugin.prototype.registerCustomComponent = function( + endpointName, opt_moduleName, opt_options) { + const type = opt_options && opt_options.replace ? + EndpointType.REPLACE : EndpointType.DECORATE; + const hook = this._domHooks.getDomHook(endpointName, opt_moduleName); + const moduleName = opt_moduleName || hook.getModuleName(); + Gerrit._endpoints.registerModule( + this, endpointName, type, moduleName, hook); + return hook.getPublicAPI(); + }; + + /** + * Returns instance of DOM hook API for endpoint. Creates a placeholder + * element for the first call. + */ + Plugin.prototype.hook = function(endpointName, opt_options) { + return this.registerCustomComponent(endpointName, undefined, opt_options); + }; + Plugin.prototype.getServerInfo = function() { return document.createElement('gr-rest-api-interface').getConfig(); }; @@ -63,37 +154,154 @@ }; Plugin.prototype.url = function(opt_path) { - return this._url.origin + '/plugins/' + this._name + (opt_path || '/'); + const base = Gerrit.BaseUrlBehavior.getBaseUrl(); + return this._url.origin + base + '/plugins/' + + this._name + (opt_path || '/'); + }; + + Plugin.prototype._send = function(method, url, opt_callback, opt_payload) { + return send(method, this.url(url), opt_callback, opt_payload); + }; + + Plugin.prototype.get = function(url, opt_callback) { + console.warn('.get() is deprecated! Use .restApi().get()'); + return this._send('GET', url, opt_callback); + }; + + Plugin.prototype.post = function(url, payload, opt_callback) { + console.warn('.post() is deprecated! Use .restApi().post()'); + return this._send('POST', url, opt_callback, payload); + }; + + Plugin.prototype.put = function(url, payload, opt_callback) { + console.warn('.put() is deprecated! Use .restApi().put()'); + return this._send('PUT', url, opt_callback, payload); + }; + + Plugin.prototype.delete = function(url, opt_callback) { + return Gerrit.delete(this.url(url), opt_callback); }; Plugin.prototype.changeActions = function() { - return new GrChangeActionsInterface(Plugin._sharedAPIElement.getElement( - Plugin._sharedAPIElement.Element.CHANGE_ACTIONS)); + return new GrChangeActionsInterface(this, + Plugin._sharedAPIElement.getElement( + Plugin._sharedAPIElement.Element.CHANGE_ACTIONS)); }; Plugin.prototype.changeReply = function() { - return new GrChangeReplyInterface(Plugin._sharedAPIElement.getElement( - Plugin._sharedAPIElement.Element.REPLY_DIALOG)); + return new GrChangeReplyInterface(this, + Plugin._sharedAPIElement.getElement( + Plugin._sharedAPIElement.Element.REPLY_DIALOG)); }; - var Gerrit = window.Gerrit || {}; + Plugin.prototype.changeView = function() { + return new GrChangeViewApi(this); + }; + + Plugin.prototype.theme = function() { + return new GrThemeApi(this); + }; + + Plugin.prototype.project = function() { + return new GrProjectApi(this); + }; + + /** + * To make REST requests for plugin-provided endpoints, use + * @example + * const pluginRestApi = plugin.restApi(plugin.url()); + * + * @param {string} Base url for subsequent .get(), .post() etc requests. + */ + Plugin.prototype.restApi = function(opt_prefix) { + return new GrPluginRestApi(opt_prefix); + }; + + Plugin.prototype.attributeHelper = function(element) { + return new GrAttributeHelper(element); + }; + + Plugin.prototype.eventHelper = function(element) { + return new GrEventHelper(element); + }; + + Plugin.prototype.popup = function(moduleName) { + if (typeof moduleName !== 'string') { + throw new Error('deprecated, use deprecated.popup'); + } + const api = new GrPopupInterface(this, moduleName); + return api.open(); + }; + + const deprecatedAPI = { + install() { + console.log('Installing deprecated APIs is deprecated!'); + for (const method in this.deprecated) { + if (method === 'install') continue; + this[method] = this.deprecated[method]; + } + }, + + popup(el) { + console.warn('plugin.deprecated.popup() is deprecated, ' + + 'use plugin.popup() insted!'); + if (!el) { + throw new Error('Popup contents not found'); + } + const api = new GrPopupInterface(this); + api.open().then(api => api._getElement().appendChild(el)); + return api; + }, + + onAction(type, action, callback) { + console.warn('plugin.deprecated.onAction() is deprecated,' + + ' use plugin.changeActions() instead!'); + if (type !== 'change' && type !== 'revision') { + console.warn(`${type} actions are not supported.`); + return; + } + this.on('showchange', (change, revision) => { + const details = this.changeActions().getActionDetails(action); + this.changeActions().addTapListener(details.__key, () => { + callback(new GrPluginActionContext(this, details, change, revision)); + }); + }); + }, + + }; + + const Gerrit = window.Gerrit || {}; + + // Provide reset plugins function to clear installed plugins between tests. + const app = document.querySelector('#app'); + if (!app) { + // No gr-app found (running tests) + Gerrit._resetPlugins = () => { + for (const k of Object.keys(plugins)) { + delete plugins[k]; + } + }; + } // Number of plugins to initialize, -1 means 'not yet known'. Gerrit._pluginsPending = -1; + Gerrit._endpoints = new GrPluginEndpoints(); + Gerrit.getPluginName = function() { console.warn('Gerrit.getPluginName is not supported in PolyGerrit.', - 'Please use self.getPluginName() instead.'); + 'Please use plugin.getPluginName() instead.'); }; Gerrit.css = function(rulesStr) { if (!Gerrit._customStyleSheet) { - var styleEl = document.createElement('style'); + const styleEl = document.createElement('style'); document.head.appendChild(styleEl); Gerrit._customStyleSheet = styleEl.sheet; } - var name = '__pg_js_api_class_' + Gerrit._customStyleSheet.cssRules.length; + const name = '__pg_js_api_class_' + + Gerrit._customStyleSheet.cssRules.length; Gerrit._customStyleSheet.insertRule('.' + name + '{' + rulesStr + '}', 0); return name; }; @@ -106,22 +314,68 @@ return; } - // TODO(andybons): Polyfill currentScript for IE10/11 (edge supports it). - var src = opt_src || (document.currentScript && document.currentScript.src); - var plugin = new Plugin(src); + const src = opt_src || (document.currentScript && + (document.currentScript.src || document.currentScript.baseURI)); + const name = getPluginNameFromUrl(new URL(src)); + const existingPlugin = plugins[name]; + const plugin = existingPlugin || new Plugin(src); try { callback(plugin); + plugins[name] = plugin; } catch (e) { - console.warn(plugin.getPluginName() + ' install failed: ' + - e.name + ': ' + e.message); + console.warn(`${name} install failed: ${e.name}: ${e.message}`); + } + // Don't double count plugins that may have an html and js install. + // TODO(beckysiegel) remove name check once name issue is resolved. + // If there isn't a name, it's due to an issue with the polyfill for + // html imports in Safari/Firefox. In this case, other plugin related + // features may still be broken, but still make sure to call. + // _pluginInstalled. + if (!name || !existingPlugin) { + Gerrit._pluginInstalled(); } - Gerrit._pluginInstalled(); }; Gerrit.getLoggedIn = function() { + console.warn('Gerrit.getLoggedIn() is deprecated! ' + + 'Use plugin.restApi().getLoggedIn()'); return document.createElement('gr-rest-api-interface').getLoggedIn(); }; + Gerrit.get = function(url, callback) { + console.warn('.get() is deprecated! Use plugin.restApi().get()'); + send('GET', url, callback); + }; + + Gerrit.post = function(url, payload, callback) { + console.warn('.post() is deprecated! Use plugin.restApi().post()'); + send('POST', url, callback, payload); + }; + + Gerrit.put = function(url, payload, callback) { + console.warn('.put() is deprecated! Use plugin.restApi().put()'); + send('PUT', url, callback, payload); + }; + + Gerrit.delete = function(url, opt_callback) { + console.warn('.delete() is deprecated! Use plugin.restApi().delete()'); + return getRestAPI().send('DELETE', url).then(response => { + if (response.status !== 204) { + return response.text().then(text => { + if (text) { + return Promise.reject(text); + } else { + return Promise.reject(response.status); + } + }); + } + if (opt_callback) { + opt_callback(response); + } + return response; + }); + }; + /** * Polyfill GWT API dependencies to avoid runtime exceptions when loading * GWT-compiled plugins. @@ -140,7 +394,7 @@ if (Gerrit._arePluginsLoaded()) { Gerrit._allPluginsPromise = Promise.resolve(); } else { - Gerrit._allPluginsPromise = new Promise(function(resolve) { + Gerrit._allPluginsPromise = new Promise(resolve => { Gerrit._resolveAllPluginsLoaded = resolve; }); } |