cytube user highlight

highlight users in chat

  1. // ==UserScript==
  2. // @name cytube user highlight
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description highlight users in chat
  6. // @author unrealfag
  7. // @match https://cytu.be/r/*
  8. // @grant none
  9. // ==/UserScript==
  10. 'use strict';
  11.  
  12. const defaultColor = '#2c902c99';
  13. const toggleOnClass = 'glyphicon glyphicon-heart';
  14. const toggleOffClass = 'glyphicon glyphicon-heart-empty';
  15. const storageId = 'unrealfags-usercolor-menu';
  16. const openMessage = `<i class="glyphicon glyphicon-list"></i> Highlights `;
  17. const closeMessage = `<i class="glyphicon glyphicon-floppy-disk"></i> Save! `;
  18.  
  19. const storage = (name, value) => {
  20. if (typeof value === 'undefined') {
  21. return localStorage[name] ? JSON.parse(localStorage[name]) : undefined;
  22. }
  23. localStorage[name] = JSON.stringify(value);
  24. };
  25.  
  26. const createElement = (tag, inner, style) => {
  27. const el = document.createElement(tag);
  28. if (style) Object.keys(style).forEach(key => { el.style[key] = style[key]; });
  29. if (typeof inner === 'object' && inner !== null) el.appendChild(inner);
  30. else el.innerHTML = inner || '';
  31. return el;
  32. };
  33.  
  34. const Menu = createElement('ul', null, {
  35. display: 'none',
  36. 'white-space': 'nowrap',
  37. padding: '5px',
  38. });
  39. const MenuHelpers = {
  40. MenuHandlers: [],
  41. getNameListInput: (nameList) => {
  42. const NamesInput = createElement('input', null, {
  43. height: '11px',
  44. display: 'inline-block',
  45. width: 'initial',
  46. });
  47. NamesInput.type = 'text';
  48. NamesInput.className = 'form-control';
  49. NamesInput.value = nameList;
  50. NamesInput.placeholder = 'user1 user2 userName...';
  51. return NamesInput;
  52. },
  53. getColorInput: (color) => {
  54. const ColorInput = createElement('input', null, {
  55. height: '11px',
  56. display: 'inline-block',
  57. color: 'white',
  58. width: '100px',
  59. 'font-family': 'monospace',
  60. margin: '0px 5px',
  61. });
  62. ColorInput.addEventListener('input', MenuHelpers.onColorInputChanged);
  63. ColorInput.className = 'form-control';
  64. ColorInput.value = color || defaultColor;
  65. ColorInput.style.background = color || defaultColor;
  66. return ColorInput;
  67. },
  68. onColorInputChanged: (target) => {
  69. const color = target.target.value.match(/#[a-fA-F0-9]{8}/g);
  70. if (color) {
  71. target.target.style.background = color;
  72. target.target.style['border-color'] = 'initial';
  73. } else {
  74. target.target.style.background = 'transparent';
  75. target.target.style['border-color'] = 'red';
  76. }
  77. },
  78. getToggleButton: (isOn) => {
  79. const ToggleButton = document.createElement('i');
  80. ToggleButton.addEventListener('click', MenuHelpers.toggleRow);
  81. ToggleButton.className = isOn ? toggleOnClass : toggleOffClass;
  82. return ToggleButton;
  83. },
  84. toggleRow: (target) => {
  85. const isOn = target.target.className === toggleOnClass;
  86. target.target.className = isOn ? toggleOffClass : toggleOnClass;
  87. },
  88. getMenuRow: (isOn, color, nameList) => {
  89. const ToggleButton = MenuHelpers.getToggleButton(isOn);
  90. const ColorInput = MenuHelpers.getColorInput(color);
  91. const NamesInput = MenuHelpers.getNameListInput(nameList);
  92. const MenuRow = createElement('div');
  93. MenuRow.appendChild(ToggleButton);
  94. MenuRow.appendChild(ColorInput);
  95. MenuRow.appendChild(NamesInput);
  96. const MenuItem = createElement('li', MenuRow);
  97. return {
  98. element: MenuItem,
  99. isEnabled: () => ToggleButton.className === toggleOnClass,
  100. getColor: () => ColorInput.value || defaultColor,
  101. getNameList: () => NamesInput.value || '',
  102. };
  103. },
  104. openMenu: () => {
  105. const menuItems = storage(storageId) || [];
  106. menuItems.push({
  107. isOn: true,
  108. color: defaultColor,
  109. names: '',
  110. });
  111. menuItems.forEach(item => {
  112. const itemData = MenuHelpers.getMenuRow(item.isOn, item.color, item.names);
  113. MenuHelpers.MenuHandlers.push(itemData);
  114. Menu.appendChild(itemData.element);
  115. });
  116. MenuButton.innerHTML = closeMessage;
  117. Menu.style.display = 'block';
  118. },
  119. closeMenu: () => {
  120. const listData = MenuHelpers.MenuHandlers.map(menuData => {
  121. const isOn = menuData.isEnabled();
  122. const color = menuData.getColor();
  123. const names = menuData.getNameList();
  124. if (names && color) {
  125. return { isOn, color, names };
  126. }
  127. return null;
  128. }).filter(i => i !== null);
  129. storage(storageId, listData);
  130. MenuHelpers.MenuHandlers.length = 0;
  131. while (Menu.firstChild) { Menu.removeChild(Menu.firstChild); }
  132. MenuButton.innerHTML = openMessage;
  133. Menu.style.display = 'none';
  134. applyColors();
  135. }
  136. };
  137. Menu.className = 'dropdown-menu';
  138.  
  139. const MenuButton = createElement('button', openMessage, {
  140. padding: '2px 5px 2px 5px',
  141. 'font-size': '11px',
  142. height: '20px',
  143. 'vertical-align': 'top',
  144. });
  145. MenuButton.addEventListener('click', () => {
  146. const isClosed = Menu.style.display === 'none';
  147. if (isClosed) MenuHelpers.openMenu();
  148. else MenuHelpers.closeMenu();
  149. });
  150. MenuButton.className = 'btn btn-primary';
  151.  
  152. const Container = createElement('div');
  153. Container.className = 'dropdown pull-right';
  154. Container.appendChild(MenuButton);
  155. Container.appendChild(Menu);
  156.  
  157. const applyColors = () => {
  158. if (!head) { return; }
  159. const styleClass = `${storageId}_style`;
  160. var oldStyles = head.getElementsByClassName(styleClass);
  161. while (oldStyles[0]) {
  162. head.removeChild(oldStyles[0]);
  163. }
  164.  
  165. const userColorData = storage(storageId);
  166. userColorData.forEach(colorData => {
  167. if (colorData.isOn) {
  168. const names = colorData.names.split(' ');
  169. const style = createElement('style', `\
  170. ${names.map(name => `.chat-msg-${name}>:not(:last-of-type)`).join(', ')} {\
  171. display: inline-block;
  172. vertical-align: middle;
  173. line-height: 20px;
  174. background-color: ${colorData.color};\
  175. }`);
  176. style.type = 'text/css';
  177. style.className = styleClass;
  178. head.appendChild(style);
  179. }
  180. });
  181. };
  182.  
  183. const ChatHeader = document.getElementById('chatheader');
  184. ChatHeader.appendChild(Container);
  185. const head = document.getElementsByTagName('head')[0];
  186. applyColors();