|  | :linkattrs: | 
|  | = Gerrit Code Review - JavaScript Plugin Development and API | 
|  |  | 
|  | Gerrit Code Review supports an API for JavaScript plugins to interact | 
|  | with the web UI and the server process. | 
|  |  | 
|  | [[loading]] | 
|  | == Plugin loading and initialization | 
|  |  | 
|  | JavaScript is loaded using a standard `<script src='...'>` HTML tag. | 
|  | Plugins should protect the global namespace by defining their code | 
|  | within an anonymous function passed to `Gerrit.install()`. The plugin | 
|  | will be passed an object describing its registration with Gerrit. | 
|  |  | 
|  | * The plugin provides pluginname.js, and can be a standalone file or a static | 
|  | asset in a jar as a link:dev-plugins.html#deployment[Web UI plugin]. | 
|  | * pluginname.js contains a call to `Gerrit.install()`. There should | 
|  | only be a single `Gerrit.install()` call per file. | 
|  | * The Gerrit web app imports pluginname.js. | 
|  | * For standalone plugins, the entry point file is a `pluginname.js` file | 
|  | located in `gerrit-site/plugins` folder, where `pluginname` is an alphanumeric | 
|  | plugin name. | 
|  |  | 
|  | === Examples | 
|  | Here's a recommended starter `myplugin.js`: | 
|  |  | 
|  | ``` js | 
|  | Gerrit.install(plugin => { | 
|  | // Your code here. | 
|  | }); | 
|  | ``` | 
|  |  | 
|  | == TypeScript API == | 
|  |  | 
|  | Gerrit provides a TypeScript plugin API. | 
|  |  | 
|  | For a plugin built inline, its `tsconfig.json` can extends Gerrit plugin | 
|  | TypeScript configuration: | 
|  |  | 
|  | `tsconfig.json`: | 
|  | ``` json | 
|  | { | 
|  | "extends": "../tsconfig-plugins-base.json" | 
|  | } | 
|  | ``` | 
|  |  | 
|  | For standalone plugins (outside of a Gerrit tree), a TypeScript plugin API is | 
|  | published: | 
|  | link:https://www.npmjs.com/package/@gerritcodereview/typescript-api[@gerritcodereview/typescript-api]. | 
|  | It provides a TypeScript configuration `tsconfig-plugins-base.json` which can | 
|  | be used in your plugin `tsconfig.json`: | 
|  |  | 
|  | ``` json | 
|  | { | 
|  | "extends": "node_modules/@gerritcodereview/typescript-api/tsconfig-plugins-base.json", | 
|  | // your custom configuration and overrides | 
|  | } | 
|  | ``` | 
|  |  | 
|  | [[low-level-api-concepts]] | 
|  | == Low-level DOM API concepts | 
|  |  | 
|  | Basically, the DOM is the API surface. Low-level API provides methods for | 
|  | decorating, replacing, and styling DOM elements exposed through a set of | 
|  | link:pg-plugin-endpoints.html[endpoints]. | 
|  |  | 
|  | Gerrit provides a simple way for accessing the DOM via DOM hooks API. A DOM | 
|  | hook is a custom element that is instantiated for the plugin endpoint. In the | 
|  | decoration case, a hook is set with a `content` attribute that points to the DOM | 
|  | element. | 
|  |  | 
|  | 1. Get the DOM hook API instance via `plugin.hook(endpointName)` | 
|  | 2. Set up an `onAttached` callback | 
|  | 3. Callback is called when the hook element is created and inserted into DOM | 
|  | 4. Use element.content to get UI element | 
|  |  | 
|  | ``` js | 
|  | Gerrit.install(plugin => { | 
|  | const domHook = plugin.hook('reply-text'); | 
|  | domHook.onAttached(element => { | 
|  | if (!element.content) { return; } | 
|  | // element.content is a reply dialog text area. | 
|  | }); | 
|  | }); | 
|  | ``` | 
|  |  | 
|  | [[low-level-decorating]] | 
|  | === Decorating DOM Elements | 
|  |  | 
|  | For each endpoint, Gerrit provides a list of DOM properties (such as | 
|  | attributes and events) that are supported in the long-term. | 
|  |  | 
|  | ``` js | 
|  | Gerrit.install(plugin => { | 
|  | const domHook = plugin.hook('reply-text'); | 
|  | domHook.onAttached(element => { | 
|  | if (!element.content) { return; } | 
|  | element.content.style.border = '1px red dashed'; | 
|  | }); | 
|  | }); | 
|  | ``` | 
|  |  | 
|  | [[low-level-replacing]] | 
|  | === Replacing DOM Elements | 
|  |  | 
|  | An endpoint's contents can be replaced by passing the replace attribute as an | 
|  | option. | 
|  |  | 
|  | ``` js | 
|  | Gerrit.install(plugin => { | 
|  | const domHook = plugin.hook('header-title', {replace: true}); | 
|  | domHook.onAttached(element => { | 
|  | element.appendChild(document.createElement('my-site-header')); | 
|  | }); | 
|  | }); | 
|  | ``` | 
|  |  | 
|  | [[low-level-style]] | 
|  | === Styling DOM Elements | 
|  |  | 
|  | Gerrit only offers customized CSS styling by setting | 
|  | link:https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_[custom_properties] | 
|  | (aka css variables). | 
|  |  | 
|  | See link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/styles/themes/[app-theme.ts] | 
|  | for the list of available variables. | 
|  |  | 
|  | You can just create `<style>` elements yourself and add them to the | 
|  | `document.head`, but for your convenience the Plugin API provides a simple | 
|  | `styleApi().insertCSSRule()` method for doing just that. Typically you would | 
|  | define a CSS rule for `html`, which is always applied, or for a specific theme | 
|  | such as `html.lightTheme`. | 
|  |  | 
|  | ``` js | 
|  | Gerrit.install(plugin => { | 
|  | plugin.styleApi().insertCSSRule(` | 
|  | html { | 
|  | --header-text-color: black; | 
|  | } | 
|  | `); | 
|  | plugin.styleApi().insertCSSRule(` | 
|  | html.lightTheme { | 
|  | --header-background-color: red; | 
|  | } | 
|  | `); | 
|  | plugin.styleApi().insertCSSRule(` | 
|  | html.darkTheme { | 
|  | --header-background-color: blue; | 
|  | } | 
|  | `); | 
|  | }); | 
|  | ``` | 
|  |  | 
|  | [[high-level-api-concepts]] | 
|  | == High-level DOM API concepts | 
|  |  | 
|  | High level API is based on low-level DOM API and is essentially a standardized | 
|  | way for doing common tasks. It's less flexible, but will be a bit more stable. | 
|  |  | 
|  | The common way to access high-level API is through `plugin` instance passed | 
|  | into setup callback parameter of `Gerrit.install()`, also sometimes referred to | 
|  | as `self`. | 
|  |  | 
|  | [[low-level-api]] | 
|  | == Low-level DOM API | 
|  |  | 
|  | The low-level DOM API methods are the base of all UI customization. | 
|  |  | 
|  | === attributeHelper | 
|  | `plugin.attributeHelper(element)` | 
|  |  | 
|  | Alternative for | 
|  | link:https://polymer-library.polymer-project.org/3.0/docs/devguide/data-binding[Polymer data | 
|  | binding,role=external,window=_blank] for plugins that don't use Polymer. Can be used to bind element | 
|  | attribute changes to callbacks. | 
|  |  | 
|  | === hook | 
|  | `plugin.hook(endpointName, opt_options)` | 
|  |  | 
|  | See link:pg-plugin-endpoints.html[endpoints]. | 
|  |  | 
|  | === registerCustomComponent | 
|  | `plugin.registerCustomComponent(endpointName, opt_moduleName, opt_options)` | 
|  |  | 
|  | See link:pg-plugin-endpoints.html[endpoints]. | 
|  |  | 
|  | === registerDynamicCustomComponent | 
|  | `plugin.registerDynamicCustomComponent(dynamicEndpointName, opt_moduleName, | 
|  | opt_options)` | 
|  |  | 
|  | See link:pg-plugin-endpoints.html[endpoints]. | 
|  |  | 
|  | === on | 
|  | Register a JavaScript callback to be invoked when events occur within | 
|  | the web interface. Signature | 
|  |  | 
|  | ``` js | 
|  | self.on(event, callback); | 
|  | ``` | 
|  |  | 
|  | Parameters | 
|  |  | 
|  | * event: A supported event type. See below for description. | 
|  |  | 
|  | * callback: JavaScript function to be invoked when event happens. | 
|  | Arguments may be passed to this function, depending on the event. | 
|  |  | 
|  | Supported events: | 
|  |  | 
|  | * `history`: Invoked when the view is changed to a new screen within | 
|  | the Gerrit web application.  The token after "#" is passed as the | 
|  | argument to the callback function, for example "/c/42/" while | 
|  | showing change 42. | 
|  |  | 
|  | * `showchange`: Invoked when a change is made visible. A | 
|  | link:rest-api-changes.html#change-info[ChangeInfo] and | 
|  | link:rest-api-changes.html#revision-info[RevisionInfo] | 
|  | are passed as arguments. Gerrit provides a third parameter which | 
|  | is an object with a `mergeable` boolean. | 
|  |  | 
|  | * `submitchange`: Invoked when the submit button is clicked | 
|  | on a change. A link:rest-api-changes.html#change-info[ChangeInfo] | 
|  | and link:rest-api-changes.html#revision-info[RevisionInfo] are | 
|  | passed as arguments. Similar to a form submit validation, the | 
|  | function must return true to allow the operation to continue, or | 
|  | false to prevent it. The function may be called multiple times, for | 
|  | example, if submitting a change shows a confirmation dialog, this | 
|  | event may be called to validate that the check whether dialog can be | 
|  | shown, and called again when the submit is confirmed to check whether | 
|  | the actual submission action can proceed. | 
|  |  | 
|  | * `comment`: Invoked when a DOM element that represents a comment is | 
|  | created. This DOM element is passed as argument. This DOM element | 
|  | contains nested elements that Gerrit uses to format the comment. The | 
|  | DOM structure may differ between comment types such as inline | 
|  | comments, file-level comments and summary comments, and it may change | 
|  | with new Gerrit versions. | 
|  |  | 
|  | * `highlightjs-loaded`: Invoked when the highlight.js library has | 
|  | finished loading. The global `hljs` object (also now accessible via | 
|  | `window.hljs`) is passed as an argument to the callback function. | 
|  | This event can be used to register a new language highlighter with | 
|  | the highlight.js library before syntax highlighting begins. | 
|  |  | 
|  | [[high-level-api]] | 
|  | == High-level API | 
|  |  | 
|  | Plugin instance provides access to a number of more specific APIs and methods | 
|  | to be used by plugin authors. | 
|  |  | 
|  | === admin | 
|  | `plugin.admin()` | 
|  |  | 
|  | .Params: | 
|  | - none | 
|  |  | 
|  | .Returns: | 
|  | - Instance of link:pg-plugin-admin-api.html[GrAdminApi]. | 
|  |  | 
|  | === changeActions | 
|  | `self.changeActions()` | 
|  |  | 
|  | Returns an instance of the | 
|  | link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/api/change-actions.ts[ChangeActionsPluginApi]. | 
|  |  | 
|  | ==== changeActions.add() | 
|  | Adds a new action to the change actions section. Returns the key of the newly | 
|  | added action. | 
|  |  | 
|  | ``` js | 
|  | changeActions.add(type, label) | 
|  | ``` | 
|  |  | 
|  | * type: The type of the action, either `change` or `revision`. | 
|  |  | 
|  | * label: The label to be used in UI for this action. | 
|  |  | 
|  |  | 
|  | ==== changeActions.remove() | 
|  | Removes an action from the change actions section. | 
|  |  | 
|  | ``` js | 
|  | changeActions.remove(key) | 
|  | ``` | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  |  | 
|  | ==== changeActions.addTapListener() | 
|  | Adds a tap listener to an action that will be invoked when the action | 
|  | is tapped. | 
|  |  | 
|  | ``` js | 
|  | changeActions.addTapListener(key, callback) | 
|  | ``` | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  | * callback: JavaScript function to be invoked when action tapped. | 
|  |  | 
|  |  | 
|  | ==== changeActions.removeTapListener() | 
|  | Removes an existing tap listener on an action. | 
|  |  | 
|  | ``` js | 
|  | changeActions.removeTapListener(key, callback) | 
|  | ``` | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  | * callback: JavaScript function to be removed. | 
|  |  | 
|  |  | 
|  | ==== changeActions.setLabel() | 
|  | Sets the label for an action. | 
|  |  | 
|  | ``` js | 
|  | changeActions.setLabel(key, label) | 
|  | ``` | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  | * label: The label of the action. | 
|  |  | 
|  |  | 
|  | ==== changeActions.setTitle() | 
|  | Sets the title for an action. | 
|  |  | 
|  | ``` js | 
|  | changeActions.setTitle(key, title) | 
|  | ``` | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  | * title: The title of the action. | 
|  |  | 
|  |  | 
|  | ==== changeActions.setIcon() | 
|  | Sets an icon for an action. | 
|  |  | 
|  | ``` js | 
|  | changeActions.setIcon(key, icon) | 
|  | ``` | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  | * icon: The name of the icon. | 
|  |  | 
|  |  | 
|  | ==== changeActions.setEnabled() | 
|  | Sets an action to enabled or disabled. | 
|  |  | 
|  | ``` js | 
|  | changeActions.setEnabled(key, enabled) | 
|  | ``` | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  | * enabled: The status of the action, true to enable. | 
|  |  | 
|  |  | 
|  | ==== changeActions.setActionHidden() | 
|  | Sets an action to be hidden. | 
|  |  | 
|  | ``` js | 
|  | changeActions.setActionHidden(type, key, hidden) | 
|  | ``` | 
|  |  | 
|  | * type: The type of the action. | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  | * hidden: True to hide the action, false to show the action. | 
|  |  | 
|  |  | 
|  | ==== changeActions.setActionOverflow() | 
|  | Sets an action to show in overflow menu. | 
|  |  | 
|  | ``` js | 
|  | changeActions.setActionOverflow(type, key, overflow) | 
|  | ``` | 
|  |  | 
|  | * type: The type of the action. | 
|  |  | 
|  | * key: The key of the action. | 
|  |  | 
|  | * overflow: True to move the action to overflow menu, false to move | 
|  | the action out of the overflow menu. | 
|  |  | 
|  |  | 
|  | === changeReply | 
|  | `plugin.changeReply()` | 
|  |  | 
|  | Returns an instance of the | 
|  | link:https://gerrit.googlesource.com/gerrit/+/master/polygerrit-ui/app/api/change-reply.ts[ChangeReplyPluginApi]. | 
|  |  | 
|  | [[checks]] | 
|  | === checks | 
|  | `plugin.checks()` | 
|  |  | 
|  | Returns an instance of the link:pg-plugin-checks-api.html[ChecksApi]. | 
|  |  | 
|  | === getPluginName | 
|  | `plugin.getPluginName()` | 
|  |  | 
|  | Returns the name this plugin was installed as by the server | 
|  | administrator. The plugin name is required to access REST API | 
|  | views installed by the plugin, or to access resources. | 
|  |  | 
|  | === getServerInfo | 
|  | `plugin.getServerInfo()` | 
|  |  | 
|  | Returns the host config as a link:rest-api-config.html#server-info[ServerInfo] | 
|  | object. | 
|  |  | 
|  | === popup | 
|  | `plugin.popup(moduleName)` | 
|  |  | 
|  | Creates a popup that contains the given web components. Can be controlled with | 
|  | calling `open()` and `close()` on the return value. | 
|  |  | 
|  | [[plugin-rest-api]] | 
|  | === restApi | 
|  | `plugin.restApi(opt_prefix)` | 
|  |  | 
|  | .Params: | 
|  | - (optional) URL prefix, for easy switching into plugin URL space, | 
|  | e.g. `changes/1/revisions/1/cookbook~say-hello` | 
|  |  | 
|  | .Returns: | 
|  | - Instance of link:pg-plugin-rest-api.html[RestPluginApi]. | 
|  |  | 
|  | [[plugin-screen]] | 
|  | === screen | 
|  | `plugin.screen(screenName, opt_moduleName)` | 
|  |  | 
|  | Registers a web component as a dedicated top-level page that the router | 
|  | understands and that has a URL (/x/pluginname/screenname) that can be navigated | 
|  | to. Extension screens are usually linked from the | 
|  | link:dev-plugins.html#top-menu-extensions[top menu]. | 
|  |  | 
|  | .Params: | 
|  | - `*string* screenName` URL path fragment of the screen, e.g. | 
|  | `/x/pluginname/*screenname*` | 
|  | - `*string* opt_moduleName` (Optional) Web component to be instantiated for this | 
|  | screen. | 
|  |  | 
|  | .Returns: | 
|  | - Instance of HookApi. | 
|  |  | 
|  | === url | 
|  | `plugin.url(opt_path)` | 
|  |  | 
|  | Returns a URL within the plugin's URL space. If invoked with no | 
|  | parameter the URL of the plugin is returned. If passed a string | 
|  | the argument is appended to the plugin URL. | 
|  |  | 
|  | A plugin's URL is where this plugin is loaded, it doesn't | 
|  | necessary to be the same as the Gerrit host. Use `window.location` | 
|  | if you need to access the Gerrit host info. | 
|  |  | 
|  | ``` js | 
|  | self.url();                    // "https://gerrit-review.googlesource.com/plugins/demo/" | 
|  | self.url('/static/icon.png');  // "https://gerrit-review.googlesource.com/plugins/demo/static/icon.png" | 
|  | ``` |