Plugin popup() api

Changes to JS API:
- popups have backdrop and is centered
- plugin.popup() takes a string custom element name
- plugin.popup() returns an API for closing and reopening the popup
- plugin.deprecated.popup() takes Element (similar to GWT Plugin JS API)

Recommended usage:

``` js
  Gerrit.install(function(plugin) {
    const popup = plugin.popup('my-plugin-popup-simple');;
    // ... work
    popup.close();
    // ... more work
    popup.open();
  });

```

``` html
<dom-module id="my-plugin-popup-simple">
  <template>
    <div>popup popup popup popup popup </div>
  </template>
  <script>
    Polymer({is: 'my-plugin-popup-simple'});
  </script>
</dom-module>
```

Change-Id: Icb3f83d35f3c60915f12b77bc8a7d548d50d5695
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.html
new file mode 100644
index 0000000..3ccb3fd
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.html
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
+
+<dom-module id="gr-plugin-popup">
+  <template>
+    <style include="shared-styles"></style>
+    <gr-overlay id="overlay" with-backdrop>
+      <content></content>
+    </gr-overlay>
+  </template>
+  <script src="gr-plugin-popup.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
new file mode 100644
index 0000000..8286eae
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+(function(window) {
+  'use strict';
+  Polymer({
+    is: 'gr-plugin-popup',
+    get opened() {
+      return this.$.overlay.opened;
+    },
+    open() {
+      return this.$.overlay.open();
+    },
+    close() {
+      this.$.overlay.close();
+    },
+  });
+})(window);
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html
new file mode 100644
index 0000000..2dbf96d
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup_test.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-plugin-popup</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-plugin-popup.html"/>
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-plugin-popup></gr-plugin-popup>
+  </template>
+</test-fixture>
+
+<script>
+  suite('gr-plugin-popup tests', () => {
+    let element;
+    let sandbox;
+
+    setup(() => {
+      sandbox = sinon.sandbox.create();
+      element = fixture('basic');
+      stub('gr-overlay', {
+        open: sandbox.stub().returns(Promise.resolve()),
+        close: sandbox.stub(),
+      });
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+
+    test('exists', () => {
+      assert.isOk(element);
+    });
+
+    test('open uses open() from gr-overlay', () => {
+      return element.open().then(() => {
+        assert.isTrue(element.$.overlay.open.called);
+      });
+    });
+
+    test('close uses close() from gr-overlay', () => {
+      element.close();
+      assert.isTrue(element.$.overlay.close.called);
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.html
new file mode 100644
index 0000000..6bf37de
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.html
@@ -0,0 +1,23 @@
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
+<link rel="import" href="gr-plugin-popup.html">
+
+<dom-module id="gr-popup-interface">
+  <script src="gr-popup-interface.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.js
new file mode 100644
index 0000000..e62e882
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface.js
@@ -0,0 +1,71 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+(function(window) {
+  'use strict';
+
+  /**
+   * Plugin popup API.
+   * Provides method for opening and closing popups from plugin.
+   * opt_moduleName is a name of custom element that will be automatically
+   * inserted on popup opening.
+   * @param {!Object} plugin
+   * @param {opt_moduleName=} string
+   */
+  function GrPopupInterface(plugin, opt_moduleName) {
+    this.plugin = plugin;
+    this._openingPromise = null;
+    this._popup = null;
+    this._moduleName = opt_moduleName || null;
+  }
+
+  GrPopupInterface.prototype._getElement = function() {
+    return Polymer.dom(this._popup);
+  };
+
+  /**
+   * Opens the popup, inserts it into DOM over current UI.
+   * Creates the popup if not previously created. Creates popup content element,
+   * if it was provided with constructor.
+   * @returns {!Promise<!Object>}
+   */
+  GrPopupInterface.prototype.open = function() {
+    if (!this._openingPromise) {
+      this._openingPromise =
+          this.plugin.hook('plugin-overlay').getLastAttached()
+      .then(hookEl => {
+        const popup = document.createElement('gr-plugin-popup');
+        if (this._moduleName) {
+          const el = Polymer.dom(popup).appendChild(
+              document.createElement(this._moduleName));
+          el.plugin = this.plugin;
+        }
+        this._popup = Polymer.dom(hookEl).appendChild(popup);
+        Polymer.dom.flush();
+        return this._popup.open().then(() => this);
+      });
+    }
+    return this._openingPromise;
+  };
+
+  /**
+   * Hides the popup.
+   */
+  GrPopupInterface.prototype.close = function() {
+    if (!this._popup) { return; }
+    this._popup.close();
+    this._openingPromise = null;
+  };
+
+  window.GrPopupInterface = GrPopupInterface;
+})(window);
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html
new file mode 100644
index 0000000..7d9dd28
--- /dev/null
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-popup-interface_test.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-popup-interface</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-popup-interface.html"/>
+
+<script>void(0);</script>
+
+<test-fixture id="container">
+  <template>
+    <div></div>
+  </template>
+</test-fixture>
+
+<dom-module id="gr-user-test-popup">
+  <template>
+    <div id="barfoo">some test module</div>
+  </template>
+  <script>Polymer({is: 'gr-user-test-popup'});</script>
+</dom-module>
+
+<script>
+  suite('gr-popup-interface tests', () => {
+    let container;
+    let instance;
+    let plugin;
+    let sandbox;
+
+    setup(() => {
+      sandbox = sinon.sandbox.create();
+      Gerrit.install(p => { plugin = p; }, '0.1',
+          'http://test.com/plugins/testplugin/static/test.js');
+      container = fixture('container');
+      sandbox.stub(plugin, 'hook').returns({
+        getLastAttached() {
+          return Promise.resolve(container);
+        },
+      });
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+
+    suite('manual', () => {
+      setup(() => {
+        instance = new GrPopupInterface(plugin);
+      });
+
+      test('open', () => {
+        return instance.open().then(api => {
+          assert.strictEqual(api, instance);
+          const manual = document.createElement('div');
+          manual.id = 'foobar';
+          manual.innerHTML = 'manual content';
+          api._getElement().appendChild(manual);
+          flushAsynchronousOperations();
+          assert.equal(
+              container.querySelector('#foobar').textContent, 'manual content');
+        });
+      });
+
+      test('close', () => {
+        return instance.open().then(api => {
+          assert.isTrue(api._getElement().node.opened);
+          api.close();
+          assert.isFalse(api._getElement().node.opened);
+        });
+      });
+    });
+
+    suite('components', () => {
+      setup(() => {
+        instance = new GrPopupInterface(plugin, 'gr-user-test-popup');
+      });
+
+      test('open', () => {
+        return instance.open().then(api => {
+          assert.isNotNull(
+              Polymer.dom(container).querySelector('gr-user-test-popup'));
+        });
+      });
+
+      test('close', () => {
+        return instance.open().then(api => {
+          assert.isTrue(api._getElement().node.opened);
+          api.close();
+          assert.isFalse(api._getElement().node.opened);
+        });
+      });
+    });
+  });
+</script>