Mad_Kiwi

8chan Qanon research Javascript - anti Spam & trolling +++

Oct 1st, 2018
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 40.50 KB | None | 0 0
  1. /* Enter your own Javascript code here... */
  2. /* You can include JS files from remote servers, for example: */
  3. /* load_js("http://example.com/script.js"); */
  4. /*
  5. * q.js v2018.3-6.1
  6. * http://anonsw.github.io/8chjs/
  7. */
  8.  
  9. //IMAGE BLACKLIST CODING
  10. var imageBlacklist = [] ;
  11. function loadImageBlacklist() { JSON.parse(localStorage.imageBlacklist || "[]").forEach(addToImageBlaclist); }
  12. function saveImageBlacklist() { localStorage.imageBlacklist = JSON.stringify(imageBlacklist); }
  13. function addToImageBlaclist(md5) { if (md5 && -1 === imageBlacklist.indexOf(md5)) imageBlacklist.push(md5); }
  14. function blacklistPostImages(post) { $(post).find('img.post-image').each(function (i, el) { var md5 = el.getAttribute('data-md5'); addToImageBlaclist(md5); el.remove(); }); }
  15. function removeBlacklistedImages() { var removed = 0; $('img.post-image').each(function (i, el) { if (-1 !== imageBlacklist.indexOf(el.getAttribute('data-md5'))) { el.remove(); removed += 1; } }); return removed; }
  16. function onNopeClicked(event) { event.preventDefault(); event.stopPropagation(); loadImageBlacklist(); var post = $(event.target).closest('.post'); blacklistPostImages(post); removeBlacklistedImages(); saveImageBlacklist(); }
  17. function addNopeButtons() { $('.post').each(function(i, post) { if ($(post).find('.nope').length === 0) { $(post).prepend("<input type='button' class='nope' onClick='onNopeClicked(event)' value='Nope'></input>"); } }) }
  18.  
  19. setInterval(function () { loadImageBlacklist(); removeBlacklistedImages(); addNopeButtons(); }, 500);
  20.  
  21.  
  22. /* Display a replies counter overlay in the top right corner */
  23. $(function(){
  24. $('head').append('<style>#thread_stats_posts_ovl { '+
  25. 'position:fixed;top:35px;right:35px;'+
  26. 'font:38px sans-serif;opacity:0.5;color:#f60;}</style>');
  27. $('body').append('<div id="thread_stats_posts_ovl"/>');
  28. function copyStats() { $('#thread_stats_posts_ovl').
  29. text($('#thread_stats_posts').text());}
  30. $(document).on('new_post',copyStats); copyStats();});
  31.  
  32. // User Settings
  33. var anonsw = {
  34. qflair: '', // Examples: REAL, &rarr;
  35. qcolor: '#99d6ff',
  36. youcolor: '#F3D74D',
  37. scrollcolor: 'rgba(153, 153, 153, 0.6)',
  38. scrollbackcolor: '#333',
  39. scrolltime: 400, // ms
  40. updateDelay: 200, // ms
  41. sidenavWidth: 30, // px
  42.  
  43. floodEnabled: false,
  44. floodThreshold: 15, // min # posts before beginning fade
  45. floodVanish: 25, // max # posts before completed fade/hide
  46. floodBehavior: 'fade', // hide, fade
  47. fadenametripregex: /^(Anon(ymous)?-.*|.*-!!mG7VJxZNCI)$/i,
  48. fadenametripfloodvalue: -1, // Effective post count for fading, or -1 for auto of floodThreshold+post count
  49. strikeThroughNameFags: true,
  50.  
  51. rateHistoryLen: 50, // Data points on chart
  52. rateAvgLen: 10 // Number of data points to average for instantaneous rate
  53.  
  54. // Suggestions from 589388.html#590283
  55. // ...shill detection features, such as
  56. // easily knowing the proportion of posts from a user that don't link.
  57. // I'd want to know any ID that was posting > 1/3 posts targetting noone.
  58.  
  59. // TODO: Behavior for post hover should be to show original post visual before all q.js mods
  60. // TODO: Add flags to turn on/off features
  61. // TODO: Custom-regex -> post color/fade (auto-filter by selecting text and choosing new menu item?)
  62. // Examples: daily reminder, guys, check this out, shill, get out, filtered, tell us more, archive everything
  63. // TODO: Manual shade
  64. // TODO: remove Q trip codes from post content?
  65. // TODO: remove Q from end of post content if not a Q post?
  66. // TODO: recognize all of known Q trip codes? (make to sure to exclude known comps)
  67. // TODO: Links to reset on current Q/(you) post
  68. // TODO: Link to go to latest post (end key doesn't always work, but try capturing that as well?)
  69. // TODO: Keyboard shortcuts for navigation
  70. // TODO: Current/Total overall post navigation
  71. // TODO: Remap end key to always go to end of page
  72. // TODO: Check box for each post to mark as "read", "spam", ?
  73. // TODO: Autocorrect all-caps posts (50% threshold)?
  74. // TODO: Correct broken links but remove referral when clicked?
  75. // TODO: Make flood post fading non-linear to give leniency to posters just passing flood threshold
  76. // TODO: Penalize reposts in flood detection (if id's different, merge?) ?
  77. // TODO: Scorecard of posters ordered by post count (post rate, reply count, ...)?
  78. // TODO: Color/shade posts where there are no references and no question marks
  79. // TODO: If Q or trip used in name field, strike them out or replace with Anonymous?
  80. // TODO: embedded posts in Q posts don't have background-color and inherit Q color, fix?
  81. };
  82.  
  83. (function( anonsw_main, $, undefined ) {
  84. // House keeping variables
  85. var qposts = [];
  86. var allqposts = [];
  87. var currq = -1;
  88. var youposts = [];
  89. var curryou = -1;
  90. var qnavposts = [];
  91. var younavposts = [];
  92. var ctx;
  93. var borderSz;
  94. var scrollWd;
  95. var minheight;
  96. var ratehistory = [];
  97.  
  98. // On scroll stop. SO #9144560
  99. (function ($) {
  100. var on = $.fn.on, timer;
  101. $.fn.on = function () {
  102. var args = Array.apply(null, arguments);
  103. var last = args[args.length - 1];
  104.  
  105. if (isNaN(last) || (last === 1 && args.pop())) return on.apply(this, args);
  106.  
  107. var delay = args.pop();
  108. var fn = args.pop();
  109.  
  110. args.push(function () {
  111. var self = this, params = arguments;
  112. clearTimeout(timer);
  113. timer = setTimeout(function () {
  114. fn.apply(self, params);
  115. }, delay);
  116. });
  117.  
  118. return on.apply(this, args);
  119. };
  120. }(this.jQuery || this.Zepto));
  121.  
  122. // Case insensitive contains selector for finding yous. SO #8746882
  123. $.expr[":"].icontains = jQuery.expr.createPseudo(function (arg) {
  124. return function (elem) {
  125. return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
  126. };
  127. });
  128.  
  129. // Get non-child text. SO #3442394
  130. function immediateText(el) {
  131. return el.contents().not(el.children()).text();
  132. }
  133.  
  134. // Scroll to element
  135. function myScrollTo(el) {
  136. $('html, body').animate({
  137. scrollTop: $(el).offset().top - $('div.boardlist').height()
  138. }, anonsw.scrolltime);
  139. }
  140.  
  141. // Highlight (you) posts/references
  142. function highlightYous() {
  143. $(youposts).each(function() {
  144. $(this).css('background-color', anonsw.youcolor);
  145. });
  146. return $.Deferred().resolve();
  147. }
  148.  
  149. // Remove invalid (you)'s
  150. function removeInvalidYous() {
  151. $('div.body:icontains("(You)")').each(function() {
  152. $(this).find(':not(small)').contents().filter(function() { return this.nodeType == 3 }).each(function() {
  153. this.textContent = this.textContent.replace(/\(+ *You *\)+/ig, "you");
  154. });
  155. });
  156. return $.Deferred().resolve();
  157. }
  158.  
  159. // Highlight Q posts
  160. function highlightQ() {
  161. $(allqposts).each(function(idx,val) {
  162. if($(val).css('background-color') !== anonsw.qcolor) {
  163. if(anonsw.qflair !== "") {
  164. $(val).find('p.intro > label > span.trip').first().prepend(anonsw.qflair + " ");
  165. }
  166. $(val).css('background-color', anonsw.qcolor);
  167. }
  168. });
  169. return $.Deferred().resolve();
  170. }
  171.  
  172. // Scroll to next Q
  173. anonsw_main.nextq = function() {
  174. if(qposts.length > 0) {
  175. if(currq < qposts.length-1) {
  176. currq++;
  177. }
  178. myScrollTo($(qposts).get(currq));
  179. }
  180. };
  181.  
  182. // Scroll to last Q
  183. anonsw_main.lastq = function() {
  184. if(qposts.length > 0) {
  185. currq = qposts.length - 1;
  186. myScrollTo($(qposts).get(currq));
  187. }
  188. };
  189.  
  190. // Scroll to previous Q
  191. anonsw_main.prevq = function() {
  192. if(qposts.length > 0) {
  193. if(currq > 0) {
  194. currq--;
  195. }
  196. myScrollTo($(qposts).get(currq));
  197. }
  198. };
  199.  
  200. // Scroll to first Q
  201. anonsw_main.firstq = function() {
  202. if(qposts.length > 0) {
  203. currq = 0;
  204. myScrollTo($(qposts).get(currq));
  205. }
  206. };
  207.  
  208. // Scroll to next (You)
  209. anonsw_main.nextyou = function() {
  210. if(youposts.length > 0) {
  211. if(curryou < youposts.length-1) {
  212. curryou++;
  213. }
  214. myScrollTo($(youposts).get(curryou));
  215. }
  216. };
  217.  
  218. // Scroll to last (You)
  219. anonsw_main.lastyou = function() {
  220. if(youposts.length > 0) {
  221. curryou = youposts.length - 1;
  222. myScrollTo($(youposts).get(curryou));
  223. }
  224. };
  225.  
  226. // Scroll to previous (You)
  227. anonsw_main.prevyou = function() {
  228. if(youposts.length > 0) {
  229. if(curryou > 0) {
  230. curryou--;
  231. }
  232. myScrollTo($(youposts).get(curryou));
  233. }
  234. };
  235.  
  236. // Scroll to first (You)
  237. anonsw_main.firstyou = function() {
  238. if(youposts.length > 0) {
  239. curryou = 0;
  240. myScrollTo($(youposts).get(curryou));
  241. }
  242. };
  243.  
  244. // Inserts Q navigation links
  245. function qnav() {
  246. $('div.boardlist').append('<span>[ <a href="javascript:anonsw_main.firstq();"><i class="fa fa-step-backward"></i></a> <a href="javascript:anonsw_main.prevq();"><i class="fa fa-backward"></i></a> <span style="filter:brightness(70%);">Q</span> <span class="qcount">(?:?)</span> <a href="javascript:anonsw_main.nextq();"><i class="fa fa-forward"></i></a> <a href="javascript:anonsw_main.lastq();"><i class="fa fa-step-forward"></i></a> ]</span>');
  247. }
  248.  
  249. // Inserts (You) navigation links
  250. function younav() {
  251. $('div.boardlist').append('<span>[ <a href="javascript:anonsw_main.firstyou();"><i class="fa fa-step-backward"></i></a> <a href="javascript:anonsw_main.prevyou();"><i class="fa fa-backward"></i></a> <span style="filter:brightness(70%);">(You)</span> <span class="youcount">(?:?)</span> </span><a href="javascript:anonsw_main.nextyou();"><i class="fa fa-forward"></i></a> <a href="javascript:anonsw_main.lastyou();"><i class="fa fa-step-forward"></i></a> ]</span>');
  252. }
  253.  
  254. // Inserts feature toggle links
  255. function togglenav() {
  256. $('div.boardlist').append('<span>[ <a href="javascript:anonsw_main.toggleFlood();">Turn Post Fading <span class="toggleFloodState">Off</span></a> ]</span>')
  257. }
  258.  
  259. // Inserts post rate count/chart
  260. function postratenav() {
  261. var height = $('div.boardlist').height() - 1;
  262. $('div.boardlist').append('<span>[ Post Rate: <span class="postRate">0</span> posts/min <canvas class="postRateChart"></canvas>]</span>')
  263. $('.postRate').css('color', $('div.boardlist a').css('color'));
  264. var charts = $('.postRateChart');
  265. $(charts).each(function() {
  266. $(this).css('width', '100px');
  267. $(this).css('height', height);
  268. $(this).css('vertical-align', 'middle');
  269. //$(this).css('border', '1px solid');
  270. //$(this).css('border-color', $('div.boardlist').css('color'));
  271. var gctx = $(this).get(0).getContext('2d');
  272. gctx.canvas.height = 20;
  273. gctx.canvas.width = 100;
  274. });
  275. }
  276.  
  277. // Inserts side navigation (bird's eye)
  278. function sidenav() {
  279. $('body').append('<canvas id="sidenav"></canvas>');
  280. var nav = $('#sidenav');
  281. $(nav).css('position', 'fixed');
  282. $(nav).css('top', $('div.boardlist').height());
  283. $(nav).css('right', 0);
  284. $(nav).css('width', anonsw.sidenavWidth);
  285. $(nav).css('height', $(window).height() - $('div.boardlist').height());
  286. $(nav).css('background-color', anonsw.scrollbackcolor);
  287. $('body').css('margin-right', anonsw.sidenavWidth);
  288. ctx = $('#sidenav').get(0).getContext('2d');
  289. //ctx.canvas.height = $(document).height() - $('div.boardlist').height();
  290. ctx.canvas.height = 2048;
  291. ctx.canvas.width = anonsw.sidenavWidth;
  292. borderSz = 1;
  293. scrollWd = ctx.canvas.width / 2;
  294. }
  295.  
  296. // Update navigation counts
  297. function updateNavCounts() {
  298. var fontSize = -1;
  299. var lineHeight;
  300.  
  301. if(currq > qposts.length) { currq = qposts.length; }
  302. if(curryou > youposts.length) { curryou = youposts.length; }
  303.  
  304. for(i=0; i<qposts.length; i++) {
  305. var el = $(qposts).get(i);
  306. if(fontSize == -1) {
  307. fontSize = $(el).css('font-size');
  308. lineHeight = Math.floor(parseInt(fontSize.replace('px', '')) * 1.5);
  309. }
  310. if(($(el).offset().top + $(el).height() - 2.25*lineHeight) > $(window).scrollTop()) {
  311. currq = i;
  312. break;
  313. }
  314. }
  315.  
  316. for(i=0; i<youposts.length; i++) {
  317. var el = $(youposts).get(i);
  318. if(fontSize == -1) {
  319. fontSize = $(el).css('font-size');
  320. lineHeight = Math.floor(parseInt(fontSize.replace('px', '')) * 1.5);
  321. }
  322. if(($(el).offset().top + $(el).height() - 2.25*lineHeight) > $(window).scrollTop()) {
  323. curryou = i;
  324. break;
  325. }
  326. }
  327.  
  328. // TODO: check for duplicates and remove from counts
  329. $('.qcount').text("(" + (currq+1) + ":" + qposts.length + ")");
  330. $('.youcount').text("(" + (curryou+1) + ":" + youposts.length + ")");
  331. }
  332.  
  333. // Update navigation graphics
  334. function updateNavGraphics() {
  335. var sidenav = $('#sidenav');
  336. if(sidenav.length) {
  337. $(sidenav).css('height', $(window).height() - $('div.boardlist').height());
  338. minheight = ctx.canvas.height / ($(window).height() - $('div.boardlist').height()); // 1px
  339. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  340.  
  341. // Draw nav q posts
  342. qnavposts = [];
  343. ctx.fillStyle = anonsw.qcolor;
  344. for (i = 0; i < qposts.length; i++) {
  345. // TODO: check if we have already added post, don't draw it again
  346. var el = $(qposts).get(i);
  347. var height = $(el).height() / $(document).height() * ctx.canvas.height;
  348. if(height < minheight) height = minheight;
  349. qnavposts[i] = {
  350. x : borderSz,
  351. y : $(el).offset().top / $(document).height() * ctx.canvas.height,
  352. width : ctx.canvas.width - borderSz*2,
  353. height : height
  354. };
  355. ctx.fillRect(qnavposts[i].x, qnavposts[i].y, qnavposts[i].width, qnavposts[i].height);
  356. }
  357.  
  358. // Draw nav you posts
  359. younavposts = [];
  360. ctx.fillStyle = anonsw.youcolor;
  361. for (i = 0; i < youposts.length; i++) {
  362. // TODO: check if we have already added post, don't add it again
  363. var el = $(youposts).get(i);
  364. var height = $(el).height() / $(document).height() * ctx.canvas.height;
  365. if(height < minheight) height = minheight;
  366. younavposts[i] = {
  367. x : borderSz,
  368. y : $(el).offset().top / $(document).height() * ctx.canvas.height,
  369. width : ctx.canvas.width - borderSz*2,
  370. height : height
  371. };
  372. ctx.fillRect(younavposts[i].x, younavposts[i].y, younavposts[i].width, younavposts[i].height);
  373. }
  374.  
  375. // Update nav window
  376. ctx.fillStyle = anonsw.scrollcolor;
  377. ctx.fillRect(
  378. scrollWd / 2,
  379. $(window).scrollTop() / $(document).height() * ctx.canvas.height,
  380. scrollWd,
  381. $(window).height() / $(document).height() * ctx.canvas.height
  382. );
  383.  
  384. // Add red marker at bottom of >750 posts
  385. if($('#thread_stats_posts').text() > 750) {
  386. var barHeight = (4 * 2048) / ($(window).height() - $('div.boardlist').height());
  387. ctx.fillStyle = '#f66';
  388. ctx.fillRect(
  389. 0,
  390. ctx.canvas.height - barHeight,
  391. ctx.canvas.width,
  392. barHeight
  393. );
  394. }
  395. }
  396. }
  397.  
  398. // Updates post rate count/chart
  399. function updateNavPostRate() {
  400. var posts = $('div.post').not('.post-hover');
  401. var startPost = posts.length - (anonsw.rateHistoryLen + anonsw.rateAvgLen) + 1;
  402. if(startPost < 1) startPost = 1;
  403. var start = $($($(posts).get(0)).find('.intro time').get(0)).attr('unixtime'); //$('div.post:first .intro time').attr('unixtime');
  404. ratehistory = [];
  405. timehistory = [];
  406. for(var i=startPost; i<posts.length; i++) {
  407. // TODO: check if we have already added post, don't add it again
  408. var step = $($($(posts).get(i)).find('.intro time').get(0)).attr('unixtime'); //$($('div.post .intro time').get(i)).attr('unixtime');
  409. timehistory[timehistory.length] = step;
  410. if(timehistory.length - anonsw.rateAvgLen - 1 >= 0) {
  411. var avgend = timehistory[timehistory.length - 1];
  412. var avgstart = timehistory[timehistory.length - anonsw.rateAvgLen - 1];
  413. ratehistory[ratehistory.length] = anonsw.rateAvgLen / ((avgend - avgstart) / 60);
  414. } else {
  415. ratehistory[ratehistory.length] = 0;
  416. }
  417. }
  418. //console.log(ratehistory);
  419.  
  420. $('.postRate').text(ratehistory[ratehistory.length-1].toFixed(1));
  421.  
  422. if(ratehistory.length > anonsw.rateAvgLen) {
  423. var maxRate = Math.max.apply(null, ratehistory);
  424. var minRate = Math.min.apply(null, ratehistory);
  425. //console.log("Max: " + maxRate);
  426. //console.log("Min: " + minRate);
  427. if(minRate > (maxRate - 0.5)) {
  428. minRate = maxRate - 0.5;
  429. maxRate = maxRate + 0.5;
  430. }
  431. if(minRate < 0) {
  432. minRate = 0;
  433. }
  434. var maxTime = timehistory[timehistory.length-1];
  435. var minTime = timehistory[anonsw.rateAvgLen];
  436. $('.postRateChart').each(function() {
  437. var gctx = $(this).get(0).getContext('2d');
  438. gctx.clearRect(0, 0, gctx.canvas.width, gctx.canvas.height);
  439. gctx.strokeStyle = $('div.boardlist a').css('color');
  440. gctx.beginPath();
  441. var x = 0;
  442. var y = gctx.canvas.height - (ratehistory[anonsw.rateAvgLen] - minRate)/(maxRate - minRate) * gctx.canvas.height;
  443. gctx.moveTo(x, y);
  444. for(var i=anonsw.rateAvgLen+1; i<ratehistory.length; i++) {
  445. x = (timehistory[i] - minTime)/(maxTime - minTime) * gctx.canvas.width;
  446. y = gctx.canvas.height - (ratehistory[i] - minRate)/(maxRate - minRate) * gctx.canvas.height;
  447. gctx.lineTo(x, y);
  448. }
  449. gctx.stroke();
  450. gctx.closePath();
  451. });
  452. }
  453. }
  454.  
  455. // Update navigation
  456. function updateNav() {
  457. updateNavCounts();
  458. updateNavGraphics();
  459. updateNavPostRate();
  460. }
  461.  
  462. // Update nav when scrolling stops
  463. $(window).on('scroll', function(e) {
  464. updateNavCounts();
  465. updateNavGraphics();
  466. }, anonsw.updateDelay);
  467.  
  468. // Update nav when resize stops
  469. $(window).on('resize', function(e) {
  470. updateNav();
  471. }, anonsw.updateDelay);
  472.  
  473. // Set which posts are Q posts
  474. function setQPosts() {
  475. qposts = $.map($('div.post:not(.post-hover) > p.intro > label > span.trip:contains("!!mG7VJxZNCI")'), function(el) {
  476. return $(el).closest('div.post');
  477. });
  478. allqposts = $.map($('div.post:not(.post-hover) > p.intro > label > span.trip:contains("!!mG7VJxZNCI")'), function(el) {
  479. return $(el).closest('div.post');
  480. });
  481. return $.Deferred().resolve();
  482. }
  483.  
  484. // Set which posts are (you) posts
  485. function setYouPosts() {
  486. youposts = $.map($('div.post:not(.post-hover) > p.intro > label span.name > span.own_post, div.post:not(.post-hover) > div.body > p.body-line > small:icontains("(You)")'), function(el) {
  487. return $(el).closest('div.post');
  488. });
  489. return $.Deferred().resolve();
  490. }
  491.  
  492. // Set flood posts
  493. function setFloodPosts() {
  494. if(anonsw.floodEnabled) {
  495. var stats = {};
  496. var firstId = null;
  497. //console.log("==[ Name Fags ]=================================================");
  498. var posts = $('div.post:not(.post-hover)').not('.you');
  499. $(posts).each(function () {
  500. var id = $(this).find('p > span.poster_id').first().text();
  501. if(firstId != null) {
  502. if (!(id in stats)) {
  503. stats[id] = {count: 0, namefag: false, floodcount: 0};
  504. }
  505. stats[id].count++;
  506. if(!stats[id].namefag) {
  507. var name = immediateText($(this).find('p > label span.name').first());
  508. var trip = $(this).find('p > label > span.trip').first().text();
  509. stats[id].namefag = !anonsw.fadenametripregex.test(name+'-'+trip);
  510. //if(stats[id].namefag) {
  511. // console.log(id + '=' + stats[id].namefag + ', ' + name + ', ' + trip);
  512. //}
  513. }
  514. if(stats[id].namefag) {
  515. if(anonsw.fadenametripfloodvalue < 0) {
  516. stats[id].floodcount = anonsw.floodThreshold + stats[id].count;
  517. } else {
  518. stats[id].floodcount = anonsw.fadenametripfloodvalue;
  519. }
  520. } else {
  521. stats[id].floodcount = stats[id].count;
  522. }
  523. }
  524. if (firstId == null) {
  525. firstId = id;
  526. }
  527. });
  528. $.each(stats, function (key, value) {
  529. if (key !== firstId) {
  530. var ids = $('span.poster_id:contains("' + key + '")');
  531. if (value.floodcount > anonsw.floodThreshold || value.namefag) {
  532. if(anonsw.strikeThroughNameFags && value.namefag) {
  533. $(ids).each(function() {
  534. $(this).closest('div.post').find('p > label span.name').first().css('text-decoration','line-through');
  535. });
  536. }
  537. if (anonsw.floodBehavior === 'fade') {
  538. var intensity = value.floodcount;
  539. if (intensity > anonsw.floodVanish) {
  540. intensity = anonsw.floodVanish;
  541. }
  542. intensity = ((anonsw.floodVanish - anonsw.floodThreshold) - (intensity - anonsw.floodThreshold)) / (anonsw.floodVanish - anonsw.floodThreshold);
  543. if (intensity < 0.1) {
  544. intensity = 0.1;
  545. }
  546. $(ids).each(function () {
  547. $(this).closest('div.post').css('opacity', intensity);
  548. $(this).closest('div.post').hover(function () {
  549. $(this).animate({opacity: 1.0}, anonsw.updateDelay);
  550. }, function () {
  551. $(this).animate({opacity: intensity}, anonsw.updateDelay);
  552. });
  553. });
  554. } else if (anonsw.floodBehavior === 'hide') {
  555. if (value.count >= anonsw.floodVanish) {
  556. $(ids).each(function () {
  557. $(this).closest('div.post').hide();
  558. });
  559. }
  560. }
  561. } else {
  562. $(ids).each(function () {
  563. $(this).closest('div.post').css('opacity', 1.0);
  564. });
  565. }
  566. }
  567. });
  568. }
  569. return $.Deferred().resolve();
  570. }
  571.  
  572. // Toggle post flooding
  573. anonsw_main.toggleFlood = function() {
  574. if(anonsw.floodEnabled) {
  575. anonsw.floodEnabled = false;
  576. $('.toggleFloodState').text('On');
  577. if(anonsw.floodBehavior === 'fade') {
  578. $('span.poster_id').each(function () {
  579. $(this).closest('div.post').css('opacity', 1);
  580. $(this).closest('div.post').off('mouseenter mouseleave');
  581. });
  582. } else if(anonsw.floodBehavior === 'hide') {
  583. $(this).closest('div.post').show();
  584. }
  585. } else {
  586. anonsw.floodEnabled = true;
  587. $('.toggleFloodState').text('Off');
  588. runq()
  589. }
  590. };
  591.  
  592. // Helper to run snippets in the right order
  593. function runq() {
  594. setQPosts()
  595. .done(highlightQ)
  596. .done(removeInvalidYous)
  597. .done(setYouPosts)
  598. .done(highlightYous)
  599. .done(setFloodPosts)
  600. .done(updateNav);
  601. }
  602.  
  603. anonsw_main.Q = function() {
  604. qnav();
  605. younav();
  606. togglenav();
  607. postratenav();
  608. sidenav();
  609. runq();
  610.  
  611. // Select the node that will be observed for mutations
  612. var targetNode = $('div.thread')[0];
  613.  
  614. // Options for the observer (which mutations to observe)
  615. var config = { childList: true };
  616.  
  617. // Callback function to execute when mutations are observed
  618. var callback = function(mutationsList) {
  619. for(var mutation in mutationsList) {
  620. if (mutationsList[mutation].type == 'childList') {
  621. runq();
  622. break;
  623. }
  624. }
  625. };
  626.  
  627. // Create an observer instance linked to the callback function
  628. var observer = new MutationObserver(callback);
  629.  
  630. // Start observing the target node for configured mutations
  631. observer.observe(targetNode, config);
  632. };
  633. }( window.anonsw_main = window.anonsw_main || {}, jQuery ));
  634.  
  635. // Attach snippets to ready/change events
  636. $(document).ready(anonsw_main.Q);
  637.  
  638.  
  639. /// extra add on
  640.  
  641. /* Display a replies counter overlay in the bottom right corner */
  642. $(function(){
  643. $('head').append('<style>#thread_stats_posts_ovl { '+
  644. 'position:fixed;bottom:15px;right:35px;'+
  645. 'font:38px sans-serif;opacity:0.5;color:#f60;}</style>');
  646. $('body').append('<div id="thread_stats_posts_ovl"/>');
  647. function copyStats() { $('#thread_stats_posts_ovl').
  648. text($('#thread_stats_posts').text());}
  649. $(document).on('new_post',copyStats); copyStats();});
  650.  
  651. // User Settings
  652. var anonsw = {
  653. qflair: '', // Examples: REAL, &rarr;
  654. qcolor: '#ffc',
  655. youcolor: '#fcc',
  656. scrollcolor: 'rgba(153, 153, 153, 0.6)',
  657. scrollbackcolor: '#333',
  658. scrolltime: 400, // ms
  659. updateDelay: 200, // ms
  660. sidenavWidth: 20, // px
  661.  
  662. floodEnabled: true,
  663. floodThreshold: 14, // min # posts before beginning fade
  664. floodVanish: 15, // max # posts before completed fade/hide
  665. floodBehavior: 'hide', // hide, hide
  666. fadenametripregex: /^(Anon(ymous)?-.*|.*-!!mG7VJxZNCI)$/i,
  667. fadenametripfloodvalue: -1, // Effective post count for fading, or -1 for auto of floodThreshold+post count
  668. strikeThroughNameFags: true,
  669.  
  670. rateHistoryLen: 50, // Data points on chart
  671. rateAvgLen: 10 // Number of data points to average for instantaneous rate
  672. };
  673.  
  674. (function( anonsw_main, $, undefined ) {
  675. // House keeping variables
  676. var qposts = [];
  677. var allqposts = [];
  678. var currq = -1;
  679. var youposts = [];
  680. var curryou = -1;
  681. var qnavposts = [];
  682. var younavposts = [];
  683. var ctx;
  684. var borderSz;
  685. var scrollWd;
  686. var minheight;
  687. var ratehistory = [];
  688.  
  689. // Case insensitive contains selector for finding yous. SO #8746882
  690. $.expr[":"].icontains = jQuery.expr.createPseudo(function (arg) {
  691. return function (elem) {
  692. return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
  693. };
  694. });
  695.  
  696. // Get non-child text. SO #3442394
  697. function immediateText(el) {
  698. return el.contents().not(el.children()).text();
  699. }
  700.  
  701. // Highlight (you) posts/references
  702. function highlightYous() {
  703. $(youposts).each(function() {
  704. $(this).css('background-color', anonsw.youcolor);
  705. });
  706. return $.Deferred().resolve();
  707. }
  708.  
  709. // Remove invalid (you)'s
  710. function removeInvalidYous() {
  711. $('div.body:icontains("(You)")').each(function() {
  712. $(this).find(':not(small)').contents().filter(function() { return this.nodeType == 3 }).each(function() {
  713. this.textContent = this.textContent.replace(/\(+ *You *\)+/ig, "you");
  714. });
  715. });
  716. return $.Deferred().resolve();
  717. }
  718.  
  719. // Highlight Q posts
  720. function highlightQ() {
  721. $(allqposts).each(function(idx,val) {
  722. if($(val).css('background-color') !== anonsw.qcolor) {
  723. if(anonsw.qflair !== "") {
  724. $(val).find('p.intro > label > span.trip').first().prepend(anonsw.qflair + " ");
  725. }
  726. $(val).css('background-color', anonsw.qcolor);
  727. }
  728. });
  729. return $.Deferred().resolve();
  730. }
  731.  
  732. // Set which posts are Q posts
  733. function setQPosts() {
  734. qposts = $.map($('div.post:not(.post-hover) > p.intro > label > span.trip:contains("!!mG7VJxZNCI")'), function(el) {
  735. return $(el).closest('div.post');
  736. });
  737. allqposts = $.map($('div.post:not(.post-hover) > p.intro > label > span.trip:contains("!!mG7VJxZNCI")'), function(el) {
  738. return $(el).closest('div.post');
  739. });
  740. return $.Deferred().resolve();
  741. }
  742.  
  743. // Set which posts are (you) posts
  744. function setYouPosts() {
  745. youposts = $.map($('div.post:not(.post-hover) > p.intro > label span.name > span.own_post, div.post:not(.post-hover) > div.body > p.body-line > small:icontains("(You)")'), function(el) {
  746. return $(el).closest('div.post');
  747. });
  748. return $.Deferred().resolve();
  749. }
  750.  
  751. // Inserts side navigation (bird's eye)
  752. function sidenav() {
  753. $('body').append('<canvas id="sidenav"></canvas>');
  754. var nav = $('#sidenav');
  755. $(nav).css('position', 'fixed');
  756. $(nav).css('top', $('div.boardlist').height());
  757. $(nav).css('right', 0);
  758. $(nav).css('width', anonsw.sidenavWidth);
  759. $(nav).css('height', $(window).height() - $('div.boardlist').height());
  760. $(nav).css('background-color', anonsw.scrollbackcolor);
  761. $('body').css('margin-right', anonsw.sidenavWidth);
  762. ctx = $('#sidenav').get(0).getContext('2d');
  763. //ctx.canvas.height = $(document).height() - $('div.boardlist').height();
  764. ctx.canvas.height = 2048;
  765. ctx.canvas.width = anonsw.sidenavWidth;
  766. borderSz = 1;
  767. scrollWd = ctx.canvas.width / 2;
  768. }
  769.  
  770. // Set flood posts
  771. function setFloodPosts() {
  772. if(anonsw.floodEnabled) {
  773. var stats = {};
  774. var firstId = null;
  775. //console.log("==[ Name Fags ]=================================================");
  776. var posts = $('div.post:not(.post-hover)').not('.you');
  777. $(posts).each(function () {
  778. var id = $(this).find('p > span.poster_id').first().text();
  779. if(firstId != null) {
  780. if (!(id in stats)) {
  781. stats[id] = {count: 0, namefag: false, floodcount: 0};
  782. }
  783. stats[id].count++;
  784. if(!stats[id].namefag) {
  785. var name = immediateText($(this).find('p > label span.name').first());
  786. var trip = $(this).find('p > label > span.trip').first().text();
  787. stats[id].namefag = !anonsw.fadenametripregex.test(name+'-'+trip);
  788. //if(stats[id].namefag) {
  789. // console.log(id + '=' + stats[id].namefag + ', ' + name + ', ' + trip);
  790. //}
  791. }
  792. if(stats[id].namefag) {
  793. if(anonsw.fadenametripfloodvalue < 0) {
  794. stats[id].floodcount = anonsw.floodThreshold + stats[id].count;
  795. } else {
  796. stats[id].floodcount = anonsw.fadenametripfloodvalue;
  797. }
  798. } else {
  799. stats[id].floodcount = stats[id].count;
  800. }
  801. }
  802. if (firstId == null) {
  803. firstId = id;
  804. }
  805. });
  806. $.each(stats, function (key, value) {
  807. if (key !== firstId) {
  808. var ids = $('span.poster_id:contains("' + key + '")');
  809. if (value.floodcount > anonsw.floodThreshold || value.namefag) {
  810. if(anonsw.strikeThroughNameFags && value.namefag) {
  811. $(ids).each(function() {
  812. $(this).closest('div.post').hide();
  813. });
  814. }
  815. if (anonsw.floodBehavior === 'fade') {
  816. var intensity = value.floodcount;
  817. if (intensity > anonsw.floodVanish) {
  818. intensity = anonsw.floodVanish;
  819. }
  820. intensity = ((anonsw.floodVanish - anonsw.floodThreshold) - (intensity - anonsw.floodThreshold)) / (anonsw.floodVanish - anonsw.floodThreshold);
  821. if (intensity < 0.1) {
  822. intensity = 0.1;
  823. }
  824. $(ids).each(function () {
  825. $(this).closest('div.post').css('opacity', intensity);
  826. $(this).closest('div.post').hover(function () {
  827. $(this).animate({opacity: 1.0}, anonsw.updateDelay);
  828. }, function () {
  829. $(this).animate({opacity: intensity}, anonsw.updateDelay);
  830. });
  831. });
  832. } else if (anonsw.floodBehavior === 'hide') {
  833. if (value.count >= anonsw.floodVanish) {
  834. $(ids).each(function () {
  835. $(this).closest('div.post').hide();
  836. });
  837. }
  838. }
  839. } else {
  840. $(ids).each(function () {
  841. $(this).closest('div.post').css('opacity', 1.0);
  842. });
  843. }
  844. }
  845. });
  846. }
  847. return $.Deferred().resolve();
  848. }
  849.  
  850. // Update navigation
  851. function updateNav() {
  852. updateNavGraphics();
  853. }
  854.  
  855.  
  856. // Update navigation graphics
  857. function updateNavGraphics() {
  858. var sidenav = $('#sidenav');
  859. if(sidenav.length) {
  860. $(sidenav).css('height', $(window).height() - $('div.boardlist').height());
  861. minheight = ctx.canvas.height / ($(window).height() - $('div.boardlist').height()); // 1px
  862. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  863.  
  864. // Draw nav q posts
  865. qnavposts = [];
  866. ctx.fillStyle = anonsw.qcolor;
  867. for (i = 0; i < qposts.length; i++) {
  868. // TODO: check if we have already added post, don't draw it again
  869. var el = $(qposts).get(i);
  870. var height = $(el).height() / $(document).height() * ctx.canvas.height;
  871. if(height < minheight) height = minheight;
  872. qnavposts[i] = {
  873. x : borderSz,
  874. y : $(el).offset().top / $(document).height() * ctx.canvas.height,
  875. width : ctx.canvas.width - borderSz*2,
  876. height : height
  877. };
  878. ctx.fillRect(qnavposts[i].x, qnavposts[i].y, qnavposts[i].width, qnavposts[i].height);
  879. }
  880.  
  881. // Draw nav you posts
  882. younavposts = [];
  883. ctx.fillStyle = anonsw.youcolor;
  884. for (i = 0; i < youposts.length; i++) {
  885. // TODO: check if we have already added post, don't add it again
  886. var el = $(youposts).get(i);
  887. var height = $(el).height() / $(document).height() * ctx.canvas.height;
  888. if(height < minheight) height = minheight;
  889. younavposts[i] = {
  890. x : borderSz,
  891. y : $(el).offset().top / $(document).height() * ctx.canvas.height,
  892. width : ctx.canvas.width - borderSz*2,
  893. height : height
  894. };
  895. ctx.fillRect(younavposts[i].x, younavposts[i].y, younavposts[i].width, younavposts[i].height);
  896. }
  897.  
  898. // Update nav window
  899. ctx.fillStyle = anonsw.scrollcolor;
  900. ctx.fillRect(
  901. scrollWd / 2,
  902. $(window).scrollTop() / $(document).height() * ctx.canvas.height,
  903. scrollWd,
  904. $(window).height() / $(document).height() * ctx.canvas.height
  905. );
  906.  
  907. // Add red marker at bottom of >750 posts
  908. if($('#thread_stats_posts').text() > 750) {
  909. var barHeight = (4 * 2048) / ($(window).height() - $('div.boardlist').height());
  910. ctx.fillStyle = '#f66';
  911. ctx.fillRect(
  912. 0,
  913. ctx.canvas.height - barHeight,
  914. ctx.canvas.width,
  915. barHeight
  916. );
  917. }
  918. }
  919. }
  920.  
  921. // Toggle post flooding
  922. anonsw_main.toggleFlood = function() {
  923. if(anonsw.floodEnabled) {
  924. anonsw.floodEnabled = false;
  925. $('.toggleFloodState').text('On');
  926. if(anonsw.floodBehavior === 'fade') {
  927. $('span.poster_id').each(function () {
  928. $(this).closest('div.post').css('opacity', 1);
  929. $(this).closest('div.post').off('mouseenter mouseleave');
  930. });
  931. } else if(anonsw.floodBehavior === 'hide') {
  932. $(this).closest('div.post').show();
  933. }
  934. } else {
  935. anonsw.floodEnabled = true;
  936. $('.toggleFloodState').text('Off');
  937. runq()
  938. }
  939. };
  940.  
  941. // Helper to run snippets in the right order
  942. function runq() {
  943. setQPosts()
  944. .done(highlightQ)
  945. .done(removeInvalidYous)
  946. .done(setYouPosts)
  947. .done(highlightYous)
  948. .done(setFloodPosts)
  949. .done(updateNav);
  950. }
  951.  
  952. anonsw_main.Q = function() {
  953. sidenav();
  954. runq();
  955.  
  956. // Select the node that will be observed for mutations
  957. var targetNode = $('div.thread')[0];
  958.  
  959. // Options for the observer (which mutations to observe)
  960. var config = { childList: true };
  961.  
  962. // Callback function to execute when mutations are observed
  963. var callback = function(mutationsList) {
  964. for(var mutation in mutationsList) {
  965. if (mutationsList[mutation].type == 'childList') {
  966. runq();
  967. break;
  968. }
  969. }
  970. };
  971.  
  972. // Create an observer instance linked to the callback function
  973. var observer = new MutationObserver(callback);
  974.  
  975. // Start observing the target node for configured mutations
  976. observer.observe(targetNode, config);
  977. };
  978. }( window.anonsw_main = window.anonsw_main || {}, jQuery ));
  979.  
  980. // Attach snippets to ready/change events
  981. $(document).ready(anonsw_main.Q);
Advertisement
Add Comment
Please, Sign In to add comment