# Gerrit Code Review - JavaScript Plugin Development and API

version v3.4.4

Gerrit Code Review supports an API for JavaScript plugins to interact with the web UI and the server process.

## 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 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`:

    Gerrit.install(plugin => {
      // Your code here.
    });

You can find more elaborate examples in the polygerrit-ui/app/samples/ directory of the source tree.

## 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 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

    Gerrit.install(plugin => {
      const domHook = plugin.hook('reply-text');
      domHook.onAttached(element => {
        if (!element.content) { return; }
        // element.content is a reply dialog text area.
      });
    });

### 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.

    Gerrit.install(plugin => {
      const domHook = plugin.hook('reply-text');
      domHook.onAttached(element => {
        if (!element.content) { return; }
        element.content.style.border = '1px red dashed';
      });
    });

### Replacing DOM Elements

An endpoint’s contents can be replaced by passing the replace attribute as an option.

    Gerrit.install(plugin => {
      const domHook = plugin.hook('header-title', {replace: true});
      domHook.onAttached(element => {
        element.appendChild(document.createElement('my-site-header'));
      });
    });

### Styling DOM Elements

A plugin may provide Polymer’s style modules to style individual endpoints using `plugin.registerStyleModule(endpointName, moduleName)`. A style must be defined as a standalone `<dom-module>` defined in the same .js file.

See samples/theme-plugin.js for an example.

    const styleElement = document.createElement('dom-module');
    styleElement.innerHTML =
     `<template>
        <style>
        html {
          --primary-text-color: red;
        }
       </style>
     </template>`;

    styleElement.register('some-style-module');

    Gerrit.install(plugin => {
      plugin.registerStyleModule('change-metadata', 'some-style-module');
    });

## 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 DOM API

The low-level DOM API methods are the base of all UI customization.

### attributeHelper

`plugin.attributeHelper(element)`

Alternative for Polymer data for plugins that don’t use Polymer. Can be used to bind element attribute changes to callbacks.

See samples/bind-parameters.js for an example.

### hook

`plugin.hook(endpointName, opt_options)`

See endpoints.

### registerCustomComponent

`plugin.registerCustomComponent(endpointName, opt_moduleName, opt_options)`

See endpoints.

### registerDynamicCustomComponent

`plugin.registerDynamicCustomComponent(dynamicEndpointName, opt_moduleName, opt_options)`

See endpoints.

### registerStyleModule

`plugin.registerStyleModule(endpointName, moduleName)`

See above.

### on

Register a JavaScript callback to be invoked when events occur within the web interface. Signature

    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 ChangeInfo and 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 ChangeInfo and 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

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 GrAdminApi.

### changeActions

`self.changeActions()`

Returns an instance of the ChangeActionsPluginApi.

#### changeActions.add()

Adds a new action to the change actions section. Returns the key of the newly added action.

    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.

    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.

    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.

    changeActions.removeTapListener(key, callback)

- key: The key of the action.

- callback: JavaScript function to be removed.

#### changeActions.setLabel()

Sets the label for an action.

    changeActions.setLabel(key, label)

- key: The key of the action.

- label: The label of the action.

#### changeActions.setTitle()

Sets the title for an action.

    changeActions.setTitle(key, title)

- key: The key of the action.

- title: The title of the action.

#### changeActions.setIcon()

Sets an icon for an action.

    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.

    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.

    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.

    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 ChangeReplyPluginApi.

### checks

`plugin.checks()`

Returns an instance of the 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 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.

### 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 RestPluginApi.

### 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 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.

    self.url();                    // "https://gerrit-review.googlesource.com/plugins/demo/"
    self.url('/static/icon.png');  // "https://gerrit-review.googlesource.com/plugins/demo/static/icon.png"