Greasy Fork is available in English.

[TS] OUJS-1

New post/issue notification, adds install and ratings history stats, improves table view, list all user scripts in one page, improves library page... It now should work on Opera and Chrome.

2016-04-02 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

  1. // ==UserScript==
  2. // @name [TS] OUJS-1
  3. // @namespace TimidScript
  4. // @version 1.0.26
  5. // @description New post/issue notification, adds install and ratings history stats, improves table view, list all user scripts in one page, improves library page... It now should work on Opera and Chrome.
  6. // @author TimidScript
  7. // @homepageURL https://openuserjs.org/users/TimidScript
  8. // @copyright © 2014 TimidScript, All Rights Reserved.
  9. // @license Creative Commons BY-NC-SA + Read Copyright inside the script
  10. // @include http*://openuserjs.org/*
  11. // @require https://openuserjs.org/src/libs/TimidScript/TSL_-_Generic.js
  12. // @require https://openuserjs.org/src/libs/TimidScript/TSL_-_GM_Update.js
  13. // @homeURL https://openuserjs.org/scripts/TimidScript/[TS]_OUJS-1
  14. // @grant GM_getValue
  15. // @grant GM_setValue
  16. // @grant GM_deleteValue
  17. // @grant GM_xmlhttpRequest
  18. // @grant GM_listValues
  19. // @grant GM_registerMenuCommand
  20. // @grant GM_setClipboard
  21. // @icon data:image/gif;base64,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
  22. // ==/UserScript==
  23.  
  24.  
  25. /* Copyright Notice
  26. ********************************************************************************************
  27. Copyright © TimidScript, All Rights Reserved.
  28. [Creative Commons BY-NC-SA](http://en.wikipedia.org/wiki/Creative_Commons_license)
  29.  
  30. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
  31. following conditions are met:
  32.  
  33. 1) This copyright must be included
  34. 2) Due credits and link to original author's homepage (included in copyright).
  35. 3) Notify the original author of redistribution
  36.  
  37. TimidScript's Homepages: [GitHub](https://github.com/TimidScript)
  38. [OpenUserJS](https://openuserjs.org/users/TimidScript)
  39. [GreasyFork](https://greatest.deepsurf.us/users/1455-timidscript)
  40. */
  41. /*
  42. ********************************************************************************************
  43. Version History
  44. ------------------------------------
  45. 1.0.26 (2016-02-27)
  46. - Ratings bar and "Flaggins" are now separate elements, so removed unnecessary code and notice.
  47. - Removed flagged information as it seems admin hide it so might as well hide it also.
  48. 1.0.25 (2015-09-06)
  49. - Interval check for new issues every 3 minutes
  50. - Option to display new issue count in title. Need to set GM_setValue("EditTitle",1);
  51. - Highlight username in forums.
  52. 1.0.24 (2015-07-16)
  53. - Bug Fix: Correctly handle negative ratings
  54. 1.0.23 (2015-07-16)
  55. - Changed the styling of blockquote
  56. 1.0.22 (2015-06-25)
  57. - Bug Fix: Rating count is wrong when it is 0 and flagged
  58. - Unhide the progress bar elements
  59. - On script replaced the deceptive ratings bar and change the way information if conveyed.
  60. - No longer altering the profile page URL. Removed the search box from profile page
  61. - Columns sort order highlighted in listings
  62. - Removed cloneInto as it's no longer needed
  63. - Script icon now base64
  64. - Removed scripts are no longer listed in the History Chart
  65. 1.0.21 (2015-06-10)
  66. - Changed the colours of the issues on the forum
  67. - Changed progress-bar colouring
  68. - Bug Fix: Total rating in author script listing now shows both negative and positive count
  69. 1.0.20 (2015-05-14)
  70. - Closed issues are now in red while open ones are in a brighter green.
  71. - Small bug fixes
  72. 1.0.19 (2015-04-13)
  73. - Added ability to remove a history record. This is used when certain dates get corrupted.
  74. 1.0.18 (2015-01-22)
  75. - Bug fix for version 1.0.17 which broke support for other browsers.
  76. - Bug fix to support querySelector in Opera and Chrome. "Discussions" links use ".getAttribute" instead of ".href"
  77. 1.0.17 (2015-01-17)
  78. - Bug fixes to handle new changes in OUJS layout
  79. - Issue count now decrements
  80. - Provided a fix for the trash values created due to FireFox 35 breaking of GM_listValues API.
  81. 1.0.16 (2015-01-04)
  82. - Handles Lazy loading of icons in profile page
  83. - Created icon for libraries
  84. 1.0.15 (2014/12/27)
  85. - Bug fix in sorting installs and ratings in other users profiles
  86. - "Discuss" now points to All discussion board.
  87. - Change in forum interface to make it better with one click access to other boards.
  88. - Changed the dual colour scheme on issues forum to one
  89. 1.0.14 (2014/11/29)
  90. - More visible highlight colour for one's own script discussions on the forum
  91. - Bug fixes to deal with new OUJS layout
  92. - Bug fix to total count in history chart
  93. 1.0.13 (2014/11/01)
  94. - Bug Fix: Copy button text changed from @grant to @require
  95. 1.0.12 (2014/11/01)
  96. - Fixed it so it works on Opera and Chrome
  97. - It now displays "History Period Installs Chart" if the length is more than 0 as oppose to 3.
  98. 1.0.11 (2014/10/31)
  99. - Fixed Bug caused by console debug iteration
  100. 1.0.10 (2014/10/24)
  101. - Bug Fix on history cleanup
  102. - Changes in CSS for profile comments
  103. 1.0.9 (2014/10/18)
  104. - Bug Fix in history cleanup
  105. - Stats history takes into account deleted and new scripts
  106. - Negative stat is now shown in brackets
  107. - History Chart always at the bottom of page
  108. - Renamed "Top Installs During Period" chart
  109. - Added frame and margin to images
  110. - Correct handling of singular and plural period nouns
  111. - Changed the history spacing
  112. - Added wait period between new issues checks. Maximum of once every 20 seconds
  113. - Selected table header highlight is more visible and saves last sort order
  114. 1.0.8 Initial public release (2014/10/01)
  115. - Improved table view with numbering.
  116. - All profile scripts listed on one page
  117. - Script are listed on profile page also, so no need to click on Scripts tab
  118. - User scripts ordered by type (Script, Library and defunct)
  119. - Stats history for installs and ratings with sensible time spacing
  120. - Top install listing
  121. - Profile table view sort implemented within the script
  122. - Notification on issues raised on users scripts
  123. - Notification on the forum
  124. - Stores logged username so you do not need to be logged in to get script stats and notifications
  125. - Changed the interface of library scripts
  126. - Added copy button on library
  127. - List all author's scripts in profile tab.
  128. - List all author's scripts in one page
  129. - Limits size of image in frame on forum and icon size in library page
  130. ********************************************************************************************/
  131.  
  132.  
  133. /*=================================================================================================*/
  134. // [VYCS] VARIABLE YOU CAN SET
  135. //----------------------------------------
  136. // GM_setValue("HistoryMIN",5); //Stats history, minimum number of consective days stored. Default 5.
  137. // GM_setValue("HistoryMAX",5); //Stats history, minimum number of mins stored. Default 15
  138. /*=================================================================================================*/
  139.  
  140. if (window !== window.top) return;
  141. //GM_setValue("EditTitle", 1); //Uncomment line to allow issue count to be displayed on title. Once run once, you need to update the script to re-enable auto-update.
  142. var HistoryMIN = parseInt(GM_getValue("HistoryMIN", 5)); //Stats history, minimum number of mins stored
  143. var HistoryMAX = parseInt(GM_getValue("HistoryMAX", 15)); //Stats histry, maximum number of dates stored
  144.  
  145.  
  146. console.info("OUJS-1 is Running");
  147.  
  148.  
  149. var DAY = 86400000;
  150.  
  151. function ToggleIssuesView(discuss, issues)
  152. {
  153. discuss.onmouseover = MouseOver;
  154. discuss.onmouseleave = MouseLeave;
  155. issues.onmouseover = MouseOver;
  156. issues.onmouseleave = MouseLeave;
  157.  
  158. function MouseOver()
  159. {
  160. clearTimeout(document.to);
  161. issues.style.display = "block";
  162. }
  163.  
  164. function MouseLeave()
  165. {
  166. document.to = setTimeout(function () { issues.style.display = "none"; }, 500);
  167. }
  168. }
  169.  
  170. function RemoveNotice(e)
  171. {
  172. var meta = JSON.parse(GM_getValue("USER-P:" + this.getAttribute("username")));
  173. meta[this.postID] = this.postdata;
  174. GM_setValue("USER-P:" + this.getAttribute("username"), JSON.stringify(meta));
  175.  
  176. document.getElementById("newIssues").textContent = (parseInt(document.getElementById("newIssues").textContent) - 1);
  177.  
  178. TSL.removeNode(this.parentElement);
  179. if (!document.querySelector("#IssuesListing li")) TSL.removeNode("IssuesListing");
  180. }
  181.  
  182.  
  183. function ClickedHistory(e)
  184. {
  185. TSL.removeClass(this.parentElement.getElementsByClassName("selected")[0], "selected");
  186. TSL.addClass(this, "selected");
  187. DisplayStats(this.data, this.parentElement.data);
  188. }
  189.  
  190. function DisplayStats(old, current)
  191. {
  192. var totalR, totalI = totalIN = totalNew = totalRp = totalRn = 0, row, sups = document.querySelectorAll(".dStatP, .dStatN, .dStatZ, .dStatNew");
  193.  
  194. var tmpStamp = Date.now();
  195.  
  196. for (var i = 0; sups && i < sups.length; i++) TSL.removeNode(sups[i]);
  197. TSL.removeNode("TopTen");
  198.  
  199. var arr = [];
  200. for (var scriptID in current)
  201. {
  202. row = document.querySelector('tr[scriptid="' + scriptID + '"]');
  203.  
  204. if (scriptID == "timestamp") continue;
  205. else if (!row) arr.push({ name: GM_getValue(scriptID), installs: -1 }); //Removed Items
  206. else if (old[scriptID])
  207. {
  208. row.tempStamp = tmpStamp;
  209. var diff = current[scriptID].installs - old[scriptID].installs;
  210. if (isNaN(diff)) diff = 0;
  211. totalI += diff;
  212. if (diff < 0) totalIN += diff; //Can happen when you remove and then re-add the script
  213. if (diff) row.querySelector("td:nth-child(2) p").appendChild(TSL.createElementHTML("<sup class='dStat" + ((diff > 0) ? "P" : "N") + "'>" + diff + "</sup>"));
  214.  
  215. if (diff) arr.push({ name: row.querySelector("b").textContent, installs: diff });
  216.  
  217. diff = current[scriptID].rating - old[scriptID].rating;
  218. if (isNaN(diff)) diff = 0;
  219. if (diff > 0) totalRp += diff;
  220. else if (diff < 0) totalRn += diff;
  221. if (diff) row.querySelector("td:nth-child(3) p").appendChild(TSL.createElementHTML("<sup class='dStat" + ((diff > 0) ? "P" : "N") + "'>" + diff + "</sup>"));
  222. }
  223. }
  224.  
  225. var rows = document.querySelectorAll(".col-xs-12 .table .tr-link");
  226. for (var i = 0, installs; i < rows.length, row = rows[i]; i++)
  227. {
  228. if (row.tempStamp != tmpStamp)
  229. {
  230. installs = parseInt(row.getAttribute("installs"));
  231. totalI += installs;
  232. totalNew += installs;
  233. row.querySelector("td:nth-child(2) p").appendChild(TSL.createElementHTML("<sup class='dStatNew'>" + installs + "</sup>"));
  234. arr.push({ name: row.querySelector(".tr-link-a > b").textContent, installs: installs, isNew: true });
  235. }
  236. }
  237.  
  238. if (totalI) document.querySelector(".table thead th:nth-child(2) a").appendChild(TSL.createElementHTML("<sup class='dStat" + ((totalI > 0) ? "P" : "N") + "'>"
  239. + totalI + (function ()
  240. {
  241. if (totalI != totalIN && totalIN < 0) return "<span style='color: red;'>[" + totalIN + "]</span>"
  242. return "";
  243. })() + "</sup>"));
  244.  
  245.  
  246.  
  247. totalR = totalRp + totalRn;
  248. if (totalRp || totalRn) document.querySelector(".table thead th:nth-child(3) a").appendChild(TSL.createElementHTML("<sup class='dStat" + ((totalR == 0) ? "Z" : (totalR > 0) ? "P" : "N") + "'>"
  249. + totalR +
  250. (function ()
  251. {
  252. if (totalRp && totalRn)
  253. {
  254. return "<span style='color:black;'>[<span style='color:green;'>" + totalRp + "</span><span style='color:red;'>" + totalRn + "</span>]</span>";
  255. }
  256. return "";
  257. })() + "</sup>"));
  258.  
  259. if (arr.length == 0) return;
  260. arr.sort(function (a, b)
  261. {
  262. if (a.installs < b.installs) return -1;
  263. if (a.installs > b.installs) return 1;
  264.  
  265. return 0;
  266. });
  267. arr.reverse();
  268.  
  269. var panel = document.createElement("div");
  270. panel.className = "panel panel-default";
  271. panel.id = "TopTen";
  272. panel.appendChild(document.createElement("div"));
  273. document.querySelector(".container-fluid.col-sm-4").appendChild(panel);
  274.  
  275. panel = panel.firstElementChild;
  276. panel.className = "panel-body";
  277.  
  278. var el = document.createElement("h3");
  279. el.innerHTML = "History Period Installs Chart (<span style=\"color:green;\">" + totalI + "</span>)";
  280. panel.appendChild(el);
  281.  
  282. var ol = document.createElement("ol");
  283. ol.setAttribute("style", "font-size:14px; font-weight: 500;");
  284. panel.appendChild(ol);
  285.  
  286. while (arr.length > 0)
  287. {
  288. if (arr[0].installs >= 0)
  289. {
  290. el = document.createElement("li");
  291. if (arr[0].installs < 0) el.innerHTML = arr[0].name + " (<span style='color:red; font-weight: 700;'>Removed</span>)";
  292. else if (arr[0].isNew) el.innerHTML = arr[0].name + " (<span style='color:blue; font-weight: 700;'>" + arr[0].installs + "</span>)";
  293. else el.innerHTML = arr[0].name + " (<span style='color:green; font-weight: 700;'>" + arr[0].installs + "</span>)";
  294. ol.appendChild(el);
  295. }
  296. arr.shift();
  297. }
  298. }
  299.  
  300.  
  301.  
  302. function addScriptListingNumbers()
  303. {
  304. TSL.addStyle("ScriptNumbers", ".script_number {display: inline-block; margin: 0 1px 0 0 !important; background-color: #2C3E50; color: white; padding: 1px 2px 0 2px; margin: 0; border-radius: 3px; font-size: 13px; line-height: 13px; font-family: 'Courier New', Courier, monospace;}"
  305. + ".col-sm-8 .table .tr-link td {vertical-align: middle !important; }"
  306. + ".col-sm-8 .table .tr-link td .progress { margin-bottom: 0px; }"
  307. //+ ".col-sm-8 .table .tr-link td:nth-child(2) p {margin-bottom: 14px;}"
  308. );
  309.  
  310. var sn = document.getElementsByClassName("script_number");
  311. while (sn.length > 0) TSL.removeNode(sn[0]);
  312.  
  313. var start = document.querySelector(".pagination .active a");
  314. if (start) start = (parseInt(start.textContent) - 1) * 25 + 1;
  315. else start = 1;
  316.  
  317. var rows = document.querySelector(".table");
  318. rows = rows.querySelectorAll(".tr-link");
  319. if (!rows) return;
  320. var prefix = "".lPad("0", rows.length.toString().length);
  321. for (var i = 0, row, cell; i < rows, row = rows[i]; i++)
  322. {
  323. var counter = document.createElement("span");
  324. counter.className = "script_number";
  325. counter.textContent = (prefix + (i + start)).slice(-1 * prefix.length);
  326. row.firstElementChild.insertBefore(counter, row.firstElementChild.children[0]);
  327. }
  328. }
  329.  
  330. function SortScriptTable(e)
  331. {
  332. e.stopImmediatePropagation();
  333.  
  334. var sortDescending = !TSL.hasClass(this.parentElement, "descen");
  335. if (document.querySelector(".descen, .ascen")) TSL.removeClass(document.querySelector(".descen, .ascen"), "descen ascen");
  336. TSL.addClass(this.parentElement, ((sortDescending) ? "descen" : "ascen"));
  337.  
  338. var tbody = document.querySelector(".table tbody");
  339. var rows = tbody.getElementsByClassName("tr-link");
  340.  
  341. var idx = this.parentElement.cellIndex;
  342. GM_setValue("SortHeader", idx);
  343. GM_setValue("SortDescending", sortDescending);
  344.  
  345. for (var n, i = 0; i < rows.length - 1; i++)
  346. {
  347. n = i;
  348. for (var j = i + 1; j < rows.length; j++)
  349. {
  350. if ((sortDescending && compareRows(rows[n], rows[j]) < 0) || (!sortDescending && compareRows(rows[n], rows[j]) > 0))
  351. {
  352. n = j;
  353. }
  354. }
  355.  
  356. if (n != i) tbody.insertBefore(rows[n], rows[i]);
  357. }
  358.  
  359. //row1 less it's negative otherwise positive
  360. function compareRows(row1, row2)
  361. {
  362. if (TSL.hasClass(row1, "header_row") || TSL.hasClass(row2, "header_row")) return 0;
  363. if (TSL.hasClass(row1, "_library") || TSL.hasClass(row1, "_defunct")) return 0;
  364. if (TSL.hasClass(row2, "_library") || TSL.hasClass(row2, "_defunct")) return 0;
  365.  
  366. var selector = "b", val1, val2;
  367. if (idx == 1) selector = "td:nth-child(2) p";
  368. else if (idx == 2) selector = "td:nth-child(3) p";
  369. else if (idx == 3) selector = "td:nth-child(4) time";
  370.  
  371.  
  372. if (idx == 0)
  373. {
  374. val1 = row1.querySelector(selector).textContent.toLowerCase();
  375. val2 = row2.querySelector(selector).textContent.toLowerCase();
  376. }
  377. else if (idx == 1)
  378. {
  379. if (row1.hasAttribute("installs"))
  380. {
  381. val1 = parseInt(row1.getAttribute("installs"));
  382. val2 = parseInt(row2.getAttribute("installs"));
  383. }
  384. else
  385. {
  386. val1 = parseInt(row1.querySelector(selector).textContent);
  387. val2 = parseInt(row2.querySelector(selector).textContent);
  388. }
  389. }
  390. else if (idx == 2)
  391. {
  392. if (row1.hasAttribute("rating"))
  393. {
  394. val1 = parseInt(row1.getAttribute("rating"));
  395. val2 = parseInt(row2.getAttribute("rating"));
  396. }
  397. else
  398. {
  399. val1 = parseInt(row1.querySelector(selector).textContent);
  400. val2 = parseInt(row2.querySelector(selector).textContent);
  401. }
  402. }
  403. else if (idx == 3)
  404. {
  405. val1 = new Date(row1.querySelector(selector).getAttribute("datetime")).getTime();
  406. val2 = new Date(row2.querySelector(selector).getAttribute("datetime")).getTime();
  407. }
  408.  
  409. if (val1 < val2) return -1;
  410. if (val1 > val2) return 1;
  411.  
  412. return 0;
  413. }
  414.  
  415. addScriptListingNumbers();
  416. return false;
  417. }
  418.  
  419.  
  420. //Fix broken saved data caused by FireFox 35 security issues
  421. (function ()
  422. {
  423. if (GM_getValue("HasBeenFixed", false)) return;
  424. alert("Due to changes in FireFox 35 GreaseMonkey GM_listValues got broken and with it, it has ruined OUJS-1 save. This will attempt to fix it. If it fails then remove, restart firefox and install again");
  425.  
  426. var scriptIDsPresent = {};
  427.  
  428. var usernames = getSavedUsernames();
  429. for (var i = 0; i < usernames.length; i++)
  430. {
  431. UpdateFireFox35BrokenData(usernames[i]);
  432. }
  433.  
  434.  
  435. var id, ids = GM_listValues();
  436. for (var i = ids.length - 1; i >= 0, id = ids[i]; i--)
  437. {
  438. if (id.match(/s\d\d\d\d/i) && !scriptIDsPresent[id])
  439. {
  440. GM_deleteValue(id);
  441. }
  442. }
  443. GM_setValue("HasBeenFixed", true);
  444.  
  445.  
  446. function UpdateFireFox35BrokenData(username)
  447. {
  448. var name,
  449. remove = false,
  450. updated = false,
  451. scriptNames = {},
  452. meta = JSON.parse(GM_getValue("USER-S:" + username));
  453.  
  454. for (var i = 0; i < meta.history.length; i++)
  455. {
  456. remove = false;
  457. for (var key in meta.history[i])
  458. {
  459. if (key.match(/s\d\d\d\d/i))
  460. {
  461. name = GM_getValue(key);
  462.  
  463. if (!scriptNames[name])
  464. {
  465. //console.log(name, key, scriptNames[name]);
  466. scriptNames[name] = key;
  467. scriptIDsPresent[key] = name;
  468. }
  469. else if (scriptNames[name] != key) //Value introduced after FF35 update and is invalid
  470. {
  471. updated = true;
  472. remove = true;
  473. }
  474. }
  475. }
  476.  
  477. if (remove)
  478. {
  479. meta.history.splice(i, 1);
  480. i = 0;
  481. }
  482. }
  483.  
  484. if (updated)
  485. {
  486. meta.current = meta.history[meta.history.length - 1];
  487. console.log(meta.current);
  488. GM_setValue("USER-S:" + username, JSON.stringify(meta));
  489. }
  490. }
  491.  
  492. function getSavedUsernames()
  493. {
  494. var arr = new Array();
  495. var names = GM_listValues();
  496.  
  497. for (var i = 0; i < names.length; i++)
  498. {
  499. if (names[i].match(/^USER-S:/))
  500. {
  501. arr.push(names[i].substr(7));
  502. }
  503. }
  504.  
  505. return arr;
  506. }
  507. })();
  508.  
  509. (function ()
  510. {
  511. if (document.getElementsByClassName("navbar-brand").length) document.getElementsByClassName("navbar-brand")[0].innerHTML = 'OpenUserJS-1 <sub><a href="/users/TimidScript" style="font-size:0.6em; color: cyan;">TimidScript</a></sub>';
  512. var timestamp = Date.now();
  513. var loggedUsername = "";
  514.  
  515. TSL.addStyle("ppp", ".progress {background-color: #E74C3C;} .progress-bar-good {background-color: #499E49;} .progress * {color: white;}");
  516. TSL.addStyle("Image-Limiter", ".user-content img, .topic-post-contents img {max-width: 98%; border: 1px solid blue; padding: 2px; color: yellow; margin: 5px 0; box-shadow: 5px 5px 2px #888888;}"
  517. + ".topic-post-contents img:last-child {margin-bottom: 10px;}"
  518. );
  519. TSL.addStyle("BetterQuotes", 'blockquote {font-size:14px;font-style: italic;border-left: 7px solid #DFE1E1;margin: 10px 30px;padding: 0px 5px; }');
  520.  
  521. TSL.addStyle("OUJS-ORDER", ".descen {background-color: rgba(200,255,255,0.6);} .ascen {background-color: rgba(255,220,255, 0.6);} ");
  522. //#F0DDFD #DAFBF6 #C2F4F7 #FF0
  523.  
  524. if (document.querySelector(".fa-sign-out") != undefined)
  525. {
  526. loggedUsername = document.querySelector('ul li a[href^="/users/"]').textContent;
  527.  
  528. var meta = GM_getValue("USER-S:" + loggedUsername);
  529. if (!meta) //Create new user data.
  530. {
  531. meta = {};
  532. meta.current = {};
  533. meta.history = new Array();
  534. GM_setValue("USER-S:" + loggedUsername, JSON.stringify(meta));
  535. }
  536.  
  537. meta = GM_getValue("USER-P:" + loggedUsername);
  538. if (!meta)
  539. {
  540. GM_setValue("USER-P:" + loggedUsername, JSON.stringify({}));
  541. }
  542.  
  543. //perform cleanup by deleting old post records
  544. var names = getSavedUsernames();
  545. for (var i = 0; i < names.length; i++)
  546. {
  547. var meta = JSON.parse(GM_getValue("USER-P:" + names[i]));
  548.  
  549. for (var key in meta)
  550. {
  551. if (timestamp - meta[key].timestamp > DAY * 14) //30 Days
  552. {
  553. delete meta[key];
  554. GM_deleteValue(key);
  555. }
  556. }
  557.  
  558. GM_setValue("USER-P:" + names[i], JSON.stringify(meta));
  559. }
  560. }
  561.  
  562. document.querySelector("nav a[href='/forum']").href = "/all"; //Change the "Discuss" link to point directly to "All"
  563. var pathname = document.location.pathname;
  564. if (pathname.match(/^\/$|^\/group\/\w+|^\/groups/))
  565. {
  566. console.log("OUJS-1: Scripts/Library/Groups listings");
  567. addLibraryIcons();
  568. addScriptListingNumbers();
  569.  
  570.  
  571. var columns = columns = ["name", "installs", "rating", "updated"],
  572. search = document.location.search,
  573. orderBy = search.match(/orderBy=(\w+)/i);
  574.  
  575. orderBy = (orderBy) ? orderBy[1] : "rating";
  576.  
  577. if (search.match(/library/i)) columns = ["name", "rating", "updated"];
  578. else if (pathname.match(/group/i)) columns = ["name", "size", "rating"];
  579.  
  580. TSL.addClass(document.querySelector(".panel > table > thead th:nth-child(" +
  581. (function ()
  582. {
  583. for (var i = 0; i < columns.length; i++)
  584. {
  585. console.log(i, columns[i].toLowerCase(), orderBy);
  586. if (orderBy.toLowerCase() == columns[i].toLowerCase()) return i + 1;
  587. }
  588. return 3;
  589. }
  590. )() + ")"), ((search.match(/orderDir=asc/i)) ? "ascen" : "descen"));
  591. }
  592. else if (pathname.match(/^\/users\/\w+$/i)) //User profile page
  593. {
  594. console.log("OUJS-1: Profile page");
  595. var container = document.getElementsByClassName("container-fluid")[0];
  596. getScriptListings(document.URL + "/scripts/?p=1");
  597.  
  598. //Search box
  599. //document.getElementsByClassName("col-sm-4")[0].innerHTML += '<div class="panel panel-transparent"><div class="input-group col-xs-12"><form action="" method="get"><div class="input-group col-xs-12"><input name="q" placeholder="Search Profile Scripts" class="search form-control" value="" type="text"><span class="input-group-btn"><button class="btn btn-default" type="submit"><i class="fa fa-search"></i></button></span></div></form></div></div>';
  600. //window.history.pushState(null, "", document.URL + "/scripts"); //Change document URL
  601. }
  602. else if (pathname.match(/^\/users\/.+\/comments/i)) //User profile comments page
  603. {
  604. console.log("OUJS-1: Profile comments page");
  605. TSL.addStyle("UserScripts", ".topic-title .breadcrumb {margin-bottom: 0px;}");
  606. }
  607. else if (pathname.match(/^\/users\/\w+\/scripts/i)) //User script listing
  608. {
  609. TSL.addStyle("UserScripts", "ul.pagination {display: none;}");
  610. var nextpage = document.querySelector("ul.pagination > .active + li > a");
  611. if (nextpage) getScriptListings(nextpage.href);
  612. else if (document.querySelector(".table .tr-link")) amendUserScriptListing();
  613. }
  614. else if (pathname.match(/^\/scripts\/[^\/]+\/[^\/]+$/i)) //Script page
  615. {
  616. console.log("OUJS-1: Scripts page");
  617. scriptPageAmendRateArea();
  618. }
  619. else if (pathname.match(/^\/(all|issues|garage|corner|discuss|announcements)$/))
  620. {
  621. console.log("OUJS-1: Forum");
  622. TSL.addStyle("NinjaStyle", ".breadcrumb {padding: 8px; } .breadcrumb > li + li {margin-left: 2px;} .breadcrumb > li + li:before { content: ''; padding: 0px;} .breadcrumb > li {width: 120px; text-align:center; border: 1px solid #CFD2D2; border-radius: 5px; } ");
  623. var el = document.querySelector(".breadcrumb");
  624. //el.className = "fishMonkey";
  625. el.innerHTML = '<li><a href="/all" title="Amalgamation of all discussion boards">All</a></li>' +
  626. '<li><a href="/issues" title="Issues raised on scripts">Script Issues</a></li>' +
  627. '<li><a href="/garage" title="Get help with script development">Developer Help</a></li>' +
  628. '<li><a href="/corner" title="Propose ideas and request user-scripts">Script Requests</a></li>' +
  629. '<li><a href="/discuss" title="Off-topic discussions">General</a></li>' +
  630. '<li><a href="/announcements" title="Official site announcements">Announcements</a></li>';
  631.  
  632.  
  633. TSL.addStyle("WoloSD3", ".selectedDiscuss {background-color: #CACAF5; border-color: #00F !important;} .selectedDiscuss a {color: #00F;}");
  634. el.querySelector("li > a[href='" + pathname + "']").parentNode.className = "selectedDiscuss";
  635. }
  636. else if ((pathname.match(/^\/libs\/[^\/]+\/[^\/]+$/i))) amendLibraryPage();
  637.  
  638. displayNewIssues();
  639.  
  640. function displayNewIssues()
  641. {
  642. if (pathname.match(/^\/(issues|forum|all|corner|garage|discuss|announcements)/))
  643. {
  644. var usernames = getSavedUsernames();
  645. for (var i = 0, username; i < usernames.length, username = usernames[i]; i++)
  646. {
  647. var els = document.querySelectorAll('.label-info a[href="/users/' + username + '"]');
  648. for (var j = 0; j < els.length; j++) els[j].parentElement.style.backgroundColor = "#6AB046";
  649. }
  650. }
  651.  
  652. if (pathname.match(/^\/(issues|forum|all)/))
  653. {
  654. TSL.addStyle("ForumHelper", "body .table .scriptIssues td {background-color: #BCF3F3 !important;}"
  655. + ".scriptIssues .tr-link-a, .scriptIssues td:nth-child(2) * {color: #04A263 !important;}"
  656. + "body .table .scriptIssues.closed td {background-color: #ECEDED !important;}"
  657. + ".scriptIssues.closed .tr-link-a, .scriptIssues.closed td:nth-child(2) * {color: gray !important;}"
  658. + "body .table .newposts td:nth-child(4) {color: red;}"
  659. + "body .table .newposts td:nth-child(4) sup {color: green;}"
  660. + "#newIssues {background-color: #D6FDF7; padding: 1px 20px; font-weight: 600; color: green;}"
  661. + "#newIssues span {margin-right: 30px;}"
  662. );
  663.  
  664. var notice = document.createElement("div");
  665. notice.id = "newIssues";
  666. var usernames = getSavedUsernames();
  667. document.querySelector(".table-responsive").parentElement.insertBefore(notice, document.querySelector(".table-responsive"));
  668. for (var i = 0, username; i < usernames.length, username = usernames[i]; i++)
  669. {
  670. var c = getNewPostCount(username);
  671. if (c.length > 0)
  672. {
  673. var el = document.createElement("span");
  674. if (usernames.length > 1) el.textContent = "(" + username + ") ";
  675. el.textContent += "New posts detected on " + c.length + " issues/threads";
  676. notice.appendChild(el);
  677. }
  678. }
  679.  
  680. if (!notice.textContent) notice.textContent = "No new issues detected";
  681. return;
  682. }
  683.  
  684. if (window.sessionStorage.getItem("NewIssuesStamp") && Date.now() - window.sessionStorage.getItem("NewIssuesStamp") < 20000)
  685. {
  686. var doc = document.implementation.createHTMLDocument("OUJS");
  687. doc.documentElement.innerHTML = window.sessionStorage.getItem("NewIssuesDoc");
  688. NewIssues(doc);
  689. }
  690. else
  691. {
  692. GetIssueCount();
  693. setInterval(GetIssueCount, 6000);
  694. //setInterval(GetIssueCount, 180000);
  695. }
  696.  
  697. function GetIssueCount()
  698. {
  699. xhrPage("https://openuserjs.org/issues", function (xhr, doc)
  700. {
  701. if (doc)
  702. {
  703. window.sessionStorage.setItem("NewIssuesDoc", xhr.responseText);
  704. window.sessionStorage.setItem("NewIssuesStamp", Date.now());
  705. NewIssues(doc);
  706. }
  707. });
  708. }
  709.  
  710. function NewIssues(doc)
  711. {
  712. TSL.removeNode("TheIssueCounter");
  713. TSL.removeNode("IssueNoticeBoard");
  714. if (GM_getValue("EditTitle")) document.title = document.title.replace(/^\[\d+\]\s*/, "");
  715.  
  716. var count = 0;
  717. var usernames = getSavedUsernames();
  718. var issues = document.createElement("section");
  719. issues.id = "IssueNoticeBoard";
  720. issues.appendChild(document.createElement("ul"));
  721.  
  722. for (var i = 0, username, arr; i < usernames.length, username = usernames[i]; i++)
  723. {
  724. var meta = JSON.parse(GM_getValue("USER-P:" + username));
  725. var updated = false;
  726. arr = getNewPostCount(username, doc);
  727. count += arr.length;
  728. for (var j = 0, li; j < arr.length; j++)
  729. {
  730. li = TSL.createElementHTML("<li><a href='" + arr[j].url + "'>" + arr[j].postTitle + "</a></li>");
  731. li.appendChild(TSL.createElementHTML("<span>❌</span>"));
  732. issues.firstElementChild.appendChild(li);
  733.  
  734. li.lastElementChild.onclick = RemoveNotice;
  735. li.lastElementChild.postdata = { timestamp: timestamp, replies: arr[j].replies };
  736. li.lastElementChild.postID = arr[j].postID;
  737. li.lastElementChild.setAttribute("username", username);
  738.  
  739. if (loggedUsername == username && decodeURI(document.location.pathname) == decodeURI(arr[j].url))
  740. {
  741. meta[arr[j].postID] = {}
  742. meta[arr[j].postID].timestamp = timestamp;
  743. meta[arr[j].postID].replies = arr[j].replies;
  744. updated = true;
  745. }
  746. }
  747.  
  748. if (updated) GM_setValue("USER-P:" + username, JSON.stringify(meta));
  749. }
  750.  
  751. if (count > 0)
  752. {
  753. if (GM_getValue("EditTitle")) document.title = "[" + count + "] " + document.title;
  754. var discuss = document.querySelector('.nav a[href="/all"]');
  755. var counter = document.createElement("span");
  756. counter.id = "TheIssueCounter";
  757. counter.style.marginLeft = "3px";
  758. discuss.appendChild(counter);
  759. counter.innerHTML += '(<span id="newIssues" style="color: lime; display:inline-block; font-weight: 600;">' + count + '</span>)';
  760.  
  761. TSL.addStyle("OUJS-IL-BT", "#IssuesListing {position: absolute;background-color: #2C3E50; color: white; font-weight: 600; z-index: 99999;"
  762. + "font-size:12px; padding: 3px 8px; color: white; border: 1px solid black; box-sizing: border-box;}"
  763. + "#IssuesListing ul {margin: 0; padding-left: 10px;}"
  764. + "#IssuesListing a {display: inline-block; color: orange; }"
  765. + "#IssuesListing li:hover {background-color: #435A71;}"
  766. + "#IssuesListing span {color: red; margin-left: 15px; cursor: default; display: inline-block;"
  767. );
  768.  
  769. issues.id = "IssuesListing";
  770. document.body.appendChild(issues);
  771.  
  772. var pos = TSL.getAbsolutePosition(discuss.parentElement);
  773. issues.style.top = (discuss.clientHeight + pos.top) + "px";
  774. if (issues.clientWidth + pos.left - 10 > window.innerWidth) issues.style.right = "2px";
  775. else issues.style.left = (pos.left - 30) + "px";
  776.  
  777. TSL.addStyle("OUJS-IL-BT2", "#IssuesListing {display: none;}");
  778.  
  779. ToggleIssuesView(discuss, issues);
  780. }
  781. }
  782. }
  783.  
  784. function getScriptListings(url)
  785. {
  786. xhrPage(url, xhrCallback);
  787.  
  788. function xhrCallback(xhr, doc)
  789. {
  790. if (!doc) return;
  791. var tb = document.querySelector(".table tbody");
  792. var scripts = doc.getElementsByClassName("tr-link");
  793. if (tb)
  794. {
  795. while (scripts.length > 0) tb.appendChild(scripts[0]);
  796. }
  797. else
  798. {
  799. var container = document.getElementsByClassName("col-xs-12")[0];
  800. var scriptPanel = doc.getElementsByClassName("panel panel-default")[0];
  801. container.appendChild(document.importNode(scriptPanel, true));
  802. }
  803.  
  804. var nextpage = doc.querySelector("ul.pagination > .active + li > a");
  805. if (nextpage) getScriptListings(document.location.origin + nextpage.href, xhrCallback);
  806. else amendUserScriptListing();
  807. }
  808. }
  809.  
  810. function amendUserScriptListing()
  811. {
  812. TSL.addStyle("CoolColors", ".header_row {padding: 0; font-size: 16px;}"
  813. + ".header_row > td {padding: 0 10px !important; font-weight: 700;}"
  814. + "tr._defunct {background-color: #FFF5F3;}"
  815. + "tr.header_row._defunct {background-color: pink; color: red;}"
  816. + "tr._library {background-color: #F7FDF7;}"
  817. + "tr.header_row._library {background-color: #ACF9AC; color: green;}"
  818. );
  819.  
  820. var tbody = document.querySelector(".table tbody");
  821. //var tbody = document.createElement("tbody");
  822. var rows = tbody.querySelectorAll(".tr-link");
  823.  
  824. ////Put libraries at the bottom of the table
  825. var added = false;
  826. for (var i = 0, row, row2, cell; i < rows, row = rows[i]; i++)
  827. {
  828. if (!row.querySelector(".script-version"))
  829. {
  830. if (!added)
  831. {
  832. added = true;
  833. row2 = tbody.insertRow(-1);
  834. row2.className = "header_row _library";
  835. cell = row2.insertCell(-1);
  836. cell.setAttribute("colspan", 5);
  837. cell.textContent = "Libraries";
  838. }
  839. TSL.addClass(row, "_library");
  840. tbody.appendChild(row);
  841. }
  842. }
  843.  
  844. //Put defunct scripts at the bottom of the table
  845. added = false;
  846. for (var i = 0, row, row2, cell; i < rows, row = rows[i]; i++)
  847. {
  848. var version = row.querySelector(".script-version")
  849. if (version && version.textContent.match(/defunct|depreciated|obselete/i))
  850. {
  851. if (!added)
  852. {
  853. added = true;
  854. row2 = tbody.insertRow(-1);
  855. row2.className = "header_row _defunct";
  856. cell = row2.insertCell(-1);
  857. cell.setAttribute("colspan", 5);
  858. cell.textContent = "Depreciated scripts that are no longer begin supported";
  859. }
  860. TSL.addClass(row, "_defunct");
  861. tbody.appendChild(row);
  862. }
  863. }
  864.  
  865. TSL.addStyle("HeaderPointer", ".col-xs-12 .table th a {cursor: pointer;}");
  866. var headers = document.querySelectorAll(".table thead th a");
  867. for (var i = 0; i < headers.length; i++)
  868. {
  869. headers[i].removeAttribute("href");
  870. headers[i].onclick = SortScriptTable;
  871. }
  872.  
  873. appendHistory();
  874. addLibraryIcons();
  875.  
  876. //Load missing icons. Occurs in profile page
  877. els = document.querySelectorAll("i.fa.fa-fw.fa-file-code-o");
  878. for (var i = 0, el, img; i < els.length; i++)
  879. {
  880. el = els[i];
  881. img = document.createElement("img");
  882. img.src = el.parentElement.getAttribute("data-icon-src");
  883. el.parentElement.appendChild(img);
  884. TSL.removeNode(el);
  885. }
  886.  
  887. var idx = GM_getValue("SortHeader", 3);
  888. var SortAscending = !GM_getValue("SortDescending", true);
  889. headers[idx].click();
  890. if (SortAscending) headers[idx].click();
  891. //addScriptListingNumbers();
  892. }
  893.  
  894. function addLibraryIcons()
  895. {
  896. TSL.addStyle("IconReplacer", ".script-icon, .script-icon img {display:inline-block; height: 16px; width: 16px;}")
  897. var els = document.querySelectorAll("._library .script-icon.hidden-xs i");
  898. if (document.location.search.match(/^\?library=/i)) els = document.querySelectorAll(".script-icon.hidden-xs i");
  899. for (var i = 0, el, img; i < els.length; i++)
  900. {
  901. el = els[i];
  902. img = document.createElement("img");
  903. img.src = "https://i.imgur.com/pFNqMgL.png"
  904. el.parentElement.appendChild(img);
  905. TSL.removeNode(el);
  906. }
  907. }
  908.  
  909. function getUID(name, prefix)
  910. {
  911. if (!prefix) prefix = "s";
  912. var id, ids = GM_listValues();
  913.  
  914. for (var i = 0; i < ids.length, id = ids[i]; i++)
  915. {
  916. if (id[0] == prefix && id.match(/.\d+$/) && GM_getValue(id) == name) return id;
  917. }
  918.  
  919. while (true)
  920. {
  921. id = (prefix || "s") + ("0000" + Math.floor(Math.random() * 10000 + 1)).slice(-4);
  922. if (GM_getValue(id, 0) == 0)
  923. {
  924. GM_setValue(id, name);
  925. return id;
  926. }
  927. }
  928. }
  929.  
  930. function appendHistory()
  931. {
  932. var username = document.querySelector(".user-name").textContent;
  933. var meta = GM_getValue("USER-S:" + username);
  934. if (!meta) return;
  935.  
  936. TSL.addStyle("HistoricalColors", ".dStatZ {color: blue;} .dStatN {color: red;} .dStatP {color: green;} .dStatNew {color: blue;} .dStatP:before, .dStatNew:before { content: '+';} ");
  937.  
  938. meta = JSON.parse(meta);
  939. var oldCurrent = meta.current;
  940. meta = JSON.parse(GM_getValue("USER-S:" + username));
  941.  
  942. var rows = document.querySelectorAll(".table .tr-link");
  943. for (var i = 0, row; i < rows, row = rows[i]; i++)
  944. {
  945. var scriptname = row.querySelector(".tr-link-a > b").textContent;
  946. var scriptID = getUID(scriptname);
  947. rows[i].setAttribute("ScriptID", scriptID);
  948.  
  949. data = {};
  950. data.installs = parseInt(row.querySelector("td:nth-child(2) p").textContent);
  951. data.rating = parseInt(row.querySelector("td:nth-child(3) p").textContent);
  952. row.setAttribute("installs", data.installs);
  953. row.setAttribute("rating", data.rating);
  954. meta.current[scriptID] = data;
  955. }
  956.  
  957. meta.current.timestamp = timestamp;
  958. if (meta.history.length == 0 || timestamp - meta.history[meta.history.length - 1].timestamp >= DAY)
  959. {
  960. meta.history.push(meta.current);
  961. }
  962.  
  963. DisplayStats(oldCurrent, meta.current);
  964.  
  965. //alert("Bring Up Console");
  966. //for (var i = meta.history.length - 1, j = HistoryMAX - HistoryMIN; i >= 0; i--, j--)
  967. //{
  968. // console.warn(i, timePassed(meta.history[i].timestamp));
  969. //}
  970.  
  971. if (meta.history.length > HistoryMAX) //History Cleanup greater than
  972. {
  973. for (var i = 0, j = HistoryMAX - HistoryMIN; i < HistoryMAX - HistoryMIN; i++, j--)
  974. {
  975. if (timestamp - meta.history[i].timestamp > DAY * 7 * j)
  976. {
  977. //console.log("GREATER", i, timePassed(meta.history[i].timestamp));
  978. meta.history.splice(i, 1);
  979. break;
  980. }
  981. }
  982. }
  983. if (meta.history.length > HistoryMAX) //History Cleanup less than
  984. {
  985. for (var i = HistoryMIN, j = 1; i < HistoryMAX; i++, j++)
  986. {
  987. if (timestamp - meta.history[i].timestamp < DAY * 7 * j)
  988. {
  989. //console.log("LESSER", i, timePassed(meta.history[i].timestamp));
  990. meta.history.splice(i, 1);
  991. break;
  992. }
  993. }
  994. }
  995. //if (meta.history.length > HistoryMAX) console.log("mm", timePassed(meta.history[i].timestamp));
  996. if (meta.history.length > HistoryMAX) meta.history.splice(HistoryMIN - 1, 1); //Last Min Date
  997.  
  998. GM_setValue("USER-S:" + username, JSON.stringify(meta));
  999.  
  1000. if (meta.history.length == 1 && meta.history[0].timestamp == meta.current.timestamp) return;
  1001.  
  1002. TSL.addStyle("Panel-History", "#HistoryList {padding-left: 20px;} "
  1003. + "#HistoryList li {cursor: pointer; background-color: #E0F9FF; border-radius: 5px; margin-bottom: 1px; padding: 0 5px;}"
  1004. + "#HistoryList li:hover {background-color: #E9FFE0;}"
  1005. + "#HistoryList li.selected {background-color: #FBE0FF;}"
  1006. );
  1007.  
  1008. var panel = document.createElement("div");
  1009. panel.className = "panel panel-default";
  1010. panel.appendChild(document.createElement("div"));
  1011. document.querySelector(".container-fluid.col-sm-4").appendChild(panel);
  1012.  
  1013. //Display History List
  1014. panel = panel.firstElementChild;
  1015. panel.className = "panel-body";
  1016.  
  1017. var el = document.createElement("h3");
  1018. el.textContent = "Stats History";
  1019. panel.appendChild(el);
  1020.  
  1021. el = document.createElement("ul");
  1022. el.id = "HistoryList";
  1023. panel.appendChild(el);
  1024.  
  1025. el.data = meta.current;
  1026.  
  1027. li = document.createElement("li");
  1028. li.textContent = "Current: " + timePassed(oldCurrent.timestamp);
  1029. li.data = oldCurrent;
  1030. li.onclick = ClickedHistory;
  1031. el.appendChild(li);
  1032.  
  1033. for (var i = meta.history.length - 1, li, remove; i >= 0; i--)
  1034. {
  1035. if (meta.history[i].timestamp != meta.current.timestamp && meta.history[i].timestamp != oldCurrent.timestamp)
  1036. {
  1037. li = document.createElement("li");
  1038. li.textContent = timePassed(meta.history[i].timestamp);
  1039. li.data = meta.history[i];
  1040. li.onclick = ClickedHistory;
  1041.  
  1042. remove = document.createElement("span");
  1043. remove.textContent = "❌"; //❎❌
  1044. remove.setAttribute("style", "float: right; color: red;");
  1045. remove.onclick = removeRecord;
  1046. li.appendChild(remove);
  1047. el.appendChild(li);
  1048. }
  1049. }
  1050.  
  1051. el.firstElementChild.className = "selected";
  1052. DisplayStats(oldCurrent, meta.current);
  1053.  
  1054. function removeRecord(e)
  1055. {
  1056. e.stopImmediatePropagation();
  1057.  
  1058. if (!confirm("Do you want to remove this history record?")) return;
  1059.  
  1060. var meta = GM_getValue("USER-S:" + username);
  1061. meta = JSON.parse(meta);
  1062. var record = this.parentElement.data.timestamp;
  1063.  
  1064. for (var i = 0; i < meta.history.length; i++)
  1065. {
  1066. if (meta.history[i].timestamp == record)
  1067. {
  1068. meta.history.splice(i, 1);
  1069. GM_setValue("USER-S:" + username, JSON.stringify(meta));
  1070. TSL.removeNode(this.parentElement);
  1071. break;
  1072. }
  1073. }
  1074. }
  1075.  
  1076.  
  1077. function timePassed(old)
  1078. {
  1079. var days, hrs, mins, secs, ms, ret;
  1080.  
  1081. ms = timestamp - old;
  1082. secs = Math.floor(ms / (1000)) % 60;
  1083. mins = Math.floor(ms / (60 * 1000)) % 60;
  1084. hrs = Math.floor(ms / (60 * 60 * 1000)) % 24;
  1085. days = Math.floor(ms / (60 * 60 * 1000 * 24) % 7);
  1086. weeks = Math.floor(ms / (60 * 60 * 1000 * 24) / 7);
  1087.  
  1088. if (weeks) return weeks + "week" + isPlural(weeks) + " and " + days + "day" + isPlural(days);
  1089. if (days) return days + "day" + isPlural(days) + " and " + hrs + "hr" + isPlural(hrs);
  1090. if (hrs) return hrs + "hr" + isPlural(hrs) + " and " + mins + "min" + isPlural(mins);
  1091. return mins + "min" + isPlural(mins) + " and " + secs + "sec" + isPlural(secs);
  1092.  
  1093. function isPlural(val)
  1094. {
  1095. if (val == 1) return "";
  1096.  
  1097. return "s";
  1098. }
  1099. }
  1100. }
  1101.  
  1102. function amendLibraryPage()
  1103. {
  1104. TSL.addStyle("LibOS", ".form-group {margin-bottom: 5px;} #copyBox {text-align: right; } #copyBtn{cursor:pointer; display: inline-block;"
  1105. + "color: #273646; border-radius: 5px; border: 1px solid #273646; background-color: #D6D6F5; width: 140px; text-align:center;}"
  1106. + ".input-group .form-control {cursor: default}"
  1107. + "#masterScripts .script-icon img {width: 26px; height: 26px;}"
  1108. + "#masterScripts ul {padding: 5px; font-size: 12px;}"
  1109. + "#masterScripts li {margin-bottom: 3px;}"
  1110. );
  1111.  
  1112.  
  1113. scriptPageAmendRateArea();
  1114.  
  1115. var require = document.querySelector(".input-group .form-control");
  1116. require.value = require.value.replace(/^http:/i, "https:");
  1117. require.readOnly = true;
  1118.  
  1119. var copyBox = document.createElement("div");
  1120. copyBox.id = "copyBox";
  1121. copyBox.innerHTML = "<span id='copyBtn'>Copy to Clipboard</span>";
  1122.  
  1123. copyBox.firstChild.setAttribute("meta", "// @require\t\t" + require.value);
  1124. copyBox.firstChild.onclick = function (e)
  1125. {
  1126. GM_setClipboard(this.getAttribute("meta"));
  1127. this.textContent = "Copied";
  1128. this.style.backgroundColor = "#FBF6AB";
  1129. setTimeout(function (btn) { btn.textContent = "Copy to Clipboard"; btn.style.backgroundColor = null; }, 2000, this);
  1130. }
  1131.  
  1132. var scriptmeta = document.querySelector(".script-meta");
  1133. scriptmeta.parentElement.insertBefore(copyBox, scriptmeta);
  1134.  
  1135. var panel = document.querySelector(".panel-default .panel-body h4");
  1136. if (panel)
  1137. {
  1138. panel.textContent += " (" + panel.parentElement.getElementsByTagName("li").length + ")";
  1139. panel = panel.parentElement.parentElement;
  1140. panel.id = "masterScripts";
  1141. var side = document.querySelector(".col-sm-4");
  1142. side.appendChild(panel);
  1143. //panel.getElementsByTagName("ul")[0].style.paddingLeft = "30px";
  1144. }
  1145. }
  1146.  
  1147. function scriptPageAmendRateArea()
  1148. {
  1149. var pd = document.querySelector(".col-sm-4.container-fluid .panel-default .panel-body");
  1150.  
  1151. //Encourage rating
  1152. var notice = document.createElement("div");
  1153. notice.textContent = "If you like the script, show your appreciation to the author by rating and favouring it.";
  1154. notice.setAttribute("style", "padding: 0 10px; color: red; font-weight: 700; border-radius: 5px; background-color: yellow;");
  1155. pd.appendChild(notice);
  1156.  
  1157. var rating = parseInt(pd.querySelector(".row p").textContent.match(/-?\d+$/)[0]),
  1158. votes = parseInt(pd.querySelector(".progress-bar-good").textContent),
  1159. votes = isNaN(votes) ? 0 : votes,
  1160. votesDown = (votes - rating) / 2,
  1161. flags = parseInt(pd.querySelector(".progress-bar-danger").textContent),
  1162. bar1 = pd.querySelector(".progress");
  1163.  
  1164. bar1.firstElementChild.innerHTML = "<span style='color: yellow;'>+" + (votes - votesDown) + "</span>";
  1165. pd.querySelector(".row p").innerHTML = pd.querySelector(".row p").innerHTML.replace(/\d+$/, rating + " (" + votes + " Votes)");
  1166. pd.querySelector(".progress-bar-danger").innerHTML = "<span style='color: yellow;'>-" + votesDown + "</span>";
  1167. pd.querySelector(".progress-bar-danger").style.textAlign = "center";
  1168. pd.querySelector(".progress-bar-danger").style.width = (100-parseFloat (pd.querySelector(".progress-bar-good").style.width)) + "%";
  1169. }
  1170.  
  1171. function xhrPage(url, callback)
  1172. {
  1173. GM_xmlhttpRequest({
  1174. url: url,
  1175. method: "GET",
  1176. //overrideMimeType: 'text/plain; charset=x-user-defined',
  1177. //overrideMimeType: 'document',
  1178. //overrideMimeType: "responseXML",
  1179. //headers: { "User-agent": navigator.userAgent},
  1180. headers: { "User-agent": navigator.userAgent, "Accept": "text/xml" },
  1181. onload: function (xhr)
  1182. {
  1183. if (xhr.status == 200)
  1184. {
  1185. //var doc = new DOMParser().parseFromString(xhr.responseText, 'text/xml');
  1186. var doc = document.implementation.createHTMLDocument("OUJS");
  1187. doc.documentElement.innerHTML = xhr.responseText;
  1188. callback(xhr, doc);
  1189. }
  1190. else callback(xhr, null);
  1191. }
  1192. });
  1193. }
  1194.  
  1195. function getSavedUsernames()
  1196. {
  1197. var arr = new Array();
  1198. var names = GM_listValues();
  1199.  
  1200. for (var i = 0; i < names.length; i++)
  1201. {
  1202. if (names[i].match(/^USER-S:/))
  1203. {
  1204. arr.push(names[i].substr(7));
  1205. }
  1206. }
  1207.  
  1208. return arr;
  1209. }
  1210.  
  1211. function getNewPostCount(username, doc)
  1212. {
  1213. var posts;
  1214. var arr = new Array();
  1215.  
  1216. if (doc) posts = doc.querySelectorAll('.table td:nth-child(2) span a[href*="/' + username + '/"]');
  1217. else posts = document.querySelectorAll('.table td:nth-child(2) span a[href*="/' + username + '/"]');
  1218.  
  1219. var meta = JSON.parse(GM_getValue("USER-P:" + username));
  1220.  
  1221. for (var i = 0, post; i < posts.length, post = posts[i]; i++)
  1222. {
  1223. while (post.tagName != "TR") post = post.parentElement;
  1224. if (!doc)
  1225. {
  1226. TSL.addClass(post, "scriptIssues");
  1227. if (post.querySelector(".label-danger")) TSL.addClass(post, "closed");
  1228. }
  1229. var replies = parseInt(post.querySelector("td:nth-child(4)").textContent);
  1230. var postTitle = post.querySelector(".tr-link-a").textContent;
  1231. var postID = getUID(postTitle, "p");
  1232.  
  1233. if (!meta[postID]) meta[postID] = {};
  1234.  
  1235. if (meta[postID].replies != replies)
  1236. {
  1237. TSL.addClass(post, "newposts");
  1238.  
  1239. if (!doc)
  1240. {
  1241. var diff = document.createElement("sup");
  1242. post.querySelector("td:nth-child(4) p").appendChild(diff);
  1243. if (replies == 0) diff.textContent = "new";
  1244. else diff.textContent = "+" + ((meta[postID].replies == undefined) ? replies : replies - meta[postID].replies);
  1245. }
  1246.  
  1247. var val = { postID: postID, postTitle: postTitle, replies: replies, url: post.querySelector(".tr-link-a").getAttribute("href") };
  1248. if (post.querySelector("td:nth-child(3) .label:last-child").textContent != username) arr.push(val);
  1249. else
  1250. {
  1251. meta[postID].replies = replies;
  1252. meta[postID].timestamp = timestamp;
  1253. }
  1254. }
  1255. }
  1256.  
  1257. GM_setValue("USER-P:" + username, JSON.stringify(meta));
  1258. return arr;
  1259. }
  1260. })();