Advertisement
Guest User

Catalog list view

a guest
Mar 25th, 2022
621
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Catalog list view
  3. // @namespace    https://codeforces.com/profile/-is-this-fft-
  4. // @version      1.0
  5. // @description  List view in Codeforces catalog
  6. // @author       -is-this-fft-
  7. // @match        https://codeforces.com/catalog*
  8. // @icon         https://www.google.com/s2/favicons?domain=codeforces.com
  9. // @require      https://cdn.datatables.net/1.11.4/js/jquery.dataTables.min.js
  10. // @resource     DATATABLES_CSS https://cdn.datatables.net/1.11.5/css/jquery.dataTables.min.css
  11. // @grant        GM_getResourceText
  12. // @grant        GM_addStyle
  13. // ==/UserScript==
  14.  
  15. (function() {
  16.     'use strict';
  17.  
  18.     function init() {
  19.         if (document.getElementsByClassName("_CatalogViewFrame_topManagementBar")[0] != undefined) {
  20.             // disable the userscript in management mode
  21.             return;
  22.         }
  23.  
  24.         const dataTablesCss = GM_getResourceText("DATATABLES_CSS");
  25.         GM_addStyle(dataTablesCss);
  26.         // oh the humanity
  27.         GM_addStyle(`
  28. #clvContentTable ._name a {
  29.   font-size: 1.3rem;
  30.   font-weight: initial;
  31. }
  32. .dataTables_wrapper {
  33.   clear: unset;
  34. }
  35. table.dataTable {
  36.   clear: unset;
  37. }
  38. #clvContentTable_wrapper {
  39.   margin-top: -3rem;
  40. }
  41. #clvContentTable tbody td:nth-child(2) {
  42.   text-align: left;
  43. }
  44. #clvContentTable ._name {
  45.   padding-left: 0;
  46.   padding-right: 0;
  47. }
  48. #clvContentTable ._name small {
  49.   margin-top: 0.5rem;
  50. }
  51. table.dataTable.cell-border thead th:first-child {
  52.   border-left: 1px solid #ddd;
  53. }
  54. #clvListViewButton {
  55.   margin-left: 1rem;
  56.   z-index: 1000;
  57.   position: relative;
  58. }`);
  59.  
  60.         addCatalogTableViewElement();
  61.         const blogItems = getAllBlogItems();
  62.  
  63.         $("#clvContentTable").DataTable({
  64.             data: blogItems,
  65.             columns: [
  66.                 {
  67.                     title: "Author",
  68.                     data: "author",
  69.                     render: data => `<a href="/profile/${data.name}" class="${data.class}">${data.name}</a>`
  70.                 },
  71.                 {
  72.                     title: "Title",
  73.                     data: "title",
  74.                     render: data => {
  75.                         let html = `<span class="_name"><a href="${data.href}">${data.title}</a>`;
  76.                         if (data.description != null) {
  77.                             html += `<br /><small>${data.description}</small>`;
  78.                         }
  79.                         html += "</span>";
  80.                         return html;
  81.                     }
  82.                 },
  83.                 {
  84.                     title: "Creation time",
  85.                     data: "blogCreationTime",
  86.                     render: data => getFormattedDate(data)
  87.                 },
  88.                 {
  89.                     title: "Catalog update",
  90.                     data: "catalogUpdateTime",
  91.                     render: data => getFormattedDate(data)
  92.                 }
  93.             ],
  94.             order: [2, "desc"], // by creation time descending
  95.             paging: false,
  96.         });
  97.         $("#clvContentTable_wrapper").toggle();
  98.  
  99.         if (document.cookie.split("; ").find(row => row.startsWith("catalogListView"))) {
  100.             toggleDisplay(false);
  101.         }
  102.  
  103.         const captionElement = document.querySelector("#pageContent .caption");
  104.         const listViewLink = document.createElement("a");
  105.         listViewLink.setAttribute("href", "#");
  106.         listViewLink.setAttribute("id", "clvListViewButton");
  107.         listViewLink.appendChild(document.createTextNode("Switch view"));
  108.         listViewLink.addEventListener("click", () => {
  109.             toggleDisplay(true);
  110.         });
  111.         captionElement.insertBefore(listViewLink, null);
  112.     }
  113.  
  114.     function toggleDisplay(updateCookie) {
  115.         $("#clvContentTable_wrapper").toggle();
  116.         $("._CatalogViewFrame_catalog").toggle();
  117.         $("._CatalogViewFrame_title_actions").toggle();
  118.  
  119.         if (updateCookie) {
  120.             if ($("#clvContentTable_wrapper").is(":visible")) {
  121.                 document.cookie = "catalogListView=true; expires=Fri, 31 Dec 9999 23:59:59 GMT";
  122.             } else {
  123.                 document.cookie = "catalogListView=false; expires=Thu, 01 Jan 1970 00:00:00 GMT";
  124.             }
  125.         }
  126.     }
  127.  
  128.     function addCatalogTableViewElement() {
  129.         const catalogTable = document.createElement("table");
  130.         catalogTable.setAttribute("id", "clvContentTable");
  131.         catalogTable.setAttribute("class", "row-border cell-border hover");
  132.  
  133.         const pageContentDiv = document.getElementById("pageContent");
  134.         const catalogSection = document.getElementById("_CatalogViewFrame_catalog");
  135.         pageContentDiv.insertBefore(catalogTable, catalogSection);
  136.     }
  137.  
  138.     function getAllBlogItems() {
  139.         const blogItemDivs = document.getElementsByClassName("_catalogBlogEntry");
  140.         return Array.from(blogItemDivs, convertBlogDivToBlogItem);
  141.     }
  142.  
  143.     function convertBlogDivToBlogItem(blogDiv) {
  144.         const catalogUpdateTimeMillis = blogDiv.getAttribute("data-updatetimemillis");
  145.         const catalogUpdateTime = new Date(parseInt(catalogUpdateTimeMillis, 10));
  146.         const blogAddress = blogDiv.getElementsByTagName("a")[0].getAttribute("href");
  147.         const blogTitle = blogDiv.getElementsByClassName("_nameContent")[0].innerHTML;
  148.         const descriptionElement = blogDiv.getElementsByTagName("small")[0];
  149.         const blogDescription = descriptionElement == undefined ? null : descriptionElement.innerText;
  150.         const authorLinkElement = blogDiv.getElementsByTagName("a")[1];
  151.         const author = authorLinkElement.getAttribute("href").substring("/profile/".length);
  152.         const authorClass = authorLinkElement.getAttribute("class");
  153.         const creationTimeFormatted = blogDiv.getElementsByClassName("format-humantime")[0].getAttribute("title");
  154.         const creationTime = convertCodeforcesDate(creationTimeFormatted);
  155.  
  156.         return {
  157.             title: {title: blogTitle, description: blogDescription, href: blogAddress},
  158.             author: {name: author, class: authorClass},
  159.             blogCreationTime: creationTime,
  160.             catalogUpdateTime: catalogUpdateTime
  161.         };
  162.     }
  163.  
  164.     function convertCodeforcesDate(dateString) {
  165.         // codeforces format is "May/21/2020 17:51 UTC+2" (English)
  166.         // or "21.05.2020 17:51 UTC+2" (Russian)
  167.         // we will convert it to a javascript date object
  168.         // doing it manually is a stupid hack but it is such a
  169.         // small thing in this case, using a lib is equally annoying.
  170.         if (dateString.indexOf('/') != -1) {
  171.             return convertEnglishDate(dateString);
  172.         } else {
  173.             return convertRussianDate(dateString);
  174.         }
  175.     }
  176.  
  177.     const monthDict = {
  178.         "Jan": 0,
  179.         "Feb": 1,
  180.         "Mar": 2,
  181.         "Apr": 3,
  182.         "May": 4,
  183.         "Jun": 5,
  184.         "Jul": 6,
  185.         "Aug": 7,
  186.         "Sep": 8,
  187.         "Oct": 9,
  188.         "Nov": 10,
  189.         "Dec": 11
  190.     };
  191.     function convertEnglishDate(dateString) {
  192.         const datePart = dateString.split(' ')[0];
  193.         const dateComponents = datePart.split('/');
  194.         const monthAbbrev = dateComponents[0];
  195.         const day = parseInt(dateComponents[1], 10);
  196.         const year = parseInt(dateComponents[2], 10);
  197.         const month = monthDict[monthAbbrev];
  198.  
  199.         return new Date(year, month, day);
  200.     }
  201.  
  202.     function convertRussianDate(dateString) {
  203.         const datePart = dateString.split(' ')[0];
  204.         const dateComponents = datePart.split('.');
  205.         const day = parseInt(dateComponents[0], 10);
  206.         const month = parseInt(dateComponents[1], 10) - 1;
  207.         const year = parseInt(dateComponents[2], 10);
  208.  
  209.         return new Date(year, month, day);
  210.     }
  211.  
  212.     function getFormattedDate(date) {
  213.         return `${date.getFullYear()}/${(date.getMonth() + 1).toString().padStart(2, '0')}/${date.getDate().toString().padStart(2, '0')}`;
  214.     }
  215.  
  216.     init();
  217. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement