Build options page programatically

Until now we manually created the options page layout, but we want to
add custom elements before each option (for the kill switch
functionality), and this is very cumbersome to do it manually. Thus,
this CL builds the options page layout programatically to be able to
easilly inject a custom component before each option.

Change-Id: Ib110679971fa70c9933be911c4750b7fafa1d40e
diff --git a/src/optionsCommon.js b/src/options/optionsCommon.js
similarity index 79%
rename from src/optionsCommon.js
rename to src/options/optionsCommon.js
index 27db635..c552da2 100644
--- a/src/optionsCommon.js
+++ b/src/options/optionsCommon.js
@@ -1,5 +1,6 @@
-import {getExtVersion, isFirefox, isReleaseVersion} from './common/extUtils.js';
-import {cleanUpOptions, optionsPrototype, specialOptions} from './common/optionsUtils.js';
+import {getExtVersion, isFirefox, isReleaseVersion} from '../common/extUtils.js';
+import {cleanUpOptions, optionsPrototype, specialOptions} from '../common/optionsUtils.js';
+import optionsPage from './optionsPage.json5';
 
 var savedSuccessfullyTimeout = null;
 
@@ -94,8 +95,6 @@
 }
 
 window.addEventListener('load', function() {
-  i18n();
-
   if (window.CONTEXT == 'options') {
     if (!isReleaseVersion()) {
       var experimentsLink = document.querySelector('.experiments-link');
@@ -105,6 +104,52 @@
       }));
     }
 
+    // Add options to page
+    let optionsContainer = document.getElementById('options-container');
+    for (let section of optionsPage.sections) {
+      if (section?.name) {
+        let sectionHeader = document.createElement('h4');
+        sectionHeader.setAttribute('data-i18n', section.name);
+        optionsContainer.append(sectionHeader);
+      }
+
+      if (section?.options) {
+        for (let option of section.options) {
+          if (option?.customHTML) {
+            optionsContainer.insertAdjacentHTML('beforeend', option.customHTML);
+            continue;
+          }
+
+          let optionEl = document.createElement('div');
+          optionEl.classList.add('option');
+
+          let checkbox = document.createElement('input');
+          checkbox.setAttribute('type', 'checkbox');
+          checkbox.id = option.codename;
+
+          let label = document.createElement('label');
+          label.setAttribute('for', checkbox.id);
+          label.setAttribute('data-i18n', option.codename);
+
+          optionEl.append(checkbox, ' ', label);
+
+          if (option?.experimental) {
+            let experimental = document.createElement('span');
+            experimental.classList.add('experimental-label');
+            experimental.setAttribute('data-i18n', 'experimental_label');
+
+            optionEl.append(experimental);
+          }
+
+          optionsContainer.append(optionEl);
+        }
+      }
+
+      if (section?.footerHTML) {
+        optionsContainer.insertAdjacentHTML('beforeend', section.footerHTML);
+      }
+    }
+
     var featuresLink = document.querySelector('.features-link');
     featuresLink.href = getDocURL('features.md');
 
@@ -113,6 +158,8 @@
     profileIndicatorLink.href = getDocURL('op_indicator.md');
   }
 
