Add flattenthreads experiment

This experiment allows users to flatten the replies in threads, so they
are shown linearly in a chronological way instead of nested.

When the option is enabled, a switch is added to the thread page which
lets the user switch between flattening replies and not flattening them.

Some UI is still missing (see the design document[1]).

[1]: https://docs.google.com/document/d/1P-HanTHxaOFF_FHh0uSv0GIhG1dxWTJTGoT6VPjjvY0/edit

Bug: twpowertools:153
Change-Id: I43f94442cadc12b752700f0e8d974522be621d3e
diff --git a/src/contentScripts/communityConsole/threadToolbar/threadToolbar.js b/src/contentScripts/communityConsole/threadToolbar/threadToolbar.js
new file mode 100644
index 0000000..6344e3c
--- /dev/null
+++ b/src/contentScripts/communityConsole/threadToolbar/threadToolbar.js
@@ -0,0 +1,57 @@
+import {getOptions} from '../../../common/optionsUtils.js';
+import {softRefreshView} from '../utils/common.js';
+
+import * as consts from './constants.js';
+
+export default class ThreadToolbar {
+  constructor() {
+    this.getOptions().then(options => {
+      this.updateBodyClasses(options);
+    });
+  }
+
+  updateBodyClasses(options) {
+    if (this.shouldSeeToolbar(options))
+      document.body.classList.add('TWPT-threadtoolbar-shown');
+    else
+      document.body.classList.remove('TWPT-threadtoolbar-shown');
+
+    if (options.flattenthreads && options.flattenthreads_switch_enabled)
+      document.body.classList.add('TWPT-flattenthreads-enabled');
+    else
+      document.body.classList.remove('TWPT-flattenthreads-enabled');
+  }
+
+  shouldSeeToolbar(options) {
+    return Object.values(options).some(option => !!option);
+  }
+
+  getOptions() {
+    return getOptions(['flattenthreads', 'flattenthreads_switch_enabled']);
+  }
+
+  inject(node, options) {
+    const toolbar = document.createElement('twpt-thread-toolbar-inject');
+    toolbar.setAttribute('options', JSON.stringify(options));
+    toolbar.addEventListener(consts.kEventFlattenThreadsUpdated, e => {
+      const enabled = e.detail?.enabled;
+      if (typeof enabled != 'boolean') return;
+      chrome.storage.sync.set({flattenthreads_switch_enabled: enabled}, _ => {
+        softRefreshView();
+      });
+    });
+    node.parentElement.insertBefore(toolbar, node);
+  }
+
+  injectIfApplicable(node) {
+    this.getOptions().then(options => {
+      this.updateBodyClasses(options);
+      if (!this.shouldSeeToolbar(options)) return;
+      return this.inject(node, options);
+    });
+  }
+
+  shouldInject(node) {
+    return node.matches(consts.kRepliesSectionSelector);
+  }
+}