Advertisement
Guest User

Untitled

a guest
Jun 21st, 2023
119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name          Tildezy
  3. // @namespace     https://github.com/TeJayH/Tildezy/
  4. // @version       1.1
  5. // @description   Adds some extra functionality to http://tildes.net/
  6. // @author        TeJay (https://github.com/TeJayH)
  7. // @match         https://*.tildes.net/*
  8. // @icon          https://www.google.com/s2/favicons?sz=64&domain=tildes.net
  9. // @supportURL    https://github.com/TeJayH/Tildezy/issuesâ—˜
  10. // @license       GPL-3.0
  11. // @grant         none
  12. // @downloadURL   https://raw.githubusercontent.com/TeJayH/Tildezy/main/Tildezy.user.js
  13. // @updateURL     https://raw.githubusercontent.com/TeJayH/Tildezy/main/Tildezy.user.js
  14. // ==/UserScript==
  15.  
  16. // ^ Feel free to remove downloadURL and updateURL to disable automatic updates, some people like them, some hate them
  17. // The script loaders generally show you any changes when you update but it's still technically a little dangerous if you're not going to check each update
  18. // I don't expect this to get tons of updates so you really do not *need* it.
  19.  
  20. // ---------------- Toggles ----------------
  21. // Gets values from storage if they exist, else sets everything to true, enabling all functiosn of script.
  22.  
  23. /* Manual overrides if you dont want the settings button */
  24. let toggleNewTabComments = false;
  25. let toggleUserColors = true;
  26. let toggleCommentCollapser = true;
  27. let toggleScrollToTopButton = true;
  28. let toggleGroupStars = true;
  29. //let toggleSettings = true
  30.  
  31.  
  32. // ---------------- Color Users ----------------
  33. // Function to generate a random color based on hashed username
  34. // Ensures whenever you see a user they consistently have the same color. Also makes sure color is not too bright or dark to read.
  35. function getConsistentColor(username) {
  36.     let hash = 0;
  37.     if (username.startsWith('@')) {
  38.         username = username.substring(1); // Remove the '@' symbol from the hash for consistent coloring.
  39.     }
  40.     if (username.startsWith('/u/')) {
  41.         username = username.substring(3); // Remove the '/u/' symbol from the hash for consistent coloring.
  42.     }
  43.  
  44.     let color;
  45.     let brightness;
  46.     let darkness;
  47.  
  48.     // Calculate a hash value of the username
  49.     for (let i = 0; i < username.length; i++) {
  50.         hash = username.charCodeAt(i) + ((hash << 5) - hash);
  51.         hash ^= 0xFFFFAA;
  52.         hash |= 0;
  53.     }
  54.  
  55.     // Convert the hash value to RGB values
  56.     const r = ((hash & 0xFF0000) >> 16);
  57.     const g = ((hash & 0x00FF00) >> 8);
  58.     const b = ((hash & 0x0000FF) >> 0);
  59.  
  60.     // Convert RGB values to hexadecimal color code
  61.     color = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
  62.  
  63.     // Calculate brightness with magic math.
  64.     brightness = (r * 299 + g * 587 + b * 114) / 1000;
  65.     darkness = (r + g + b) / 3;
  66.  
  67.     // Check if the brightness or darkness is out of the desired range
  68.     //Adjust the values however you want, if you make the possible range too small you may near infinite loop while it tries over and over to generate colors for people
  69.     const brightnessThreshold = 200;
  70.     const darknessThreshold = 50;
  71.     if (brightness > brightnessThreshold || darkness < darknessThreshold || brightness < darknessThreshold) {
  72.         // Append an underscore to the username and recursively call the function again to generate a fresh color
  73.         username += '_';
  74.         return getConsistentColor(username);
  75.     }
  76.     return color;
  77. }
  78.  
  79. // function to loop the usernames on the page, compileing names into maps then applies color to those maps.
  80. // Idea behind the mapping is that coloring all of a users comments at once skips the need to hash their name and calculate the color over and over, should be more efficient, but I'm dumb so who knows.
  81. function applyConsistentColorToUserNames() {
  82.     const userLinks = document.getElementsByClassName('link-user');
  83.     const userMap = {};
  84.  
  85.     // Group user links by username
  86.     for (let i = 0; i < userLinks.length; i++) {
  87.         const link = userLinks[i];
  88.         const username = link.textContent;
  89.         if (!userMap.hasOwnProperty(username)) {
  90.             userMap[username] = [];
  91.         }
  92.         userMap[username].push(link);
  93.     }
  94.  
  95.     // Apply consistent color to all instances of each username
  96.     for (const username in userMap) {
  97.         if (userMap.hasOwnProperty(username)) {
  98.             const color = getConsistentColor(username);
  99.             const userInstances = userMap[username];
  100.             for (let i = 0; i < userInstances.length; i++) {
  101.                 const link = userInstances[i];
  102.                 link.style.color = color;
  103.             }
  104.         }
  105.     }
  106. }
  107.  
  108.  
  109. // ---------------- Comment Collapse ----------------
  110.  
  111. // Function to toggle collapse a comment
  112. function toggleCommentCollapse(event) {
  113.     event.stopPropagation();
  114.     const comment = event.target.closest('.comment');
  115.     comment.classList.toggle('is-comment-collapsed');
  116.     if (window.getSelection) {
  117.         window.getSelection().removeAllRanges(); //Clears selection that happens if your triple click is on text.
  118.     } else if (document.selection) {
  119.         document.selection.empty();
  120.     }
  121. }
  122.  
  123. // Function to handle click event on comments (to trigger the collapse)
  124. function handleCommentClicks(event) {
  125.     if (event.detail === 3) { //Change amount of clicks required to toggle a comment 3 because you sometimes doubleclick a word to select it for copying
  126.         toggleCommentCollapse(event);
  127.     }
  128. }
  129.  
  130. if (toggleCommentCollapser) {
  131.     const commentsContainer = document.querySelector('#comments');
  132.     if (commentsContainer) {
  133.         commentsContainer.addEventListener('click', (event) => {
  134.             const target = event.target;
  135.             const isCommentHeader = target.matches('.comment-header');
  136.             const isCommentText = target.matches('.comment-text');
  137.             if (isCommentHeader || isCommentText || target.closest('.comment-header') || target.closest('.comment-text')) {
  138.                 handleCommentClicks(event);
  139.             }
  140.         });
  141.     }
  142. }
  143.  
  144.  
  145. // ---------------- Group Stars ----------------
  146. // Function to handle clicking on a star
  147. function handleStarClick(event) {
  148.     event.preventDefault();
  149.     event.stopPropagation();
  150.     const starElement = event.target;
  151.     const groupLink = starElement.nextSibling;
  152.     if (groupLink) {
  153.         const groupName = groupLink.getAttribute('href');
  154.         const starredGroups = GM_getValue('starredGroups', []);
  155.         if (starredGroups.includes(groupName)) {
  156.             // Group is already starred, remove it
  157.             const index = starredGroups.indexOf(groupName);
  158.             if (index !== -1) {
  159.                 starredGroups.splice(index, 1);
  160.                 GM_setValue('starredGroups', starredGroups);
  161.             }
  162.             starElement.textContent = '☆';
  163.             starElement.style.color = '';
  164.         } else {
  165.             // Group is not starred, add it
  166.             starredGroups.push(groupName);
  167.             starredGroups.sort(); // Sort alphabetically
  168.             GM_setValue('starredGroups', starredGroups);
  169.             starElement.textContent = '★';
  170.             starElement.style.color = 'yellow';
  171.         }
  172.         rearrangeGroupLinks();
  173.     }
  174. }
  175.  
  176. // Function to check if a group is starred
  177. function isGroupStarred(groupName) {
  178.     const starredGroups = GM_getValue('starredGroups', []);
  179.     return starredGroups.includes(groupName);
  180. }
  181.  
  182. // Function to rearrange group links based on star status
  183. function rearrangeGroupLinks() {
  184.     const groupContainer = document.querySelector('.nav-group-list');
  185.     if (groupContainer) {
  186.         const groupLinks = Array.from(groupContainer.querySelectorAll('li'));
  187.         const starredGroups = [];
  188.         const unstarredGroups = [];
  189.  
  190.         groupLinks.forEach((groupItem) => {
  191.             const groupLink = groupItem.querySelector('.link-group');
  192.             const groupName = groupLink.getAttribute('href');
  193.             const isStarred = isGroupStarred(groupName);
  194.  
  195.             if (isStarred) {
  196.                 starredGroups.push(groupItem);
  197.             } else {
  198.                 unstarredGroups.push(groupItem);
  199.             }
  200.         });
  201.  
  202.         starredGroups.sort((a, b) => {
  203.             const groupNameA = a.querySelector('.link-group').getAttribute('href').toLowerCase();
  204.             const groupNameB = b.querySelector('.link-group').getAttribute('href').toLowerCase();
  205.             if (groupNameA < groupNameB) return -1;
  206.             if (groupNameA > groupNameB) return 1;
  207.             return 0;
  208.         });
  209.  
  210.         unstarredGroups.sort((a, b) => {
  211.             const groupNameA = a.querySelector('.link-group').getAttribute('href').toLowerCase();
  212.             const groupNameB = b.querySelector('.link-group').getAttribute('href').toLowerCase();
  213.             if (groupNameA < groupNameB) return -1;
  214.             if (groupNameA > groupNameB) return 1;
  215.             return 0;
  216.         });
  217.  
  218.         groupLinks.forEach((groupItem) => {
  219.             groupItem.parentNode.removeChild(groupItem);
  220.         });
  221.  
  222.         starredGroups.forEach((groupItem) => {
  223.             groupContainer.appendChild(groupItem);
  224.         });
  225.  
  226.         unstarredGroups.forEach((groupItem) => {
  227.             groupContainer.appendChild(groupItem);
  228.         });
  229.     }
  230. }
  231.  
  232.  
  233. // Function to add clickable stars to the group links
  234. function addGroupStars() {
  235.     const groupItems = document.querySelectorAll('.nav-item');
  236.     const groups = Array.from(groupItems).map((groupItem) => {
  237.         const groupLink = groupItem.querySelector('.link-group');
  238.         if (groupLink) { // Avoids a null when logged in.
  239.             const groupName = groupLink.getAttribute('href');
  240.             const isStarred = isGroupStarred(groupName);
  241.             const starElement = document.createElement('span');
  242.             starElement.classList.add('star');
  243.             starElement.style.cursor = 'pointer';
  244.             starElement.textContent = isStarred ? '★' : '☆';
  245.             starElement.addEventListener('click', handleStarClick);
  246.             starElement.style.color = isStarred ? 'yellow' : '';
  247.  
  248.             groupItem.insertBefore(starElement, groupLink);
  249.         }
  250.     });
  251.  
  252.     // Sort the group list
  253.     sortGroupList();
  254. }
  255.  
  256. function sortGroupList() {
  257.     const groupList = document.querySelector('.nav-group-list');
  258.     if (!groupList) {
  259.         return;
  260.     }
  261.     const groupItems = Array.from(groupList.children);
  262.  
  263.     groupItems.sort((a, b) => {
  264.         const aIsStarred = a.querySelector('.star').textContent === '★';
  265.         const bIsStarred = b.querySelector('.star').textContent === '★';
  266.         const aText = a.querySelector('.link-group').textContent.toLowerCase();
  267.         const bText = b.querySelector('.link-group').textContent.toLowerCase();
  268.  
  269.         if (aIsStarred && !bIsStarred) {
  270.             return -1;
  271.         } else if (!aIsStarred && bIsStarred) {
  272.             return 1;
  273.         } else {
  274.             return aText.localeCompare(bText);
  275.         }
  276.     });
  277.     groupItems.forEach((item) => groupList.appendChild(item));
  278. }
  279.  
  280.  
  281. // ---------------- New Tab Comments button ----------------
  282. if (toggleNewTabComments) {
  283.     document.querySelectorAll('span').forEach(function(span) {
  284.         if (span.innerText.toLowerCase().includes('comment')) {
  285.             var anchor = span.closest('a');
  286.             if (anchor) {
  287.                 anchor.addEventListener('click', function(event) {
  288.                     event.preventDefault();
  289.                     window.open(this.href, '_blank');
  290.                 });
  291.             }
  292.         }
  293.     });
  294. }
  295.  
  296.  
  297. // ---------------- Scroll to top ----------------
  298. // Function to scroll to the top of the page
  299. function scrollToTop() {
  300.     window.scrollTo({
  301.         top: 0,
  302.         behavior: 'smooth'
  303.     });
  304. }
  305.  
  306. // Function to scroll to the top of the page
  307. function scrollToTop() {
  308.     window.scrollTo({
  309.         top: 0,
  310.         behavior: 'smooth'
  311.     });
  312. }
  313.  
  314. // Function to add a scroll-to-top button to the footer
  315. function addScrollToTopButton() {
  316.     var scrollToTopButton = document.createElement('button');
  317.     scrollToTopButton.textContent = '↑ Return to Top ↑';
  318.     scrollToTopButton.setAttribute('id', 'scrollToTopButton');
  319.     scrollToTopButton.style.marginBottom = '15px';
  320.  
  321.     // Apply styles to the button
  322.     scrollToTopButton.style.backgroundColor = 'var(--background-input-color)';
  323.     scrollToTopButton.style.border = '1px solid var(--border-color)';
  324.     scrollToTopButton.style.color = 'var(--foreground-primary-color)';
  325.  
  326.     var themeSelection = document.querySelector('.site-footer-theme-selection');
  327.     themeSelection.parentNode.insertBefore(scrollToTopButton, themeSelection);
  328.     scrollToTopButton.addEventListener('click', scrollToTop);
  329. }
  330.  
  331.  
  332.  
  333.  
  334. // ---------------- Run the stuff ----------------
  335.  
  336. //////if (toggleSettings){addSettingsButton();} // Yes you can even turn the settings off if you'd rather set the bools manually and leave the header untouched.
  337. if (toggleScrollToTopButton){addScrollToTopButton();}
  338. if (toggleUserColors){applyConsistentColorToUserNames();}
  339. if (toggleGroupStars){addGroupStars();}
  340.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement