You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2019/11/05 17:25:15 UTC

[myfaces-tobago] branch master updated (85883c3 -> c02277e)

This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git.


    from 85883c3  npm audit fix
     new df17592  tobago-select-many-checkbox: custom elements
     new c02277e  tobago-select-many-shuttle: custom elements

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../renderer/SelectManyCheckboxRenderer.java       |   4 +
 .../renderer/SelectManyShuttleRenderer.java        |   4 +
 .../tobago/renderkit/html/HtmlElements.java        |   2 +
 .../70-selectManyShuttle/Shuttle.test.js           | 141 ++++++++++++++++++
 .../030-select/70-selectManyShuttle/Shuttle.xhtml  |   8 +-
 .../src/main/npm/ts/tobago-select-many-checkbox.ts |  32 +++--
 .../src/main/npm/ts/tobago-select-many-shuttle.ts  | 158 ++++++++++++---------
 7 files changed, 271 insertions(+), 78 deletions(-)
 create mode 100644 tobago-example/tobago-example-demo/src/main/webapp/content/20-component/030-select/70-selectManyShuttle/Shuttle.test.js


[myfaces-tobago] 01/02: tobago-select-many-checkbox: custom elements

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit df175929cf09533798b07418d316064203bac3a9
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Mon Nov 4 09:28:10 2019 +0100

    tobago-select-many-checkbox: custom elements
    
    Using custom elements for tobago-select-many-checkbox.ts
    
    issue: TOBAGO-1633: TS refactoring
---
 .../renderer/SelectManyCheckboxRenderer.java       |  4 +++
 .../tobago/renderkit/html/HtmlElements.java        |  1 +
 .../src/main/npm/ts/tobago-select-many-checkbox.ts | 32 +++++++++++++++-------
 3 files changed, 27 insertions(+), 10 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyCheckboxRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyCheckboxRenderer.java