+  i18n();
+
   chrome.storage.sync.get(null, function(items) {
     items = cleanUpOptions(items, false);
 
diff --git a/src/options/optionsPage.json5 b/src/options/optionsPage.json5
new file mode 100644
index 0000000..694c7dc
--- /dev/null
+++ b/src/options/optionsPage.json5
@@ -0,0 +1,40 @@
+// This file defines the sections of the options page, in which section each
+// option appears in, whether the "experimental" label should appear next to the
+// option label, and whether each option should use custom HTML.
+{
+  sections: [
+    {
+      name: null,
+      options: [
+        {codename: 'list'},
+        {codename: 'thread'},
+        {codename: 'threadall'},
+      ],
+    },
+    {
+      name: 'enhancements',
+      options: [
+        {codename: 'fixedtoolbar'},
+        {codename: 'redirect'},
+        {codename: 'history'},
+        {codename: 'loaddrafts', experimental: true},
+        {codename: 'increasecontrast'},
+        {codename: 'stickysidebarheaders'},
+        {codename: 'ccdarktheme'},
+        {codename: 'ccforcehidedrawer'},
+        {codename: 'ccdragndropfix', customHTML: '<div id="dragndrop-wrapper" class="option" hidden><input type="checkbox" id="ccdragndropfix"> <label for="ccdragndropfix" data-i18n="ccdragndropfix"></label></div>'},
+        {codename: 'batchlock'},
+        {codename: 'enhancedannouncementsdot'},
+        {codename: 'repositionexpandthread', experimental: true},
+      ],
+    },
+    {
+      name: 'profileindicator_header',
+      options: [
+        {codename: 'profileindicator'},
+        {codename: 'profileindicatoralt'},
+      ],
+      footerHTML: '<div class="option"><a id="profileIndicatorMoreInfo" target="_blank" rel="noreferrer noopener" data-i18n="profileindicator_moreinfo"></a></div>',
+    },
+  ],
+}
diff --git a/src/static/options/options.html b/src/static/options/options.html
index 91a8205..8dde2a4 100644
--- a/src/static/options/options.html
+++ b/src/static/options/options.html
@@ -40,26 +40,7 @@
       </div>
       <form>
         <div class="kill-switch-text" id="kill-switch-warning" hidden data-i18n="killswitchwarning"></div>
-        <div class="option"><input type="checkbox" id="list"> <label for="list" data-i18n="list"></label></div>
-        <div class="option"><input type="checkbox" id="thread"> <label for="thread" data-i18n="thread"></label></div>
-        <div class="option"><input type="checkbox" id="threadall"> <label for="threadall" data-i18n="threadall"></label></div>
-        <h4 data-i18n="enhancements"></h4>
-        <div class="option"><input type="checkbox" id="fixedtoolbar"> <label for="fixedtoolbar" data-i18n="fixedtoolbar"></label></div>
-        <div class="option"><input type="checkbox" id="redirect"> <label for="redirect" data-i18n="redirect"></label></div>
-        <div class="option"><input type="checkbox" id="history"> <label for="history" data-i18n="history"></label></div>
-        <div class="option"><input type="checkbox" id="loaddrafts"> <label for="loaddrafts" data-i18n="loaddrafts"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
-        <div class="option"><input type="checkbox" id="increasecontrast"> <label for="increasecontrast" data-i18n="increasecontrast"></label></div>
-        <div class="option"><input type="checkbox" id="stickysidebarheaders"> <label for="stickysidebarheaders" data-i18n="stickysidebarheaders"></label></div>
-        <div class="option"><input type="checkbox" id="ccdarktheme"> <label for="ccdarktheme" data-i18n="ccdarktheme"></label></div>
-        <div class="option"><input type="checkbox" id="ccforcehidedrawer"> <label for="ccforcehidedrawer" data-i18n="ccforcehidedrawer"></label></div>
-        <div id="dragndrop-wrapper" class="option" hidden><input type="checkbox" id="ccdragndropfix"> <label for="ccdragndropfix" data-i18n="ccdragndropfix"></label></div>
-        <div class="option"><input type="checkbox" id="batchlock"> <label for="batchlock" data-i18n="batchlock"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
-        <div class="option"><input type="checkbox" id="enhancedannouncementsdot"> <label for="enhancedannouncementsdot" data-i18n="enhancedannouncementsdot"></label></div>
-        <div class="option"><input type="checkbox" id="repositionexpandthread"> <label for="repositionexpandthread" data-i18n="repositionexpandthread"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
-        <h4 data-i18n="profileindicator_header"></h4>
-        <div class="option"><input type="checkbox" id="profileindicator"> <label for="profileindicator" data-i18n="profileindicator"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
-        <div class="option"><input type="checkbox" id="profileindicatoralt"> <label for="profileindicatoralt" data-i18n="profileindicatoralt"></label> <span class="experimental-label" data-i18n="experimental_label"></span></div>
-        <div class="option"><a id="profileIndicatorMoreInfo" target="_blank" rel="noreferrer noopener" data-i18n="profileindicator_moreinfo"></a></div>
+        <div id="options-container"></div>
         <div class="actions"><button id="save" data-i18n="save"></button></div>
       </form>
       <div id="save-indicator"></div>