The version of vichan running on lainchan.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

853 lines
25KB

  1. if (active_page === 'thread' || active_page === 'index' || active_page === 'catalog' || active_page === 'ukko') {
  2. $(document).on('menu_ready', function () {
  3. 'use strict';
  4. // returns blacklist object from storage
  5. function getList() {
  6. return JSON.parse(localStorage.postFilter);
  7. }
  8. // stores blacklist into storage and reruns the filter
  9. function setList(blacklist) {
  10. localStorage.postFilter = JSON.stringify(blacklist);
  11. $(document).trigger('filter_page');
  12. }
  13. // unit: seconds
  14. function timestamp() {
  15. return Math.floor((new Date()).getTime() / 1000);
  16. }
  17. function initList(list, boardId, threadId) {
  18. if (typeof list.postFilter[boardId] == 'undefined') {
  19. list.postFilter[boardId] = {};
  20. list.nextPurge[boardId] = {};
  21. }
  22. if (typeof list.postFilter[boardId][threadId] == 'undefined') {
  23. list.postFilter[boardId][threadId] = [];
  24. }
  25. list.nextPurge[boardId][threadId] = {timestamp: timestamp(), interval: 86400}; // 86400 seconds == 1 day
  26. }
  27. function addFilter(type, value, useRegex) {
  28. var list = getList();
  29. var filter = list.generalFilter;
  30. var obj = {
  31. type: type,
  32. value: value,
  33. regex: useRegex
  34. };
  35. for (var i=0; i<filter.length; i++) {
  36. if (filter[i].type == type && filter[i].value == value && filter[i].regex == useRegex)
  37. return;
  38. }
  39. filter.push(obj);
  40. setList(list);
  41. drawFilterList();
  42. }
  43. function removeFilter(type, value, useRegex) {
  44. var list = getList();
  45. var filter = list.generalFilter;
  46. for (var i=0; i<filter.length; i++) {
  47. if (filter[i].type == type && filter[i].value == value && filter[i].regex == useRegex) {
  48. filter.splice(i, 1);
  49. break;
  50. }
  51. }
  52. setList(list);
  53. drawFilterList();
  54. }
  55. function nameSpanToString(el) {
  56. var s = '';
  57. $.each($(el).contents(), function(k,v) {
  58. if (v.nodeName === 'IMG')
  59. s=s+$(v).attr('alt')
  60. if (v.nodeName === '#text')
  61. s=s+v.nodeValue
  62. });
  63. return s.trim();
  64. }
  65. var blacklist = {
  66. add: {
  67. post: function (boardId, threadId, postId, hideReplies) {
  68. var list = getList();
  69. var filter = list.postFilter;
  70. initList(list, boardId, threadId);
  71. for (var i in filter[boardId][threadId]) {
  72. if (filter[boardId][threadId][i].post == postId) return;
  73. }
  74. filter[boardId][threadId].push({
  75. post: postId,
  76. hideReplies: hideReplies
  77. });
  78. setList(list);
  79. },
  80. uid: function (boardId, threadId, uniqueId, hideReplies) {
  81. var list = getList();
  82. var filter = list.postFilter;
  83. initList(list, boardId, threadId);
  84. for (var i in filter[boardId][threadId]) {
  85. if (filter[boardId][threadId][i].uid == uniqueId) return;
  86. }
  87. filter[boardId][threadId].push({
  88. uid: uniqueId,
  89. hideReplies: hideReplies
  90. });
  91. setList(list);
  92. }
  93. },
  94. remove: {
  95. post: function (boardId, threadId, postId) {
  96. var list = getList();
  97. var filter = list.postFilter;
  98. // thread already pruned
  99. if (typeof filter[boardId] == 'undefined' || typeof filter[boardId][threadId] == 'undefined')
  100. return;
  101. for (var i=0; i<filter[boardId][threadId].length; i++) {
  102. if (filter[boardId][threadId][i].post == postId) {
  103. filter[boardId][threadId].splice(i, 1);
  104. break;
  105. }
  106. }
  107. if ($.isEmptyObject(filter[boardId][threadId])) {
  108. delete filter[boardId][threadId];
  109. delete list.nextPurge[boardId][threadId];
  110. if ($.isEmptyObject(filter[boardId])) {
  111. delete filter[boardId];
  112. delete list.nextPurge[boardId];
  113. }
  114. }
  115. setList(list);
  116. },
  117. uid: function (boardId, threadId, uniqueId) {
  118. var list = getList();
  119. var filter = list.postFilter;
  120. // thread already pruned
  121. if (typeof filter[boardId] == 'undefined' || typeof filter[boardId][threadId] == 'undefined')
  122. return;
  123. for (var i=0; i<filter[boardId][threadId].length; i++) {
  124. if (filter[boardId][threadId][i].uid == uniqueId) {
  125. filter[boardId][threadId].splice(i, 1);
  126. break;
  127. }
  128. }
  129. if ($.isEmptyObject(filter[boardId][threadId])) {
  130. delete filter[boardId][threadId];
  131. delete list.nextPurge[boardId][threadId];
  132. if ($.isEmptyObject(filter[boardId])) {
  133. delete filter[boardId];
  134. delete list.nextPurge[boardId];
  135. }
  136. }
  137. setList(list);
  138. }
  139. }
  140. };
  141. /*
  142. * hide/show the specified thread/post
  143. */
  144. function hide(ele) {
  145. var $ele = $(ele);
  146. if ($(ele).data('hidden'))
  147. return;
  148. $(ele).data('hidden', true);
  149. if ($ele.hasClass('op')) {
  150. $ele.parent().find('.body, .files, .video-container').not($ele.children('.reply').children()).hide();
  151. // hide thread replies on index view
  152. if (active_page == 'index' || active_page == 'ukko') $ele.parent().find('.omitted, .reply:not(.hidden), post_no, .mentioned, br').hide();
  153. } else {
  154. // normal posts
  155. $ele.children('.body, .files, .video-container').hide();
  156. }
  157. }
  158. function show(ele) {
  159. var $ele = $(ele);
  160. $(ele).data('hidden', false);
  161. if ($ele.hasClass('op')) {
  162. $ele.parent().find('.body, .files, .video-container').show();
  163. if (active_page == 'index') $ele.parent().find('.omitted, .reply:not(.hidden), post_no, .mentioned, br').show();
  164. } else {
  165. // normal posts
  166. $ele.children('.body, .files, .video-container').show();
  167. }
  168. }
  169. /*
  170. * create filter menu when the button is clicked
  171. */
  172. function initPostMenu(pageData) {
  173. var Menu = window.Menu;
  174. var submenu;
  175. Menu.add_item('filter-menu-hide', _('Hide post'));
  176. Menu.add_item('filter-menu-unhide', _('Unhide post'));
  177. submenu = Menu.add_submenu('filter-menu-add', _('Add filter'));
  178. submenu.add_item('filter-add-post-plus', _('Post +'), _('Hide post and all replies'));
  179. submenu.add_item('filter-add-id', _('ID'));
  180. submenu.add_item('filter-add-id-plus', _('ID +'), _('Hide ID and all replies'));
  181. submenu.add_item('filter-add-name', _('Name'));
  182. submenu.add_item('filter-add-trip', _('Tripcode'));
  183. submenu = Menu.add_submenu('filter-menu-remove', _('Remove filter'));
  184. submenu.add_item('filter-remove-id', _('ID'));
  185. submenu.add_item('filter-remove-name', _('Name'));
  186. submenu.add_item('filter-remove-trip', _('Tripcode'));
  187. Menu.onclick(function (e, $buffer) {
  188. var ele = e.target.parentElement.parentElement;
  189. var $ele = $(ele);
  190. var threadId = $ele.parent().attr('id').replace('thread_', '');
  191. var boardId = $ele.parent().data('board');
  192. var postId = $ele.find('.post_no').not('[id]').text();
  193. if (pageData.hasUID) {
  194. var postUid = $ele.find('.poster_id').text();
  195. }
  196. var postName;
  197. var postTrip = '';
  198. if (!pageData.forcedAnon) {
  199. postName = (typeof $ele.find('.name').contents()[0] == 'undefined') ? '' : nameSpanToString($ele.find('.name')[0]);
  200. postTrip = $ele.find('.trip').text();
  201. }
  202. /* display logic and bind click handlers
  203. */
  204. // unhide button
  205. if ($ele.data('hidden')) {
  206. $buffer.find('#filter-menu-unhide').click(function () {
  207. // if hidden due to post id, remove it from blacklist
  208. // otherwise just show this post
  209. blacklist.remove.post(boardId, threadId, postId);
  210. show(ele);
  211. });
  212. $buffer.find('#filter-menu-hide').addClass('hidden');
  213. } else {
  214. $buffer.find('#filter-menu-unhide').addClass('hidden');
  215. $buffer.find('#filter-menu-hide').click(function () {
  216. blacklist.add.post(boardId, threadId, postId, false);
  217. });
  218. }
  219. // post id
  220. if (!$ele.data('hiddenByPost')) {
  221. $buffer.find('#filter-add-post-plus').click(function () {
  222. blacklist.add.post(boardId, threadId, postId, true);
  223. });
  224. } else {
  225. $buffer.find('#filter-add-post-plus').addClass('hidden');
  226. }
  227. // UID
  228. if (pageData.hasUID && !$ele.data('hiddenByUid')) {
  229. $buffer.find('#filter-add-id').click(function () {
  230. blacklist.add.uid(boardId, threadId, postUid, false);
  231. });
  232. $buffer.find('#filter-add-id-plus').click(function () {
  233. blacklist.add.uid(boardId, threadId, postUid, true);
  234. });
  235. $buffer.find('#filter-remove-id').addClass('hidden');
  236. } else if (pageData.hasUID) {
  237. $buffer.find('#filter-remove-id').click(function () {
  238. blacklist.remove.uid(boardId, threadId, postUid);
  239. });
  240. $buffer.find('#filter-add-id').addClass('hidden');
  241. $buffer.find('#filter-add-id-plus').addClass('hidden');
  242. } else {
  243. // board doesn't use UID
  244. $buffer.find('#filter-add-id').addClass('hidden');
  245. $buffer.find('#filter-add-id-plus').addClass('hidden');
  246. $buffer.find('#filter-remove-id').addClass('hidden');
  247. }
  248. // name
  249. if (!pageData.forcedAnon && !$ele.data('hiddenByName')) {
  250. $buffer.find('#filter-add-name').click(function () {
  251. addFilter('name', postName, false);
  252. });
  253. $buffer.find('#filter-remove-name').addClass('hidden');
  254. } else if (!pageData.forcedAnon) {
  255. $buffer.find('#filter-remove-name').click(function () {
  256. removeFilter('name', postName, false);
  257. });
  258. $buffer.find('#filter-add-name').addClass('hidden');
  259. } else {
  260. // board has forced anon
  261. $buffer.find('#filter-remove-name').addClass('hidden');
  262. $buffer.find('#filter-add-name').addClass('hidden');
  263. }
  264. // tripcode
  265. if (!pageData.forcedAnon && !$ele.data('hiddenByTrip') && postTrip !== '') {
  266. $buffer.find('#filter-add-trip').click(function () {
  267. addFilter('trip', postTrip, false);
  268. });
  269. $buffer.find('#filter-remove-trip').addClass('hidden');
  270. } else if (!pageData.forcedAnon && postTrip !== '') {
  271. $buffer.find('#filter-remove-trip').click(function () {
  272. removeFilter('trip', postTrip, false);
  273. });
  274. $buffer.find('#filter-add-trip').addClass('hidden');
  275. } else {
  276. // board has forced anon
  277. $buffer.find('#filter-remove-trip').addClass('hidden');
  278. $buffer.find('#filter-add-trip').addClass('hidden');
  279. }
  280. /* hide sub menus if all items are hidden
  281. */
  282. if (!$buffer.find('#filter-menu-remove > ul').children().not('.hidden').length) {
  283. $buffer.find('#filter-menu-remove').addClass('hidden');
  284. }
  285. if (!$buffer.find('#filter-menu-add > ul').children().not('.hidden').length) {
  286. $buffer.find('#filter-menu-add').addClass('hidden');
  287. }
  288. });
  289. }
  290. /*
  291. * hide/unhide thread on index view
  292. */
  293. function quickToggle(ele, threadId, pageData) {
  294. /*if ($(ele).find('.hide-thread-link').length)
  295. $('.hide-thread-link').remove();*/
  296. if ($(ele).hasClass('op') && !$(ele).find('.hide-thread-link').length) {
  297. $('<a class="hide-thread-link" style="float:left;margin-right:5px" href="javascript:void(0)">[' + ($(ele).data('hidden') ? '+' : '&ndash;') + ']</a>')
  298. .insertBefore($(ele).find(':not(h2,h2 *):first'))
  299. .click(function() {
  300. var postId = $(ele).find('.post_no').not('[id]').text();
  301. var hidden = $(ele).data('hidden');
  302. var boardId = $(ele).parents('.thread').data('board');
  303. if (hidden) {
  304. blacklist.remove.post(boardId, threadId, postId, false);
  305. $(this).html('[&ndash;]');
  306. } else {
  307. blacklist.add.post(boardId, threadId, postId, false);
  308. $(this).text('[+]');
  309. }
  310. });
  311. }
  312. }
  313. /*
  314. * determine whether the reply post should be hidden
  315. * - applies to all posts on page load or filtering rule change
  316. * - apply to new posts on thread updates
  317. * - must explicitly set the state of each attributes because filter will reapply to all posts after filtering rule change
  318. */
  319. function filter(post, threadId, pageData) {
  320. var $post = $(post);
  321. var list = getList();
  322. var postId = $post.find('.post_no').not('[id]').text();
  323. var name, trip, uid, subject, comment;
  324. var i, length, array, rule, pattern; // temp variables
  325. var boardId = $post.data('board');
  326. if (!boardId) boardId = $post.parents('.thread').data('board');
  327. var localList = pageData.localList;
  328. var noReplyList = pageData.noReplyList;
  329. var hasUID = pageData.hasUID;
  330. var forcedAnon = pageData.forcedAnon;
  331. var hasTrip = ($post.find('.trip').length > 0);
  332. var hasSub = ($post.find('.subject').length > 0);
  333. $post.data('hidden', false);
  334. $post.data('hiddenByUid', false);
  335. $post.data('hiddenByPost', false);
  336. $post.data('hiddenByName', false);
  337. $post.data('hiddenByTrip', false);
  338. $post.data('hiddenBySubject', false);
  339. $post.data('hiddenByComment', false);
  340. // add post with matched UID to localList
  341. if (hasUID &&
  342. typeof list.postFilter[boardId] != 'undefined' &&
  343. typeof list.postFilter[boardId][threadId] != 'undefined') {
  344. uid = $post.find('.poster_id').text();
  345. array = list.postFilter[boardId][threadId];
  346. for (i=0; i<array.length; i++) {
  347. if (array[i].uid == uid) {
  348. $post.data('hiddenByUid', true);
  349. localList.push(postId);
  350. if (array[i].hideReplies) noReplyList.push(postId);
  351. break;
  352. }
  353. }
  354. }
  355. // match localList
  356. if (localList.length) {
  357. if ($.inArray(postId, localList) != -1) {
  358. if ($post.data('hiddenByUid') !== true) $post.data('hiddenByPost', true);
  359. hide(post);
  360. }
  361. }
  362. // matches generalFilter
  363. if (!forcedAnon)
  364. name = (typeof $post.find('.name').contents()[0] == 'undefined') ? '' : nameSpanToString($post.find('.name')[0]);
  365. if (!forcedAnon && hasTrip)
  366. trip = $post.find('.trip').text();
  367. if (hasSub)
  368. subject = $post.find('.subject').text();
  369. array = $post.find('.body').contents().filter(function () {if ($(this).text() !== '') return true;}).toArray();
  370. array = $.map(array, function (ele) {
  371. return $(ele).text().trim();
  372. });
  373. comment = array.join(' ');
  374. for (i = 0, length = list.generalFilter.length; i < length; i++) {
  375. rule = list.generalFilter[i];
  376. if (rule.regex) {
  377. pattern = new RegExp(rule.value);
  378. switch (rule.type) {
  379. case 'name':
  380. if (!forcedAnon && pattern.test(name)) {
  381. $post.data('hiddenByName', true);
  382. hide(post);
  383. }
  384. break;
  385. case 'trip':
  386. if (!forcedAnon && hasTrip && pattern.test(trip)) {
  387. $post.data('hiddenByTrip', true);
  388. hide(post);
  389. }
  390. break;
  391. case 'sub':
  392. if (hasSub && pattern.test(subject)) {
  393. $post.data('hiddenBySubject', true);
  394. hide(post);
  395. }
  396. break;
  397. case 'com':
  398. if (pattern.test(comment)) {
  399. $post.data('hiddenByComment', true);
  400. hide(post);
  401. }
  402. break;
  403. }
  404. } else {
  405. switch (rule.type) {
  406. case 'name':
  407. if (!forcedAnon && rule.value == name) {
  408. $post.data('hiddenByName', true);
  409. hide(post);
  410. }
  411. break;
  412. case 'trip':
  413. if (!forcedAnon && hasTrip && rule.value == trip) {
  414. $post.data('hiddenByTrip', true);
  415. hide(post);
  416. }
  417. break;
  418. case 'sub':
  419. pattern = new RegExp('\\b'+ rule.value+ '\\b');
  420. if (hasSub && pattern.test(subject)) {
  421. $post.data('hiddenBySubject', true);
  422. hide(post);
  423. }
  424. break;
  425. case 'com':
  426. pattern = new RegExp('\\b'+ rule.value+ '\\b');
  427. if (pattern.test(comment)) {
  428. $post.data('hiddenByComment', true);
  429. hide(post);
  430. }
  431. break;
  432. }
  433. }
  434. }
  435. // check for link to filtered posts
  436. $post.find('.body a').not('[rel="nofollow"]').each(function () {
  437. var replyId = $(this).text().match(/^>>(\d+)$/);
  438. if (!replyId)
  439. return;
  440. replyId = replyId[1];
  441. if ($.inArray(replyId, noReplyList) != -1) {
  442. hide(post);
  443. }
  444. });
  445. // post didn't match any filters
  446. if (!$post.data('hidden')) {
  447. show(post);
  448. }
  449. }
  450. /* (re)runs the filter on the entire page
  451. */
  452. function filterPage(pageData) {
  453. var list = getList();
  454. if (active_page != 'catalog') {
  455. // empty the local and no-reply list
  456. pageData.localList = [];
  457. pageData.noReplyList = [];
  458. $('.thread').each(function () {
  459. var $thread = $(this);
  460. // disregard the hidden threads constructed by post-hover.js
  461. if ($thread.css('display') == 'none')
  462. return;
  463. var threadId = $thread.attr('id').replace('thread_', '');
  464. var boardId = $thread.data('board');
  465. var op = $thread.children('.op')[0];
  466. var i, array; // temp variables
  467. // add posts to localList and noReplyList
  468. if (typeof list.postFilter[boardId] != 'undefined' && typeof list.postFilter[boardId][threadId] != 'undefined') {
  469. array = list.postFilter[boardId][threadId];
  470. for (i=0; i<array.length; i++) {
  471. if ( typeof array[i].post == 'undefined')
  472. continue;
  473. pageData.localList.push(array[i].post);
  474. if (array[i].hideReplies) pageData.noReplyList.push(array[i].post);
  475. }
  476. }
  477. // run filter on OP
  478. filter(op, threadId, pageData);
  479. quickToggle(op, threadId, pageData);
  480. // iterate filter over each post
  481. if (!$(op).data('hidden') || active_page == 'thread') {
  482. $thread.find('.reply').not('.hidden').each(function () {
  483. filter(this, threadId, pageData);
  484. });
  485. }
  486. });
  487. } else {
  488. var postFilter = list.postFilter[pageData.boardId];
  489. var $collection = $('.mix');
  490. if ($.isEmptyObject(postFilter))
  491. return;
  492. // for each thread that has filtering rules
  493. // check if filter contains thread OP and remove the thread from catalog
  494. $.each(postFilter, function (key, thread) {
  495. var threadId = key;
  496. $.each(thread, function () {
  497. if (this.post == threadId) {
  498. $collection.filter('[data-id='+ threadId +']').remove();
  499. }
  500. });
  501. });
  502. }
  503. }
  504. function initStyle() {
  505. var $ele, cssStyle, cssString;
  506. $ele = $('<div>').addClass('post reply').hide().appendTo('body');
  507. cssStyle = $ele.css(['background-color', 'border-color']);
  508. cssStyle.hoverBg = $('body').css('background-color');
  509. $ele.remove();
  510. cssString = '\n/*** Generated by post-filter ***/\n' +
  511. '#filter-control input[type=text] {width: 130px;}' +
  512. '#filter-control input[type=checkbox] {vertical-align: middle;}' +
  513. '#filter-control #clear {float: right;}\n' +
  514. '#filter-container {margin-top: 20px; border: 1px solid; height: 270px; overflow: auto;}\n' +
  515. '#filter-list {width: 100%; border-collapse: collapse;}\n' +
  516. '#filter-list th {text-align: center; height: 20px; font-size: 14px; border-bottom: 1px solid;}\n' +
  517. '#filter-list th:nth-child(1) {text-align: center; width: 70px;}\n' +
  518. '#filter-list th:nth-child(2) {text-align: left;}\n' +
  519. '#filter-list th:nth-child(3) {text-align: center; width: 58px;}\n' +
  520. '#filter-list tr:not(#header) {height: 22px;}\n' +
  521. '#filter-list tr:nth-child(even) {background-color:rgba(255, 255, 255, 0.5);}\n' +
  522. '#filter-list td:nth-child(1) {text-align: center; width: 70px;}\n' +
  523. '#filter-list td:nth-child(3) {text-align: center; width: 58px;}\n' +
  524. '#confirm {text-align: right; margin-bottom: -18px; padding-top: 2px; font-size: 14px; color: #FF0000;}';
  525. if (!$('style.generated-css').length) $('<style class="generated-css">').appendTo('head');
  526. $('style.generated-css').html($('style.generated-css').html() + cssString);
  527. }
  528. function drawFilterList() {
  529. var list = getList().generalFilter;
  530. var $ele = $('#filter-list');
  531. var $row, i, length, obj, val;
  532. var typeName = {
  533. name: 'name',
  534. trip: 'tripcode',
  535. sub: 'subject',
  536. com: 'comment'
  537. };
  538. $ele.empty();
  539. $ele.append('<tr id="header"><th>Type</th><th>Content</th><th>Remove</th></tr>');
  540. for (i = 0, length = list.length; i < length; i++) {
  541. obj = list[i];
  542. // display formatting
  543. val = (obj.regex) ? '/'+ obj.value +'/' : obj.value;
  544. $row = $('<tr>');
  545. $row.append(
  546. '<td>'+ typeName[obj.type] +'</td>',
  547. '<td>'+ val +'</td>',
  548. $('<td>').append(
  549. $('<a>').html('X')
  550. .addClass('del-btn')
  551. .attr('href', '#')
  552. .data('type', obj.type)
  553. .data('val', obj.value)
  554. .data('useRegex', obj.regex)
  555. )
  556. );
  557. $ele.append($row);
  558. }
  559. }
  560. function initOptionsPanel() {
  561. if (window.Options && !Options.get_tab('filter')) {
  562. Options.add_tab('filter', 'list', _('Filters'));
  563. Options.extend_tab('filter',
  564. '<div id="filter-control">' +
  565. '<select>' +
  566. '<option value="name">'+_('Name')+'</option>' +
  567. '<option value="trip">'+_('Tripcode')+'</option>' +
  568. '<option value="sub">'+_('Subject')+'</option>' +
  569. '<option value="com">'+_('Comment')+'</option>' +
  570. '</select>' +
  571. '<input type="text">' +
  572. '<input type="checkbox">' +
  573. 'regex ' +
  574. '<button id="set-filter">'+_('Add')+'</button>' +
  575. '<button id="clear">'+_('Clear all filters')+'</button>' +
  576. '<div id="confirm" class="hidden">' +
  577. _('This will clear all filtering rules including hidden posts.')+' <a id="confirm-y" href="#">'+_('yes')+'</a> | <a id="confirm-n" href="#">'+_('no')+'</a>' +
  578. '</div>' +
  579. '</div>' +
  580. '<div id="filter-container"><table id="filter-list"></table></div>'
  581. );
  582. drawFilterList();
  583. // control buttons
  584. $('#filter-control').on('click', '#set-filter', function () {
  585. var type = $('#filter-control select option:selected').val();
  586. var value = $('#filter-control input[type=text]').val();
  587. var useRegex = $('#filter-control input[type=checkbox]').prop('checked');
  588. //clear the input form
  589. $('#filter-control input[type=text]').val('');
  590. addFilter(type, value, useRegex);
  591. drawFilterList();
  592. });
  593. $('#filter-control').on('click', '#clear', function () {
  594. $('#filter-control #clear').addClass('hidden');
  595. $('#filter-control #confirm').removeClass('hidden');
  596. });
  597. $('#filter-control').on('click', '#confirm-y', function (e) {
  598. e.preventDefault();
  599. $('#filter-control #clear').removeClass('hidden');
  600. $('#filter-control #confirm').addClass('hidden');
  601. setList({
  602. generalFilter: [],
  603. postFilter: {},
  604. nextPurge: {},
  605. lastPurge: timestamp()
  606. });
  607. drawFilterList();
  608. });
  609. $('#filter-control').on('click', '#confirm-n', function (e) {
  610. e.preventDefault();
  611. $('#filter-control #clear').removeClass('hidden');
  612. $('#filter-control #confirm').addClass('hidden');
  613. });
  614. // remove button
  615. $('#filter-list').on('click', '.del-btn', function (e) {
  616. e.preventDefault();
  617. var $ele = $(e.target);
  618. var type = $ele.data('type');
  619. var val = $ele.data('val');
  620. var useRegex = $ele.data('useRegex');
  621. removeFilter(type, val, useRegex);
  622. });
  623. }
  624. }
  625. /*
  626. * clear out pruned threads
  627. */
  628. function purge() {
  629. var list = getList();
  630. var board, thread, boardId, threadId;
  631. var deferred;
  632. var requestArray = [];
  633. var successHandler = function (boardId, threadId) {
  634. return function () {
  635. // thread still alive, keep it in the list and increase the time between checks.
  636. var list = getList();
  637. var thread = list.nextPurge[boardId][threadId];
  638. thread.timestamp = timestamp();
  639. thread.interval = Math.floor(thread.interval * 1.5);
  640. setList(list);
  641. };
  642. };
  643. var errorHandler = function (boardId, threadId) {
  644. return function (xhr) {
  645. if (xhr.status == 404) {
  646. var list = getList();
  647. delete list.nextPurge[boardId][threadId];
  648. delete list.postFilter[boardId][threadId];
  649. if ($.isEmptyObject(list.nextPurge[boardId])) delete list.nextPurge[boardId];
  650. if ($.isEmptyObject(list.postFilter[boardId])) delete list.postFilter[boardId];
  651. setList(list);
  652. }
  653. };
  654. };
  655. if ((timestamp() - list.lastPurge) < 86400) // less than 1 day
  656. return;
  657. for (boardId in list.nextPurge) {
  658. board = list.nextPurge[boardId];
  659. for (threadId in board) {
  660. thread = board[threadId];
  661. if (timestamp() > (thread.timestamp + thread.interval)) {
  662. // check if thread is pruned
  663. deferred = $.ajax({
  664. cache: false,
  665. url: '/'+ boardId +'/res/'+ threadId +'.json',
  666. success: successHandler(boardId, threadId),
  667. error: errorHandler(boardId, threadId)
  668. });
  669. requestArray.push(deferred);
  670. }
  671. }
  672. }
  673. // when all requests complete
  674. $.when.apply($, requestArray).always(function () {
  675. var list = getList();
  676. list.lastPurge = timestamp();
  677. setList(list);
  678. });
  679. }
  680. function init() {
  681. if (typeof localStorage.postFilter === 'undefined') {
  682. localStorage.postFilter = JSON.stringify({
  683. generalFilter: [],
  684. postFilter: {},
  685. nextPurge: {},
  686. lastPurge: timestamp()
  687. });
  688. }
  689. var pageData = {
  690. boardId: board_name, // get the id from the global variable
  691. localList: [], // all the blacklisted post IDs or UIDs that apply to the current page
  692. noReplyList: [], // any posts that replies to the contents of this list shall be hidden
  693. hasUID: (document.getElementsByClassName('poster_id').length > 0),
  694. forcedAnon: ($('th:contains(Name)').length === 0) // tests by looking for the Name label on the reply form
  695. };
  696. initStyle();
  697. initOptionsPanel();
  698. initPostMenu(pageData);
  699. filterPage(pageData);
  700. // on new posts
  701. $(document).on('new_post', function (e, post) {
  702. var threadId;
  703. if ($(post).hasClass('reply')) {
  704. threadId = $(post).parents('.thread').attr('id').replace('thread_', '');
  705. } else {
  706. threadId = $(post).attr('id').replace('thread_', '');
  707. post = $(post).children('.op')[0];
  708. }
  709. filter(post, threadId, pageData);
  710. quickToggle(post, threadId, pageData);
  711. });
  712. $(document).on('filter_page', function () {
  713. filterPage(pageData);
  714. });
  715. // shift+click on catalog to hide thread
  716. if (active_page == 'catalog') {
  717. $(document).on('click', '.mix', function(e) {
  718. if (e.shiftKey) {
  719. var threadId = $(this).data('id').toString();
  720. var postId = threadId;
  721. blacklist.add.post(pageData.boardId, threadId, postId, false);
  722. }
  723. });
  724. }
  725. // clear out the old threads
  726. purge();
  727. }
  728. init();
  729. });
  730. if (typeof window.Menu !== "undefined") {
  731. $(document).trigger('menu_ready');
  732. }
  733. }