mod-unminified.js
1 /** 2 * Mod Extension 3 */ 4 5 (function () { 6 var J = { 7 isCatalog: false, 8 colours: {}, 9 posterids: {}, 10 nextChunkIndex: 0, 11 nextChunk: null, 12 chunkSize: 100, 13 sameIDActive: false, 14 15 parserEventBound: false, 16 17 autoReloadCatInterval: null, 18 autoReloadCatDelay: 30000, 19 20 samePostersMap: {}, 21 22 xhrs: {}, 23 24 reportsSubDomain: 'reports', 25 teamSubDomain: 'team', 26 27 flags: [] 28 }; 29 30 J.bin2hex = function(data) { 31 var i, l, hex, c; 32 33 hex = ''; 34 l = data.length; 35 36 for (i = 0; i < l; ++i) { 37 c = data.charCodeAt(i); 38 hex += (c >> 4).toString(16); 39 hex += (c & 0xF).toString(16); 40 } 41 42 return hex; 43 }; 44 45 J.getFileMD5FromPid = function(pid) { 46 var el, data; 47 48 el = $.id('f' + pid); 49 50 if (!el) { 51 return false; 52 } 53 54 el = $.qs('img[data-md5]', el); 55 56 if (!el) { 57 return false; 58 } 59 60 data = window.atob(el.getAttribute('data-md5')); 61 62 return J.bin2hex(data); 63 }; 64 65 J.onGetMD5Click = function(el) { 66 var md5, pid = el.getAttribute('data-id'); 67 68 md5 = J.getFileMD5FromPid(pid); 69 70 if (md5 === false) { 71 alert('Post or file not found'); 72 } 73 else { 74 prompt('', md5); 75 } 76 }; 77 78 J.apiUrlFilter = function(url) { 79 return url + '?' + Math.round(Date.now() / 1000 / 3); 80 }; 81 82 J.openDeletePrompt = function(id) { 83 var html, cnt; 84 85 id = id.getAttribute('data-id'); 86 87 html = '<div class="extPanel reply"><div class="panelHeader">Delete Post No.' + id 88 + '<span class="panelCtrl"><img alt="Close" title="Close" class="pointer" data-cmd="close-delete-prompt" src="' 89 + Main.icons.cross + '"></a>' 90 + '</span></div><span id="delete-prompt-inner">' 91 + '<input type="button" value="Delete Post" tabindex="-1" data-cmd="delete-post" data-id="' + id + '"> ' 92 + '<input type="button" value="Delete Image Only" data-cmd="delete-image" data-id="' + id + '">'; 93 94 if ($.id((J.isCatalog ? 'thread-' : 't') + id) && !window.thread_archived) { 95 html += ' <input type="button" value="Archive Thread" data-cmd="force-archive" data-id="' + id + '">'; 96 } 97 98 if (!window.thread_archived && !J.isCatalog) { 99 html += '<br>[<input type="checkbox" id="delete-all-by-ip"><label for="delete-all-by-ip">Delete all by IP?</label>]'; 100 } 101 102 html += '</span></div>'; 103 104 cnt = document.createElement('div'); 105 cnt.className = 'UIPanel'; 106 cnt.id = 'delete-prompt'; 107 108 cnt.innerHTML = html; 109 110 document.addEventListener('keydown', J.onKeyDown, false); 111 cnt.addEventListener('click', J.closeDeletePrompt, false); 112 document.body.appendChild(cnt); 113 114 $.id('delete-prompt-inner').firstElementChild.focus(); 115 }; 116 117 J.addPosterIds = function(pid, hash, isMobile) { 118 var post, cnt, el, name, hand, p; 119 120 post = !isMobile ? $.id('pi' + pid) : $.id('pim' + pid); 121 122 if (!window.user_ids || !(el = $.cls('posteruid', post)[0])) { 123 el = $.el('span'); 124 125 cnt = $.cls('nameBlock', post)[0]; 126 name = $.cls('name', cnt)[0]; 127 128 if (name.classList.contains('capcode')) { 129 return; 130 } 131 132 cnt.insertBefore(el, name.nextSibling); 133 134 if (!isMobile) { 135 cnt.insertBefore(document.createTextNode(' '), name.nextSibling); 136 } 137 } 138 139 el.innerHTML = '(ID: <span class="hand" title="Highlight posts by this ID">' + hash + '</span>)'; 140 el.className = 'posteruid id_' + hash; 141 142 hand = el.firstElementChild; 143 144 IDColor.apply(hand); 145 146 el.addEventListener('click', window.idClick, false); 147 148 if (window.currentHighlighted && el.className.indexOf('id_' + window.currentHighlighted) != -1) { 149 p = el.parentNode.parentNode.parentNode; 150 p.className = 'highlight ' + p.className; 151 } 152 } 153 154 J.onSamePostersLoaded = function() { 155 var posts, hash, pid, tmp, isMobile; 156 157 if (this.status != 200 && this.status != 304) { 158 return; 159 } 160 161 posts = JSON.parse(this.responseText); 162 163 if (!posts) { 164 return; 165 } 166 167 isMobile = Main.hasMobileLayout; 168 169 if (!IDColor.enabled) { 170 tmp = window.user_ids; 171 window.user_ids = true; 172 IDColor.init(); 173 window.user_ids = tmp; 174 } 175 176 if (!J.sameIDActive) { 177 J.sameIDActive = true; 178 } 179 180 for (pid in posts) { 181 if (J.samePostersMap[pid]) { 182 continue; 183 } 184 185 hash = posts[pid]; 186 187 J.samePostersMap[pid] = true; 188 189 J.addPosterIds(pid, hash, isMobile); 190 } 191 } 192 193 J.loadSamePosters = function(from) { 194 var url, theNode, xhr; 195 196 if (!J.parserEventBound) { 197 document.addEventListener('4chanParsingDone', J.onParsingDone, false); 198 } 199 200 url = 'https://sys.' + $L.d(Main.board) + '/' + Main.board + '/admin?admin=adminext&thread=' + Main.tid; 201 202 if (from) { 203 url += '&from=' + from; 204 } 205 206 xhr = new XMLHttpRequest(); 207 xhr.open('GET', url); 208 xhr.withCredentials = true; 209 xhr.onload = J.onSamePostersLoaded; 210 211 xhr.send(null); 212 }; 213 214 J.closeDeletePrompt = function (e) { 215 var prompt; 216 217 if (!e || e.target.id == 'delete-prompt') { 218 if (prompt = $.id('delete-prompt')) { 219 document.removeEventListener('keydown', J.onKeyDown, false); 220 prompt.removeEventListener('click', J.closeDeletePrompt, false); 221 document.body.removeChild(prompt); 222 } 223 } 224 }; 225 226 J.checkDeletedPosts = function () { 227 var url, xhr; 228 229 if (!Main.tid) { 230 return; 231 } 232 233 url = '//a.4cdn.org/' + Main.board + '/res/' + Main.tid + '.json'; 234 235 xhr = new XMLHttpRequest(); 236 xhr.open('GET', url); 237 xhr.onload = function () { 238 if (this.status == 200 || this.status == 304) { 239 ThreadUpdater.markDeletedReplies(Parser.parseThreadJSON(this.responseText)); 240 } 241 }; 242 243 xhr.send(null); 244 }; 245 246 J.get_random_light_color = function () { 247 var letters = 'ABCDE'.split(''); 248 var color = '#'; 249 for (var i = 0; i < 3; i++) { 250 color += letters[Math.floor(Math.random() * letters.length)]; 251 } 252 return color; 253 }; 254 255 J.deletePost = function (btn, imageOnly) { 256 var id, xhr, form, msg, el, url, mode, delall, del, isOp, resp; 257 258 id = btn.getAttribute('data-id'); 259 260 isOp = !J.isCatalog && $.id('t' + id); 261 262 form = new FormData(); 263 msg = 'Delete Post No.'; 264 url = 'https://sys.' + $L.d(Main.board) + '/' + Main.board; 265 266 if (window.thread_archived) { 267 mode = 'arcdel'; 268 } 269 else { 270 mode = 'usrdel'; 271 delall = !J.isCatalog && $.id('delete-all-by-ip').checked; 272 } 273 274 if (delall) { 275 mode = 'admin.php'; 276 form.append('admin', 'delall'); 277 form.append('id', id); 278 } 279 280 if (delall) { 281 url += '/admin'; 282 } 283 else { 284 url += '/post'; 285 } 286 287 if (imageOnly) { 288 msg = 'Delete Image No.'; 289 form.append('onlyimgdel', 'on'); 290 } 291 292 form.append(id, 'delete'); 293 form.append('mode', mode); 294 form.append('pwd', 'janitorise'); 295 296 (del = $.id('delete-prompt-inner')).textContent = 'Deleting...'; 297 298 xhr = new XMLHttpRequest(); 299 xhr.open('POST', url); 300 xhr.withCredentials = true; 301 xhr.onload = function () { 302 var builtMsg; 303 btn.src = Main.icons.cross; 304 if (this.status == 200) { 305 if ((!delall && this.responseText.indexOf('Updating') != -1) || (delall && this.responseText.indexOf('deleted') != -1)) { 306 if (J.isCatalog) { 307 if (el = $.id('thread-' + id)) { 308 $.addClass(el, 'disabled'); 309 } 310 } 311 else if (!imageOnly) { 312 if (id == Main.tid) { 313 location.href = '//boards.' + $L.d(Main.board) + '/' + Main.board + '/'; 314 return; 315 } 316 else { 317 if (delall) { 318 builtMsg = document.createElement('span'); 319 builtMsg.innerHTML = '<br><br><strong style="font-color: red;">(YOU HAVE DELETED ALL POSTS BY THIS IP)</strong>'; 320 el = $.id('m' + id); 321 el.appendChild(builtMsg); 322 J.checkDeletedPosts(); 323 } 324 else { 325 if (isOp) { 326 el = isOp.parentNode; 327 el.removeChild(isOp.nextSibling); 328 el.removeChild(isOp); 329 } 330 else { 331 el = $.id('pc' + id); 332 el.parentNode.removeChild(el); 333 } 334 } 335 } 336 } 337 else { 338 el = $.id('f' + id); 339 el.innerHTML = '<span class="fileThumb"><img alt="File deleted."' 340 + ' src="//s.4cdn.org/image/filedeleted' + (isOp ? '' : '-res') + '.gif"></span>'; 341 342 if (delall) { 343 builtMsg = document.createElement('span'); 344 builtMsg.innerHTML = '<br><br><strong style="font-color: red;">(YOU HAVE DELETED ALL IMAGES BY THIS IP)</strong>'; 345 el = $.id('m' + id); 346 el.appendChild(builtMsg); 347 } 348 } 349 350 J.closeDeletePrompt(); 351 } 352 else { 353 if (resp = this.responseText.match(/"errmsg"[^>]*>(.*?)<\/span/)) { 354 del.textContent = resp[1]; 355 } 356 else { 357 del.textContent = 'Error: Something went wrong.'; 358 } 359 } 360 } 361 else { 362 del.textContent = 'Error: Wrong status while deleting No.' + id + ' (Status: ' + this.status + ').'; 363 } 364 }; 365 xhr.onerror = function () { 366 del.textContent = 'Error: Error while deleting No.' + id + ' (Status: ' + this.status + ').'; 367 }; 368 369 xhr.send(form); 370 }; 371 372 J.forceArchive = function(btn) { 373 var id, xhr, form, msg, url, del, resp; 374 375 id = btn.getAttribute('data-id'); 376 377 form = new FormData(); 378 msg = 'Archive Thread No.'; 379 380 url = 'https://sys.' + $L.d(Main.board) + '/' + Main.board + '/post'; 381 382 form.append('id', id); 383 form.append('mode', 'forcearchive'); 384 385 (del = $.id('delete-prompt-inner')).textContent = 'Archiving...'; 386 387 xhr = new XMLHttpRequest(); 388 xhr.open('POST', url); 389 xhr.withCredentials = true; 390 xhr.onload = function () { 391 var el; 392 if (btn.src) { 393 btn.src = Main.icons.cross; 394 } 395 if (this.status == 200) { 396 if (this.responseText.indexOf('Updating') != -1) { 397 if (J.isCatalog) { 398 if (el = $.id('thread-' + id)) { 399 $.addClass(el, 'disabled'); 400 } 401 } 402 J.closeDeletePrompt(); 403 } 404 else { 405 if (resp = this.responseText.match(/"errmsg"[^>]*>(.*?)<\/span/)) { 406 del.textContent = resp[1]; 407 } 408 else { 409 del.textContent = 'Error: Something went wrong.'; 410 } 411 } 412 } 413 else { 414 del.textContent = 'Error: Wrong status while archiving No.' + id + ' (Status: ' + this.status + ').'; 415 } 416 }; 417 xhr.onerror = function () { 418 del.textContent = 'Error: Error while archiving No.' + id + ' (Status: ' + this.status + ').'; 419 }; 420 421 xhr.send(form); 422 }; 423 424 J.openBanWindow = function (btn) { 425 var id; 426 427 id = btn.getAttribute('data-id'); 428 window.open('https://sys.' + $L.d(Main.board) + '/' + Main.board + '/admin?mode=admin&admin=ban&id=' + id, '_blank', 'scrollBars=yes,resizable=no,toolbar=no,menubar=no,location=no,directories=no,width=400,height=470'); 429 }; 430 431 J.openBanFrame = function(btn) { 432 var id; 433 434 if (this.banReqCnt) { 435 this.closeBanFrame(); 436 } 437 438 id = btn.getAttribute('data-id'); 439 440 this.banReqCnt = document.createElement('div'); 441 this.banReqCnt.id = 'banReq'; 442 this.banReqCnt.className = 'extPanel reply'; 443 this.banReqCnt.setAttribute('data-trackpos', 'banReq-position'); 444 445 if (Config['banReq-position']) { 446 this.banReqCnt.style.cssText = Config['banReq-position']; 447 } 448 else { 449 this.banReqCnt.style.right = '0px'; 450 this.banReqCnt.style.top = '50px'; 451 } 452 453 this.banReqCnt.innerHTML = 454 '<div id="banReqHeader" class="drag postblock">Ban No.' + id 455 + '<img alt="X" src="' + Main.icons.cross + '" id="banReqClose" ' 456 + 'class="extButton" title="Close Window"></div>' 457 + '<iframe src="https://sys.' + $L.d(Main.board) + '/' 458 + Main.board + '/admin?mode=admin&admin=ban&id=' + id 459 + '&noheader=true" width="400" height="470" frameborder="0"></iframe>'; 460 461 document.body.appendChild(this.banReqCnt); 462 463 window.addEventListener('message', J.onMessage, false); 464 document.addEventListener('keydown', J.onKeyDown, false); 465 466 $.id('banReqClose').addEventListener('click', J.closeBanFrame, false); 467 Draggable.set($.id('banReqHeader')); 468 }; 469 470 J.closeBanFrame = function() { 471 window.removeEventListener('message', J.onMessage, false); 472 document.removeEventListener('keydown', J.onKeyDown, false); 473 Draggable.unset($.id('banReqHeader')); 474 $.id('banReqClose').removeEventListener('click', J.closeBanFrame, false); 475 document.body.removeChild(J.banReqCnt); 476 J.banReqCnt = null; 477 }; 478 479 J.processMessage = function(data) { 480 if (!data) { 481 return {}; 482 } 483 484 data = data.split('-'); 485 486 return { 487 cmd: data[0], 488 type: data[1], 489 id: data.slice(2).join('-') 490 }; 491 }; 492 493 J.onKeyDown = function(e) { 494 if (e.keyCode == 27 && !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) { 495 if (J.banReqCnt) { 496 J.closeBanFrame(); 497 } 498 if (J.threadOptsCnt) { 499 J.closeThreadOptionsFrame(); 500 } 501 if ($.id('delete-prompt')) { 502 J.closeDeletePrompt(); 503 } 504 } 505 }; 506 507 J.onCatalogKeyDown = function(e) { 508 if (e.keyCode == 82 && e.shiftKey) { 509 J.initCatAutoReload(); 510 } 511 }; 512 513 J.onMessage = function(e) { 514 var msg; 515 516 if (e.origin !== 'https://sys.' + $L.d(Main.board)) { 517 return; 518 } 519 520 msg = J.processMessage(e.data); 521 522 if (msg.type !== 'ban') { 523 return; 524 } 525 526 if (msg.cmd === 'done' || msg.cmd === 'cancel') { 527 J.closeBanFrame(); 528 } 529 }; 530 531 J.initCatAutoReload = function(init) { 532 var flag; 533 534 flag = sessionStorage.getItem('4chan-c-ar'); 535 536 if (flag) { 537 if (init) { 538 window.scrollTo(0, +flag); 539 J.toggleCatAutoReload(true); 540 } 541 else { 542 J.toggleCatAutoReload(false); 543 } 544 } 545 else { 546 if (init) { 547 return; 548 } 549 J.toggleCatAutoReload(true); 550 } 551 }; 552 553 J.toggleCatAutoReload = function(flag) { 554 if (flag) { 555 sessionStorage.setItem('4chan-c-ar', document.documentElement.scrollTop); 556 J.autoReloadCatInterval = setInterval(J.autoRefreshWindow, J.autoReloadCatDelay); 557 $.addClass($.id('refresh-btn'), 'active-btn'); 558 } 559 else { 560 sessionStorage.removeItem('4chan-c-ar'); 561 clearInterval(J.autoReloadCatInterval); 562 $.removeClass($.id('refresh-btn'), 'active-btn'); 563 } 564 }; 565 566 J.autoRefreshWindow = function() { 567 var el = $.id('ctrl'); 568 569 if (document.documentElement.scrollTop <= el.offsetTop + el.offsetHeight) { 570 sessionStorage.setItem('4chan-c-ar', document.documentElement.scrollTop); 571 location.href = location.href; 572 } 573 } 574 575 J.openThreadOptions = function(btn) { 576 var id = btn.getAttribute('data-id'); 577 window.open('https://sys.' + $L.d(Main.board) + '/' + Main.board + '/admin?mode=admin&admin=opt&id=' + id, '_blank', 'scrollBars=yes,resizable=no,toolbar=no,menubar=no,location=no,directories=no,width=400,height=290'); 578 }; 579 580 J.openThreadOptionsFrame = function(btn) { 581 var id; 582 583 if (this.threadOptsCnt) { 584 this.closeThreadOptionsFrame(); 585 } 586 587 id = btn.getAttribute('data-id'); 588 589 this.threadOptsCnt = document.createElement('div'); 590 this.threadOptsCnt.id = 'threadOpts'; 591 this.threadOptsCnt.className = 'extPanel reply'; 592 this.threadOptsCnt.setAttribute('data-trackpos', 'threadOpts-position'); 593 594 if (Config['threadOpts-position']) { 595 this.threadOptsCnt.style.cssText = Config['threadOpts-position']; 596 } 597 else { 598 this.threadOptsCnt.style.right = '0px'; 599 this.threadOptsCnt.style.top = '50px'; 600 } 601 602 this.threadOptsCnt.innerHTML = 603 '<div id="threadOptsHeader" class="drag postblock">Thread Options No.' + id 604 + '<img alt="X" src="' + Main.icons.cross + '" id="threadOptsClose" ' 605 + 'class="extButton" title="Close Window"></div>' 606 + '<iframe src="https://sys.' + $L.d(Main.board) + '/' 607 + Main.board + '/admin?mode=admin&admin=opt&id=' + id 608 + '&noheader=true" width="400" height="175" frameborder="0"></iframe>'; 609 610 document.body.appendChild(this.threadOptsCnt); 611 612 window.addEventListener('message', J.onThreadOptsDone, false); 613 document.addEventListener('keydown', J.onKeyDown, false); 614 615 $.id('threadOptsClose').addEventListener('click', J.closeThreadOptionsFrame, false); 616 Draggable.set($.id('threadOptsHeader')); 617 }; 618 619 J.closeThreadOptionsFrame = function() { 620 window.removeEventListener('message', J.onThreadOptsDone, false); 621 document.removeEventListener('keydown', J.onKeyDown, false); 622 Draggable.unset($.id('threadOptsHeader')); 623 $.id('threadOptsClose').removeEventListener('click', J.closeThreadOptionsFrame, false); 624 document.body.removeChild(J.threadOptsCnt); 625 J.threadOptsCnt = null; 626 }; 627 628 J.onThreadOptsDone = function(e) { 629 if (J.threadOptsCnt && e.origin === 'https://sys.' + $L.d(Main.board) && e.data === 'done-threadopt') { 630 J.closeThreadOptionsFrame(); 631 } 632 }; 633 634 J.setFileSpoiler = function(t) { 635 var xhr, pid, flag, el; 636 637 pid = t.getAttribute('data-id'); 638 flag = t.getAttribute('data-flag'); 639 640 if (!pid) { 641 return; 642 } 643 644 el = $.id('f' + pid); 645 646 if (!flag) { 647 flag = $.cls('imgspoiler', el.parentNode)[0] ? 0 : 1; 648 } 649 650 if (!el || el.hasAttribute('data-processing')) { 651 return; 652 } 653 654 xhr = new XMLHttpRequest(); 655 xhr.open('GET', 'https://sys.' + $L.d(Main.board) + '/' + Main.board 656 + '/admin.php?admin=spoiler&pid=' + pid + '&flag=' + flag, true); 657 xhr.withCredentials = true; 658 xhr.onload = J.onFileSpoilerLoad; 659 xhr.onerror = J.onFileSpoilerError; 660 xhr._pid = +pid; 661 xhr._flag = +flag; 662 663 Feedback.notify('Processing...', null); 664 665 el.setAttribute('data-processing', '1'); 666 667 xhr.send(null); 668 }; 669 670 J.onFileSpoilerLoad = function() { 671 var el, el2; 672 673 Feedback.hideMessage(); 674 675 if (this.responseText !== '1') { 676 if (this.responseText === '-1') { 677 Feedback.error('You are not logged in'); 678 } 679 else { 680 Feedback.error("Couldn't set spoiler flag for post No." + this._pid); 681 } 682 683 return; 684 } 685 686 if (!(el = $.id('f' + this._pid))) { 687 return; 688 } 689 690 el.removeAttribute('data-processing'); 691 692 if (!(el = $.cls('fileThumb', el)[0])) { 693 return; 694 } 695 696 if (this._flag) { 697 $.addClass(el, 'imgspoiler'); 698 699 el2 = el.previousElementSibling; 700 el2.setAttribute('title', el2.firstElementChild.textContent); 701 702 if (!Config.revealSpoilers) { 703 el = $.tag('img', el)[0]; 704 el.style.width = el.style.height = '100px'; 705 el.src = '//s.4cdn.org/image/spoiler-' + Main.board + '.png'; 706 } 707 } 708 else { 709 if (!Config.revealSpoilers) { 710 Parser.revealImageSpoiler(el); 711 } 712 $.removeClass(el, 'imgspoiler'); 713 } 714 }; 715 716 J.onFileSpoilerError = function() { 717 var el; 718 719 if (!(el = $.id('f' + this._pid))) { 720 return; 721 } 722 723 el.removeAttribute('data-processing'); 724 Feedback.error("Couldn't update the spoiler flag for post No." + this.pid); 725 }; 726 727 /** 728 * Multi 729 */ 730 var Multi = {}; 731 732 Multi.exec = function(btn) { 733 var pid, sel; 734 735 if (UA.isOpera && typeof (sel = document.getSelection()) == 'string') {} 736 else { 737 sel = window.getSelection().toString(); 738 } 739 740 if (sel) { 741 window.open('https://' + J.teamSubDomain 742 + '.4chan.org/search#{"comment":"' + sel.replace(/[\r\n]+/g, ' ') + '"}'); 743 } 744 else { 745 pid = btn.getAttribute('data-id'); 746 747 window.open('https://team.4chan.org/search?action=from_pid&board=' + Main.board 748 + '&pid=' + pid); 749 } 750 }; 751 752 Multi.prompt = function (ip, pid) { 753 var cnt, btn, link; 754 755 cnt = $.id('pi' + pid); 756 btn = $.cls('postMenuBtn', cnt)[0]; 757 758 link = document.createElement('a'); 759 link.href = 'https://' + J.teamSubDomain + '.4chan.org/search#{"ip":"' + ip + '"}'; 760 link.setAttribute('target', '_blank'); 761 link.className = 'post-ip'; 762 link.textContent = ip; 763 764 cnt.insertBefore(link, btn); 765 }; 766 767 /** 768 * Admin tools 769 */ 770 var AdminTools = { 771 cacheTTL: 60000, 772 autoRefreshDelay: 120000, 773 autoRefreshTimeout: null 774 }; 775 776 // FIXME, put it as a helper in extension.js 777 AdminTools.initVisibilityAPI = function() { 778 this.hidden = 'hidden'; 779 this.visibilitychange = 'visibilitychange'; 780 781 if (typeof document.hidden === 'undefined') { 782 if ('mozHidden' in document) { 783 this.hidden = 'mozHidden'; 784 this.visibilitychange = 'mozvisibilitychange'; 785 } 786 else if ('webkitHidden' in document) { 787 this.hidden = 'webkitHidden'; 788 this.visibilitychange = 'webkitvisibilitychange'; 789 } 790 else if ('msHidden' in document) { 791 this.hidden = 'msHidden'; 792 this.visibilitychange = 'msvisibilitychange'; 793 } 794 } 795 796 document.addEventListener(this.visibilitychange, this.onVisibilityChange, false); 797 }; 798 799 J.initIconsCatalog = function() { 800 var key, paths, url; 801 802 Main.icons = { 803 up: 'arrow_up.png', 804 down: 'arrow_down.png', 805 right: 'arrow_right.png', 806 download: 'arrow_down2.png', 807 refresh: 'refresh.png', 808 cross: 'cross.png', 809 gis: 'gis.png', 810 iqdb: 'iqdb.png', 811 minus: 'post_expand_minus.png', 812 plus: 'post_expand_plus.png', 813 rotate: 'post_expand_rotate.gif', 814 quote: 'quote.png', 815 report: 'report.png', 816 notwatched: 'watch_thread_off.png', 817 watched: 'watch_thread_on.png', 818 help: 'question.png' 819 }; 820 821 paths = { 822 yotsuba_new: 'futaba/', 823 futaba_new: 'futaba/', 824 yotsuba_b_new: 'burichan/', 825 burichan_new: 'burichan/', 826 tomorrow: 'tomorrow/', 827 photon: 'photon/' 828 }; 829 830 url = '//s.4cdn.org/image/'; 831 832 if (window.devicePixelRatio >= 2) { 833 for (key in Main.icons) { 834 Main.icons[key] = Main.icons[key].replace('.', '@2x.'); 835 } 836 } 837 838 url += 'buttons/' + paths[Main.stylesheet]; 839 for (key in Main.icons) { 840 Main.icons[key] = url + Main.icons[key]; 841 } 842 }; 843 844 AdminTools.init = function () { 845 var cnt, html; 846 847 AdminTools.initVisibilityAPI(); 848 849 cnt = document.createElement('div'); 850 cnt.className = 'extPanel reply'; 851 cnt.id = 'adminToolbox'; 852 cnt.setAttribute('data-trackpos', 'AT-position'); 853 854 if (Config['AT-position']) { 855 cnt.style.cssText = Config['AT-position']; 856 } else { 857 cnt.style.right = '10px'; 858 cnt.style.top = '380px'; 859 } 860 861 cnt.style.position = Config.fixedAdminToolbox ? 'fixed' : ''; 862 863 html = '<div class="drag" id="atHeader">Moderator Tools' 864 + '<img alt="Refresh" title="Refresh" src="' + Main.icons.refresh 865 + '" id="atRefresh" data-cmd="at-refresh" class="pointer right"></div>' 866 + '<h4><a href="https://' + J.reportsSubDomain + '.4chan.org/" target="_blank">Reports</a>: ' 867 + '<span title="Total" id="at-total">?</span> (' 868 + '<span title="Illegal" id="at-illegal">?</span>)</h4>' 869 + '<h4><a href="https://' + J.reportsSubDomain + '.4chan.org/?action=ban_requests" target="_blank">Ban Requests</a>: ' 870 + '<span id="at-banreqs">?</span> (<span title="Illegal" id="at-illegal-br">?</span>)</h4>' 871 + '<h4><a href="https://' + J.teamSubDomain + '.4chan.org/appeals" target="_blank">Appeals</a>: ' 872 + '<span id="at-appeals">?</span> (<span title="4chan Pass Users" id="at-prio-appeals">?</span>)</h4>' 873 + '<h4 id="at-msg-cnt"><a data-cmd="at-msg" href="https://' + J.reportsSubDomain 874 + '.4chan.org/?action=staffmessages" target="_blank">Messages</a>: <span id="at-msg">?</span></h4>'; 875 876 if (Main.tid) { 877 html += '<hr><h4><a href="javascript:void(0);" data-cmd="poster-id">Same Poster ID</a></h4>'; 878 } 879 880 cnt.innerHTML = html; 881 document.body.appendChild(cnt); 882 AdminTools.refreshReportCount(); 883 884 Draggable.set($.id('atHeader')); 885 }; 886 887 AdminTools.onVisibilityChange = function() { 888 var self; 889 890 self = AdminTools; 891 892 if (document[AdminTools.hidden]) { 893 clearInterval(self.autoRefreshTimeout); 894 self.autoRefreshTimeout = null; 895 } 896 else { 897 self.refreshReportCount(); 898 self.autoRefreshTimeout = setInterval(self.refreshReportCount, self.autoRefreshDelay); 899 } 900 }; 901 902 AdminTools.refreshReportCount = function(force) { 903 var xhr, cache, msg_count; 904 905 if (force !== true && (cache = localStorage.getItem('4chan-cache-rc'))) { 906 cache = JSON.parse(cache); 907 908 if (cache.ts > Date.now() - AdminTools.cacheTTL) { 909 $.id('at-total').textContent = cache.data[0]; 910 $.id('at-illegal').textContent = cache.data[1]; 911 $.id('at-banreqs').textContent = cache.data[2]; 912 $.id('at-appeals').textContent = cache.data[3]; 913 $.id('at-illegal-br').textContent = cache.data[4] || 0; 914 $.id('at-prio-appeals').textContent = cache.data[5] || 0; 915 916 $.id('at-msg-cnt').style.display = cache.data[6] ? 'block' : ''; 917 $.id('at-msg').textContent = cache.data[6] || 0; 918 919 return; 920 } 921 } 922 923 xhr = new XMLHttpRequest(); 924 925 xhr.open('GET', 'https://' + J.reportsSubDomain + '.4chan.org/H429f6uIsUqU.php', true); 926 927 xhr.withCredentials = true; 928 929 xhr.onload = function () { 930 var cache, resp, data; 931 932 if (this.status == 200) { 933 try { 934 resp = JSON.parse(this.responseText); 935 } 936 catch (e) { 937 console.log(e); 938 return; 939 } 940 941 if (resp.status !== 'success') { 942 console.log(resp.message); // FIXME, use global message 943 return; 944 } 945 946 data = resp.data; 947 948 msg_count = data.msg || 0; 949 950 $.id('at-msg-cnt').style.display = msg_count ? 'block' : ''; 951 $.id('at-msg').textContent = msg_count; 952 $.id('at-total').textContent = data.total; 953 $.id('at-illegal').textContent = data.illegal; 954 $.id('at-banreqs').textContent = data.banreqs; 955 $.id('at-illegal-br').textContent = data.illegal_banreqs; 956 $.id('at-appeals').textContent = data.appeals; 957 $.id('at-prio-appeals').textContent = data.prio_appeals; 958 959 cache = { 960 ts: Date.now(), 961 data: [ 962 data.total, 963 data.illegal, 964 data.banreqs, 965 data.appeals, 966 data.illegal_banreqs, 967 data.prio_appeals, 968 data.msg 969 ] 970 }; 971 972 cache = JSON.stringify(cache); 973 974 localStorage.setItem('4chan-cache-rc', cache); 975 976 document.dispatchEvent(new CustomEvent('4chanATUpdated')); 977 } 978 else { 979 this.onerror(); 980 } 981 }; 982 983 xhr.onerror = function () { 984 console.log('Error while refreshing the report count (Status: ' + this.status + ').'); 985 }; 986 987 xhr.onloadend = function () { 988 $.id('atRefresh').src = Main.icons.refresh; 989 }; 990 991 $.id('atRefresh').src = Main.icons.rotate; 992 993 xhr.send(null); 994 }; 995 996 AdminTools.resetMsgCount = function() { 997 var cache; 998 999 $.id('at-msg').textContent = 0; 1000 1001 if (cache = localStorage.getItem('4chan-cache-rc')) { 1002 cache = JSON.parse(cache); 1003 cache.data[6] = 0; 1004 cache = JSON.stringify(cache); 1005 localStorage.setItem('4chan-cache-rc', cache); 1006 } 1007 }; 1008 1009 /** 1010 * Click handler 1011 */ 1012 J.onClick = function (e) { 1013 var t, cmd; 1014 1015 if ((t = e.target) == document) { 1016 return; 1017 } 1018 1019 if (cmd = t.getAttribute('data-cmd')) { 1020 switch (cmd) { 1021 case 'at-refresh': 1022 AdminTools.refreshReportCount(true); 1023 break; 1024 case 'delete-post': 1025 case 'delete-image': 1026 J.deletePost(t, (cmd === 'delete-image')); 1027 break; 1028 case 'force-archive': 1029 J.forceArchive(t); 1030 break; 1031 1032 case 'open-delete-prompt': 1033 J.openDeletePrompt(t); 1034 break; 1035 1036 case 'close-delete-prompt': 1037 J.closeDeletePrompt(); 1038 break; 1039 1040 case 'at-msg': 1041 AdminTools.resetMsgCount(); 1042 break; 1043 1044 case 'toggle-file-spoiler': 1045 J.setFileSpoiler(t); 1046 break; 1047 1048 case 'prompt-spoiler': 1049 if (confirm('Toggle spoiler?')) { 1050 J.setFileSpoiler(t); 1051 } 1052 break; 1053 1054 case 'thread-options': 1055 if (Config.inlinePopups) { 1056 J.openThreadOptionsFrame(t); 1057 } 1058 else { 1059 J.openThreadOptions(t); 1060 } 1061 break; 1062 1063 case 'multi': 1064 e.preventDefault(); 1065 Multi.exec(t); 1066 break; 1067 1068 case 'get-md5': 1069 J.onGetMD5Click(t); 1070 break; 1071 1072 case 'html-toggle': 1073 J.onHTMLToggle(t); 1074 break; 1075 1076 case 'preview-html': 1077 e.preventDefault(); 1078 J.onPreviewHTMLClick(t); 1079 break; 1080 1081 case 'close-html-preview': 1082 J.closeHTMLPreview(); 1083 break; 1084 1085 case 'poster-id': 1086 J.loadSamePosters(); 1087 break; 1088 1089 case 'ban': 1090 if (Config.inlinePopups) { 1091 J.openBanFrame(t); 1092 } 1093 else { 1094 J.openBanWindow(t); 1095 } 1096 break; 1097 } 1098 } 1099 }; 1100 1101 J.onScroll = function () { 1102 var end; 1103 1104 while (J.nextChunk.offsetTop < (document.documentElement.clientHeight + window.scrollY)) { 1105 end = J.nextChunkIndex + J.chunkSize; 1106 if (end >= J.postCount) { 1107 J.parseRange(J.nextChunkIndex, J.postCount); 1108 window.removeEventListener('scroll', J.onScroll, false); 1109 return false; 1110 } 1111 else { 1112 J.parseRange(J.nextChunkIndex, end); 1113 } 1114 } 1115 1116 return true; 1117 }; 1118 1119 J.parseRange = function (start, end) { 1120 var i, j, posts; 1121 1122 posts = document.getElementById('t' + Main.tid).getElementsByClassName('postInfo'); 1123 1124 for (i = start; i < end; ++i) { 1125 j = posts[i]; 1126 1127 if (!j) { 1128 break; 1129 } 1130 1131 J.parsePost(j); 1132 } 1133 1134 J.nextChunkIndex = i; 1135 J.nextChunk = posts[i]; 1136 }; 1137 1138 J.onParsingDone = function(e) { 1139 var i, tid, offset, limit, posts; 1140 1141 if (e) { 1142 tid = e.detail.threadId; 1143 offset = e.detail.offset; 1144 limit = e.detail.limit; 1145 posts = document.getElementById('t' + tid).getElementsByClassName('postInfo'); 1146 1147 if (J.sameIDActive) { 1148 J.loadSamePosters(posts[offset].id.slice(2)); 1149 } 1150 } 1151 else { 1152 offset = 0; 1153 posts = document.getElementsByClassName('postInfo'); 1154 limit = posts.length; 1155 } 1156 1157 if (Config.useIconButtons) { 1158 for (i = offset; i < limit; ++i) { 1159 J.parsePost(posts[i]); 1160 } 1161 } 1162 }; 1163 1164 J.onPostMenuReady = function(e) { 1165 var elw, el, pid, menu, flag; 1166 1167 pid = e.detail.postId; 1168 menu = e.detail.node; 1169 1170 if (window.thread_archived && $.id('f' + pid)) { 1171 elw = document.createElement('li'); 1172 elw.className = 'dd-admin'; 1173 el = document.createElement('a'); 1174 el.href = '#'; 1175 el.setAttribute('data-cmd', 'get-md5'); 1176 el.setAttribute('data-id', pid); 1177 el.textContent = 'File MD5'; 1178 elw.appendChild(el); 1179 menu.appendChild(elw); 1180 } 1181 1182 if (window.spoilers && (el = $.id('fT' + pid))) { 1183 flag = $.cls('imgspoiler', el.parentNode)[0] ? 0 : 1; 1184 elw = document.createElement('li'); 1185 elw.className = 'dd-admin'; 1186 el = document.createElement('a'); 1187 el.setAttribute('data-cmd', 'toggle-file-spoiler'); 1188 el.setAttribute('data-id', pid); 1189 el.setAttribute('data-flag', flag); 1190 el.textContent = (flag ? 'Set' : 'Unset') + ' Spoiler'; 1191 elw.appendChild(el); 1192 menu.appendChild(elw); 1193 } 1194 1195 if (Config.useIconButtons && !Main.hasMobileLayout) { 1196 return; 1197 } 1198 1199 elw = document.createElement('li'); 1200 elw.className = 'dd-admin'; 1201 el = document.createElement('a'); 1202 el.setAttribute('data-cmd', 'open-delete-prompt'); 1203 el.setAttribute('data-id', pid); 1204 el.textContent = 'Delete'; 1205 elw.appendChild(el); 1206 menu.appendChild(elw); 1207 1208 if (window.thread_archived) { 1209 return; 1210 } 1211 1212 elw = document.createElement('li'); 1213 elw.className = 'dd-admin'; 1214 el = document.createElement('a'); 1215 el.setAttribute('data-cmd', 'ban'); 1216 el.setAttribute('data-id', pid); 1217 el.textContent = 'Ban'; 1218 elw.appendChild(el); 1219 menu.appendChild(elw); 1220 1221 elw = document.createElement('li'); 1222 elw.className = 'dd-admin'; 1223 el = document.createElement('a'); 1224 el.setAttribute('data-cmd', 'multi'); 1225 el.setAttribute('data-id', pid); 1226 el.textContent = 'Search'; 1227 elw.appendChild(el); 1228 menu.appendChild(elw); 1229 1230 if (e.detail.isOP) { 1231 elw = document.createElement('li'); 1232 elw.className = 'dd-admin'; 1233 el = document.createElement('a'); 1234 el.setAttribute('data-cmd', 'thread-options'); 1235 el.setAttribute('data-id', pid); 1236 el.textContent = 'Thread options'; 1237 elw.appendChild(el); 1238 menu.appendChild(elw); 1239 } 1240 }; 1241 1242 J.parsePost = function(postInfo) { 1243 var pid, html, cnt, tail; 1244 1245 pid = postInfo.id.slice(2); 1246 1247 html = '<img class="extButton" alt="X" data-cmd="open-delete-prompt" data-id="' 1248 + pid + '" src="' + Main.icons.cross 1249 + '" title="Delete">'; 1250 1251 if (window.spoilers && (el = $.id('fT' + pid))) { 1252 html += '<img class="extButton" alt="S" data-cmd="prompt-spoiler" data-id="' 1253 + pid + '" src="' + J.icons.spoiler 1254 + '" title="Toggle Spoiler">'; 1255 } 1256 1257 if (!window.thread_archived) { 1258 html += '<img class="extButton" alt="M" data-cmd="multi" data-id="' 1259 + pid + '" src="' + J.icons.multi 1260 + '" title="Display posts by this IP">' 1261 + '<img class="extButton" alt="B" data-cmd="ban" data-id="' 1262 + pid + '" src="' + J.icons.ban 1263 + '" title="Ban">'; 1264 1265 if ($.id('t' + pid)) { 1266 html += '<img class="extButton" alt=">" data-cmd="thread-options" data-id="' 1267 + pid + '" src="' + J.icons.arrow_right + '" title="Thread Options">'; 1268 } 1269 } 1270 1271 cnt = document.createElement('div'); 1272 cnt.className = 'extControls'; 1273 cnt.innerHTML = html; 1274 1275 tail = postInfo.getElementsByClassName('postMenuBtn')[0]; 1276 1277 postInfo.insertBefore(cnt, tail); 1278 }; 1279 1280 J.displayJCount = function(jLink, jLinkBot, no, delta) { 1281 var msg; 1282 1283 $.addClass(jLink, 'j-newposts'); 1284 $.addClass(jLinkBot, 'j-newposts'); 1285 jLink.setAttribute('data-no', no); 1286 jLinkBot.setAttribute('data-no', no); 1287 jLink.textContent = jLinkBot.textContent = 'j +' + delta; 1288 1289 msg = delta + ' new post' + (delta > 1 ? 's' : ''); 1290 1291 Main.addTooltip(jLink, msg, 'j-tooltip'); 1292 Main.addTooltip(jLinkBot, msg, 'j-tooltip-bot'); 1293 }; 1294 1295 J.refreshJCount = function() { 1296 var stored, jLink, jLinkBot, xhr; 1297 1298 jLink = $.id('j-link'); 1299 jLinkBot = $.id('j-link-bot'); 1300 1301 if (!jLink || !jLinkBot) { 1302 return; 1303 } 1304 1305 jLink = jLink.firstElementChild; 1306 jLinkBot = jLinkBot.firstElementChild; 1307 1308 if (stored = localStorage.getItem('4chan-j-count')) { 1309 stored = JSON.parse(stored); 1310 } 1311 1312 if (!stored || (Date.now() - stored.time) >= 10000) { 1313 xhr = new XMLHttpRequest(); 1314 xhr.open('GET', 'https://sys.4chan.org/j/1mcQTXbjW5WO.php?&' + Date.now()); 1315 xhr.withCredentials = true; 1316 xhr.onloadend = function() { 1317 var data, obj, delta; 1318 if (this.status == 200 || this.status == 304) { 1319 data = JSON.parse(this.responseText); 1320 if (!stored || Main.board == 'j') { 1321 obj = { time: Date.now(), no: data.no }; 1322 } 1323 else if (data.no > stored.no) { 1324 delta = data.no - stored.no; 1325 J.displayJCount(jLink, jLinkBot, data.no, delta); 1326 obj = { time: Date.now(), no: stored.no, delta: delta }; 1327 } 1328 if (obj) { 1329 localStorage.setItem('4chan-j-count', JSON.stringify(obj)); 1330 } 1331 } 1332 else { 1333 console.log('Error: Could not load /j/ post count (Status: ' + this.status + ').'); 1334 } 1335 }; 1336 xhr.send(null); 1337 } 1338 else if (stored.delta) { 1339 J.displayJCount(jLink, jLinkBot, stored.no, stored.delta); 1340 } 1341 }; 1342 1343 J.clearJCount = function() { 1344 var obj, no, tt, ttbot; 1345 1346 tt = $.id('j-tooltip'); 1347 ttbot = $.id('j-tooltip-bot'); 1348 1349 if (!tt) { 1350 return; 1351 } 1352 1353 no = this.getAttribute('data-no'); 1354 obj = { time: Date.now(), no: no }; 1355 localStorage.setItem('4chan-j-count', JSON.stringify(obj)); 1356 1357 tt.parentNode.removeChild(tt); 1358 ttbot.parentNode.removeChild(ttbot); 1359 1360 setTimeout(function() { 1361 var nodes = $.cls('j-newposts'); 1362 if (nodes[0]) { 1363 nodes[0].textContent = 'j'; 1364 $.removeClass(nodes[0], 'j-newposts'); 1365 nodes[0].textContent = 'j'; 1366 $.removeClass(nodes[0], 'j-newposts'); 1367 } 1368 }, 10); 1369 }; 1370 1371 J.hasFlag = function(flag) { 1372 return this.flags.indexOf(flag) != -1; 1373 }; 1374 1375 J.icons = { 1376 multi: 'multi.png', 1377 ban: 'ban.png', 1378 arrow_right: 'arrow_right.png', 1379 spoiler: 's.png' 1380 }; 1381 1382 J.initIcons = function () { 1383 var key, paths, url; 1384 1385 paths = { 1386 yotsuba_new:'futaba/', 1387 futaba_new:'futaba/', 1388 yotsuba_b_new:'burichan/', 1389 burichan_new:'burichan/', 1390 tomorrow:'tomorrow/', 1391 photon:'photon/' 1392 }; 1393 1394 url = '//s.4cdn.org/image/buttons/' + paths[Main.stylesheet]; 1395 1396 if (window.devicePixelRatio >= 2) { 1397 for (key in J.icons) { 1398 J.icons[key] = J.icons[key].replace('.', '@2x.'); 1399 } 1400 } 1401 1402 for (key in J.icons) { 1403 J.icons[key] = url + J.icons[key]; 1404 } 1405 }; 1406 1407 J.initNavLinks = function () { 1408 var el, txt, frag, fragBot, nav, navbot; 1409 1410 nav = $.id('navtopright'); 1411 navbot = $.id('navbotright'); 1412 1413 // [j] link 1414 el = document.createElement('span'); 1415 el.id = 'j-link'; 1416 el.innerHTML = '[<a href="https://sys.4chan.org/j/" title="Janitor & Moderator Discussion">j</a>]'; 1417 el.firstElementChild.addEventListener('mouseup', J.clearJCount, false); 1418 nav.parentNode.insertBefore(el, nav); 1419 1420 // [j] bottom link 1421 el = el.cloneNode(true); 1422 el.id = 'j-link-bot'; 1423 el.firstElementChild.addEventListener('mouseup', J.clearJCount, false); 1424 navbot.parentNode.insertBefore(el, navbot); 1425 1426 J.refreshJCount(); 1427 1428 // Team link 1429 txt = nav.lastElementChild.previousSibling; 1430 frag = document.createDocumentFragment(); 1431 frag.appendChild(document.createTextNode('] [')); 1432 el = document.createElement('a'); 1433 el.textContent = 'Team'; 1434 el.href = 'https://' + J.teamSubDomain + '.4chan.org/'; 1435 el.setAttribute('target', '_blank'); 1436 frag.appendChild(el); 1437 frag.appendChild(document.createTextNode('] [')); 1438 fragBot = frag.cloneNode(true); 1439 nav.replaceChild(frag, txt); 1440 1441 // Team bottom link 1442 txt = navbot.lastElementChild.previousSibling; 1443 navbot.replaceChild(fragBot, txt); 1444 1445 // Poster IDs 1446 if (Main.tid && Main.hasMobileLayout) { 1447 el = document.createElement('span'); 1448 el.className = 'mobileib button redButton'; 1449 el.innerHTML = '<label data-cmd="poster-id">Same Poster ID</label>'; 1450 if (nav = $.cls('navLinks')[0]) { 1451 nav.appendChild(el); 1452 } 1453 } 1454 }; 1455 1456 J.initPostForm = function () { 1457 var form, el, field, cnt, cookie; 1458 1459 form = $.id('postForm'); 1460 1461 if (!form && Main.tid && (cnt = $.cls('closed')[0])) { 1462 el = document.createElement('form'); 1463 el.name = 'post'; 1464 el.method = 'POST'; 1465 el.enctype = 'multipart/form-data'; 1466 el.action = 'https://sys.' + $L.d(Main.board) + '/' + Main.board + '/post'; 1467 el.innerHTML = J.postFormHTML 1468 + '<input type="hidden" name="mode" value="regist">' 1469 + '<input type="hidden" name="resto" value="' + Main.tid + '">'; 1470 1471 cnt.parentNode.insertBefore(el, cnt); 1472 1473 form = el.firstElementChild; 1474 1475 QR.enabled = true; 1476 } 1477 1478 if (form) { 1479 el = document.forms.post.name; 1480 1481 if (el.type == 'hidden' && J.hasFlag('forcedanonname')) { 1482 field = document.createElement('tr'); 1483 field.setAttribute('data-type', 'Name'); 1484 field.innerHTML = '<td>Name</td><td><input name="name" type="text"></td>'; 1485 if (cookie = Main.getCookie('4chan_name')) { 1486 field.lastChild.firstChild.value = cookie; 1487 } 1488 cnt = $.id('postForm').firstElementChild; 1489 cnt.insertBefore(field, cnt.firstElementChild); 1490 el.parentNode.removeChild(el); 1491 } 1492 1493 if (J.hasFlag('html')) { 1494 el = document.createElement('tr'); 1495 el.innerHTML = '<tr><td style="height: 20px;">Extra</td><td>[<label><input type="checkbox" data-cmd="html-toggle" name="html" value="1">HTML</label>] <span class="html-otp">[<label data-tip="2FA One-Time Password">OTP</label> <input type="text" autocomplete="off" name="otp" maxlength="6">] <a href="#" class="preview-html-btn" data-cmd="preview-html">Preview</a></span></td></tr>'; 1496 form = form.firstElementChild; 1497 form.insertBefore(el, form.lastElementChild); 1498 } 1499 } 1500 }; 1501 1502 J.onHTMLToggle = function(t) { 1503 var el = $.cls('html-otp', t.parentNode.parentNode)[0]; 1504 1505 if (!el) { 1506 return; 1507 } 1508 1509 el.style.display = t.checked ? 'inline' : ''; 1510 }; 1511 1512 J.onPreviewHTMLClick = function(t) { 1513 var data, form, xhr; 1514 1515 if (J.xhrs['html']) { 1516 return; 1517 } 1518 1519 if (document.forms.qrPost) { 1520 form = document.forms.qrPost; 1521 } 1522 else { 1523 form = document.forms.post; 1524 } 1525 1526 if (form.com.value === '') { 1527 return; 1528 } 1529 1530 data = new FormData(); 1531 data.append('com', form.com.value); 1532 1533 xhr = new XMLHttpRequest(); 1534 xhr.open('post', form.action + '?mode=preview_html', true); 1535 xhr.withCredentials = true; 1536 xhr.onload = J.onHTMLPReviewLoaded; 1537 xhr.onerror = J.onHTMLPReviewError; 1538 1539 J.xhrs['html'] = xhr; 1540 1541 $.addClass(t, 'disabled'); 1542 1543 xhr.send(data); 1544 1545 Feedback.notify('Processing...', null); 1546 }; 1547 1548 J.resetHTMLPreviewBtn = function() { 1549 var nodes = $.cls('preview-html-btn'); 1550 $.removeClass(nodes[0], 'disabled'); 1551 nodes[1] && $.removeClass(nodes[1], 'disabled'); 1552 }; 1553 1554 J.onHTMLPReviewLoaded = function() { 1555 var resp; 1556 1557 J.xhrs['html'] = null; 1558 1559 Feedback.hideMessage(); 1560 1561 J.resetHTMLPreviewBtn(); 1562 1563 try { 1564 resp = JSON.parse(this.responseText); 1565 1566 if (resp.status == 'error') { 1567 Feedback.error(resp.message); 1568 } 1569 } 1570 catch (err) { 1571 Feedback.error('Something went wrong.'); 1572 } 1573 1574 J.buildHTMLPreview(resp.data); 1575 }; 1576 1577 J.onHTMLPReviewError = function() { 1578 J.xhrs['html'] = null; 1579 1580 Feedback.hideMessage(); 1581 1582 J.resetHTMLPreviewBtn(); 1583 1584 console.log(this); 1585 }; 1586 1587 J.closeHTMLPreview = function() { 1588 var el; 1589 1590 if (el = $.id('html-preview-cnt')) { 1591 el.parentNode.removeChild(el); 1592 } 1593 1594 J.resetHTMLPreviewBtn(); 1595 }; 1596 1597 J.buildHTMLPreview = function(html) { 1598 var el; 1599 1600 J.closeHTMLPreview(); 1601 1602 el = document.createElement('div'); 1603 el.id = 'html-preview-cnt'; 1604 el.setAttribute('data-cmd', 'close-html-preview'); 1605 el.innerHTML = '\ 1606 <div class="extPanel reply"><div class="panelHeader">Preview HTML Post\ 1607 <span class="panelCtrl"><img alt="Close" title="Close" class="pointer" data-cmd="close-html-preview" src="' 1608 + Main.icons.cross + '"></span></div>' + html + '</div>'; 1609 1610 document.body.appendChild(el); 1611 }; 1612 1613 J.onThreadMouseOver = function(e) { 1614 var t = e.target; 1615 1616 if ($.hasClass(t, 'thumb')) { 1617 if (J.hasCatalogControls) { 1618 J.hideCatalogControls(); 1619 } 1620 if (!$.hasClass(t.parentNode.parentNode, 'disabled')) { 1621 J.showCatalogControls(t); 1622 } 1623 } 1624 }; 1625 /* 1626 J.onThreadMouseOut = function(e) { 1627 var t = e.target; 1628 1629 if (J.hasCatalogControls && $.hasClass(t, 'thumb')) { 1630 J.hideCatalogControls(); 1631 } 1632 }; 1633 */ 1634 J.showCatalogControls = function(t) { 1635 var el, id, cnt; 1636 1637 id = t.getAttribute('data-id'); 1638 1639 el = document.createElement('div'); 1640 el.id = 'cat-ctrl'; 1641 el.className = J.stylesheet; 1642 el.innerHTML = '<span class="threadIcon deleteIcon" data-cmd="open-delete-prompt" data-id="' 1643 + id + '"></span><span class="threadIcon multiIcon" data-cmd="multi" data-id="' 1644 + id + '"></span><span class="threadIcon banIcon" data-cmd="ban" data-id="' 1645 + id + '"></span>'; 1646 1647 if (cnt = $.cls('threadIcons', t.parentNode.parentNode)[0]) { 1648 cnt.insertBefore(el, cnt.firstElementChild); 1649 } 1650 else { 1651 cnt = document.createElement('div'); 1652 cnt.className = 'threadIcons'; 1653 cnt.appendChild(el); 1654 t.parentNode.parentNode.insertBefore(cnt, t.parentNode.nextElementSibling); 1655 } 1656 1657 J.hasCatalogControls = true; 1658 }; 1659 1660 J.hideCatalogControls = function() { 1661 var el = $.id('cat-ctrl'); 1662 1663 if (el) { 1664 el.parentNode.removeChild(el); 1665 } 1666 1667 J.hasCatalogControls = false; 1668 }; 1669 1670 J.initCatalog = function() { 1671 var storage; 1672 1673 window.Main = { 1674 board: location.pathname.split(/\//)[1] 1675 }; 1676 1677 Main.addTooltip = function(link, message, id) { 1678 var el, pos; 1679 1680 el = document.createElement('div'); 1681 el.className = 'click-me'; 1682 if (id) { 1683 el.id = id; 1684 } 1685 el.innerHTML = message || 'Change your settings'; 1686 link.parentNode.appendChild(el); 1687 1688 pos = (link.offsetWidth - el.offsetWidth + link.offsetLeft - el.offsetLeft) / 2; 1689 el.style.marginLeft = pos + 'px'; 1690 1691 return el; 1692 }; 1693 1694 if (J.stylesheet = J.getCookie(window.style_group)) { 1695 J.stylesheet = J.stylesheet.toLowerCase().replace(/ /g, '_'); 1696 } 1697 else { 1698 J.stylesheet = 1699 style_group == 'nws_style' ? 'yotsuba_new' : 'yotsuba_b_new'; 1700 } 1701 1702 Main.stylesheet = J.stylesheet; 1703 1704 J.initIconsCatalog(); 1705 1706 J.addCss(); // fixme 1707 1708 document.addEventListener('click', J.onClick, false); 1709 1710 J.runCatalog(); 1711 }; 1712 1713 J.runCatalog = function () { 1714 var threads; 1715 //J.addCss(); // fixme 1716 //document.removeEventListener('4chanMainInit', J.runCatalog, false); 1717 1718 J.initNavLinks(); 1719 1720 if (!FC.hasMobileLayout) { 1721 AdminTools.init(); 1722 } 1723 1724 threads = $.id('threads'); 1725 1726 J.initCatAutoReload(true); 1727 1728 document.addEventListener('keydown', J.onCatalogKeyDown, false); 1729 1730 $.on(threads, 'mouseover', J.onThreadMouseOver); 1731 1732 if (window.text_only) { 1733 document.addEventListener('4chanPostMenuReady', J.onPostMenuReady, false); 1734 } 1735 //$.on(threads, 'mouseout', J.onThreadMouseOut); 1736 }; 1737 1738 J.init = function () { 1739 var flags, ts; 1740 1741 SettingsMenu.options['Moderator'] = { 1742 useIconButtons: [ 'Use icon buttons', 'Display old-style buttons instead of using drop-down' ], 1743 changeUpdateDelay:[ 'Reduce auto-update delay', 'Reduce the thread updater delay', true ], 1744 fixedAdminToolbox:[ 'Pin Moderator Tools to the page', 'Moderator Tools will scroll with you' ], 1745 inlinePopups: [ 'Inline admin panels', 'Open admin panels in browser window, instead of a popup' ], 1746 disableMngExt:[ 'Disable moderator extension', 'Completely disable the moderator extension (overrides any checked boxes)', true ] 1747 }; 1748 1749 if (Config.disableMngExt) { 1750 return; 1751 } 1752 1753 if (flags = Main.getCookie('4chan_aflags')) { 1754 J.flags = flags.split(','); 1755 } 1756 1757 J.addCss(); 1758 1759 if (Config.useIconButtons) { 1760 J.initIcons(); 1761 } 1762 1763 QR.noCooldown = QR.noCaptcha = true; 1764 //QR.comLength = window.comlen = 10000; 1765 1766 document.addEventListener('click', J.onClick, false); 1767 document.addEventListener('DOMContentLoaded', J.run, false); 1768 }; 1769 1770 J.run = function() { 1771 var posts, el, nav; 1772 1773 document.removeEventListener('DOMContentLoaded', J.run, false); 1774 1775 J.initNavLinks(); 1776 J.initPostForm(); 1777 1778 if (!Main.hasMobileLayout) { 1779 AdminTools.init(); 1780 } 1781 1782 if (Config.revealSpoilers) { 1783 $.addClass(document.body, 'reveal-img-spoilers'); 1784 } 1785 1786 if (Config.threadUpdater && Main.tid) { 1787 if (Config.changeUpdateDelay) { 1788 ThreadUpdater.delayIdHidden = 3; 1789 ThreadUpdater.delayRange = [ 5, 10, 15, 20, 30, 60 ]; 1790 ThreadUpdater.apiUrlFilter = J.apiUrlFilter; 1791 } 1792 } 1793 1794 if (Config.useIconButtons && !Main.hasMobileLayout) { 1795 if (Main.tid) { 1796 posts = document.getElementById('t' + Main.tid).getElementsByClassName('postInfo'); 1797 J.postCount = posts.length; 1798 if (J.postCount > J.chunkSize) { 1799 J.nextChunk = posts[0]; 1800 window.addEventListener('scroll', J.onScroll, false); 1801 J.onScroll(); 1802 } 1803 else { 1804 J.onParsingDone(); 1805 } 1806 1807 nav = $.cls('navLinksBot')[0]; 1808 el = document.createElement('span'); 1809 el.id = 'threadOptsButtom'; 1810 el.innerHTML = '[<a href="javascript:;" data-id="' + Main.tid + '" data-cmd="thread-options">Thread Options</a>]'; 1811 nav.appendChild(el); 1812 } 1813 else { 1814 J.onParsingDone(); 1815 } 1816 1817 document.addEventListener('4chanParsingDone', J.onParsingDone, false); 1818 1819 J.parserEventBound = true; 1820 } 1821 1822 document.addEventListener('4chanPostMenuReady', J.onPostMenuReady, false); 1823 1824 if (nav = $.id('boardSelectMobile')) { 1825 el = document.createElement('option'); 1826 el.value = 'j'; 1827 el.textContent = '/j/ - Janitors & Moderators'; 1828 nav.insertBefore(el, nav.firstElementChild); 1829 } 1830 }; 1831 1832 J.getCookie = function(name) { 1833 var i, c, ca, key; 1834 1835 key = name + "="; 1836 ca = document.cookie.split(';'); 1837 1838 for (i = 0; c = ca[i]; ++i) { 1839 while (c.charAt(0) == ' ') { 1840 c = c.substring(1, c.length); 1841 } 1842 if (c.indexOf(key) === 0) { 1843 return decodeURIComponent(c.substring(key.length, c.length)); 1844 } 1845 } 1846 return null; 1847 }; 1848 1849 J.postFormHTML = '<table class="postForm" id="postForm"><tbody>\ 1850 <tr data-type="Name"><td>Name</td><td>\ 1851 <input name="name" type="text" tabindex="1" \ 1852 placeholder="Anonymous"></td></tr><tr data-type="Options"><td>Options</td>\ 1853 <td><input name="email" type="text" tabindex="2"><input type="submit" \ 1854 value="Post" tabindex="6"></td></tr><tr data-type="Comment"><td>Comment</td>\ 1855 <td><textarea name="com" cols="48" rows="4" tabindex="4" wrap="soft">\ 1856 </textarea></td></tr><tr data-type="File"><td>File</td><td>\ 1857 <input id="postFile" name="upfile" type="file" tabindex="7">\ 1858 <span class="desktop">[<label><input type="checkbox" name="spoiler" value="on" \ 1859 tabindex="8">Spoiler?</label>]</span></td></tr><tr><td></td></tr></tbody></table>'; 1860 1861 J.addCss = function () { 1862 var style, css; 1863 1864 css = '\ 1865 #adminToolbox {\ 1866 max-width: 256px;\ 1867 display: block;\ 1868 position: absolute;\ 1869 padding: 3px;\ 1870 }\ 1871 #adminToolbox h4 {\ 1872 font-size: 12px;\ 1873 margin: 2px 0 0;\ 1874 padding: 0;\ 1875 font-weight: normal;\ 1876 }\ 1877 #adminToolbox li {\ 1878 list-style: none;\ 1879 }\ 1880 #adminToolbox ul {\ 1881 padding: 0;\ 1882 margin: 2px 0 0 10px;\ 1883 }\ 1884 #atHeader {\ 1885 height: 17px;\ 1886 font-weight: bold;\ 1887 padding-bottom: 2px;\ 1888 }\ 1889 #atRefresh {\ 1890 margin: -1px 0 0 3px;\ 1891 }\ 1892 .post-ip {\ 1893 margin-left: 5px;\ 1894 }\ 1895 #delete-prompt > div {\ 1896 text-align: center;\ 1897 }\ 1898 #watchList li:first-child {\ 1899 margin-top: 3px;\ 1900 padding-top: 2px;\ 1901 border-top: 1px solid rgba(0, 0, 0, 0.20);\ 1902 }\ 1903 .photon #atHeader {\ 1904 border-bottom: 1px solid #ccc;\ 1905 }\ 1906 .yotsuba_new #atHeader {\ 1907 border-bottom: 1px solid #d9bfb7;\ 1908 }\ 1909 .yotsuba_b_new #atHeader {\ 1910 border-bottom: 1px solid #b7c5d9;\ 1911 }\ 1912 .tomorrow #atHeader {\ 1913 border-bottom: 1px solid #111;\ 1914 }\ 1915 #captchaFormPart {\ 1916 display: none;\ 1917 }\ 1918 #at-prio-appeals {\ 1919 color: blue;\ 1920 }\ 1921 #at-illegal-br,\ 1922 #at-illegal {\ 1923 color: red;\ 1924 }\ 1925 #at-msg-cnt {\ 1926 display: none;\ 1927 }\ 1928 .j-newposts {\ 1929 font-weight: bold !important;\ 1930 }\ 1931 #j-link,\ 1932 #j-link-bot {\ 1933 margin-right: 3px;\ 1934 display: inline-block;\ 1935 margin-left: 3px;\ 1936 }\ 1937 #adminToolbox hr {\ 1938 margin: 2px 0;\ 1939 }\ 1940 #threadOptsClose,\ 1941 #banReqClose {\ 1942 float: right;\ 1943 }\ 1944 #threadOpts iframe,\ 1945 #banReq iframe {\ 1946 overflow: hidden;\ 1947 }\ 1948 #threadOpts,\ 1949 #banReq {\ 1950 display: block;\ 1951 position: fixed;\ 1952 padding: 2px;\ 1953 font-size: 10pt;\ 1954 }\ 1955 #banReq {\ 1956 height: 490px;\ 1957 }\ 1958 #threadOpts {\ 1959 height: 194px;\ 1960 }\ 1961 #threadOptsHeader,\ 1962 #banReqHeader {\ 1963 text-align: center;\ 1964 margin-bottom: 1px;\ 1965 padding: 0;\ 1966 height: 18px;\ 1967 line-height: 18px;\ 1968 }\ 1969 #threadOptsButtom {\ 1970 float: right;\ 1971 margin-right: 4px;\ 1972 }\ 1973 .mobileExtControls {\ 1974 float: right;\ 1975 font-size: 11px;\ 1976 margin-bottom: 3px;\ 1977 }\ 1978 .ws .mobileExtControls {\ 1979 color: #34345C;\ 1980 }\ 1981 .nws .mobileExtControls {\ 1982 color: #0000EE;\ 1983 }\ 1984 .reply .mobileExtControls {\ 1985 margin-right: 5px;\ 1986 }\ 1987 .mobileExtControls span {\ 1988 margin-left: 10px;\ 1989 }\ 1990 .mobileExtControls span:after {\ 1991 content: "]";\ 1992 }\ 1993 .mobileExtControls span:before {\ 1994 content: "[";\ 1995 }\ 1996 .nws .mobileExtControls span:after {\ 1997 color: #800000;\ 1998 }\ 1999 .nws .mobileExtControls span:before {\ 2000 color: #800000;\ 2001 }\ 2002 .ws .mobileExtControls span:after {\ 2003 color: #000;\ 2004 }\ 2005 .ws .mobileExtControls span:before {\ 2006 color: #000;\ 2007 }\ 2008 .m-dark .mobileExtControls {\ 2009 color: #81a2be !important;\ 2010 }\ 2011 .m-dark .mobileExtControls span:after,\ 2012 .m-dark .mobileExtControls span:before {\ 2013 color: #1d1f21 !important;\ 2014 }\ 2015 #cat-ctrl {\ 2016 display: inline-block;\ 2017 margin-right: 2px;\ 2018 margin-top: -1px;\ 2019 }\ 2020 #cat-ctrl .threadIcon {\ 2021 cursor: pointer;\ 2022 }\ 2023 .disabled {\ 2024 opacity: 0.5;\ 2025 }\ 2026 .burichan_new .deleteIcon,\ 2027 .yotsuba_b_new .deleteIcon {\ 2028 background-image: url("//s.4cdn.org/image/buttons/burichan/cross.png");\ 2029 }\ 2030 .burichan_new .banIcon,\ 2031 .yotsuba_b_new .banIcon {\ 2032 background-image: url("//s.4cdn.org/image/buttons/burichan/ban.png");\ 2033 }\ 2034 .burichan_new .fileIcon,\ 2035 .yotsuba_b_new .fileIcon {\ 2036 background-image: url("//s.4cdn.org/image/buttons/burichan/f.png");\ 2037 }\ 2038 .burichan_new .multiIcon,\ 2039 .yotsuba_b_new .multiIcon {\ 2040 background-image: url("//s.4cdn.org/image/buttons/burichan/multi.png");\ 2041 }\ 2042 .futaba_new .deleteIcon,\ 2043 .yotsuba_new .deleteIcon {\ 2044 background-image: url("//s.4cdn.org/image/buttons/futaba/cross.png");\ 2045 }\ 2046 .futaba_new .banIcon,\ 2047 .yotsuba_new .banIcon {\ 2048 background-image: url("//s.4cdn.org/image/buttons/futaba/ban.png");\ 2049 }\ 2050 .futaba_new .fileIcon,\ 2051 .yotsuba_new .fileIcon {\ 2052 background-image: url("//s.4cdn.org/image/buttons/futaba/f.png");\ 2053 }\ 2054 .futaba_new .multiIcon,\ 2055 .yotsuba_new .multiIcon {\ 2056 background-image: url("//s.4cdn.org/image/buttons/futaba/multi.png");\ 2057 }\ 2058 .photon .deleteIcon {\ 2059 background-image: url("//s.4cdn.org/image/buttons/photon/cross.png");\ 2060 }\ 2061 .photon .banIcon {\ 2062 background-image: url("//s.4cdn.org/image/buttons/photon/ban.png");\ 2063 }\ 2064 .photon .fileIcon {\ 2065 background-image: url("//s.4cdn.org/image/buttons/photon/f.png");\ 2066 }\ 2067 .photon .multiIcon {\ 2068 background-image: url("//s.4cdn.org/image/buttons/photon/multi.png");\ 2069 }\ 2070 .tomorrow .deleteIcon {\ 2071 background-image: url("//s.4cdn.org/image/buttons/tomorrow/cross.png");\ 2072 }\ 2073 .tomorrow .banIcon {\ 2074 background-image: url("//s.4cdn.org/image/buttons/tomorrow/ban.png");\ 2075 }\ 2076 .tomorrow .fileIcon {\ 2077 background-image: url("//s.4cdn.org/image/buttons/tomorrow/f.png");\ 2078 }\ 2079 .tomorrow .multiIcon {\ 2080 background-image: url("//s.4cdn.org/image/buttons/tomorrow/multi.png");\ 2081 }\ 2082 .dd-admin {\ 2083 text-indent: 5px;\ 2084 }\ 2085 .dd-admin:before {\ 2086 color: #FF0000;\ 2087 content: "•";\ 2088 left: -3px;\ 2089 position: absolute;\ 2090 }\ 2091 .extPanel {\ 2092 border: 1px solid rgba(0, 0, 0, 0.2);\ 2093 }\ 2094 .extPanel img.pointer { width: 18px; height: 18px }\ 2095 .preview-html-btn { font-size: 11px; }\ 2096 #html-preview-cnt .extPanel { width: 800px; margin-left: -400px; }\ 2097 #html-preview-cnt {\ 2098 position: fixed;\ 2099 width: 100%;\ 2100 height: 100%;\ 2101 z-index: 9002;\ 2102 top: 0;\ 2103 left: 0;\ 2104 }\ 2105 #html-preview-cnt {\ 2106 line-height: 14px;\ 2107 font-size: 14px;\ 2108 background-color: rgba(0, 0, 0, 0.25);\ 2109 }\ 2110 #html-preview-cnt:after {\ 2111 display: inline-block;\ 2112 height: 100%;\ 2113 vertical-align: middle;\ 2114 content: "";\ 2115 }\ 2116 #html-preview-cnt > div {\ 2117 -moz-box-sizing: border-box;\ 2118 box-sizing: border-box;\ 2119 display: inline-block;\ 2120 height: auto;\ 2121 max-height: 100%;\ 2122 position: relative;\ 2123 width: 400px;\ 2124 left: 50%;\ 2125 margin-left: -200px;\ 2126 overflow: auto;\ 2127 box-shadow: 0 0 5px rgba(0, 0, 0, 0.25);\ 2128 vertical-align: middle;\ 2129 }\ 2130 .reveal-img-spoilers .imgspoiler::before {\ 2131 content: " ";\ 2132 width:0.75em;\ 2133 height:0.75em;\ 2134 border-radius: 0.5em;\ 2135 position: absolute;\ 2136 display: block;\ 2137 background: red;\ 2138 margin-top: 1px;\ 2139 margin-left: 1px;\ 2140 pointer-events: none;\ 2141 }\ 2142 .reveal-img-spoilers.is_catalog .imgspoiler::before { margin-top: 4px; margin-left: 12px;}\ 2143 .reveal-img-spoilers .imgspoiler:hover::before { background: #fff; }\ 2144 .html-otp { display: none; width: 50px; }\ 2145 .html-otp input { width: 50px !important; height: 12px; }\ 2146 .drag {\ 2147 -moz-user-select: none !important;\ 2148 cursor: move !important;\ 2149 }' + (J.isCatalog ? '\ 2150 .panelHeader .panelCtrl {\ 2151 position: absolute;\ 2152 right: 5px;\ 2153 top: 5px;\ 2154 }\ 2155 .active-btn { border-bottom: 3px double; }\ 2156 .UIPanel {\ 2157 position: fixed;\ 2158 width: 100%;\ 2159 height: 100%;\ 2160 top: 0;\ 2161 left: 0;\ 2162 z-index: 9000 !important;\ 2163 }\ 2164 .UIPanel {\ 2165 line-height: 14px;\ 2166 font-size: 14px;\ 2167 background-color: rgba(0, 0, 0, 0.25);\ 2168 }\ 2169 .UIPanel:after {\ 2170 display: inline-block;\ 2171 height: 100%;\ 2172 vertical-align: middle;\ 2173 content: "";\ 2174 }\ 2175 .UIPanel > div {\ 2176 -moz-box-sizing: border-box;\ 2177 box-sizing: border-box;\ 2178 display: inline-block;\ 2179 height: auto;\ 2180 max-height: 100%;\ 2181 position: relative;\ 2182 width: 400px;\ 2183 left: 50%;\ 2184 margin-left: -200px;\ 2185 overflow: auto;\ 2186 box-shadow: 0 0 5px rgba(0, 0, 0, 0.25);\ 2187 vertical-align: middle;\ 2188 }\ 2189 .UIPanel .extPanel {\ 2190 padding: 2px;\ 2191 }' : ''); 2192 2193 style = document.createElement('style'); 2194 style.setAttribute('type', 'text/css'); 2195 style.textContent = css; 2196 document.head.appendChild(style); 2197 }; 2198 2199 if (/https?:\/\/boards\.(?:4chan|4channel)\.org\/[a-z0-9]+\/catalog($|#.*$)/.test(location.href)) { 2200 J.isCatalog = true; 2201 J.initCatalog(); 2202 } 2203 else { 2204 J.init(); 2205 //J.run(); 2206 } 2207 2208 })();