您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds functionality for plaintext list export for backup purposes
当前为
// ==UserScript== // @name Listography Backup // @namespace petracoding // @description Adds functionality for plaintext list export for backup purposes // @version 0.0.1 // @author petracoding // @match https://listography.com/* // @match http://listography.com/* // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js // ==/UserScript== //////////////////////////////////// VARIABLES const pathname = getPathOfUrl(); const userName = getPathOfUrl( document.querySelector(".user-box .title a").href ).substring(1); const userId = document .querySelector(".about img") .getAttribute("src") .replace("/action/user-image?uid=", ""); const indexPath = "/" + userName + "/index"; let popup; let popupoverlay; let currentBatch = 1; let listCount; let listsToBackup = []; let output = ""; //////////////////////////////////// START $(document).ready(function () { // only if the user is on their profile if (document.querySelector(".global-menu .create-list")) { HTML(); CSS(); // start backup if user clicked the backup all link and was redirected to the archive if (pathname == indexPath + "?backup=true") { startBackupAll(); } console.log(":)"); } }); //////////////////////////////////// BACKUP async function startBackupAll() { const listLinksToOpen = document.querySelectorAll(".body_folder .list a"); if (!listLinksToOpen) { showPopup("No lists found.", true); return; } startOutput(); await asyncForEach([...listLinksToOpen], async (link) => { let list = await openListInArchive(link); await editListAndAddToOutput(list); }); finishOutput(); } async function startBackupVisible() { const listSelector = ".list-container"; const listSelectorInArchive = "#list_container .slot"; const listNodes = document.querySelectorAll( listSelector + ", " + listSelectorInArchive ); if (!listNodes || listNodes.length < 1) { showPopup("No visible lists found.", true); return; } listsToBackup = [...listNodes]; startOutput(); await asyncForEach(listsToBackup, async (list) => { await editListAndAddToOutput(list); }); finishOutput(); } function openListInArchive(link) { let listOpenPromise = new Promise(function (resolve, reject) { link.click(); let listId = link.getAttribute("id").replace("list_" + userId + "_", ""); let attempt = 1; let checkIfIsInEditMode = setInterval(function () { if (document.querySelector("#listbox-" + listId + " .menu")) { resolve(document.querySelector("#listbox-" + listId)); clearInterval(checkIfIsInEditMode); } else { if (attempt > 100) { reject("List could not be backed up."); clearInterval(checkIfIsInEditMode); } attempt = attempt + 1; } }, 100); }); listOpenPromise.then( function (list) { return list; }, function (errorMsg) { alert(errorMsg); } ); return listOpenPromise; } function editListAndAddToOutput(list) { let listEditPromise = new Promise(function (resolve, reject) { const editButton = list.querySelector(".menu .item a[href*=edit-list]"); if (!editButton) { reject("List could not be edited."); } editButton.click(); let attempt = 1; let checkIfIsInEditMode = setInterval(function () { if (list.querySelector(".category_editor")) { let listContent = list.querySelector("textarea").innerHTML; list.querySelector(".cancel.button_1_of_3").click(); resolve(listContent); clearInterval(checkIfIsInEditMode); } else { if (attempt > 100) { reject("List could not be backed up."); clearInterval(checkIfIsInEditMode); } attempt = attempt + 1; } }, 100); }); listEditPromise.then( function (listContent) { output += "\n\n\n--------------------------------------------------------\n\n\n"; output += getListOutput(list, listContent); }, function (errorMsg) { alert(errorMsg); } ); return listEditPromise; } //////////////////////////////////// HELPERS function replaceAll(str, whatStr, withStr) { return str.split(whatStr).join(withStr); } function getPathOfUrl(url, tld) { let href; if (!url) { href = window.location.href; } else { href = url; } let ending; if (!tld) { ending = ".com/"; } else { ending = "." + tld + "/"; } return href.substring(href.indexOf(ending) + ending.length - 1); } function startOutput() { document.querySelector("#backup-loading").style.display = "block"; popupoverlay.style.display = "block"; const url = location.href.replace("?backup=true", ""); output = "<h1>Here are your lists:</h1><textarea id='backup-output'>Backup of " + url; } function finishOutput() { document.querySelector("#backup-loading").style.display = "none"; popupoverlay.style.display = "none"; showPopup(output + "</textarea>", true); } function getListOutput(list, listContent) { if (!list || !listContent) return; let listId; if (list.querySelector(".listbox")) { listId = list .querySelector(".listbox") .getAttribute("id") .replace("listbox-", ""); } else { listId = list .querySelector("[id*=listbox-content-slot]") .getAttribute("id") .replace("listbox-content-slot-", ""); } let listLink = "Link: " + list.querySelector(".box-title a").getAttribute("href"); let listTitle = list .querySelector(".box-title a") .innerHTML.replace('<span class="box-subtitle">', "") .replace("</span>", "") .replace(/\s\s+/g, " ") .trim(); let listDates = "created on " + list .querySelector(".dates") .innerHTML.replace("∞", "") .replace("+", "") .replace(" <br>", ", last updated on ") .replace(/\s\s+/g, " ") .trim(); let listCommentCount = list .querySelector(".menu .item:nth-child(3) a") .innerHTML.replace(/\s\s+/g, " ") .trim(); if (listCommentCount == "comment") { listCommentCount = ""; } else { listCommentCount = ", " + listCommentCount; } let listImage = list.querySelector(".icon"); if (listImage) { listImage = "\nIcon: " + listImage.getAttribute("src").replace("&small=1", ""); } else { listImage = ""; } return ( listTitle + "\n" + listLink + "\n(" + listDates + listCommentCount + ")" + listImage + "\n\n" + adjustListContent(listContent, listId) ); } function adjustListContent(content, listId) { // Add image urls const attachmentUrl = "https://listography.com/user/" + userId + "/list/" + listId + "/attachment/"; content = content.replace(/\[([a-z]+)\]/g, "[$1: " + attachmentUrl + "$1]"); return content; } // "forEach" is not async. here is our own async version of it. // usage: await asyncForEach(myArray, async () => { ... }) const asyncForEach = async (array, callback) => { for (let index = 0; index < array.length; index++) { await callback(array[index], index, array); } }; //////////////////////////////////// HTML function HTML() { createPopup(); createLoading(); createBackupAllButton(); createBackupVisibleButton(); } function createBackupAllButton() { const menu = document.querySelector(".global-menu tbody"); const tr = document.createElement("tr"); const td = document.createElement("td"); const button = document.createElement("input"); button.type = "button"; button.value = "backup all"; button.className = "backup-button"; if (onIndexPage()) { button.onclick = startBackupAll; } else { button.onclick = goToIndex; } td.appendChild(button); tr.appendChild(td); menu.appendChild(tr); } function createBackupVisibleButton() { const menu = document.querySelector(".global-menu tbody"); const tr = document.createElement("tr"); const td = document.createElement("td"); const button = document.createElement("input"); button.type = "button"; button.value = "backup visible"; button.className = "backup-button"; button.onclick = startBackupVisible; td.appendChild(button); tr.appendChild(td); menu.appendChild(tr); } function onIndexPage() { return pathname.startsWith(indexPath) && pathname.indexOf("?v") < 0; } function goToIndex() { location.href = indexPath + "?backup=true"; } function createPopup() { popupoverlay = document.createElement("div"); popupoverlay.className = "backup-popup-overlay"; popup = document.createElement("div"); popup.className = "backup-popup"; document.body.appendChild(popup); document.body.appendChild(popupoverlay); hidePopup(); } function createLoading() { let loading = document.createElement("div"); loading.setAttribute("id", "backup-loading"); loading.style.display = "none"; loading.innerHTML = "<h1>Loading...</h1><h2>Please wait.</h2>This may take a while if you have more than 100 lists."; document.body.appendChild(loading); } function showPopup(text, allowClosing) { if (text) popup.innerHTML = text; popupoverlay.style.display = "block"; popup.style.display = "block"; document.body.style.overflow = "hidden"; if (allowClosing) { popupoverlay.onclick = hidePopup; } } function hidePopup() { popup.style.display = "none"; popupoverlay.style.display = "none"; document.body.style.overflow = "auto"; } //////////////////////////////////// CSS function CSS() { var styleSheet = document.createElement("style"); styleSheet.type = "text/css"; document.head.appendChild(styleSheet); styleSheet.innerText = ` .backup-button { background: none; border: none; color: rgb(119, 119, 119); font-family: helvetica, arial, sans-serif; font-size: 12px; cursor: pointer; font-style: italic; } .backup-button:hover, .backup-button:focus { text-decoration: underline; } #backup-loading, .backup-popup { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 50px; background: white; z-index: 999; border: 2px dotted lightgray; border-radius: 5px; min-width: 500px; min-height: 200px; justify-content: center; align-items: center; } .backup-popup h1 { margin-top: 0; } .backup-popup textarea { width: 100%; min-height: 300px; } .backup-popup-overlay { content: ""; position: fixed; z-index: 99; background: rgba(0, 0, 0, 0.5); width: 100%; height: 100%; top: 0; left: 0; } `; }