--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hugo/data/pluginTypes.json Wed Jan 01 22:12:52 2020 +0000
@@ -0,0 +1,50 @@
+ "type": "Security and Privacy", + "en": "Security and Privacy" + "type": "Notifications", + "type": "Profile and Status Updates", + "en": "Profile and Status Updates" + "type": "Interface Tweaks", + "en": "Interface Tweaks" + "type": "Accounts and Logs", + "en": "Accounts and Logs" + "type": "Miscellaneous", \ No newline at end of file
--- a/hugo/layouts/shortcodes/plugintable.html Wed Jan 01 03:34:44 2020 +0000
+++ b/hugo/layouts/shortcodes/plugintable.html Wed Jan 01 22:12:52 2020 +0000
@@ -20,7 +20,16 @@
<li>{{ template "community" }} {{ T "Community-Info" }}</li>
-<div id="plugin-filters">
+{{/* No point in showing search filters if there's no JS */}} + #plugin-filters, #search-link-wrap { +<div id="plugin-filters" class="hidden"> <div id="publisher-selector">
<label style="display: inline-block;">
<input type="radio" id="all" name="publisher" style="margin-right: 0; margin-left: 0.75rem;" value="all" checked>
@@ -43,7 +52,14 @@
<input type="text" id="plugin-filter-search" placeholder="{{ T "Search-placeholder" }}" />
- <div id="plugin-selector"></div>
+ <div id="plugin-selector"> + {{ range .Site.Data.pluginTypes }} + <label class="pidgin-plugin-filter-checkbox"> + <input type="checkbox" data-type="{{ .type }}"> <table id="plugin-table">
@@ -70,6 +86,7 @@
<td class="plugin-publisher">
{{ if eq true .isTrusted }}
@@ -94,10 +111,16 @@
+<p id="search-link-wrap" class="hidden"> + Share this search:<br /> + <a id="search-link"></a> <script src="/js/plugin-table.js"></script>
@@ -120,4 +143,9 @@
--- a/hugo/static/js/plugin-table.js Wed Jan 01 03:34:44 2020 +0000
+++ b/hugo/static/js/plugin-table.js Wed Jan 01 22:12:52 2020 +0000
@@ -1,96 +1,158 @@
-document.addEventListener("DOMContentLoaded", () => {
- const selectorsContainer = document.getElementById("plugin-selector");
- const search = document.getElementById("plugin-filter-search");
- search.addEventListener("input", debounce(1000 * 0.5, filterRows));
- const [, ...rows] = document
- .getElementById("plugin-table")
- .getElementsByTagName("tr");
- const typeFilter = new Set();
- const rowinfo = rows.map(elem => {
- const type = elem.dataset.type;
- if (!types[type]) types[type] = [];
+ * Array representing each plugin row + * String Search element + * @type {HTMLInputElement} - types[type].push(elem);
+var url = new URL(window.location); +var params = url.searchParams; + * @type {"All" | "Trusted" | "Community"} + publisher: params.get("publisher") || "all", + query: params.get("query") || "", + * What type of plugin has been selected +document.addEventListener("DOMContentLoaded", function() { + // Iterate through rows and populate + pluginRow = Array.from($("#plugin-table>tbody>tr")).map(function(elem) { + var contents = function(selectorQuery) { - head: getContents("plugin-heading", elem),
- info: getContents("plugin-info", elem),
- repo: getContents("plugin-repo", elem),
+ head: contents(".plugin-heading"), + info: contents(".plugin-info"), + repo: contents(".plugin-repo"), isTrusted: elem.getAttribute("isTrusted") == "true",
- .getElementById("publisher-selector")
- .addEventListener("click", filterRows);
+ // Add click handler to All|Trusted|Community selector + $("#publisher-selector").click(updateFilters); + searchTextBox = document.querySelector("#plugin-filter-search"); + searchTextBox.addEventListener( + debounce(1000 * 0.5, updateFilters) - Object.keys(types).forEach(type => {
- const label = createAndAppend("label", selectorsContainer);
- label.classList.add("pidgin-plugin-filter-checkbox");
- const input = createAndAppend("input", label);
- input.type = "checkbox";
- input.dataset.type = type;
- input.addEventListener("click", clickEvent);
- label.appendChild(document.createTextNode(type));
+ // Set up plugin type selector interface + document.querySelectorAll("#plugin-selector>label>input") + ).forEach(function(elem) { + elem.addEventListener("click", pluginTypeSelectorClickEvent); - /////////////////////////
+ $("#plugin-filters,#search-link-wrap").removeClass("hidden"); + * Event handler for plugin type checkboxes + * @param {MouseEvent} event + * @param {HTMLInputElement} event.target +function pluginTypeSelectorClickEvent(target) { + searchState.type[target.checked ? "add" : "delete"](target.dataset.type); - function getContents(className, elem) {
- .getElementsByClassName(className)[0]
- .textContent.toLowerCase();
+ * Visually filters Plugins by toggling visibility +function updateFilters() { + searchState.query = (searchTextBox.value || "").toLowerCase(); + searchState.publisher = $('input[name="publisher"]:checked')[0].value; - function clickEvent({ target }) {
- typeFilter[target.checked ? "add" : "delete"](target.dataset.type);
+ pluginRow.forEach(function(row) { + if (shouldFilter(row)) { + row.elem.classList.remove("hidden"); + row.elem.classList.add("hidden"); + // Updates query string + params.set("publisher", searchState.publisher); + params.set("query", searchState.query); + params.set("type", Array.from(searchState.type).join(",")); + var urlString = String(url); + if (history.pushState) { + history.pushState({ path: urlString }, "", urlString); - const publisherRadioQuery = 'input[name="publisher"]:checked';
- function filterRows() {
- const str = (search.value || "").toLowerCase();
- const publisherSelector = document.querySelector(publisherRadioQuery)
- rowinfo.forEach(row => {
- if (shouldFilter(row, publisherSelector, str))
- row.elem.classList.remove("filter-hide");
- else row.elem.classList.add("filter-hide");
+ var link = $("#search-link"); + link.attr("href", urlString); - function shouldFilter(row, publisher, str) {
- (!typeFilter.size || typeFilter.has(row.elem.dataset.type)) &&
- (publisher == "pidgin" && row.isTrusted) ||
- (publisher == "community" && !row.isTrusted)) &&
- row.head.includes(str) ||
- row.info.includes(str) ||
- row.repo.includes(str))
+ * Decides if a plugin row should be filterd +function shouldFilter(row) { + (!searchState.type.size || + searchState.type.has(row.elem.dataset.type)) && + (searchState.publisher == "all" || + (searchState.publisher == "pidgin" && row.isTrusted) || + (searchState.publisher == "community" && !row.isTrusted)) && + row.head.includes(searchState.query) || + row.info.includes(searchState.query) || + row.repo.includes(searchState.query)) - function debounce(t, fn) {
- return function(...args) {
- timer = setTimeout(() => fn(...args), t);
- function createAndAppend(tag, elem = document) {
- return elem.appendChild(document.createElement(tag));
+ * Simple Debounce function + * @param {function} callback +function debounce(time, callback) { + return function(...args) { + timer = setTimeout(function() { --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/plugin-map.js Wed Jan 01 22:12:52 2020 +0000
@@ -0,0 +1,20 @@
+const fs = require("fs"); +const data = require("../hugo/data/plugins.json"); +const types = new Set(); +data.forEach(plugin => { + types.add(plugin.type); +const newData = Array.from(types).map(type => ({ + "../hugo/data/pluginTypes.json", + JSON.stringify(newData, null, "\t")