index 3af82f7..ca8ede2 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyCheckboxRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyCheckboxRenderer.java
@@ -42,6 +42,10 @@ import java.io.IOException;
 
 public class SelectManyCheckboxRenderer extends SelectManyRendererBase {
 
+  public HtmlElements getComponentTag() {
+    return HtmlElements.TOBAGO_SELECT_MANY_CHECKBOX;
+  }
+
   @Override
   public void encodeBeginField(final FacesContext facesContext, final UIComponent component) throws IOException {
     final AbstractUISelectManyCheckbox select = (AbstractUISelectManyCheckbox) component;
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
index 870eeb6..3b1a990 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
@@ -144,6 +144,7 @@ public enum HtmlElements {
   TOBAGO_SPLIT_LAYOUT("tobago-split-layout"),
   TOBAGO_SELECT_BOOLEAN_CHECKBOX("tobago-select-boolean-checkbox"),
   TOBAGO_SELECT_BOOLEAN_TOGGLE("tobago-select-boolean-toggle"),
+  TOBAGO_SELECT_MANY_CHECKBOX("tobago-select-many-checkbox"),
   TOBAGO_STARS("tobago-stars"),
   TOBAGO_SUGGEST("tobago-suggest"),
   TOBAGO_TAB("tobago-tab"),
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-select-many-checkbox.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-select-many-checkbox.ts
index 01a04c0..a9f74ef 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-select-many-checkbox.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-select-many-checkbox.ts
@@ -15,20 +15,32 @@
  * limitations under the License.
  */
 
-import {Listener, Phase} from "./tobago-listener";
-import {DomUtils} from "./tobago-utils";
+class SelectManyCheckbox extends HTMLElement {
 
-class SelectManyCheckbox {
+  constructor() {
+    super();
+  }
 
-  static init = function (element: HTMLElement): void {
-    for (const checkbox of DomUtils.selfOrQuerySelectorAll(element, ".tobago-selectManyCheckbox input[readonly]")) {
-      checkbox.addEventListener("click", (event: Event) => {
+  connectedCallback(): void {
+    for (const input of this.inputs) {
+      if (input.readOnly) {
+        input.addEventListener("click", preventClick);
+      }
+
+      function preventClick(event: MouseEvent): void {
         // in the "readonly" case, prevent the default, which is changing the "checked" state
         event.preventDefault();
-      });
+        console.log("slectmanycheckbox bubb.");
+      }
     }
-  };
+  }
+
+  get inputs(): NodeListOf<HTMLInputElement> {
+    const rootNode = this.getRootNode() as ShadowRoot | Document;
+    return rootNode.querySelectorAll("input[name='" + this.id + "']");
+  }
 }
 
-Listener.register(SelectManyCheckbox.init, Phase.DOCUMENT_READY);
-Listener.register(SelectManyCheckbox.init, Phase.AFTER_UPDATE);
+document.addEventListener("DOMContentLoaded", function (event: Event): void {
+  window.customElements.define("tobago-select-many-checkbox", SelectManyCheckbox);
+});


[myfaces-tobago] 02/02: tobago-select-many-shuttle: custom elements

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit c02277e886afa174dbe8fd7336e867efaecfc813
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Tue Nov 5 18:25:00 2019 +0100

    tobago-select-many-shuttle: custom elements
    
    Using custom elements for tobago-select-many-shuttle.ts
    Test added.
    
    issue: TOBAGO-1633: TS refactoring
---
 .../renderer/SelectManyShuttleRenderer.java        |   4 +
 .../tobago/renderkit/html/HtmlElements.java        |   1 +
 .../70-selectManyShuttle/Shuttle.test.js           | 141 ++++++++++++++++++
 .../030-select/70-selectManyShuttle/Shuttle.xhtml  |   8 +-
 .../src/main/npm/ts/tobago-select-many-shuttle.ts  | 158 ++++++++++++---------
 5 files changed, 244 insertions(+), 68 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyShuttleRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyShuttleRenderer.java
index 563a070..ea31468 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyShuttleRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SelectManyShuttleRenderer.java
@@ -40,6 +40,10 @@ import java.util.List;
 
 public class SelectManyShuttleRenderer extends SelectManyRendererBase {
 
+  public HtmlElements getComponentTag() {
+    return HtmlElements.TOBAGO_SELECT_MANY_SHUTTLE;
+  }
+
   @Override
   public void encodeBeginField(final FacesContext facesContext, final UIComponent component) throws IOException {
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
index 3b1a990..f6ecb2c 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
@@ -145,6 +145,7 @@ public enum HtmlElements {
   TOBAGO_SELECT_BOOLEAN_CHECKBOX("tobago-select-boolean-checkbox"),
   TOBAGO_SELECT_BOOLEAN_TOGGLE("tobago-select-boolean-toggle"),
   TOBAGO_SELECT_MANY_CHECKBOX("tobago-select-many-checkbox"),
+  TOBAGO_SELECT_MANY_SHUTTLE("tobago-select-many-shuttle"),
   TOBAGO_STARS("tobago-stars"),
   TOBAGO_SUGGEST("tobago-suggest"),
   TOBAGO_TAB("tobago-tab"),
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/030-select/70-selectManyShuttle/Shuttle.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/030-select/70-selectManyShuttle/Shuttle.test.js
new file mode 100644
index 0000000..1593716
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/030-select/70-selectManyShuttle/Shuttle.test.js
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import {testFrameQuerySelectorAllFn, testFrameQuerySelectorFn} from "/script/tobago-test.js";
+import {TobagoTestTool} from "/tobago/test/tobago-test-tool.js";
+
+QUnit.test("submit: addAll, removeAll, addItem0to4, removeItem2to3", function (assert) {
+  let unselectedOptions = testFrameQuerySelectorAllFn("#page\\:mainForm\\:submitExample\\:\\:unselected option");
+  let selectedOptions = testFrameQuerySelectorAllFn("#page\\:mainForm\\:submitExample\\:\\:selected option");
+  let addAllButton = testFrameQuerySelectorFn("#page\\:mainForm\\:submitExample\\:\\:addAll");
+  let addButton = testFrameQuerySelectorFn("#page\\:mainForm\\:submitExample\\:\\:add");
+  let removeButton = testFrameQuerySelectorFn("#page\\:mainForm\\:submitExample\\:\\:remove");
+  let removeAllButton = testFrameQuerySelectorFn("#page\\:mainForm\\:submitExample\\:\\:removeAll");
+  let submitButton = testFrameQuerySelectorFn("#page\\:mainForm\\:submitButton");
+  let output = testFrameQuerySelectorFn("#page\\:mainForm\\:submitExampleOutput span");
+
+  let TTT = new TobagoTestTool(assert);
+  TTT.action(function () {
+    addAllButton().click();
+    submitButton().click();
+  });
+  TTT.waitForResponse();
+  TTT.asserts(3, function () {
+    assert.equal(unselectedOptions().length, 0);
+    assert.equal(selectedOptions().length, 9);
+    assert.equal(output().textContent, "[Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto]");
+  });
+  TTT.action(function () {
+    removeAllButton().click();
+    submitButton().click();
+  });
+  TTT.waitForResponse();
+  TTT.asserts(3, function () {
+    assert.equal(unselectedOptions().length, 9);
+    assert.equal(selectedOptions().length, 0);
+    assert.equal(output().textContent, "[]");
+  });
+  TTT.action(function () {
+    unselectedOptions().item(0).selected = true;
+    unselectedOptions().item(1).selected = true;
+    unselectedOptions().item(2).selected = true;
+    unselectedOptions().item(3).selected = true;
+    unselectedOptions().item(4).selected = true;
+    addButton().click();
+    submitButton().click();
+  });
+  TTT.waitForResponse();
+  TTT.asserts(3, function () {
+    assert.equal(unselectedOptions().length, 4);
+    assert.equal(selectedOptions().length, 5);
+    assert.equal(output().textContent, "[Mercury, Venus, Earth, Mars, Jupiter]");
+  });
+  TTT.action(function () {
+    selectedOptions().item(2).selected = true;
+    selectedOptions().item(3).selected = true;
+    removeButton().click();
+    submitButton().click();
+  });
+  TTT.waitForResponse();
+  TTT.asserts(3, function () {
+    assert.equal(unselectedOptions().length, 6);
+    assert.equal(selectedOptions().length, 3);
+    assert.equal(output().textContent, "[Mercury, Venus, Jupiter]");
+  });
+  TTT.startTest();
+});
+
+QUnit.test("ajax: addAll, removeAll, addItem1to2, removeItem0", function (assert) {
+  let unselectedOptions = testFrameQuerySelectorAllFn("#page\\:mainForm\\:ajaxExample\\:\\:unselected option");
+  let selectedOptions = testFrameQuerySelectorAllFn("#page\\:mainForm\\:ajaxExample\\:\\:selected option");
+  let addAllButton = testFrameQuerySelectorFn("#page\\:mainForm\\:ajaxExample\\:\\:addAll");
+  let addButton = testFrameQuerySelectorFn("#page\\:mainForm\\:ajaxExample\\:\\:add");
+  let removeButton = testFrameQuerySelectorFn("#page\\:mainForm\\:ajaxExample\\:\\:remove");
+  let removeAllButton = testFrameQuerySelectorFn("#page\\:mainForm\\:ajaxExample\\:\\:removeAll");
+  let output = testFrameQuerySelectorFn("#page\\:mainForm\\:outputStars span");
+
+  let TTT = new TobagoTestTool(assert);
+  TTT.action(function () {
+    addAllButton().click();
+  });
+  // TTT.waitForResponse(); //TODO use waitForResponse()
+  TTT.waitMs(5000);
+  TTT.asserts(6, function () {
+    assert.equal(unselectedOptions().length, 0);
+    assert.equal(selectedOptions().length, 4);
+    assert.ok(output().textContent.indexOf(selectedOptions().item(0).value) > 0);
+    assert.ok(output().textContent.indexOf(selectedOptions().item(1).value) > 0);
+    assert.ok(output().textContent.indexOf(selectedOptions().item(2).value) > 0);
+    assert.ok(output().textContent.indexOf(selectedOptions().item(3).value) > 0);
+  });
+  TTT.action(function () {
+    removeAllButton().click();
+  });
+  // TTT.waitForResponse(); //TODO use waitForResponse()
+  TTT.waitMs(5000);
+  TTT.asserts(3, function () {
+    assert.equal(unselectedOptions().length, 4);
+    assert.equal(selectedOptions().length, 0);
+    assert.equal(output().textContent, "[]");
+  });
+  TTT.action(function () {
+    unselectedOptions().item(1).selected = true;
+    unselectedOptions().item(2).selected = true;
+    addButton().click();
+  });
+  // TTT.waitForResponse(); //TODO use waitForResponse()
+  TTT.waitMs(5000);
+  TTT.asserts(4, function () {
+    assert.equal(unselectedOptions().length, 2);
+    assert.equal(selectedOptions().length, 2);
+    assert.ok(output().textContent.indexOf(selectedOptions().item(0).value) > 0);
+    assert.ok(output().textContent.indexOf(selectedOptions().item(1).value) > 0);
+  });
+  TTT.action(function () {
+    selectedOptions().item(0).selected = true;
+    selectedOptions().item(1).selected = false;
+    removeButton().click();
+  });
+  // TTT.waitForResponse(); //TODO use waitForResponse()
+  TTT.waitMs(5000);
+  TTT.asserts(3, function () {
+    assert.equal(unselectedOptions().length, 3);
+    assert.equal(selectedOptions().length, 1);
+    assert.ok(output().textContent.indexOf(selectedOptions().item(0).value) > 0);
+  });
+  TTT.startTest();
+});
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/030-select/70-selectManyShuttle/Shuttle.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/030-select/70-selectManyShuttle/Shuttle.xhtml
index b7de6b4..aaf990e 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/030-select/70-selectManyShuttle/Shuttle.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/030-select/70-selectManyShuttle/Shuttle.xhtml
@@ -98,14 +98,14 @@
     <pre><code class="language-markup">&lt;tc:selectManyShuttle label="Planets" ...>
   &lt;tc:selectItems value="\#{selectManyShuttleController.planets}" .../>
 &lt;/tc:selectManyShuttle></code></pre>
-    <tc:selectManyShuttle id="s4" label="Planets" labelLayout="top"
+    <tc:selectManyShuttle id="submitExample" label="Planets" labelLayout="top"
                           unselectedLabel="Left List" selectedLabel="Right List"
                           value="#{selectManyShuttleController.selectedPlanets}">
       <tc:selectItems value="#{selectManyShuttleController.planets}"
                       var="planet" itemLabel="#{planet.name}" itemValue="#{planet}"/>
     </tc:selectManyShuttle>
-    <tc:out id="o1" label="Selected Planets" value="#{selectManyShuttleController.selectedPlanetsAsString}"/>
-    <tc:button label="Submit"/>
+    <tc:out id="submitExampleOutput" label="Selected Planets" value="#{selectManyShuttleController.selectedPlanetsAsString}"/>
+    <tc:button id="submitButton" label="Submit"/>
   </tc:section>
 
   <tc:section label="Ajax">
@@ -114,7 +114,7 @@
       <code class="language-markup">&lt;tc:selectManyShuttle .../></code> contain
       <code class="language-markup">&lt;f:ajax render="outputStars"/></code>. Every time, the value in the shuttle list
       change, the outputfield is rerendered.</p>
-    <tc:selectManyShuttle id="s5" label="Stars" labelLayout="top" value="#{selectManyShuttleController.selectedStars}">
+    <tc:selectManyShuttle id="ajaxExample" label="Stars" labelLayout="top" value="#{selectManyShuttleController.selectedStars}">
       <tc:selectItems value="#{selectManyShuttleController.stars}"/>
       <f:ajax render="outputStars"/>
     </tc:selectManyShuttle>
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-select-many-shuttle.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-select-many-shuttle.ts
index 8db3da2..1cf4161 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-select-many-shuttle.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-select-many-shuttle.ts
@@ -15,73 +15,103 @@
  * limitations under the License.
  */
 
-import {Listener, Phase} from "./tobago-listener";
-import {DomUtils} from "./tobago-utils";
-
-class SelectManyShuttle {
-
-  static init = function (element: HTMLElement): void {
-    for (const shuttle of DomUtils.selfOrQuerySelectorAll(
-        element, ".tobago-selectManyShuttle:not(.tobago-selectManyShuttle-disabled)")) {
-
-      shuttle.querySelector(".tobago-selectManyShuttle-unselected").addEventListener(
-          "dblclick", (event: Event) => {
-            SelectManyShuttle.moveSelectedItems(event, true, false);
-          });
-
-      shuttle.querySelector(".tobago-selectManyShuttle-selected").addEventListener(
-          "dblclick", (event: Event) => {
-            SelectManyShuttle.moveSelectedItems(event, false, false);
-          });
-
-      shuttle.querySelector(".tobago-selectManyShuttle-addAll").addEventListener(
-          "click", (event: Event) => {
-            SelectManyShuttle.moveSelectedItems(event, true, true);
-          });
-
-      shuttle.querySelector(".tobago-selectManyShuttle-add").addEventListener(
-          "click", (event: Event) => {
-            SelectManyShuttle.moveSelectedItems(event, true, false);
-          });
-
-      shuttle.querySelector(".tobago-selectManyShuttle-removeAll").addEventListener(
-          "click", (event: Event) => {
-            SelectManyShuttle.moveSelectedItems(event, false, true);
-          });
-
-      shuttle.querySelector(".tobago-selectManyShuttle-remove").addEventListener(
-          "click", (event: Event) => {
-            SelectManyShuttle.moveSelectedItems(event, false, false);
-          });
+class SelectManyShuttle extends HTMLElement {
+
+  constructor() {
+    super();
+  }
+
+  connectedCallback(): void {
+    if (this.unselectedSelect.getAttribute("readonly") !== "readonly" && !this.unselectedSelect.disabled) {
+      this.unselectedSelect.addEventListener("dblclick", this.addSelectedItems.bind(this));
+    }
+
+    if (this.selectedSelect.getAttribute("readonly") !== "readonly" && !this.selectedSelect.disabled) {
+      this.selectedSelect.addEventListener("dblclick", this.removeSelectedItems.bind(this));
     }
-  };
-
-  static moveSelectedItems = function (event: Event, direction: boolean, all: boolean): void {
-    const currentTarget = event.currentTarget as HTMLElement;
-    const shuttle = currentTarget.closest(".tobago-selectManyShuttle");
-    const unselected = shuttle.querySelector(".tobago-selectManyShuttle-unselected");
-    const selected = shuttle.querySelector(".tobago-selectManyShuttle-selected");
-    const oldCount = selected.childElementCount;
-    const source = direction ? unselected : selected;
-    const target = direction ? selected : unselected;
-    const options = source.querySelectorAll(all ? "option:not(:disabled)" : "option:checked");
-    const hidden = shuttle.querySelector(".tobago-selectManyShuttle-hidden");
-    const hiddenOptions = hidden.querySelectorAll("option");
-    for (const option of options as NodeListOf<HTMLOptionElement>) {
-      source.removeChild(option);
-      target.appendChild(option);
-      for (const hiddenOption of hiddenOptions) {
-        if (hiddenOption.value === option.value) {
-          hiddenOption.selected = direction;
-        }
-      }
+
+    if (!this.addAllButton.disabled) {
+      this.addAllButton.addEventListener("click", this.addAllItems.bind(this));
+    }
+
+    if (!this.addButton.disabled) {
+      this.addButton.addEventListener("click", this.addSelectedItems.bind(this));
     }
 
-    if (oldCount !== selected.childElementCount) {
-      hidden.dispatchEvent(new Event("change"));
+    if (!this.removeButton.disabled) {
+      this.removeButton.addEventListener("click", this.removeSelectedItems.bind(this));
     }
-  };
+
+    if (!this.removeAllButton.disabled) {
+      this.removeAllButton.addEventListener("click", this.removeAllItems.bind(this));
+    }
+  }
+
+  private addAllItems(event: MouseEvent): void {
+    this.addItems(this.unselectedSelect.querySelectorAll("option:not(:disabled)"));
+  }
+
+  private addSelectedItems(event: MouseEvent): void {
+    this.addItems(this.unselectedSelect.querySelectorAll("option:checked"));
+  }
+
+  private removeSelectedItems(event: MouseEvent): void {
+    this.removeItems(this.selectedSelect.querySelectorAll("option:checked"));
+  }
+
+  private removeAllItems(event: MouseEvent): void {
+    this.removeItems(this.selectedSelect.querySelectorAll("option:not(:disabled)"));
+  }
+
+  private addItems(options: NodeListOf<HTMLOptionElement>): void {
+    for (const option of options) {
+      this.selectedSelect.add(option);
+      this.changeHiddenOption(option, true);
+    }
+  }
+
+  private removeItems(options: NodeListOf<HTMLOptionElement>): void {
+    for (const option of options) {
+      this.unselectedSelect.add(option);
+      this.changeHiddenOption(option, false);
+    }
+  }
+
+  private changeHiddenOption(option: HTMLOptionElement, select: boolean): void {
+    const hiddenOption: HTMLOptionElement = this.hiddenSelect.querySelector("option[value='" + option.value + "']");
+    hiddenOption.selected = select;
+    this.dispatchEvent(new Event("change"));
+  }
+
+  get unselectedSelect(): HTMLSelectElement {
+    return this.querySelector(".tobago-selectManyShuttle-unselected");
+  }
+
+  get selectedSelect(): HTMLSelectElement {
+    return this.querySelector(".tobago-selectManyShuttle-selected");
+  }
+
+  get hiddenSelect(): HTMLSelectElement {
+    return this.querySelector(".tobago-selectManyShuttle-hidden");
+  }
+
+  get addAllButton(): HTMLButtonElement {
+    return this.querySelector(".tobago-selectManyShuttle-addAll");
+  }
+
+  get addButton(): HTMLButtonElement {
+    return this.querySelector(".tobago-selectManyShuttle-add");
+  }
+
+  get removeButton(): HTMLButtonElement {
+    return this.querySelector(".tobago-selectManyShuttle-remove");
+  }
+
+  get removeAllButton(): HTMLButtonElement {
+    return this.querySelector(".tobago-selectManyShuttle-removeAll");
+  }
 }
 
-Listener.register(SelectManyShuttle.init, Phase.DOCUMENT_READY);
-Listener.register(SelectManyShuttle.init, Phase.AFTER_UPDATE);
+document.addEventListener("DOMContentLoaded", function (event: Event): void {
+  window.customElements.define("tobago-select-many-shuttle", SelectManyShuttle);
+});