Greasy Fork 支持简体中文。

TorViet Shoutbox Enhancer

A small script to tweak the shoutbox

  1. // ==UserScript==
  2. // @name TorViet Shoutbox Enhancer
  3. // @namespace http://torviet.com/userdetails.php?id=1662
  4. // @version 1.1.5
  5. // @license http://www.wtfpl.net/txt/copying/
  6. // @homepageURL https://github.com/S-a-l-a-d/TorViet-Shoutbox-Enhancer
  7. // @supportURL https://github.com/S-a-l-a-d/TorViet-Shoutbox-Enhancer/issues
  8. // @icon http://torviet.com/pic/salad.png
  9. // @description A small script to tweak the shoutbox
  10. // @author Salad
  11. // @match http://torviet.com/qa.php*
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_deleteValue
  15. // @grant GM_addStyle
  16. // ==/UserScript==
  17.  
  18. ((window, document) => {
  19. const allWrapper = document.getElementById('all-wrapper');
  20. const boxHead = document.getElementById('boxHead');
  21. const marquee = document.getElementById('marquee');
  22. const sltTheme = document.getElementById('sltTheme');
  23. const clock = document.getElementById('clock');
  24. const idQuestion = document.getElementById('idQuestion');
  25. const emoGroup = document.getElementById('emo-group');
  26. const emoGroupDetail = document.getElementById('emo-group-detail');
  27. const apiPath = 'qa_smiley_ajax.php';
  28.  
  29. class DomElementHelper {
  30. static appendSibling(newElement, referenceElement) {
  31. if (!newElement || !referenceElement) {
  32. return false;
  33. }
  34.  
  35. referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling);
  36.  
  37. return true;
  38. }
  39.  
  40. static remove(element) {
  41. if (!element) {
  42. return false;
  43. }
  44.  
  45. element.parentNode.removeChild(element);
  46.  
  47. return true;
  48. }
  49. }
  50.  
  51. class EmoticonService {
  52. static getEmoticon(emoticonName) {
  53. if (isNaN(emoticonName) || !this.isInteger(emoticonName)) {
  54. return Promise.resolve('');
  55. }
  56.  
  57. return Promise.resolve(`<div style="height:43px;width:43px;float:left;display:inline-block;margin: 0 0 1px 1px;"><img style="max-width:43px;max-height:43px;cursor:pointer;" src="/pic/smilies/${emoticonName}.gif" alt="[em${emoticonName}]"></div>`);
  58. }
  59.  
  60. static isInteger(number) {
  61. return number === parseInt(number, 10) || number === parseInt(number, 10).toString();
  62. }
  63.  
  64. static getEmoticons(url, emoticonGroupName) {
  65. if (!url || !emoticonGroupName || !isNaN(emoticonGroupName)) {
  66. return null;
  67. }
  68.  
  69. return new Promise((resolve, reject) => {
  70. const request = new XMLHttpRequest();
  71.  
  72. request.open('POST', url);
  73. request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  74.  
  75. request.onload = () => {
  76. if (request.status === 200) {
  77. resolve(JSON.parse(request.responseText).str);
  78. } else {
  79. reject(Error(request.statusText));
  80. }
  81. };
  82.  
  83. request.onerror = () => {
  84. reject(Error('Network error'));
  85. };
  86.  
  87. request.send(`group=${emoticonGroupName}`);
  88. });
  89. }
  90. }
  91.  
  92. const EMOTICON = (() => {
  93. let cachedEmoticonList = GM_getValue('emoticonList');
  94. let cachedEmoticonListHtml = GM_getValue('emoticonListHtml') || '';
  95.  
  96. const promptForEmoticonList = (action, emoticonList) => {
  97. let message = `Chn b emoticon bn mun ${action}:\n`;
  98. let answer = '';
  99.  
  100. message += emoticonList.reduce((previous, current, index) =>
  101. previous + `${index + 1}. ${current}\n`, '');
  102.  
  103. message += 'Điền tên bộ emoticon, ngăn cách bằng dấu phẩy, phân biệt hoa/thường. Có thể điền emoticon đơn bằng cách điền tên tập tin emoticon đó.\nVí dụ: Voz,707,Rage';
  104.  
  105. answer = prompt(message);
  106.  
  107. if (!answer || !answer.trim()) { return null; }
  108.  
  109. return answer.trim().split(',');
  110. };
  111.  
  112. const initEmoticonList = () => {
  113. const availableEmoticonList = [...emoGroup.options].map(element => element.text);
  114.  
  115. cachedEmoticonList = promptForEmoticonList('sử dụng', availableEmoticonList);
  116.  
  117. if (!cachedEmoticonList) { return; }
  118.  
  119. GM_setValue('emoticonList', cachedEmoticonList);
  120. };
  121.  
  122. return {
  123. emoticonListExists: () => {
  124. if (!cachedEmoticonList) initEmoticonList();
  125. },
  126. addToDom: async () => {
  127. emoGroupDetail.innerHTML = '';
  128.  
  129. if (!cachedEmoticonListHtml) {
  130. cachedEmoticonListHtml = (await Promise.all(cachedEmoticonList.map(async (item) => {
  131. return isNaN(item) ?
  132. await EmoticonService.getEmoticons(apiPath, item) :
  133. await EmoticonService.getEmoticon(item);
  134. }))).join('');
  135.  
  136. GM_setValue('emoticonListHtml', cachedEmoticonListHtml);
  137. }
  138.  
  139. emoGroupDetail.innerHTML = cachedEmoticonListHtml;
  140. },
  141. add: () => {
  142. const availableEmoticonList = [...emoGroup.options]
  143. .map(item => item.text)
  144. .filter(item => !cachedEmoticonList.includes(item));
  145.  
  146. const emoticonListToAdd = promptForEmoticonList('thêm', availableEmoticonList);
  147.  
  148. if (!emoticonListToAdd) { return; }
  149.  
  150. cachedEmoticonList = [
  151. ...cachedEmoticonList,
  152. ...emoticonListToAdd.filter(item => !cachedEmoticonList.includes(item))
  153. ];
  154.  
  155. GM_setValue('emoticonList', cachedEmoticonList);
  156. GM_deleteValue('emoticonListHtml');
  157. window.location.href = window.location.pathname;
  158. },
  159. remove: () => {
  160. const emoticonListToRemove = promptForEmoticonList('xóa', cachedEmoticonList);
  161.  
  162. if (!emoticonListToRemove) { return; }
  163.  
  164. cachedEmoticonList =
  165. cachedEmoticonList.filter(item => !emoticonListToRemove.includes(item));
  166.  
  167. GM_setValue('emoticonList', cachedEmoticonList);
  168. GM_deleteValue('emoticonListHtml');
  169. window.location.href = window.location.pathname;
  170. },
  171. clear: () => {
  172. GM_deleteValue('emoticonList');
  173. GM_deleteValue('emoticonListHtml');
  174. window.location.href = window.location.pathname;
  175. },
  176. };
  177. })();
  178.  
  179. function isFirefoxBrowser() {
  180. return typeof InstallTrigger !== 'undefined';
  181. }
  182.  
  183. function createButton(text, event) {
  184. const button = document.createElement('input');
  185. button.type = 'button';
  186. button.value = text;
  187. button.addEventListener('click', event);
  188.  
  189. return button;
  190. }
  191.  
  192. allWrapper.className = '';
  193. DomElementHelper.remove(boxHead);
  194. DomElementHelper.remove(marquee);
  195. DomElementHelper.remove(sltTheme);
  196. while (clock.lastChild) {
  197. DomElementHelper.remove(clock.lastChild);
  198. }
  199.  
  200. const stylesheet = isFirefoxBrowser() ?
  201. `
  202. #wrapper-below {
  203. height: calc(100% - 67px);
  204. }
  205.  
  206. #emo-section {
  207. height: calc(100% - 74px);
  208. }
  209. ` :
  210. `
  211. #wrapper-below {
  212. height: calc(100% - 62px);
  213. }
  214.  
  215. #emo-section {
  216. height: calc(100% - 69px);
  217. }
  218. `;
  219.  
  220. GM_addStyle(
  221. `
  222. .slimScrollDiv, #emo-group-detail {
  223. height: 100% !important;
  224. }
  225. ${stylesheet}`);
  226.  
  227. const clockChild = document.createDocumentFragment();
  228. const span = document.createElement('span');
  229.  
  230. span.innerHTML = 'For custom emoticon group<br>';
  231.  
  232. clockChild.appendChild(emoGroup.parentNode);
  233. clockChild.appendChild(span)
  234. .parentNode.appendChild(createButton('Add', EMOTICON.add))
  235. .parentNode.appendChild(createButton('Remove', EMOTICON.remove))
  236. .parentNode.appendChild(createButton('Clear', EMOTICON.clear));
  237. clock.appendChild(clockChild);
  238.  
  239. EMOTICON.emoticonListExists();
  240. EMOTICON.addToDom();
  241.  
  242. idQuestion.focus();
  243. })(window, document);