From 7f5c1266eab0f10638b436f068ffd203aaedfefa Mon Sep 17 00:00:00 2001
From: Brandon Rodriguez <brodriguez8774@gmail.com>
Date: Wed, 19 Oct 2022 01:12:53 -0400
Subject: [PATCH] Commit current wikitab logic to project

---
 src/wikitabs.css |  41 +++++++
 src/wikitabs.js  | 282 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 323 insertions(+)
 create mode 100644 src/wikitabs.css
 create mode 100644 src/wikitabs.js

diff --git a/src/wikitabs.css b/src/wikitabs.css
new file mode 100644
index 0000000..48fca1e
--- /dev/null
+++ b/src/wikitabs.css
@@ -0,0 +1,41 @@
+/**
+ * Wikitabs CSS Stylings
+ */
+
+
+.wikitabs-tab-container {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  margin-top: -10px;
+  margin-bottom: 10px;
+  border-top: #bbb solid 1px;
+  border-bottom: #bbb solid 1px;
+}
+
+
+.wikitabs-tab-container div {
+    display: flex;
+    justify-content: center;
+    padding-top: 10px;
+    padding-bottom: 10px;
+
+    background-color: #f5fcff;
+
+    transition: background-color 0.2s ease;
+}
+
+
+.wikitabs-tab-container div:hover {
+    background-color: white;
+}
+
+
+.wikitabs-tab-element {
+  flex-grow: 1;
+  padding-right: 10px;
+  padding-left: 10px;
+
+  cursor: pointer;
+}
+
diff --git a/src/wikitabs.js b/src/wikitabs.js
new file mode 100644
index 0000000..76a35f9
--- /dev/null
+++ b/src/wikitabs.js
@@ -0,0 +1,282 @@
+/**
+ * Javascript logic for "WikiTabs".
+ * This is what creates the clickable tabbed filters at the top of wiki pages.
+ *
+ * Creates tabs based on H2 headers. Note that at least two matching H2 headers must be found on page.
+ * If no header values are provided as a "wikitabs-template-variables" element, then no tabbing will be generated.
+ * Note that the values are also case-sensitive.
+ *
+ * As an example, somewhere on the wiki page, there needs a HMTL element of the format:
+ *      <div id="wikitabs-template-variables">["H2 Header Value #1", "H2 Header Value #2"]</div>
+ * Where each "H2 Header Value" is replaced by the title of an actual H2 on the page, that is desired to show up as a
+ * tab. There needs to be at minimum two elements. And there can be as many as you want (but more than three to five
+ * elements tends to crowd the tabs).
+ */
+
+
+// Global Variables.
+var wiki_content_container = document.getElementById('mw-content-text').getElementsByClassName("mw-parser-output")[0];
+var wikitabs_header_dict = {
+    "All": [],
+    "Other": [],
+};
+var wikitabs_header_ordering = [];
+var generate_wiki_tab_bool = true;
+
+// Get "tabbable topics" if provided.
+var wiki_tabbable_topics = []
+if (document.getElementById("wikitabs-template-variables")) {
+    // Topics were provided by template.
+    wiki_tabbable_topics = document.getElementById("wikitabs-template-variables").innerHTML;
+
+    // Topics acquired. Element no longer servers a purpose. Remove.
+    document.getElementById("wikitabs-template-variables").remove();
+} else {
+    // Topics were not provided by template. Do not tab.
+    wiki_tabbable_topics =[];
+}
+console.log("wiki_tabbable_topics:");
+console.log(wiki_tabbable_topics);
+
+
+function wiki_tab_generation_main() {
+    // console.log("Starting \"Wiki Tab Generation\" logic.");
+
+    // Populate variables.
+    populate_wiki_tab_variables();
+    generate_wiki_tabs();
+}
+
+
+/**
+ * Loops through all page content elements. All elements are automatically stored into header_dict["all"].
+ *
+ * If element is a H2 header, then a new key is created in header_dict, using the H2 text value.
+ *
+ * If at least one H2 header has been found, then the element is stored under both header_dict["all"] and
+ * header_dict[<header_name>], where <header_name> is the most recently found H2 element.
+ */
+function populate_wiki_tab_variables() {
+    var current_header = "Other";
+
+    // Loop through all content elements. Aka, elements found in within the 'mw-context-tree' id.
+    var page_length = wiki_content_container.childNodes.length;
+    for (var index = 0; index < page_length; index++) {
+        // Save current element for easy reference.
+        var current_element = wiki_content_container.childNodes[index];
+
+        // Check if element is H2 header. If so, create a new key in header_dict, using element's text value.
+        if (current_element.tagName == 'H2') {
+            current_header = current_element.firstChild.innerHTML;
+
+            // Check if item is in "tabbable topics" array.
+            if (wiki_tabbable_topics.includes(current_header)) {
+                // Item is in "tabbable topics" array. We can create a new page tab for this content.
+                // Create new key with header value, and also push to ordering dict to preserve page content ordering.
+                wikitabs_header_dict[current_header] = [];
+                wikitabs_header_ordering.push(current_header);
+            } else {
+                // Item is not in "tabbable topics" array. Put content into "Other" tab.
+                current_header = "Other";
+            }
+        }
+
+        // Add element into "all" key.
+        wikitabs_header_dict["All"].push(wiki_content_container.childNodes[index].cloneNode(true));
+        // Also add element to dictionary, under current found header.
+        wikitabs_header_dict[current_header].push(wiki_content_container.childNodes[index]);
+    }
+
+    // Check if any elements were added to "Other" section. If so, add "Other" to list of dict values.
+    if (wikitabs_header_dict["Other"] != []) {
+        wikitabs_header_ordering.push("Other");
+    }
+
+    // Workaround for weird bug with extra, unecessary <p><br></p> elements at top of page.
+    // Fix for "All" tab.
+    while (wikitabs_header_dict["All"].length > 0 && wikitabs_header_dict["All"][0].outerHTML == "<p><br>\n</p>") {
+        wikitabs_header_dict["All"].shift();
+    }
+    // Also fix for "Other" tab.
+    while (wikitabs_header_dict["Other"].length > 0 && wikitabs_header_dict["Other"][0].outerHTML == "<p><br>\n</p>") {
+        wikitabs_header_dict["Other"].shift();
+    }
+}
+
+
+/**
+ * Generates actual tab elements on page.
+ */
+function generate_wiki_tabs() {
+
+    // Check if we have at least two unique headers to create tabs for.
+    // If so, generate wikitabs html. Otherwise, leave page as is.
+    header_array_clone = wikitabs_header_ordering.slice();
+
+    // We don't want to count the "Other" tab for counting purposes. It may or may not be present.
+    if (header_array_clone.includes("Other")) {
+        var index = header_array_clone.indexOf("Other");
+        header_array_clone.splice(index, 1);
+    }
+
+    // Now that we have an array of only "unique" tab values, check length.
+    if (header_array_clone.length >= 2) {
+        generate_wiki_tab_bool = true;
+    } else {
+        generate_wiki_tab_bool = false;
+    }
+
+    // Only proceed to manipulate page if wiki tabs should be generated.
+    if (generate_wiki_tab_bool) {
+        var page_tabs = document.createElement("div");
+        page_tabs.className = "wikitabs-tab-container";
+        var page_divs = document.createElement("div");
+        page_divs.className = "wikitabs-content-container";
+
+        // Default "All" tab. Will always be present in a wiki tabs page.
+        // Create tab for "All".
+        var all_tab = document.createElement("div");
+        all_tab.id = "wikitabs-all-tab";
+        all_tab.className = "wikitabs-tab-element";
+        all_tab.innerHTML = "All";
+        page_tabs.append(all_tab);
+
+        // Create page content div for "All".
+        var all_div = document.createElement("div");
+        all_div.id = "wikitabs-all-div";
+        all_div.className = "wikitabs-content-element";
+        var all_arr_length = wikitabs_header_dict["All"].length;
+        for (index = 0; index < all_arr_length; index++) {
+            all_div.appendChild(wikitabs_header_dict["All"][index]);
+        }
+        page_divs.append(all_div);
+
+        // In most cases, we will have an automatically generated "Other" tab, too.
+        // Create tab for "Other".
+        var other_tab = document.createElement("div");
+        other_tab.id = "wikitabs-other-tab";
+        other_tab.className = "wikitabs-tab-element";
+        other_tab.innerHTML = "Other";
+
+        // Create page content for "Other".
+        var other_div = document.createElement("div");
+        other_div.id = "wikitabs-other-div";
+        other_div.className = "wikitabs-content-element";
+
+        // Loop through all values present in ordering array to dynamically add all other headers.
+        // Looping through this way ensures we preserve page content ordering.
+        for (var index = 0; index < wikitabs_header_ordering.length; index++) {
+
+            // Check if header item is in "tabbable topics" array.
+            if (wiki_tabbable_topics.includes(wikitabs_header_ordering[index])) {
+                // Item is in "tabbable topics" array. We can create a new page tab for this content.
+                var curr_header = wikitabs_header_ordering[index];
+                var header_slug = slugify_text(curr_header);
+
+                // Create tab for current header.
+                var curr_tab = document.createElement("div");
+                curr_tab.id = "wikitabs-" + header_slug + "-tab";
+                curr_tab.className = "wikitabs-tab-element";
+                curr_tab.innerHTML = curr_header;
+                page_tabs.append(curr_tab);
+
+                // Create page content div for current header.
+                var curr_div = document.createElement("div");
+                curr_div.id = "wikitabs-" + header_slug + "-div";
+                curr_div.className = "wikitabs-content-element";
+                var array_length = wikitabs_header_dict[curr_header].length;
+                for (var inner_index = 0; inner_index < array_length; inner_index++) {
+                    curr_div.appendChild(wikitabs_header_dict[curr_header][inner_index]);
+                }
+                page_divs.append(curr_div);
+
+            } else {
+                // Item is not in "tabbable topics" array. Put content into "Other" tab.
+                var array_length = wikitabs_header_dict["Other"].length;
+                for (var inner_index = 0; inner_index < array_length; inner_index++) {
+                    other_div.appendChild(wikitabs_header_dict["Other"][inner_index]);
+                }
+            }
+        }
+
+        // Check if "other" section was populated at all. If so, append to main wikitab containers.
+        if (other_div.childElementCount > 0) {
+            // Other tab has elements and should exist.
+            page_tabs.append(other_tab);
+            page_divs.append(other_div);
+        } else {
+            // Other tab does not have elements and should not exist.
+            wikitabs_header_ordering.splice("Other", 1);
+        }
+
+        // Make sure nothing is in parent element. Everything should be processed by now.
+        parent_element = wiki_content_container;
+        while (parent_element.firstChild) {
+            parent_element.removeChild(parent_element.firstChild);
+        }
+
+        // Finally, append elements to page itself.
+        wiki_content_container.appendChild(page_tabs);
+        wiki_content_container.appendChild(page_divs);
+
+        // Add "All" value to start of array.
+        //If we have tabs at all, then this is always present and first.
+        wikitabs_header_ordering.unshift("All");
+
+        // Add click event listeners for wikitabs.
+        for (var index = 0; index < wikitabs_header_ordering.length; index++) {
+            // Get tab elements.
+            var current_tab = document.getElementsByClassName('wikitabs-tab-container')[0].childNodes[index];
+
+            // Add click handling to tab elements.
+            current_tab.addEventListener("click", function() {
+                // Get index of clicked tab element.
+                // Do this by cycling back through parent container until we get to first child, and counting each.
+                var element_index = 0;
+                var current_tab = this;
+                while (current_tab.previousSibling != null) {
+                    current_tab = current_tab.previousSibling;
+                    element_index++;
+                }
+
+                // Hide all div tab-sections, except the one clicked.
+                for (var tab_index = 0; tab_index < wikitabs_header_ordering.length; tab_index++) {
+                    // Get parent of content elements to hide/show.
+                    content_container = document.getElementsByClassName('wikitabs-content-container')[0];
+
+                    // Check if same index as clicked. If so, show instead.
+                    if (element_index == tab_index) {
+                        // On clicked element. Set to "block" to display.
+                        content_container.childNodes[tab_index].style.display = "block";
+                    } else {
+                        // On a sibling element. Set to display none to hide.
+                        content_container.childNodes[tab_index].style.display = "none";
+                    }
+                }
+            });
+
+            // Also hide all content, initially. Otherwise we'll have duplicates on page.
+            var tab_content = document.getElementsByClassName('wikitabs-content-container')[0].childNodes[index];
+            tab_content.style.display = "none";
+        }
+
+        // Set "All" content to be visible. Everything else should be hidden by now.
+        document.getElementsByClassName('wikitabs-content-container')[0].childNodes[0].style.display = "block";
+    }
+}
+
+
+/**
+ * Takes given text string and returns equivalent "slug" version.
+ *
+ * Slugs are safe for urls and html id's.
+ * Essentially, all letters are converted to lowercase, and whitespace is converted to dashes.
+ */
+function slugify_text(orig_text) {
+    return orig_text.toLowerCase().replace(/ /g, "-");
+}
+
+
+// Run "Wiki Tab Generation" logic.
+wiki_tab_generation_main();
+
-- 
GitLab