Rule34 Save Search

Save search queries

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         Rule34 Save Search
// @description  Save search queries
// @namespace    User_314159_R34SS
// @version      0.55
// @author       User_314159
// @license      MIT
// @match        https://rule34.xxx/index.php?page=post&s=list*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=rule34.xxx
// @grant        none
// ==/UserScript==

// display logic
function get_search() {
    let contents = document.getElementById("content");
    if (contents.getElementsByClassName("image-list")[0] != undefined) {
        let search_bar = contents.querySelector('input[name="tags"]');
        if (search_bar.value != "") {
            return [true, search_bar.value];
        }
    }
    return [false, ""];
}

//io
function getSavedSearches() { // gets the localstorage array and parses it into an actual array, creates a new one if there is no key in localStorage
    let artists;
    if (localStorage.getItem('rule34SavedSearches') === null) {
        console.log('generated new saved array');
        artists = [];
        saveSearches(artists);
    }
    else {
        artists = JSON.parse(localStorage.getItem('rule34SavedSearches'));
    }
    return artists;
}

function deleteSearch(search) { // deletes the specified search out of the array and saves it
    let artists = getSavedSearches();
    let target = artists.indexOf(search);
    artists.splice(target, 1);
    saveSearches(artists);
    console.log('Deleted ' + search);
}

function saveSearches(searches) { // saves an updated version of the searches to local storage
    localStorage.setItem('rule34SavedSearches', JSON.stringify(searches));
}
function addSearch(search) {
    let searches = getSavedSearches();
    search = sortStr(search);
    if(searches.includes(search)) {
        return;
    }
    searches.push(search);
    searches.sort();
    saveSearches(searches);
}

function exportSearches() { // gets the local storage and logs it to the console as a string
    let searches = localStorage.getItem('rule34SavedSearches');
    console.log(searches); // Don't need to stringify here the localstorage got it stringified
    navigator.clipboard.writeText(searches);
    alert("Copied searches to clipboard");
}

function importSearches() { // takes an inputted array string and overwrites the local storage with it
    let input = window.prompt("Enter previously exported searches"); // window.prompt seems to return a double-escaped string
    let data = JSON.parse(input); // due to this, we need to de-escape twice to get the actual json
    let current_searches = getSavedSearches();
    for(let index = 0; index < data.length; index++) {
        if(current_searches.includes(data[index])) {
            continue;
        } else {
            current_searches.push(data[index]);
            console.log("New search: " + data[index]);
        }
    }
    localStorage.setItem('rule34SavedSearches', JSON.stringify(current_searches));
}

//sorting
function sortStr(string) {
    //this btw relies on the fact that rule34 only has lowercase tags cuz it sorts all uppercase before the lowercase letters (i.e. I, X, a, b, ...)
    let split_str = string.split(' ');
    let sortable = [];
    let non_sortable = [];
    let bracketed = false;
    for(let i = 0; i < split_str.length; i++) { // this iterates over each token (thing separated by a space) and if its contained in brackets, it won't get sorted
        switch (split_str[i]) {
            case "(":
                bracketed = true;
                non_sortable.push(split_str[i]);
                break;
            case ")":
                bracketed = false;
                non_sortable.push(split_str[i]);
                break;
            default:
                if(bracketed){
                    non_sortable.push(split_str[i]);
                } else {
                    sortable.push(split_str[i]);
                }
                break;
        }
    }
    return sortable.sort().join(' ') + ' ' + non_sortable.join(' ');
}
// button logic
function createButton(name, width) {
    let button = document.createElement("button");
    button.innerHTML = name;
    button.style.width = width;
    return button;
}
function createA(target) {
    let href = createLink(target);
    let a = document.createElement("a");
    a.href = href;
    a.innerHTML = target;
    a.style.overflow = "hidden";
    return a;
}
function createLink(target) {
    return "index.php?page=post&s=list&tags=" + target + "&is_saved_search";
}
function createAddButton(search) {
    let button = createButton("Save search", "100%");

    button.addEventListener("click", function() {
        addSearch(search);
        //window.location.href = createLink(search);
    });
    return button;
}
function createDeleteButton(search) {
    let button = createButton("Delete search", "100%");

    button.addEventListener("click", function() {
        deleteSearch(search);
        window.location.href = createLink(search).replace("&is_saved_search", "");
    });
    return button;
}
function createExportButton() {
    let button = createButton("Export", "100%");

    button.addEventListener("click", function() {
        exportSearches();
    });
    return button;
}
function createImportButton() {
    let button = createButton("Import", "100%");

    button.addEventListener("click", function() {
        importSearches();
    });
    return button;
}
function createDeleteAllButton() {
    let button = createButton("Delete all", "100%");

    button.addEventListener("click", function() {
        if(window.confirm("WARNING: This will delete all saved searches and cannot be undone")) {
            if(window.confirm("Are you sure?")) {
                localStorage.removeItem("rule34SavedSearches");
                console.log("removed localStorage 'rule34SavedSearches' key");
                return;
            }
        }
        console.log("Reset was aborted");
    });
    return button;
}

function generateSearchList() {
    let collapsable = document.createElement("details");
    let collapsable_name = document.createElement("summary");
    collapsable_name.innerHTML = "Saved";
    collapsable.style.maxHeight = "50vh";
    collapsable.style.overflow = "hidden scroll";
    collapsable.style.textOverflow = "ellipsis";
    collapsable.style.whiteSpace = "nowrap";
    collapsable.style.scrollbarWidth = "thin";
    collapsable.appendChild(collapsable_name);

    let searches = getSavedSearches();
    let linebreak = document.createElement("br");
    for (let i = 0; i < searches.length; i++) {
        collapsable.appendChild(createA(searches[i]));
        collapsable.appendChild(linebreak.cloneNode());
    }
    return collapsable;
}
function generateDevOptions() {
    let collapsable = document.createElement("details");
    let collapsable_name = document.createElement("summary");
    collapsable_name.innerHTML = "Dev Options";
    collapsable.style.overflow = "hidden scroll";
    collapsable.style.textOverflow = "ellipsis";
    collapsable.style.whiteSpace = "nowrap";
    collapsable.style.scrollbarWidth = "thin";
    collapsable.appendChild(collapsable_name);

    let linebreak = document.createElement("br");
    collapsable.appendChild(createImportButton());
    collapsable.appendChild(linebreak.cloneNode());
    collapsable.appendChild(createExportButton());
    collapsable.appendChild(linebreak.cloneNode());
    collapsable.appendChild(createDeleteAllButton());
    collapsable.appendChild(linebreak.cloneNode());
    return collapsable;
}

(function() {
    'use strict';
    let [search_not_empty, search_value] = get_search();
    let search_bar = document.getElementsByClassName("tag-search")[0];

    let collapsable = generateSearchList();

    if (search_not_empty && !window.location.href.includes("&is_saved_search")) {
        let addButton = createAddButton(search_value);
        search_bar.append(addButton);
    }
    if (window.location.href.includes("&is_saved_search")) {
        let deleteButton = createDeleteButton(search_value);
        search_bar.append(deleteButton);
    }

    let tag_sidebar = document.getElementById("tag-sidebar");
    tag_sidebar.append(generateDevOptions());
    search_bar.append(collapsable);
})();