Advertisement
Guest User

Untitled

a guest
Oct 29th, 2020
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.49 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Youtube Special Comment Sticker
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description try to take over the world!
  6. // @author You
  7. // @include https://www.youtube.com/live_chat*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (() => {
  12. let IS_DARK = document.querySelector('html').hasAttribute('dark');
  13.  
  14. let canScroll = true;
  15. let chat;
  16. let itemScroller;
  17. let scrollButton;
  18. let sticky;
  19. let stickyItems;
  20. let stickyItemsContainer;
  21. let toggleSticky;
  22. let toggleStickyText;
  23.  
  24. let scrollSticky = () => {
  25. if (canScroll) {
  26. stickyItems.scrollTop = stickyItems.scrollHeight;
  27. }
  28. }
  29.  
  30. let isSpecial = (authorElement) => {
  31. return authorElement.classList.contains('moderator') || authorElement.classList.contains('owner');
  32. };
  33.  
  34. let stickItem = (node) => {
  35. let authorElement = node.querySelector('#author-name');
  36. if (authorElement && isSpecial(authorElement)) {
  37. let stickyItem = document.createElement('div');
  38. let authorPhoto = document.createElement('div');
  39. let content = document.createElement('div');
  40. let timestamp = document.createElement('span');
  41. let author = document.createElement('div');
  42. let authorName = document.createElement('span');
  43. content.id = 'content';
  44. stickyItem.classList.add('sticky-item');
  45. content.classList.add('style-scope', 'yt-live-chat-text-message-renderer');
  46. timestamp.classList.add('timestamp');
  47. author.classList.add('author');
  48. authorName.classList.add('author-name');
  49. authorElement.classList.contains('owner') ? authorName.classList.add('owner') : null;
  50. authorPhoto.innerHTML = node.querySelector('#author-photo').outerHTML.replace(/(\<|\/)(yt\-img\-shadow)/g, '$1div');
  51. timestamp.innerText = node.querySelector('#timestamp').innerText;
  52. authorName.innerText = authorElement.innerText;
  53. author.append(authorName);
  54. content.append(timestamp);
  55. content.append(author);
  56. content.append(node.querySelector('#message').cloneNode(true));
  57. stickyItem.append(authorPhoto.firstChild);
  58. stickyItem.append(content);
  59. stickyItems.append(stickyItem);
  60.  
  61. if (sticky.style.display === 'none') {
  62. chat = document.querySelector('#chat');
  63. itemScroller = chat.querySelector('#item-scroller');
  64. sticky.style.display = 'inherit';
  65. chat.setAttribute('style', `max-height: ${chat.offsetHeight - sticky.offsetHeight}px;`);
  66. itemScroller.scrollTop = itemScroller.scrollHeight;
  67. }
  68.  
  69. scrollSticky();
  70. }
  71. };
  72.  
  73. let monitor = () => {
  74. let chatItems = document.querySelector('#items.style-scope.yt-live-chat-item-list-renderer');
  75. let observer = new MutationObserver((mutations) => {
  76. mutations.forEach((mutation) => {
  77. mutation.addedNodes.forEach(stickItem);
  78. });
  79. });
  80. chatItems.querySelectorAll('yt-live-chat-text-message-renderer').forEach(stickItem);
  81. observer.observe(chatItems, { childList: true });
  82. };
  83.  
  84. let setUpCss = () => {
  85. let style = document.createElement('style');
  86. document.head.appendChild(style);
  87. style.sheet.insertRule('#sticky {position: relative;}');
  88. style.sheet.insertRule('#sticky #show-hide-button {border-bottom: #e0e0e0 solid 1px; font-size: 11px;}');
  89. style.sheet.insertRule('#sticky #show-hide-button div {font-size: 11px;}');
  90. style.sheet.insertRule('#sticky #show-hide-button div a {color: #11111199; display: flex;}');
  91. style.sheet.insertRule('#sticky #show-hide-button div a:hover {color: #030303;}');
  92. style.sheet.insertRule('#sticky #show-hide-button div a #button {border-radius: 0; margin: 0; padding: 8px 24px; width: 100%;}');
  93. style.sheet.insertRule('#sticky #show-hide-button div a #button #text {font-weight: 500;}');
  94. style.sheet.insertRule('#sticky #chat-header {box-sizing: border-box; display: flex; border-bottom: #e0e0e0 solid 1px; background-color: #fffffffa; height: 150px; padding: 0 0 0 4px; transition: all .15s cubic-bezier(0.0, 0.0, 0.2, 1);}');
  95. style.sheet.insertRule('#sticky yt-icon-button {z-index: 0; transition: all .15s cubic-bezier(0.0, 0.0, 0.2, 1); visibility: hidden;}');
  96. style.sheet.insertRule('#sticky #primary-content {height: inherit; overflow-x: hidden;}');
  97. style.sheet.insertRule('#sticky #primary-content::-webkit-scrollbar {content: "";}');
  98. style.sheet.insertRule('#sticky #primary-content::-webkit-scrollbar-track {background-color: #f1f1f1;}');
  99. style.sheet.insertRule('#sticky #primary-content::-webkit-scrollbar-thumb {border: 2px solid #f1f1f1; min-height: 30px;}');
  100. style.sheet.insertRule('#sticky #primary-content .sticky-item {padding: 4px; font-size: 13px; display: flex;}');
  101. style.sheet.insertRule('#sticky #primary-content .sticky-item #author-photo {display: inline-table;}');
  102. style.sheet.insertRule('#sticky #primary-content .sticky-item #img {max-width: inherit;}');
  103. style.sheet.insertRule('#sticky #primary-content .timestamp {display: inline; color: #11111166; font-size: 11px; margin-right: 8px;}');
  104. style.sheet.insertRule('#sticky #primary-content .author {display: inline-flex; margin-right: 8px;}');
  105. style.sheet.insertRule('#sticky #primary-content .author .author-name {color: #5f84f1; font-weight: 500;}');
  106. style.sheet.insertRule('#sticky #primary-content .author .author-name.owner {color: #000000de; background-color: #ffd600; padding: 2px 4px; border-radius: 2px;}');
  107. style.sheet.insertRule('#sticky #primary-content #message {display: inline; line-height: 17px;}');
  108. style.sheet.insertRule('#sticky.dark #show-hide-button {border-color: #303030; background-color: #181818;}');
  109. style.sheet.insertRule('#sticky.dark #show-hide-button div a {color: #ffffffb3;}');
  110. style.sheet.insertRule('#sticky.dark #show-hide-button div a:hover {color: #ffffff;}');
  111. style.sheet.insertRule('#sticky.dark #chat-header {border-color: #303030; background-color: #202020;}');
  112. style.sheet.insertRule('#sticky.dark #primary-content::-webkit-scrollbar-track {background-color: #0f0f0f;}');
  113. style.sheet.insertRule('#sticky.dark #primary-content::-webkit-scrollbar-thumb {background-color: hsla(0, 0%, 53.3%, .2); border-color: #0f0f0f;');
  114. style.sheet.insertRule('#sticky.dark #primary-content .timestamp {color: #ffffff8a;}');
  115. style.sheet.insertRule('#sticky.dark #primary-content .author .author-name {color: #5e84f1;}');
  116. style.sheet.insertRule('#sticky.dark #primary-content .author .author-name.owner {color: #111111;}');
  117. style.sheet.insertRule('#sticky.hide-timestamps #primary-content .timestamp {display: none;}');
  118. };
  119.  
  120. let setUpSticky = () => {
  121. let hideTimestamp = document.querySelector('yt-live-chat-renderer').hasAttribute('hide-timestamps');
  122. let stickyAnchor = document.querySelector('yt-live-chat-app');
  123. sticky = document.createElement('div');
  124.  
  125. sticky.id = 'sticky';
  126. sticky.setAttribute('class', `style-scope ytd-watch-flexy ${IS_DARK ? 'dark' : ''} ${hideTimestamp ? 'hide-timestamps': ''}`);
  127. sticky.style.display = 'none';
  128. sticky.innerHTML = '<div id="show-hide-button" class="style-scope ytd-live-chat-frame"><div class="style-scope ytd-live-chat-frame" use-keyboard-focused="" is-paper-button="" button-renderer="true"><a class="yt-simple-endpoint style-scope ytd-toggle-button-renderer" tabindex="-1"><paper-button id="button" class="style-scope ytd-toggle-button-renderer" role="button" tabindex="0" animated="" elevation="0" aria-disabled="false"><yt-formatted-string id="text" class="style-scope ytd-toggle-button-renderer"></yt-formatted-string><paper-ripple class="style-scope paper-button"><div id="background" class="style-scope paper-ripple"></div><div id="waves" class="style-scope paper-ripple"></div></paper-ripple></paper-button></a></div></div>'
  129. sticky.innerHTML += '<div id="chat-header" role="heading" class="style-scope yt-live-chat-renderer"><div id="primary-content" class="style-scope yt-live-chat-header-renderer"></div>';
  130. sticky.innerHTML += '<yt-icon-button id="show-more" class="style-scope yt-live-chat-item-list-renderer" style="transform: translateY(42px);"><button id="button" class="style-scope yt-icon-button"><button id="button" class="style-scope yt-icon-button" aria-label="More comments below"><yt-icon icon="down_arrow" class="style-scope yt-live-chat-item-list-renderer"><svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope yt-icon" style="pointer-events: none; display: block; width: 100%; height: 100%;"><g class="style-scope yt-icon"><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z" class="style-scope yt-icon"></path></g></svg></yt-icon></button></button><paper-ripple class="style-scope yt-icon-button circle"><div id="background" class="style-scope paper-ripple" style="opacity: 0.006272;"></div><div id="waves" class="style-scope paper-ripple"></div></paper-ripple></yt-icon-button>'
  131. stickyItemsContainer = sticky.querySelector('#chat-header');
  132. toggleSticky = sticky.querySelector('#show-hide-button');
  133. toggleStickyText = toggleSticky.querySelector('#text');
  134. scrollButton = sticky.querySelector('yt-icon-button');
  135. scrollButton.querySelector('#button').style.height = 'auto';
  136.  
  137. stickyAnchor.parentNode.insertBefore(sticky, stickyAnchor);
  138. stickyAnchor.style.position = 'relative';
  139. toggleStickyText.innerText = 'Hide Sticky';
  140.  
  141. sticky.querySelectorAll('#title, #view-selector, #action-buttons, #overflow').forEach(element => element.parentNode.removeChild(element));
  142. stickyItems = sticky.querySelector('#primary-content');
  143. };
  144.  
  145. let monitorDarkTheme = () => {
  146. let observer = new MutationObserver(() => {
  147. sticky.classList.toggle('dark');
  148. });
  149. observer.observe(document.querySelector('html'), {
  150. attributes: true,
  151. attributeFilter: ['dark']
  152. });
  153. };
  154.  
  155. let monitorTimestampToggle = () => {
  156. let observer = new MutationObserver(() => {
  157. sticky.classList.toggle('hide-timestamps');
  158. scrollSticky();
  159. });
  160. observer.observe(document.querySelector('yt-live-chat-renderer'), {
  161. attributes: true,
  162. attributeFilter: ['hide-timestamps']
  163. });
  164. };
  165.  
  166. let setUpEvents = () => {
  167. stickyItems.addEventListener('scroll', () => {
  168. setTimeout(() => {
  169. canScroll = stickyItems.scrollHeight - stickyItems.scrollTop === stickyItems.clientHeight;
  170. scrollButton.style.transform = `translateY(${canScroll ? 42 : 0}px)`;
  171. scrollButton.style.visibility = canScroll ? 'hidden' : 'visible';
  172. });
  173. });
  174. scrollButton.addEventListener('click', () => {
  175. canScroll = true;
  176. stickyItems.scrollTop = stickyItems.scrollHeight;
  177. });
  178. toggleSticky.addEventListener('click', () => {
  179. if (stickyItemsContainer.offsetHeight > 0) {
  180. chat.setAttribute('style', `max-height: ${chat.offsetHeight + 150}px;`);
  181. toggleStickyText.innerText = 'Show Sticky';
  182. stickyItemsContainer.setAttribute('style', 'border: none;');
  183. stickyItemsContainer.style.height = '0px';
  184. } else {
  185. chat.setAttribute('style', `max-height: ${chat.offsetHeight - 150}px;`);
  186. itemScroller.scrollTop += 150;
  187. toggleStickyText.innerText = 'Hide Sticky';
  188. stickyItemsContainer.setAttribute('style', '');
  189. stickyItemsContainer.style.height = '150px';
  190. }
  191. });
  192. };
  193.  
  194. let initMonitoring = () => {
  195. setUpCss();
  196. setUpSticky();
  197. monitor();
  198. monitorDarkTheme();
  199. monitorTimestampToggle();
  200. setUpEvents();
  201. };
  202.  
  203. let init = () => {
  204. if (window.top.document.readyState === 'complete') {
  205. initMonitoring();
  206. } else {
  207. window.top.addEventListener('load', initMonitoring);
  208. }
  209. };
  210.  
  211. init();
  212.  
  213. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement