Added option to duplicate multiple threads at once
diff --git a/_locales/ca/messages.json b/_locales/ca/messages.json
index d44063b..64ad764 100644
--- a/_locales/ca/messages.json
+++ b/_locales/ca/messages.json
@@ -43,6 +43,10 @@
     "message": "Mostra un enllaç \"historial de publicacions\" als perfils d'usuari.",
     "description": "Feature checkbox in the options page"
   },
+  "options_batchduplicate": {
+    "message": "Afegeix l'opció per duplicar diversos fils alhora a la llista de fils de la Consola de la Comunitat.",
+    "description": "Feature checkbox in the options page"
+  },
   "options_save": {
     "message": "Desa",
     "description": "Button in the options page to save the settings"
@@ -54,5 +58,29 @@
   "inject_previousposts": {
     "message": "Historial de publicacions",
     "description": "Link shown in a user profile (in TW and the Community Console) which points to a search showing the user's posts and messages"
+  },
+  "inject_duplicatebtn": {
+    "message": "Duplicar fils",
+    "description": "Tooltip of the 'duplicate treads' icon shown when selecting multiple threads in the Community Console."
+  },
+  "inject_duplicateinto": {
+    "message": "Duplicar a:",
+    "description": "Label for the text field in which the user has to enter the thread they want to duplicate into."
+  },
+  "inject_duplicateinto_placeholder": {
+    "message": "Introdueix la URL de la Consola de la Comunitat",
+    "description": "Placeholder for the URL field."
+  },
+  "inject_duplicate_cancel": {
+    "message": "Cancel·lar",
+    "description": "Button to cancel the duplication process."
+  },
+  "inject_duplicate_confirm": {
+    "message": "Duplicar",
+    "description": "Button to duplicate all the threads."
+  },
+  "inject_duplicate_progress": {
+    "message": "Progrés",
+    "description": "Title for a progress bar which indicates how many threads were duplicated."
   }
 }
\ No newline at end of file
diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 2592504..ed227e3 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -31,8 +31,8 @@
     "message": "Redirect all threads opened in TW to the Community Console.",
     "description": "Feature checkbox in the options page"
   },
-  "options_loaddrafts":{
-    "message": "Activate the <code class=\"help\" title=\"This flag allows the Community Console to load a previously autosaved reply when loading a thread and clicking the reply button.\">enableLoadingDraftMessages</code> Community Console flag.",
+  "options_loaddrafts": {
+    "message": "Activate the <code class=\"help\" title=\"This flag allows the Community Console to load a previously autosaved reply when loading a thread and clicking the reply button.\">enableLoadingDraftMessages<\/code> Community Console flag.",
     "description": "Feature checkbox in the options page"
   },
   "options_experimental_label": {
@@ -43,6 +43,10 @@
     "message": "Show a \"previous posts\" link in user profiles.",
     "description": "Feature checkbox in the options page"
   },
+  "options_batchduplicate": {
+    "message": "Add the option to duplicate multiple threads at the same time from the Community Console thread list.",
+    "description": "Feature checkbox in the options page"
+  },
   "options_save": {
     "message": "Save",
     "description": "Button in the options page to save the settings"
@@ -54,5 +58,29 @@
   "inject_previousposts": {
     "message": "Previous posts",
     "description": "Link shown in a user profile (in TW and the Community Console) which points to a search showing the user's posts and messages"
+  },
+  "inject_duplicatebtn": {
+    "message": "Mark as duplicate",
+    "description": "Tooltip of the 'duplicate treads' icon shown when selecting multiple threads in the Community Console."
+  },
+  "inject_duplicateinto": {
+    "message": "Duplicate into:",
+    "description": "Label for the text field in which the user has to enter the thread they want to duplicate into."
+  },
+  "inject_duplicateinto_placeholder": {
+    "message": "Enter the Community Console URL",
+    "description": "Placeholder for the URL field."
+  },
+  "inject_duplicate_cancel": {
+    "message": "Cancel",
+    "description": "Button to cancel the duplication process."
+  },
+  "inject_duplicate_confirm": {
+    "message": "Duplicate",
+    "description": "Button to duplicate all the threads."
+  },
+  "inject_duplicate_progress": {
+    "message": "Progress",
+    "description": "Title for a progress bar which indicates how many threads were duplicated."
   }
-}
+}
\ No newline at end of file
diff --git a/_locales/es/messages.json b/_locales/es/messages.json
index 97adb10..a816b45 100644
--- a/_locales/es/messages.json
+++ b/_locales/es/messages.json
@@ -43,6 +43,10 @@
     "message": "Muestra un enlace \"historial de publicaciones\" en los perfiles de usuario.",
     "description": "Feature checkbox in the options page"
   },
+  "options_batchduplicate": {
+    "message": "Añade la opción para duplicar varios hilos a la vez en la lista de hilos de la Consola de la Comunidad.",
+    "description": "Feature checkbox in the options page"
+  },
   "options_save": {
     "message": "Guardar",
     "description": "Button in the options page to save the settings"
@@ -54,5 +58,29 @@
   "inject_previousposts": {
     "message": "Historial de publicaciones",
     "description": "Link shown in a user profile (in TW and the Community Console) which points to a search showing the user's posts and messages"
+  },
+  "inject_duplicatebtn": {
+    "message": "Duplicar hilos",
+    "description": "Tooltip of the 'duplicate treads' icon shown when selecting multiple threads in the Community Console."
+  },
+  "inject_duplicateinto": {
+    "message": "Duplicar a:",
+    "description": "Label for the text field in which the user has to enter the thread they want to duplicate into."
+  },
+  "inject_duplicateinto_placeholder": {
+    "message": "Introduce la URL de la Consola de la Comunidad",
+    "description": "Placeholder for the URL field."
+  },
+  "inject_duplicate_cancel": {
+    "message": "Cancelar",
+    "description": "Button to cancel the duplication process."
+  },
+  "inject_duplicate_confirm": {
+    "message": "Duplicar",
+    "description": "Button to duplicate all the threads."
+  },
+  "inject_duplicate_progress": {
+    "message": "Progreso",
+    "description": "Title for a progress bar which indicates how many threads were duplicated."
   }
 }
diff --git a/background.js b/background.js
index d518431..d8e93ff 100644
--- a/background.js
+++ b/background.js
@@ -9,7 +9,8 @@
   "fixedtoolbar": false,
   "redirect": false,
   "history": false,
-  "loaddrafts": false
+  "loaddrafts": false,
+  "batchduplicate": false
 };
 
 function cleanUpOptions() {
diff --git a/console_inject.js b/console_inject.js
index 7c0d339..3e74266 100644
--- a/console_inject.js
+++ b/console_inject.js
@@ -1,5 +1,19 @@
 var mutationObserver, intersectionObserver, options;
 
+function parseUrl(url) {
+  var forum_a = url.match(/forum\/([0-9]+)/i);
+  var thread_a = url.match(/thread\/([0-9]+)/i);
+
+  if (forum_a === null || thread_a === null) {
+    return false;
+  }
+
+  return {
+    "forum": forum_a[1],
+    "thread": thread_a[1]
+  };
+}
+
 function mutationCallback(mutationList, observer) {
   mutationList.forEach((mutation) => {
     if (mutation.type == "childList") {
@@ -28,6 +42,46 @@
               node.querySelector(".main-card").appendChild(link);
             }
           }
+
+          if (options.batchduplicate && ("tagName" in node) && node.tagName == "MATERIAL-BUTTON" && node.getAttribute("debugid") !== null && (node.getAttribute("debugid") == "mark-read-button" || node.getAttribute("debugid") == "mark-unread-button") && ("parentNode" in node) && document.querySelector("[debugid=\"batchduplicate\"]") === null && node.parentNode !== null && ("parentNode" in node.parentNode) && node.parentNode.parentNode !== null && ("tagName" in node.parentNode.parentNode) && node.parentNode.parentNode.tagName == "EC-BULK-ACTIONS") {
+            var clone = node.cloneNode(true);
+            clone.setAttribute("debugid", "batchduplicate");
+            clone.setAttribute("title", chrome.i18n.getMessage("inject_duplicatebtn"));
+            clone.querySelector("material-icon").setAttribute("icon", "control_point_duplicate");
+            clone.querySelector("i.material-icon-i").innerText = "control_point_duplicate";
+            node.parentNode.prepend(clone);
+            clone.addEventListener("click", function() {
+              var modal = document.querySelector(".pane[pane-id=\"default-1\"]");
+              modal.classList.add("visible");
+              modal.style.display = "block";
+              modal.innerHTML = '<material-dialog role="dialog" aria-modal="true" style="display: block!important; width: 600px; max-width: 100%; margin: 8px auto; padding: 16px 0; background: white; box-shadow: 0 24px 38px 3px rgba(0,0,0,.14), 0 9px 46px 8px rgba(0,0,0,.12), 0 11px 15px -7px rgba(0,0,0,.2);"><header role="presentation" style="padding: 24px 24px 0; width: 100%; box-sizing: border-box;"><div style="color: #202124; font-family: \'Google Sans\',sans-serif; font-size: 18px; font-weight: 400; line-height: 24px; margin-bottom: 4px; text-align: center;">'+chrome.i18n.getMessage("inject_duplicatebtn")+'</div></header><main role="presentation" style="font-size: 13px; font-weight: 400; color: rgba(0,0,0,.87); overflow: auto; padding: 0 24px;"><p><label for="duplicate_thread">'+chrome.i18n.getMessage("inject_duplicateinto")+' </label><input type="url" id="infinitegforums_duplicateinto" placeholder="'+chrome.i18n.getMessage("inject_duplicateinto_placeholder")+'" style="width: 400px;"></p></main><footer role="presentation" style="padding: 0 24px;"><material-button role="button" style="display: inline-block; float: right; color: #1a73e8; height: 36px; min-width: 64px; margin: 0 4px; cursor: pointer;" id="infinitegforums_duplicate"><div class="content" style="line-height: 36px; text-align: center;">'+chrome.i18n.getMessage("inject_duplicate_confirm")+'</div></material-button><material-button role="button" style="display: inline-block; float: right; color: #1a73e8; height: 36px; min-width: 64px; margin: 0 4px; cursor: pointer;" id="infinitegforums_cancel"><div class="content" style="line-height: 36px; text-align: center;">'+chrome.i18n.getMessage("inject_duplicate_cancel")+'</div></material-button><div style="clear: both;"></div></footer></material-dialog>';
+              modal.querySelector("#infinitegforums_cancel").addEventListener("click", function() {
+                var modal = document.querySelector(".pane[pane-id=\"default-1\"]");
+                modal.classList.remove("visible");
+                modal.style.display = "none";
+                modal.innerHTML = "";
+              });
+              modal.querySelector("#infinitegforums_duplicate").addEventListener("click", function() {
+                var modal = document.querySelector(".pane[pane-id=\"default-1\"]");
+                var duplicateinto = parseUrl(modal.querySelector("#infinitegforums_duplicateinto").value);
+                if (duplicateinto === false) {
+                  return;
+                }
+                console.log(duplicateinto);
+                modal.querySelector("footer").style.display = "none";
+                var checkboxes = document.querySelectorAll(".thread-group material-checkbox[aria-checked=\"true\"]");
+                modal.querySelector("main").innerHTML = '<p style="text-align: center;">'+chrome.i18n.getMessage("inject_duplicate_progress")+':<br><progress id="infinitegforums_progress" max="'+checkboxes.length+'" value="0"></progress></p>';
+                checkboxes.forEach(checkbox => {
+                  var thread = parseUrl(checkbox.parentNode.parentNode.querySelector("a.header-content").href);
+                  console.log(thread);
+                  var script = document.createElement('script');
+                  script.textContent = 'fetch("https://support.google.com/s/community/api/MarkDuplicateThread", {"credentials":"include","headers":{"content-type":"text/plain; charset=utf-8"},"body":\'{\"1\":\"'+thread.forum+'\",\"2\":\"'+thread.thread+'\",\"3\":{\"2\":{\"1\":\"'+duplicateinto.forum+'\",\"2\":\"'+duplicateinto.thread+'\"}}}\',"method":"POST","mode":"cors"}).then(_ => { var progress = document.querySelector("#infinitegforums_progress"); progress.value = parseInt(progress.value) + 1; if (progress.value == progress.getAttribute("max")) { location.reload(); } });';
+                  document.head.appendChild(script);
+                  script.remove();
+                });
+              });
+            });
+          }
         }
       });
     }
diff --git a/options.html b/options.html
index 6b120c5..83e5958 100644
--- a/options.html
+++ b/options.html
@@ -16,7 +16,8 @@
       <input type="checkbox" id="fixedtoolbar"> <label for="fixedtoolbar" data-i18n="fixedtoolbar"></label><br>
       <input type="checkbox" id="redirect"> <label for="redirect" data-i18n="redirect"></label> <span style="color: gray;" data-i18n="experimental_label"></span><br>
       <input type="checkbox" id="history"> <label for="history" data-i18n="history"></label><br>
-      <input type="checkbox" id="loaddrafts"> <label for="loaddrafts" data-i18n="loaddrafts"></label> <span style="color: gray;" data-i18n="experimental_label"></span>
+      <input type="checkbox" id="loaddrafts"> <label for="loaddrafts" data-i18n="loaddrafts"></label> <span style="color: gray;" data-i18n="experimental_label"></span><br>
+      <input type="checkbox" id="batchduplicate"> <label for="batchduplicate" data-i18n="batchduplicate"></label> <span style="color: gray;" data-i18n="experimental_label"></span>
     </p>
     <p style="text-align: center;"><button id="save" data-i18n="save"></button></p>
     <script src="options.js"></script>
diff --git a/options.js b/options.js
index 026776c..b93ea86 100644
--- a/options.js
+++ b/options.js
@@ -9,7 +9,8 @@
   "fixedtoolbar": false,
   "redirect": false,
   "history": false,
-  "loaddrafts": false
+  "loaddrafts": false,
+  "batchduplicate": false
 };
 
 function cleanUpOptions(options) {
@@ -41,7 +42,7 @@
 }
 
 function i18n() {
-  var messages = ["list", "thread", "threadall", "enhancements", "fixedtoolbar", "redirect", "loaddrafts", "experimental_label", "history", "save"];
+  var messages = ["list", "thread", "threadall", "enhancements", "fixedtoolbar", "redirect", "loaddrafts", "experimental_label", "history", "batchduplicate", "save"];
 
   messages.forEach(function(msg) {
     document.querySelectorAll("[data-i18n=\""+msg+"\"]").forEach(el => el.innerHTML = chrome.i18n.getMessage("options_"+msg));