// ==UserScript== // @author jvlflame // @name arca.live enhancements // @version 0.0.2 // @license MIT // @include https://arca.live/* // @description Adds: list previews, post visit history // @namespace https://greasyfork.org/users/63118 // @downloadURL none // ==/UserScript== let GLOBAL_STYLES = ` .vrow.column.head { height: initial !important; } .vrow.column { height: 135px !important; } .vrow.column.visited { opacity: 0.4; } .vrow-inner { padding-left: 115px; } .vrow-preview { display: block !important; top: 10px !important; } .notice.column { height: 2.4rem !important; } .sidebar-item { display: none; } `; const CURRENT_PAGE = window.location.pathname; const IS_POST_PAGE = CURRENT_PAGE.match(/\/b\/.*\/\d+/g); const IS_LIST_PAGE = !IS_POST_PAGE; const CATEGORY = CURRENT_PAGE.match(/\/b\/(\w+)/g); const VISITED_POSTS = getPostsFromLocalStorage(CATEGORY); const BASE64_REGEX = /^[-A-Za-z0-9+\/]*={1,3}$/g; const styleSheet = document.createElement("style"); styleSheet.type = "text/css"; styleSheet.innerText = GLOBAL_STYLES; document.head.appendChild(styleSheet); function getPost(id, title, unixTimestamp) { const dateTimeString = unixTimestamp ? new Date(unixTimestamp * 1000).toISOString() : new Date().toISOString(); const post = { t: title, v: dateTimeString } return { id: id, post: post }; } function getPostsFromLocalStorage(category) { const posts = JSON.parse(localStorage.getItem(`visited-posts-${category}`)); return posts ? posts : {}; } function isPostVisited(visitedPosts, id) { return visitedPosts[id]; } function appendPostToLocalStorage(post, category) { const posts = getPostsFromLocalStorage(category); if (isPostVisited(posts, post.id)) { return; } posts[post.id] = post.post; localStorage.setItem(`visited-posts-${category}`, JSON.stringify(posts)); } function migrateRecentArticles() { /* recent_articles { boardName: string; slug: string; articleId: number; title: string; regdateAt: number }[] */ const isMigrated = localStorage.getItem('migrated-timestamp'); if (isMigrated) { return; } const nativeVisitedPosts = localStorage.getItem('recent_articles'); if (!nativeVisitedPosts) { return; } for (const visitedPost of JSON.parse(nativeVisitedPosts)) { const post = getPost(visitedPost.articleId, visitedPost.title, visitedPost.regdateAt); const category = `/b/${visitedPost.slug}`; appendPostToLocalStorage(post, category); } localStorage.setItem('migrated-timestamp', new Date().toISOString()); } // Get the list table element const table = document.getElementsByClassName("list-table table"); // Get the list table row elements const rows = table[0].querySelectorAll("a.vrow.column"); for (const row of rows) { if (row.classList.contains("notice")) { continue; }; const previewElement = row.querySelectorAll('.vrow-preview'); const hasPreview = Boolean(previewElement[0]) if (!hasPreview) { const dummyPreviewElement = document.createElement('div'); dummyPreviewElement.classList.add('vrow-preview'); row.appendChild(dummyPreviewElement); } const href = row.href; // Remove the query string so it's easier to copy article id when archiving const hrefWithoutQuery = href.split('?')[0]; row.setAttribute("href", hrefWithoutQuery); const id = href.split(/\/b\/\w+\//)[1].split(/\?/)[0]; const titleElement = row.querySelectorAll(".title")[0]; const title = titleElement ? titleElement.outerText : ''; const isVisited = isPostVisited(VISITED_POSTS, id); if (isVisited) { row.classList.add("visited"); } else { row.addEventListener("click", () => { const post = getPost(id, title); appendPostToLocalStorage(post, CATEGORY); }); row.addEventListener("auxclick", () => { const post = getPost(id, title); appendPostToLocalStorage(post, CATEGORY); }); } } if (IS_POST_PAGE) { const title = document.querySelectorAll(".title-row .title")[0].outerText; const id = CURRENT_PAGE.split(/\/b\/\w+\//)[1].split(/\?/)[0]; const post = getPost(id, title); // Attempt to automatically decode base64 links inside the article content const articleContentElement = document.querySelector(".fr-view.article-content"); const textBlocks = articleContentElement.querySelectorAll("p"); for (const text of textBlocks) { const innerText = text.innerText; const isBase64 = innerText.match(BASE64_REGEX); if (isBase64) { const decoded = atob(innerText); const linkElement = document.createElement('a'); const brElement = document.createElement('br'); linkElement.setAttribute('href', decoded); linkElement.textContent += decoded; text.appendChild(brElement); text.appendChild(linkElement); } } if (isPostVisited(VISITED_POSTS, id)) { return; }; appendPostToLocalStorage(post, CATEGORY); } migrateRecentArticles();