Load the shadow DOM of edit forms lazily.

Loads the shadow DOM of an edit form only when it is shown on the
screen (that is when the user clicks the corresponding Edit button).
This keeps the DOM as small as possible at any given point in time.

Change-Id: Icc1f43bfb54bd303a7182a02515c72522068415a
diff --git a/src/main/resources/META-INF/resources/bookmarks/MlkBookmarkSubmissionForm.js b/src/main/resources/META-INF/resources/bookmarks/MlkBookmarkSubmissionForm.js
index 7c71518..68f5dfd 100644
--- a/src/main/resources/META-INF/resources/bookmarks/MlkBookmarkSubmissionForm.js
+++ b/src/main/resources/META-INF/resources/bookmarks/MlkBookmarkSubmissionForm.js
@@ -48,6 +48,21 @@
 
   constructor() {
     super();
+    this.loaded = false;
+  }
+
+  static get observedAttributes() {
+    return [];
+  }
+
+  show() {
+    this.createShadow();
+  }
+
+  createShadow() {
+    if (this.shadowRoot !== null) {
+      return;
+    }
 
     let shadow = this.attachShadow({mode: "open"});
     shadow.appendChild(template.content.cloneNode(true));
@@ -65,11 +80,15 @@
     this.visibilityInput =
         cast(shadow.getElementById('visibility-input'));
 
-    this.loaded = false;
-  }
+    if (this.editedId !== null) {
+      this.mainForm.method = "post";
+      this.mainForm.action = `/bookmarks/${this.editedId}/edit`;
+    }
 
-  static get observedAttributes() {
-    return [];
+    this.uriInput.addEventListener('blur', this.onUriBlur.bind(this));
+    this.uriInput.value = this.uri || "";
+    this.titleInput.value = this.titleText || "";
+    this.descriptionInput.innerText = this.description || "";
   }
 
   get editedId() /*:number | null*/ {
@@ -85,17 +104,7 @@
     return this.editedId !== null;
   }
 
-  connectedCallback() {
-    if (this.editedId !== null) {
-      this.mainForm.method = "post";
-      this.mainForm.action = `/bookmarks/${this.editedId}/edit`;
-    }
-
-    this.uriInput.addEventListener('blur', this.onUriBlur.bind(this));
-    this.uriInput.value = this.uri || "";
-    this.titleInput.value = this.titleText || "";
-    this.descriptionInput.innerText = this.description || "";
-  }
+  connectedCallback() {}
 
   disconnectedCallback () {
     this.uriInput.removeEventListener('blur', this.onUriBlur.bind(this));
@@ -117,6 +126,7 @@
   }
 
   focus() {
+    this.show();
     if (!this.uriInput.value) {
       this.uriInput.focus();
     } else if (!this.titleInput.value) {
diff --git a/src/main/resources/META-INF/resources/lazychat/MlkLazychatSubmissionForm.js b/src/main/resources/META-INF/resources/lazychat/MlkLazychatSubmissionForm.js
index 7bd6d4d..d4e774a 100644
--- a/src/main/resources/META-INF/resources/lazychat/MlkLazychatSubmissionForm.js
+++ b/src/main/resources/META-INF/resources/lazychat/MlkLazychatSubmissionForm.js
@@ -38,6 +38,21 @@
 
   constructor() {
     super();
+    this.loaded = false;
+  }
+
+  static get observedAttributes() {
+    return [];
+  }
+
+  show() {
+    this.createShadow();
+  }
+
+  createShadow() {
+    if (this.shadowRoot !== null) {
+      return;
+    }
 
     let shadow = this.attachShadow({mode: "open"});
     shadow.appendChild(template.content.cloneNode(true));
@@ -49,11 +64,10 @@
     this.visibilityInput =
         cast(shadow.getElementById('visibility-input'));
 
-    this.loaded = false;
-  }
-
-  static get observedAttributes() {
-    return [];
+    if (this.editedId !== null) {
+      this.mainForm.method = "post";
+      this.mainForm.action = `/lazychat/${this.editedId}/edit`;
+    }
   }
 
   get editedId() /*:number | null*/ {
@@ -69,18 +83,14 @@
     return this.editedId !== null;
   }
 
-  connectedCallback() {
-    if (this.editedId !== null) {
-      this.mainForm.method = "post";
-      this.mainForm.action = `/lazychat/${this.editedId}/edit`;
-    }
-  }
+  connectedCallback() {}
 
   disconnectedCallback() {}
 
   attributeChangedCallback(name /*:string*/, oldValue /*:string*/, newValue /*:string*/) {}
 
   focus() {
+    this.show();
     this.textInput.focus();
     this.load();
   }