The version of vichan running on lainchan.org
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

853 lignes
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. }