core-test.js
1 var $L = { 2 nws: {"aco":1,"b":1,"bant":1,"d":1,"e":1,"f":1,"gif":1,"h":1,"hc":1,"hm":1,"hr":1,"i":1,"ic":1,"pol":1,"r":1,"r9k":1,"s":1,"s4s":1,"soc":1,"t":1,"trash":1,"u":1,"wg":1,"y":1}, 3 blue: '4chan.org', red: '4chan.org', 4 d: function(b) { 5 return $L.nws[b] ? $L.red : $L.blue; 6 } 7 }; 8 9 /** 10 * Captcha 11 */ 12 var TCaptcha = { 13 node: null, 14 15 frameNode: null, 16 imgCntNode: null, 17 bgNode: null, 18 fgNode: null, 19 msgNode: null, 20 sliderNode: null, 21 respNode: null, 22 reloadNode: null, 23 nextNode: null, 24 helpNode: null, 25 challengeNode: null, 26 27 ticketCaptchaNode: null, 28 29 challenge: null, 30 31 reloadTs: null, 32 reloadTimeout: null, 33 expireTimeout: null, 34 frameTimeout: null, 35 36 pcdBypassable: false, 37 38 errorCb: null, 39 40 path: '/captcha', 41 42 ticketKey: '4chan-tc-ticket', 43 44 domain: '4chan.org', 45 46 failCd: 60, 47 48 tabindex: null, 49 50 hCaptchaSiteKey: '49d294fa-f15c-41fc-80ba-c2544c52ec2a', 51 52 init: function(el, board, thread_id, tabindex) { 53 if (this.node) { 54 this.destroy(); 55 } 56 57 if (tabindex) { 58 this.tabindex = tabindex; 59 } 60 61 this.node = el; 62 63 el.style.position = 'relative'; 64 el.style.width = '300px'; 65 66 this.frameNode = null; 67 this.imgCntNode = this.buildImgCntNode(); 68 this.bgNode = this.buildImgNode('bg'); 69 this.fgNode = this.buildImgNode('fg'); 70 this.sliderNode = this.buildSliderNode(); 71 72 this.respNode = this.buildRespField(); 73 this.reloadNode = this.buildReloadNode(board, thread_id); 74 this.nextNode = this.buildNextNode(); 75 this.helpNode = this.buildHelpNode(); 76 this.msgNode = this.buildMsgNode(); 77 this.challengeNode = this.buildChallengeNode(); 78 79 el.appendChild(this.reloadNode); 80 el.appendChild(this.respNode); 81 el.appendChild(this.nextNode); 82 el.appendChild(this.helpNode); 83 84 this.imgCntNode.appendChild(this.bgNode); 85 this.imgCntNode.appendChild(this.fgNode); 86 el.appendChild(this.imgCntNode); 87 88 el.appendChild(this.sliderNode); 89 el.appendChild(this.msgNode); 90 el.appendChild(this.challengeNode); 91 92 window.addEventListener('message', this.onFrameMessage); 93 }, 94 95 destroy: function() { 96 let self = TCaptcha; 97 98 if (!self.node) { 99 return; 100 } 101 102 window.removeEventListener('message', self.onFrameMessage); 103 104 clearTimeout(self.frameTimeout); 105 clearTimeout(self.reloadTimeout); 106 clearTimeout(self.expireTimeout); 107 108 self.node.textContent = ''; 109 110 self.node = null; 111 self.frameNode = null; 112 self.imgCntNode = null; 113 self.bgNode = null; 114 self.fgNode = null; 115 self.msgNode = null; 116 self.sliderNode = null; 117 self.respNode = null; 118 self.reloadNode = null; 119 self.nextNode = null; 120 self.helpNode = null; 121 self.challengeNode = null; 122 123 self.ticketCaptchaNode = null; 124 125 self.challenge = null; 126 127 self.pcdBypassable = false; 128 129 self.errorCb = null; 130 131 self.reloadTs = null; 132 133 self.onReloadCdDone = null; 134 }, 135 136 setErrorCb: function(func) { 137 TCaptcha.errorCb = func; 138 }, 139 140 toggleImgCntNode: function(flag) { 141 TCaptcha.imgCntNode.style.display = flag ? 'block' : 'none'; 142 }, 143 144 getTicket: function() { 145 return localStorage.getItem(TCaptcha.ticketKey); 146 }, 147 148 setTicket: function(val) { 149 if (val) { 150 localStorage.setItem(TCaptcha.ticketKey, val); 151 } 152 else if (val === false) { 153 localStorage.removeItem(TCaptcha.ticketKey); 154 } 155 }, 156 157 buildFrameNode: function() { 158 let el = document.createElement('iframe'); 159 el.id = 't-frame'; 160 el.style.border = '0'; 161 el.style.width = '100%'; 162 el.style.height = '80px'; 163 el.style.marginTop = '2px'; 164 el.style.position = 'relative'; 165 el.style.display = 'block'; 166 el.onerror = TCaptcha.onFrameError; 167 return el; 168 }, 169 170 buildImgCntNode: function() { 171 let el = document.createElement('div'); 172 el.id = 't-cnt'; 173 el.style.height = '80px'; 174 el.style.marginTop = '2px'; 175 el.style.position = 'relative'; 176 return el; 177 }, 178 179 buildImgNode: function(id) { 180 let el = document.createElement('div'); 181 el.id = 't-' + id; 182 el.style.width = '100%'; 183 el.style.height = '100%'; 184 el.style.position = 'absolute'; 185 el.style.backgroundRepeat = 'no-repeat'; 186 el.style.backgroundPosition = 'top left'; 187 el.style.pointerEvents = 'none'; 188 return el; 189 }, 190 191 buildMsgNode: function() { 192 let el = document.createElement('div'); 193 el.id = 't-msg'; 194 el.style.width = '100%'; 195 el.style.height = 'calc(100% - 20px)'; 196 el.style.position = 'absolute'; 197 el.style.top = '20px'; 198 el.style.textAlign = 'center'; 199 el.style.fontSize = '12px'; 200 el.style.filter = 'inherit'; 201 el.style.display = 'none'; 202 el.style.alignContent = 'center'; 203 return el; 204 }, 205 206 buildRespField: function() { 207 let el = document.createElement('input'); 208 el.id = 't-resp'; 209 el.name = 't-response'; 210 el.placeholder = 'Type CAPTCHA here'; 211 el.setAttribute('autocomplete', 'off'); 212 el.type = 'text'; 213 el.style.width = '120px'; 214 el.style.boxSizing = 'border-box'; 215 el.style.textTransform = 'uppercase'; 216 el.style.fontSize = '11px'; 217 el.style.height = '18px'; 218 el.style.margin = '0'; 219 el.style.padding = '0 2px'; 220 el.style.fontFamily = 'monospace'; 221 el.style.verticalAlign = 'middle'; 222 if (this.tabindex) { 223 el.setAttribute('tabindex', this.tabindex + 2); 224 } 225 return el; 226 }, 227 228 buildSliderNode: function() { 229 let el = document.createElement('input'); 230 el.id = 't-slider'; 231 el.setAttribute('autocomplete', 'off'); 232 el.type = 'range'; 233 el.style.width = '100%'; 234 el.style.boxSizing = 'border-box'; 235 el.style.visibility = 'hidden'; 236 el.style.margin = '0'; 237 el.style.transition = 'box-shadow 15s linear'; 238 el.style.boxShadow = '0 0 6px 4px #1d8dc4'; 239 el.style.position = 'relative'; 240 el.value = 0; 241 el.min = 0; 242 el.max = 100; 243 el.addEventListener('input', this.onSliderInput, false); 244 if (this.tabindex) { 245 el.setAttribute('tabindex', this.tabindex + 1); 246 } 247 return el; 248 }, 249 250 buildChallengeNode: function() { 251 let el = document.createElement('input'); 252 el.name = 't-challenge'; 253 el.type = 'hidden'; 254 return el; 255 }, 256 257 buildReloadNode: function(board, thread_id) { 258 let el = document.createElement('button'); 259 el.id = 't-load'; 260 el.type = 'button'; 261 el.style.fontSize = '11px'; 262 el.style.padding = '0'; 263 el.style.width = '90px'; 264 el.style.boxSizing = 'border-box'; 265 el.style.margin = '0 6px 0 0'; 266 el.style.verticalAlign = 'middle'; 267 el.style.height = '18px'; 268 el.textContent = 'Get Captcha'; 269 el.setAttribute('data-board', board); 270 el.setAttribute('data-tid', thread_id); 271 el.addEventListener('click', this.onReloadClick, false); 272 if (this.tabindex) { 273 el.setAttribute('tabindex', this.tabindex); 274 } 275 return el; 276 }, 277 278 buildNextNode: function() { 279 let el = document.createElement('button'); 280 el.id = 't-next'; 281 el.type = 'button'; 282 el.style.fontSize = '11px'; 283 el.style.padding = '0'; 284 el.style.width = '40px'; 285 el.style.boxSizing = 'border-box'; 286 el.style.margin = '0 0 0 6px'; 287 el.style.verticalAlign = 'middle'; 288 el.style.height = '18px'; 289 el.style.display = 'none'; 290 el.textContent = 'Next'; 291 el.addEventListener('click', this.onNextClick, false); 292 return el; 293 }, 294 295 buildHelpNode: function() { 296 let el = document.createElement('button'); 297 el.id = 't-help'; 298 el.type = 'button'; 299 el.style.fontSize = '11px'; 300 el.style.padding = '0'; 301 el.style.width = '20px'; 302 el.style.boxSizing = 'border-box'; 303 el.style.margin = '0 0 0 6px'; 304 el.style.verticalAlign = 'middle'; 305 el.style.height = '18px'; 306 el.textContent = '?'; 307 el.setAttribute('data-tip', 'Help'); 308 el.tabIndex = -1; 309 el.addEventListener('click', this.onHelpClick, false); 310 return el; 311 }, 312 313 onHelpClick: function() { 314 let str = `- Only type letters and numbers displayed in the image. 315 - If needed, use the slider to align the image to make it readable. 316 - Make sure to not block any cookies set by 4chan.`; 317 alert(str); 318 }, 319 320 toggleNextBtn: function(flag) { 321 TCaptcha.nextNode.style.display = flag ? 'inline-block' : 'none'; 322 }, 323 324 onNextClick: function() { 325 let self = TCaptcha; 326 327 let btn = self.reloadNode; 328 let board = btn.getAttribute('data-board'); 329 let thread_id = btn.getAttribute('data-tid'); 330 331 let challenge = self.challengeNode.value; 332 let response = self.respNode.value; 333 334 self.toggleNextBtn(false); 335 self.toggleReloadBtn(false, 'Loading'); 336 self.load(board, thread_id, challenge, response); 337 }, 338 339 onReloadClick: function() { 340 let btn = TCaptcha.reloadNode; 341 let board = btn.getAttribute('data-board'); 342 let thread_id = btn.getAttribute('data-tid'); 343 TCaptcha.toggleNextBtn(false); 344 TCaptcha.toggleReloadBtn(false, 'Loading'); 345 TCaptcha.load(board, thread_id); 346 }, 347 348 onFrameMessage: function(e) { 349 if (e.origin !== `https://sys.${TCaptcha.domain}`) { 350 return; 351 } 352 353 if (e.data && e.data.twister) { 354 TCaptcha.destroyFrame(); 355 TCaptcha.buildFromJson(e.data.twister); 356 } 357 }, 358 359 onFrameError: function(e) { 360 TCaptcha.unlockReloadBtn(); 361 362 console.log(e); 363 364 if (TCaptcha.errorCb) { 365 TCaptcha.errorCb.call(null, 366 "Couldn't load the captcha frame. Check your content blocker settings." 367 ); 368 } 369 }, 370 371 load: function(board, thread_id, t_challenge, t_response) { 372 let self = TCaptcha; 373 374 clearTimeout(self.frameTimeout); 375 clearTimeout(self.reloadTimeout); 376 clearTimeout(self.expireTimeout); 377 378 let params = ['framed=1']; 379 380 if (board) { 381 params.push('board=' + board); 382 } 383 384 if (thread_id > 0) { 385 params.push('thread_id=' + thread_id); 386 } 387 388 if (t_challenge && t_response) { 389 params.push('t-challenge=' + encodeURIComponent(t_challenge)); 390 params.push('t-response=' + t_response); 391 } 392 393 let ticket = self.getTicket(); 394 395 if (ticket) { 396 params.push('ticket=' + ticket); 397 } 398 399 if (params.length > 0) { 400 params = '?' + params.join('&'); 401 } 402 403 let src = 'https://sys.' + self.domain + self.path + params; 404 405 self.frameNode = self.buildFrameNode(); 406 self.toggleImgCntNode(false); 407 self.node.insertBefore(self.frameNode, self.imgCntNode); 408 self.frameTimeout = setTimeout(self.onFrameTimeout, 60000, src); 409 self.frameNode.src = src; 410 }, 411 412 onFrameTimeout: function(src) { 413 let self = TCaptcha; 414 415 self.destroyFrame(); 416 417 console.log('Captcha frame timeout'); 418 419 if (self.errorCb) { 420 self.errorCb.call(null, `Couldn't get the captcha. 421 Make sure your browser doesn't block content on 4chan then click 422 <a href="${src.replace('framed=1', 'opened=1')}" target="_blank">here</a>.`); 423 } 424 }, 425 426 destroyFrame: function() { 427 let self = TCaptcha; 428 429 clearTimeout(self.frameTimeout); 430 self.frameTimeout = null; 431 if (self.frameNode) { 432 self.frameNode.remove(); 433 self.frameNode = null; 434 } 435 self.toggleImgCntNode(true); 436 self.unlockReloadBtn(); 437 }, 438 439 unlockReloadBtn: function() { 440 TCaptcha.reloadTs = null; 441 TCaptcha.toggleReloadBtn(true, 'Get Captcha'); 442 }, 443 444 toggleReloadBtn: function(flag, label) { 445 let self = TCaptcha; 446 447 if (self.reloadNode) { 448 self.reloadNode.disabled = !flag; 449 450 if (label !== undefined) { 451 self.reloadNode.textContent = label; 452 } 453 } 454 }, 455 456 onCaptchaFailed: function() { 457 let self = TCaptcha; 458 459 let cd = self.failCd * 1000; 460 461 if (self.reloadTs && self.reloadTs < cd) { 462 self.setReloadCd(cd, true); 463 } 464 }, 465 466 setReloadCd: function(cd, visible, onDone) { 467 let self = TCaptcha; 468 469 if (!self.node) { 470 return; 471 } 472 473 clearTimeout(self.reloadTimeout); 474 475 self.onReloadCdDone = onDone; 476 477 self.pcdBypassable = visible === -1; 478 479 if (cd) { 480 self.toggleReloadBtn(false); 481 if (visible) { 482 self.reloadTs = Date.now() + cd; 483 self.onReloadCdTick(); 484 } 485 else { 486 self.reloadTimeout = setTimeout(self.stopReloadCd, cd); 487 } 488 } 489 else { 490 self.stopReloadCd(); 491 } 492 }, 493 494 stopReloadCd: function() { 495 let self = TCaptcha; 496 self.unlockReloadBtn(); 497 if (self.onReloadCdDone) { 498 self.onReloadCdDone.call(self); 499 } 500 }, 501 502 onReloadCdTick: function() { 503 let self = TCaptcha; 504 505 if (!self.reloadNode || !self.reloadTs) { 506 return; 507 } 508 509 let cd = self.reloadTs - Date.now(); 510 511 if (self.pcdBypassable) { 512 if (document.cookie.indexOf('_ev1=') !== -1) { 513 cd = 0; 514 } 515 } 516 517 if (cd > 0) { 518 self.reloadNode.textContent = Math.ceil(cd / 1000); 519 self.reloadTimeout = setTimeout(self.onReloadCdTick, Math.min(cd, 1000)); 520 } 521 else { 522 self.stopReloadCd(); 523 } 524 }, 525 526 clearChallenge: function() { 527 let self = TCaptcha; 528 529 if (self.node) { 530 self.challengeNode.value = ''; 531 self.respNode.value = ''; 532 self.fgNode.style.backgroundImage = ''; 533 self.bgNode.style.backgroundImage = ''; 534 self.toggleSlider(false); 535 self.toggleMsgOverlay(false); 536 self.toggleNextBtn(false); 537 } 538 }, 539 540 toggleSlider: function(flag) { 541 TCaptcha.sliderNode.style.visibility = flag ? '' : 'hidden'; 542 TCaptcha.sliderNode.style.boxShadow = flag ? '' : '0 0 4px 2px #1d8dc4'; 543 }, 544 545 toggleMsgOverlay: function(flag, txt) { 546 if (txt !== undefined) { 547 TCaptcha.msgNode.innerHTML = `<div>${txt}</div>`; 548 } 549 TCaptcha.msgNode.style.display = flag ? 'grid' : 'none'; 550 }, 551 552 onSliderInput: function() { 553 var m = -Math.floor((+this.value) / 100 * this.twisterDelta); 554 TCaptcha.bgNode.style.backgroundPositionX = m + 'px'; 555 }, 556 557 onTicketPcdTick: function() { 558 let self = TCaptcha; 559 560 let el = document.getElementById('t-pcd'); 561 562 if (!el) { 563 return; 564 } 565 566 let pcd = +el.getAttribute('data-pcd'); 567 568 pcd = pcd - (0 | (Date.now() / 1000)); 569 570 if (pcd <= 0) { 571 self.onTicketPcdEnd(); 572 return; 573 } 574 575 el.textContent = pcd; 576 577 setTimeout(self.updateTicketPcd, 1000); 578 }, 579 580 clearTicketOverlay: function() { 581 TCaptcha.toggleMsgOverlay(false); 582 }, 583 584 buildFromJson: function(data) { 585 let self = TCaptcha; 586 587 if (!self.node) { 588 return; 589 } 590 591 self.unlockReloadBtn(); 592 self.toggleSlider(false); 593 self.toggleMsgOverlay(false); 594 595 self.setTicket(data.ticket); 596 597 if (self.errorCb) { 598 self.errorCb.call(null, ''); 599 } 600 601 if (data.cd) { 602 self.setReloadCd(data.cd * 1000, !data.challenge); 603 } 604 605 if (data.pcd) { 606 self.buildTicket(data); 607 return; 608 } 609 610 if (data.error) { 611 console.log(data.error); 612 613 self.clearChallenge(); 614 615 if (self.errorCb) { 616 self.errorCb.call(null, data.error); 617 } 618 619 return; 620 } 621 622 if (data.mpcd) { 623 self.toggleNextBtn(true); 624 } 625 626 self.imgCntNode.style.width = data.img_width + 'px'; 627 self.imgCntNode.style.height = data.img_height + 'px'; 628 629 self.challengeNode.value = data.challenge; 630 631 self.expireTimeout = setTimeout(self.clearChallenge, data.ttl * 1000 - 3000); 632 633 if (data.bg_width) { 634 self.buildTwister(data); 635 } 636 else if (data.img) { 637 self.buildStatic(data); 638 } 639 else { 640 self.buildNoop(data); 641 } 642 }, 643 644 buildTwister: function(data) { 645 let self = TCaptcha; 646 647 self.fgNode.style.backgroundImage = 'url(data:image/png;base64,' + data.img + ')'; 648 self.bgNode.style.backgroundImage = 'url(data:image/png;base64,' + data.bg + ')'; 649 650 self.bgNode.style.backgroundPositionX = '0px'; 651 652 self.toggleSlider(true); 653 self.sliderNode.value = 0; 654 self.sliderNode.twisterDelta = data.bg_width - data.img_width; 655 self.sliderNode.focus(); 656 }, 657 658 buildStatic: function(data) { 659 let self = TCaptcha; 660 self.fgNode.style.backgroundImage = 'url(data:image/png;base64,' + data.img + ')'; 661 self.bgNode.style.backgroundImage = ''; 662 }, 663 664 buildTicket: function(data) { 665 let self = TCaptcha; 666 self.clearChallenge(); 667 self.toggleMsgOverlay(true, data.pcd_msg || 'Please wait a while.'); 668 self.setReloadCd(data.pcd * 1000, data.bpcd ? -1 : true, self.clearTicketOverlay); 669 }, 670 671 buildNoop: function(data) { 672 let self = TCaptcha; 673 self.toggleMsgOverlay(true, 'Verification not required.'); 674 self.fgNode.style.backgroundImage = ''; 675 self.bgNode.style.backgroundImage = ''; 676 } 677 }; 678 679 /** 680 * Tooltips 681 */ 682 var Tip = { 683 node: null, 684 timeout: null, 685 delay: 300, 686 687 init: function() { 688 document.addEventListener('mouseover', this.onMouseOver, false); 689 document.addEventListener('mouseout', this.onMouseOut, false); 690 }, 691 692 onMouseOver: function(e) { 693 var cb, data, t; 694 695 t = e.target; 696 697 if (Tip.timeout) { 698 clearTimeout(Tip.timeout); 699 Tip.timeout = null; 700 } 701 702 if (t.hasAttribute('data-tip')) { 703 data = null; 704 705 if (t.hasAttribute('data-tip-cb')) { 706 cb = t.getAttribute('data-tip-cb'); 707 if (window[cb]) { 708 data = window[cb](t); 709 } 710 } 711 Tip.timeout = setTimeout(Tip.show, Tip.delay, e.target, data); 712 } 713 }, 714 715 onMouseOut: function(e) { 716 if (Tip.timeout) { 717 clearTimeout(Tip.timeout); 718 Tip.timeout = null; 719 } 720 721 Tip.hide(); 722 }, 723 724 show: function(t, data, pos) { 725 var el, rect, style, left, top; 726 727 rect = t.getBoundingClientRect(); 728 729 el = document.createElement('div'); 730 el.id = 'tooltip'; 731 732 if (data) { 733 el.innerHTML = data; 734 } 735 else { 736 el.textContent = t.getAttribute('data-tip'); 737 } 738 739 if (!pos) { 740 pos = 'top'; 741 } 742 743 el.className = 'tip-' + pos; 744 745 document.body.appendChild(el); 746 747 left = rect.left - (el.offsetWidth - t.offsetWidth) / 2; 748 749 if (left < 0) { 750 left = rect.left + 2; 751 el.className += '-right'; 752 } 753 else if (left + el.offsetWidth > document.documentElement.clientWidth) { 754 left = rect.left - el.offsetWidth + t.offsetWidth + 2; 755 el.className += '-left'; 756 } 757 758 top = rect.top - el.offsetHeight - 5; 759 760 style = el.style; 761 style.top = (top + window.pageYOffset) + 'px'; 762 style.left = left + window.pageXOffset + 'px'; 763 764 Tip.node = el; 765 }, 766 767 hide: function() { 768 if (Tip.node) { 769 document.body.removeChild(Tip.node); 770 Tip.node = null; 771 } 772 } 773 }; 774 775 /** 776 * Settings Syncher 777 */ 778 /* 779 var StorageSync = { 780 queue: [], 781 782 init: function() { 783 var el, self = StorageSync; 784 785 if (self.inited || !document.body) { 786 return; 787 } 788 789 self.remoteFrame = null; 790 791 self.remoteOrigin = location.protocol + '//boards.' 792 + (location.host === 'boards.4channel.org' ? '4chan' : '4channel') 793 + '.org'; 794 795 window.addEventListener('message', self.onFrameMessage, false); 796 797 el = document.createElement('iframe'); 798 el.width = 0; 799 el.height = 0; 800 el.style.display = 'none'; 801 el.style.visibility = 'hidden'; 802 803 el.src = self.remoteOrigin + '/syncframe.html'; 804 805 document.body.appendChild(el); 806 807 self.inited = true; 808 }, 809 810 onFrameMessage: function(e) { 811 var self = StorageSync; 812 813 if (e.origin !== self.remoteOrigin) { 814 return; 815 } 816 817 if (e.data === 'ready') { 818 self.remoteFrame = e.source; 819 820 if (self.queue.length) { 821 self.send(); 822 } 823 824 return; 825 } 826 }, 827 828 sync: function(key) { 829 var self = StorageSync; 830 831 self.queue.push(key); 832 self.send(); 833 }, 834 835 send: function() { 836 var i, key, data, self = StorageSync; 837 838 if (!self.inited) { 839 return self.init(); 840 } 841 842 if (!self.remoteFrame) { 843 return; 844 } 845 846 data = {}; 847 848 for (i = 0; key = self.queue[i]; ++i) { 849 data[key] = localStorage.getItem(key); 850 } 851 852 self.queue = []; 853 854 self.remoteFrame.postMessage({ storage: data }, self.remoteOrigin); 855 } 856 }; 857 */ 858 function mShowFull(t) { 859 var el, data; 860 861 if (t.className === 'name') { 862 if (el = t.parentNode.parentNode.parentNode 863 .getElementsByClassName('name')[1]) { 864 data = el.innerHTML; 865 } 866 } 867 else if (t.parentNode.className === 'subject') { 868 if (el = t.parentNode.parentNode.parentNode.parentNode 869 .getElementsByClassName('subject')[1]) { 870 data = el.innerHTML; 871 } 872 } 873 else if (/fileThumb/.test(t.parentNode.className)) { 874 if (el = t.parentNode.parentNode.getElementsByClassName('fileText')[0]) { 875 el = el.firstElementChild; 876 data = el.getAttribute('title') || el.innerHTML; 877 } 878 } 879 880 return data; 881 } 882 883 function loadBannerImage() { 884 var cnt = document.getElementById('bannerCnt'); 885 886 if (!cnt || cnt.offsetWidth <= 0) { 887 return; 888 } 889 890 cnt.innerHTML = '<img alt="4chan" src="//s.4cdn.org/image/title/' 891 + cnt.getAttribute('data-src') + '">'; 892 } 893 894 function onMobileSelectChange() { 895 var board, page; 896 897 board = this.options[this.selectedIndex].value; 898 page = (board !== 'f' && /\/catalog$/.test(location.pathname)) ? 'catalog' : ''; 899 900 window.location = '//boards.' + $L.d(board) + '/' + board + '/' + page; 901 } 902 903 function buildMobileNav() { 904 var el, boards, i, b, html, order; 905 906 if (el = document.getElementById('boardSelectMobile')) { 907 html = ''; 908 order = []; 909 910 boards = document.querySelectorAll('#boardNavDesktop .boardList a'); 911 912 for (i = 0; b = boards[i]; ++i) { 913 order.push(b); 914 } 915 916 order.sort(function(a, b) { 917 if (a.textContent < b.textContent) { 918 return -1; 919 } 920 if (a.textContent > b.textContent) { 921 return 1; 922 } 923 return 0; 924 }); 925 926 for (i = 0; b = order[i]; ++i) { 927 html += '<option class="' 928 + (b.parentNode.classList.contains('nwsb') ? 'nwsb' : '') + '" value="' 929 + b.textContent + '">/' 930 + b.textContent + '/ - ' 931 + b.title + '</option>'; 932 } 933 934 el.innerHTML = html; 935 } 936 } 937 938 function cloneTopNav() { 939 var navT, navB, ref, el; 940 941 navT = document.getElementById('boardNavDesktop'); 942 943 if (!navT) { 944 return; 945 } 946 947 ref = document.getElementById('absbot'); 948 949 navB = navT.cloneNode(true); 950 navB.id = navB.id + 'Foot'; 951 952 if (el = navB.querySelector('#navtopright')) { 953 el.id = 'navbotright'; 954 } 955 956 if (el = navB.querySelector('#settingsWindowLink')) { 957 el.id = el.id + 'Bot'; 958 } 959 960 document.body.insertBefore(navB, ref); 961 } 962 963 function initPass() { 964 if (get_cookie("pass_enabled") == '1' || get_cookie('extra_path')) { 965 window.passEnabled = true; 966 } 967 else { 968 window.passEnabled = false; 969 } 970 } 971 972 function initBlotter() { 973 var mTime, seenTime, el; 974 975 el = document.getElementById('toggleBlotter'); 976 977 if (!el) { 978 return; 979 } 980 981 el.addEventListener('click', toggleBlotter, false); 982 983 seenTime = localStorage.getItem('4chan-blotter'); 984 985 if (!seenTime) { 986 return; 987 } 988 989 mTime = +el.getAttribute('data-utc'); 990 991 if (mTime <= +seenTime) { 992 toggleBlotter(); 993 } 994 } 995 996 function toggleBlotter(e) { 997 var el, btn; 998 999 e && e.preventDefault(); 1000 1001 el = document.getElementById('blotter-msgs'); 1002 1003 if (!el) { 1004 return; 1005 } 1006 1007 btn = document.getElementById('toggleBlotter'); 1008 1009 if (el.style.display == 'none') { 1010 el.style.display = ''; 1011 localStorage.removeItem('4chan-blotter'); 1012 btn.textContent = 'Hide'; 1013 1014 el = btn.nextElementSibling; 1015 1016 if (el.style.display) { 1017 el.style.display = ''; 1018 } 1019 } 1020 else { 1021 el.style.display = 'none'; 1022 localStorage.setItem('4chan-blotter', btn.getAttribute('data-utc')); 1023 btn.textContent = 'Show Blotter'; 1024 btn.nextElementSibling.style.display = 'none'; 1025 } 1026 } 1027 1028 function onRecaptchaLoaded() { 1029 if (document.getElementById('postForm').style.display == 'table') { 1030 initRecaptcha(); 1031 } 1032 } 1033 1034 function initRecaptcha() { 1035 var el; 1036 1037 el = document.getElementById('g-recaptcha'); 1038 1039 if (!el || el.firstElementChild) { 1040 return; 1041 } 1042 1043 if (!window.passEnabled && window.grecaptcha) { 1044 grecaptcha.render(el, { 1045 sitekey: window.recaptchaKey, 1046 theme: (activeStyleSheet === 'Tomorrow' || window.dark_captcha) ? 'dark' : 'light' 1047 }); 1048 } 1049 } 1050 1051 function initTCaptcha() { 1052 let el = document.getElementById('t-root'); 1053 1054 if (el) { 1055 let board = location.pathname.split(/\//)[1]; 1056 1057 let thread_id; 1058 1059 if (document.forms.post && document.forms.post.resto) { 1060 thread_id = +document.forms.post.resto.value; 1061 } 1062 else { 1063 thread_id = 0; 1064 } 1065 1066 TCaptcha.init(el, board, thread_id, 5); 1067 TCaptcha.setErrorCb(window.showPostFormError); 1068 } 1069 } 1070 1071 function initAnalytics() { 1072 var tid = location.host.indexOf('.4channel.org') !== -1 ? 'UA-166538-5' : 'UA-166538-1'; 1073 1074 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 1075 1076 ga('create', tid, {'sampleRate': 1}); 1077 ga('set', 'anonymizeIp', true); 1078 ga('send','pageview'); 1079 } 1080 1081 function initAdsPF(cnt, slot_id) { 1082 let sid, nid; 1083 1084 if (slot_id == 1) { 1085 sid = '657b2d8958f9186175770b1f'; 1086 nid = 'pf-6892-1'; 1087 } 1088 else if (slot_id == 2) { 1089 sid = '657b2d9d58f9186175770b37'; 1090 nid = 'pf-6893-1'; 1091 } 1092 else if (slot_id == 3) { 1093 sid = '657b2d56256794003cd16fe4'; 1094 nid = 'pf-6890-1'; 1095 } 1096 else if (slot_id == 4) { 1097 sid = '657b2d74256794003cd17019'; 1098 nid = 'pf-6891-1'; 1099 } 1100 else { 1101 return; 1102 } 1103 1104 cnt.innerHTML = ''; 1105 1106 let d = document.createElement('div'); 1107 d.id = nid; 1108 cnt.appendChild(d); 1109 1110 window.pubfuturetag = window.pubfuturetag || []; 1111 window.pubfuturetag.push({unit: sid, id: nid}); 1112 } 1113 1114 function initAdsADT(scope) { 1115 var el, nodes, i, cls, s; 1116 1117 if (window.matchMedia && window.matchMedia('(max-width: 480px)').matches && localStorage.getItem('4chan_never_show_mobile') != 'true') { 1118 cls = 'adg-m'; 1119 } 1120 else { 1121 cls = 'adg'; 1122 } 1123 1124 nodes = (scope || document).getElementsByClassName(cls); 1125 1126 for (i = 0; el = nodes[i]; ++i) { 1127 if (el.hasAttribute('data-abc')) { 1128 s = document.createElement('iframe'); 1129 s.setAttribute('scrolling', 'no'); 1130 s.setAttribute('frameborder', '0'); 1131 s.setAttribute('allowtransparency', 'true'); 1132 s.setAttribute('marginheight', '0'); 1133 s.setAttribute('marginwidth', '0'); 1134 1135 if (cls === 'adg') { 1136 s.setAttribute('width', '728'); 1137 s.setAttribute('height', '90'); 1138 } 1139 else { 1140 s.setAttribute('width', '300'); 1141 s.setAttribute('height', '250'); 1142 } 1143 1144 s.setAttribute('name', 'spot_id_' + el.getAttribute('data-abc')); 1145 s.src = 'https://a.adtng.com/get/' + el.getAttribute('data-abc') + '?time=' + Date.now(); 1146 el.appendChild(s); 1147 } 1148 } 1149 } 1150 1151 function danboAddSlot(n, b, m, s) { 1152 let pubid = 27; 1153 1154 let el = document.createElement('div'); 1155 el.className = 'danbo_dta'; 1156 1157 if (m) { 1158 if (s) { 1159 s = '3'; 1160 } 1161 else { 1162 s = '4'; 1163 el.id = 'js-danbo-rld'; 1164 } 1165 el.setAttribute('data-danbo', `${pubid}-${b}-${s}-300-250`); 1166 el.classList.add('danbo-m'); 1167 } 1168 else { 1169 if (s) { 1170 s = '1'; 1171 } 1172 else { 1173 s = '2'; 1174 el.id = 'js-danbo-rld'; 1175 } 1176 el.setAttribute('data-danbo', `${pubid}-${b}-${s}-728-90`); 1177 el.classList.add('danbo-d'); 1178 } 1179 1180 n.appendChild(el); 1181 1182 return el; 1183 } 1184 1185 function initAdsDanbo() { 1186 if (!window.Danbo) { 1187 return; 1188 } 1189 1190 let b = location.pathname.split(/\//)[1] || '_'; 1191 1192 let m = window.matchMedia && window.matchMedia('(max-width: 480px)').matches; 1193 1194 let nodes = document.getElementsByClassName('danbo-slot'); 1195 1196 for (let cnt of nodes) { 1197 let s = cnt.id === 'danbo-s-t'; 1198 danboAddSlot(cnt, b, m, s); 1199 } 1200 1201 window.addEventListener('message', function(e) { 1202 if (e.origin === 'https://hakurei.cdnbo.org' && e.data && e.data.origin === 'danbo') { 1203 window.initAdsFallback(e.data.unit_id); 1204 } 1205 }); 1206 1207 window.Danbo.initialize(); 1208 } 1209 1210 function reloadAdsDanbo() { 1211 let cnt = document.getElementById('danbo-s-b'); 1212 1213 if (!cnt) { 1214 return; 1215 } 1216 1217 cnt.innerHTML = ''; 1218 1219 let b = 'a';//location.pathname.split(/\//)[1] || '_'; 1220 1221 let m = window.matchMedia && window.matchMedia('(max-width: 480px)').matches; 1222 1223 danboAddSlot(cnt, b, m, false); 1224 1225 window.Danbo.reload('js-danbo-rld'); 1226 } 1227 1228 function initAdsFallback(slot_id) { 1229 let fb = window.danbo_fb; 1230 1231 let cnt_id; 1232 1233 if (slot_id == 1 || slot_id == 3) { 1234 cnt_id = 'danbo-s-t'; 1235 } 1236 else { 1237 cnt_id = 'danbo-s-b'; 1238 } 1239 1240 let cnt = document.getElementById(cnt_id); 1241 1242 if (!cnt) { 1243 return; 1244 } 1245 1246 let is_burichan = document.body.classList.contains('ws'); 1247 1248 let hr = is_burichan ? 0.1 : 0.01; 1249 1250 if (Math.random() < hr) { 1251 return initAdsHome(cnt); 1252 } 1253 1254 if (cnt_id === 'danbo-s-t') { 1255 if (is_burichan) { 1256 initAdsPF(cnt, slot_id); 1257 } 1258 else if (fb) { 1259 if (slot_id == 1 && fb.t_abc_d) { 1260 cnt.innerHTML = `<div class="adg-rects desktop"><div class="adg adp-90" data-abc="${fb.t_abc_d}"></div></div>`; 1261 initAdsADT(cnt); 1262 } 1263 else if (slot_id == 3 && fb.t_abc_m) { 1264 cnt.innerHTML = `<div class="adg-rects mobile"><div class="adg-m adp-250" data-abc="${fb.t_abc_m}"></div></div>`; 1265 initAdsADT(cnt); 1266 } 1267 else { 1268 initAdsHome(cnt); 1269 } 1270 } 1271 else { 1272 initAdsHome(cnt); 1273 } 1274 } 1275 else if (cnt_id === 'danbo-s-b') { 1276 if (is_burichan) { 1277 initAdsPF(cnt, slot_id); 1278 } 1279 else if (fb) { 1280 if (slot_id == 4 && fb.b_abc_m) { 1281 cnt.innerHTML = `<div class="adg-rects mobile"><div class="adg-m adp-250" data-abc="${fb.b_abc_m}"></div></div>`; 1282 initAdsADT(cnt); 1283 } 1284 else { 1285 initAdsHome(cnt); 1286 } 1287 } 1288 else { 1289 initAdsHome(cnt); 1290 } 1291 } 1292 else { 1293 console.log('Fallback', slot_id); 1294 } 1295 } 1296 1297 function initAdsHome(cnt) { 1298 let banners = [ 1299 ['advertise', '1.png', '2.png', '3.png'], 1300 ['pass', '4.png'], 1301 ]; 1302 1303 let banners_m = [ 1304 ['advertise', '1m.png'], 1305 ]; 1306 1307 let d; 1308 1309 if (location.host.indexOf('4channel')) { 1310 d = '4channel'; 1311 } 1312 else { 1313 d = '4chan'; 1314 } 1315 1316 let b; 1317 1318 if (window.matchMedia && window.matchMedia('(max-width: 480px)').matches) { 1319 b = banners_m; 1320 } 1321 else { 1322 b = banners; 1323 } 1324 1325 b = b[Math.floor(Math.random() * b.length)]; 1326 1327 let href = b[0]; 1328 let src = b[1 + Math.floor(Math.random() * (b.length - 1))]; 1329 1330 let a = document.createElement('a'); 1331 a.href = `https://www.${d}.org/${href}`; 1332 a.target = '_blank'; 1333 1334 let img = document.createElement('img'); 1335 img.src = '//s.4cdn.org/image/banners/' + src; 1336 1337 a.appendChild(img); 1338 1339 if (cnt.children.length) { 1340 cnt.innerHTML = ''; 1341 } 1342 1343 cnt.appendChild(a); 1344 } 1345 1346 function applySearch(e) { 1347 var str; 1348 1349 e && e.preventDefault(); 1350 1351 str = document.getElementById('search-box').value; 1352 1353 if (str !== '') { 1354 window.location.href = 'catalog#s=' + str; 1355 } 1356 } 1357 1358 function onKeyDownSearch(e) { 1359 if (e.keyCode == 13) { 1360 applySearch(); 1361 } 1362 } 1363 1364 function onReportClick(e) { 1365 var i, input, nodes, board; 1366 1367 nodes = document.getElementsByTagName('input'); 1368 1369 board = location.pathname.split(/\//)[1]; 1370 1371 for (i = 0; input = nodes[i]; ++i) { 1372 if (input.type == 'checkbox' && input.checked && input.value == 'delete') { 1373 return reppop('https://sys.' + $L.d(board) + '/' + board + '/imgboard.php?mode=report&no=' 1374 + input.name.replace(/[a-z]+/, '') 1375 ); 1376 } 1377 } 1378 } 1379 1380 function onStyleSheetChange(e) { 1381 setActiveStyleSheet(this.value); 1382 } 1383 1384 function onPageSwitch(e) { 1385 e.preventDefault(); 1386 window.location = this.action; 1387 } 1388 1389 function onMobileFormClick(e) { 1390 var index = location.pathname.split(/\//).length < 4; 1391 1392 e.preventDefault(); 1393 1394 if (window.QR && Main.tid && QR.enabled) { 1395 QR.show(Main.tid); 1396 } 1397 else if (this.parentNode.id == 'mpostform') { 1398 toggleMobilePostForm(index); 1399 } 1400 else { 1401 toggleMobilePostForm(index, 1); 1402 } 1403 } 1404 1405 function onMobileRefreshClick(e) { 1406 locationHashChanged(this); 1407 } 1408 1409 function toggle(name) { 1410 var a = document.getElementById(name); 1411 a.style.display = ((a.style.display != 'block') ? 'block' : 'none'); 1412 } 1413 1414 function quote(text) { 1415 if (document.selection) { 1416 document.post.com.focus(); 1417 var sel = document.selection.createRange(); 1418 sel.text = ">>" + text + "\n"; 1419 } else if (document.post.com.selectionStart || document.post.com.selectionStart == "0") { 1420 var startPos = document.post.com.selectionStart; 1421 var endPos = document.post.com.selectionEnd; 1422 document.post.com.value = document.post.com.value.substring(0, startPos) + ">>" + text + "\n" + document.post.com.value.substring(endPos, document.post.com.value.length); 1423 } else { 1424 document.post.com.value += ">>" + text + "\n"; 1425 } 1426 } 1427 1428 function repquote(rep) { 1429 if (document.post.com.value == "") { 1430 quote(rep); 1431 } 1432 } 1433 1434 function reppop(url) { 1435 var height; 1436 1437 if (window.passEnabled || !window.grecaptcha) { 1438 height = 205; 1439 } 1440 else { 1441 height = 510; 1442 } 1443 1444 window.open(url, Date.now(), 1445 'toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=380,height=' + height 1446 ); 1447 1448 return false; 1449 } 1450 1451 function recaptcha_load() { 1452 var d = document.getElementById("recaptcha_div"); 1453 if (!d) return; 1454 1455 Recaptcha.create("6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc", "recaptcha_div",{theme: "clean"}); 1456 } 1457 1458 function onParsingDone(e) { 1459 var i, nodes, n, p, tid, offset, limit; 1460 1461 tid = e.detail.threadId; 1462 offset = e.detail.offset; 1463 1464 if (!offset) { 1465 return; 1466 } 1467 1468 nodes = document.getElementById('t' + tid).getElementsByClassName('nameBlock'); 1469 limit = e.detail.limit ? (e.detail.limit * 2) : nodes.length; 1470 for (i = offset * 2 + 1; i < limit; i+=2) { 1471 if (n = nodes[i].children[1]) { 1472 if (currentHighlighted 1473 && n.className.indexOf('id_' + currentHighlighted) != -1) { 1474 p = n.parentNode.parentNode.parentNode; 1475 p.className = 'highlight ' + p.className; 1476 } 1477 n.addEventListener('click', idClick, false); 1478 } 1479 } 1480 } 1481 1482 function loadExtraScripts() { 1483 var el, path; 1484 1485 path = readCookie('extra_path'); 1486 1487 if (!path || !/^[a-z0-9]+$/.test(path)) { 1488 return false; 1489 } 1490 1491 if (window.FC) { 1492 el = document.createElement('script'); 1493 el.type = 'text/javascript'; 1494 el.src = 'https://s.4cdn.org/js/' + path + '.' + jsVersion + '.js'; 1495 document.head.appendChild(el); 1496 } 1497 else { 1498 document.write('<script type="text/javascript" src="https://s.4cdn.org/js/' + path + '.' + jsVersion + '.js"></script>'); 1499 } 1500 1501 return true; 1502 } 1503 1504 1505 function toggleMobilePostForm(index, scrolltotop) { 1506 var elem = document.getElementById('mpostform').firstElementChild; 1507 var postForm = document.getElementById('postForm'); 1508 1509 if (elem.className.match('hidden')) { 1510 elem.className = elem.className.replace('hidden', 'shown'); 1511 postForm.className = postForm.className.replace(' hideMobile', ''); 1512 elem.innerHTML = 'Close Post Form'; 1513 initRecaptcha(); 1514 initTCaptcha(); 1515 checkIncognito(); 1516 } 1517 else { 1518 elem.className = elem.className.replace('shown', 'hidden'); 1519 postForm.className += ' hideMobile'; 1520 elem.innerHTML = (index) ? 'Start New Thread' : 'Post Reply'; 1521 } 1522 1523 if (scrolltotop) { 1524 elem.scrollIntoView(); 1525 } 1526 } 1527 1528 function toggleGlobalMessage(e) { 1529 var elem, postForm; 1530 1531 if (e) { 1532 e.preventDefault(); 1533 } 1534 1535 elem = document.getElementById('globalToggle'); 1536 postForm = document.getElementById('globalMessage'); 1537 1538 if( elem.className.match('hidden') ) { 1539 elem.className = elem.className.replace('hidden', 'shown'); 1540 postForm.className = postForm.className.replace(' hideMobile', ''); 1541 1542 elem.innerHTML = 'Close Announcement'; 1543 } else { 1544 elem.className = elem.className.replace('shown', 'hidden'); 1545 postForm.className += ' hideMobile'; 1546 1547 elem.innerHTML = 'View Announcement'; 1548 } 1549 } 1550 1551 function checkRecaptcha() 1552 { 1553 if( typeof RecaptchaState.timeout != 'undefined' ) { 1554 if( RecaptchaState.timeout == 1800 ) { 1555 RecaptchaState.timeout = 570; 1556 Recaptcha._reset_timer(); 1557 clearInterval(captchainterval); 1558 } 1559 } 1560 } 1561 1562 function setPassMsg() { 1563 var el, msg; 1564 1565 el = document.getElementById('captchaFormPart'); 1566 1567 if (!el) { 1568 return; 1569 } 1570 1571 msg = 'You are using a 4chan Pass. [<a href="https://sys.' + $L.d(location.pathname.split(/\//)[1]) + '/auth?act=logout" onclick="confirmPassLogout(event);" tabindex="-1">Logout</a>]'; 1572 el.children[1].innerHTML = '<div style="padding: 5px;">' + msg + '</div>'; 1573 } 1574 1575 function confirmPassLogout(event) 1576 { 1577 var conf = confirm('Are you sure you want to logout?'); 1578 if( !conf ) { 1579 event.preventDefault(); 1580 return false; 1581 } 1582 } 1583 1584 var activeStyleSheet; 1585 1586 function initStyleSheet() { 1587 var i, rem, link, len; 1588 1589 // fix me 1590 if (window.FC) { 1591 return; 1592 } 1593 1594 if (window.style_group) { 1595 var cookie = readCookie(style_group); 1596 activeStyleSheet = cookie ? cookie : getPreferredStyleSheet(); 1597 } 1598 1599 if (window.css_event && localStorage.getItem('4chan_stop_css_event') !== `${window.css_event}-${window.css_event_v}`) { 1600 activeStyleSheet = '_special'; 1601 } 1602 1603 switch(activeStyleSheet) { 1604 case "Yotsuba B": 1605 setActiveStyleSheet("Yotsuba B New", true); 1606 break; 1607 1608 case "Yotsuba": 1609 setActiveStyleSheet("Yotsuba New", true); 1610 break; 1611 1612 case "Burichan": 1613 setActiveStyleSheet("Burichan New", true); 1614 break; 1615 1616 case "Futaba": 1617 setActiveStyleSheet("Futaba New", true); 1618 break; 1619 1620 default: 1621 setActiveStyleSheet(activeStyleSheet, true); 1622 break; 1623 } 1624 1625 if (localStorage.getItem('4chan_never_show_mobile') == 'true') { 1626 link = document.querySelectorAll('link'); 1627 len = link.length; 1628 for (i = 0; i < len; i++) { 1629 if (link[i].getAttribute('href').match('mobile')) { 1630 (rem = link[i]).parentNode.removeChild(rem); 1631 } 1632 } 1633 } 1634 } 1635 1636 function pageHasMath() { 1637 var i, el, nodes; 1638 1639 nodes = document.getElementsByClassName('postMessage'); 1640 1641 for (i = 0; el = nodes[i]; ++i) { 1642 if (/\[(?:eqn|math)\]|"math">/.test(el.innerHTML)) { 1643 return true; 1644 } 1645 } 1646 1647 return false; 1648 } 1649 1650 function cleanWbr(el) { 1651 var i, nodes, n; 1652 1653 nodes = el.getElementsByTagName('wbr'); 1654 1655 for (i = nodes.length - 1; n = nodes[i]; i--) { 1656 n.parentNode.removeChild(n); 1657 } 1658 } 1659 1660 function parseMath() { 1661 var i, el, nodes; 1662 1663 nodes = document.getElementsByClassName('postMessage'); 1664 1665 for (i = 0; el = nodes[i]; ++i) { 1666 if (/\[(?:eqn|math)\]/.test(el.innerHTML)) { 1667 cleanWbr(el); 1668 } 1669 } 1670 1671 MathJax.Hub.Queue(['Typeset', MathJax.Hub, nodes]); 1672 } 1673 1674 function loadMathJax() { 1675 var head, script; 1676 1677 head = document.getElementsByTagName('head')[0]; 1678 1679 script = document.createElement('script'); 1680 script.type = 'text/x-mathjax-config'; 1681 script.text = "MathJax.Hub.Config({\ 1682 extensions: ['Safe.js'],\ 1683 tex2jax: { processRefs: false, processEnvironments: false, preview: 'none', inlineMath: [['[math]','[/math]']], displayMath: [['[eqn]','[/eqn]']] },\ 1684 Safe: { allow: { URLs: 'none', classes: 'none', cssIDs: 'none', styles: 'none', fontsize: 'none', require: 'none' } },\ 1685 displayAlign: 'left', messageStyle: 'none', skipStartupTypeset: true,\ 1686 'CHTML-preview': { disabled: true }, MathMenu: { showRenderer: false, showLocale: false },\ 1687 TeX: { Macros: { color: '{}', newcommand: '{}', renewcommand: '{}', newenvironment: '{}', renewenvironment: '{}', def: '{}', let: '{}'}}});"; 1688 head.appendChild(script); 1689 1690 script = document.createElement('script'); 1691 script.src = '//cdn.mathjax.org/mathjax/2.6-latest/MathJax.js?config=TeX-AMS_HTML-full'; 1692 script.onload = parseMath; 1693 head.appendChild(script); 1694 } 1695 1696 captchainterval = null; 1697 function init() { 1698 var el, i; 1699 var error = typeof is_error != "undefined"; 1700 var board = location.href.match(/(?:4chan|4channel)\.org\/(\w+)/)[1]; 1701 var arr = location.href.split(/#/); 1702 if( arr[1] && arr[1].match(/q[0-9]+$/) ) { 1703 repquote( arr[1].match(/q([0-9]+)$/)[1] ); 1704 } 1705 1706 1707 if (window.math_tags && pageHasMath()) { 1708 loadMathJax(); 1709 } 1710 1711 if(navigator.userAgent) { 1712 if( navigator.userAgent.match( /iP(hone|ad|od)/i ) ) { 1713 links = document.querySelectorAll('s'); 1714 len = links.length; 1715 1716 for(i = 0; i < len; i++ ) { 1717 links[i].onclick = function() { 1718 if (this.hasAttribute('style')) { 1719 this.removeAttribute('style'); 1720 } 1721 else { 1722 this.setAttribute('style', 'color: #fff!important;'); 1723 } 1724 }; 1725 } 1726 } 1727 } 1728 1729 if( document.getElementById('styleSelector') ) { 1730 styleSelect = document.getElementById('styleSelector'); 1731 len = styleSelect.options.length; 1732 for (i = 0; i < len; i++) { 1733 if (styleSelect.options[i].value == activeStyleSheet) { 1734 styleSelect.selectedIndex = i; 1735 continue; 1736 } 1737 } 1738 } 1739 1740 if (!error && document.forms.post) { 1741 if (board != 'i' && board != 'ic' && board != 'f') { 1742 if (window.File && window.FileReader && window.FileList && window.Blob) { 1743 el = document.getElementById('postFile'); 1744 el && el.addEventListener('change', handleFileSelect, false); 1745 } 1746 } 1747 } 1748 1749 //window.addEventListener('onhashchange', locationHashChanged, false); 1750 1751 if( typeof extra != "undefined" && extra && !error ) extra.init(); 1752 } 1753 1754 var coreLenCheckTimeout = null; 1755 function onComKeyDown() { 1756 clearTimeout(coreLenCheckTimeout); 1757 coreLenCheckTimeout = setTimeout(coreCheckComLength, 500); 1758 } 1759 1760 function coreCheckComLength() { 1761 var byteLength, comField, error; 1762 1763 if (comlen) { 1764 comField = document.getElementsByName('com')[0]; 1765 byteLength = encodeURIComponent(comField.value).split(/%..|./).length - 1; 1766 1767 if (byteLength > comlen) { 1768 if (!(error = document.getElementById('comlenError'))) { 1769 error = document.createElement('div'); 1770 error.id = 'comlenError'; 1771 error.style.cssText = 'font-weight:bold;padding:5px;color:red;'; 1772 comField.parentNode.appendChild(error); 1773 } 1774 error.textContent = 'Error: Comment too long (' + byteLength + '/' + comlen + ').'; 1775 } 1776 else if (error = document.getElementById('comlenError')) { 1777 error.parentNode.removeChild(error); 1778 } 1779 } 1780 } 1781 1782 function disableMobile() { 1783 localStorage.setItem('4chan_never_show_mobile', 'true'); 1784 location.reload(true); 1785 } 1786 1787 function enableMobile() { 1788 localStorage.removeItem('4chan_never_show_mobile'); 1789 location.reload(true); 1790 } 1791 1792 var currentHighlighted = null; 1793 function enableClickableIds() 1794 { 1795 var i = 0, len = 0; 1796 var elems = document.getElementsByClassName('posteruid'); 1797 var capcode = document.getElementsByClassName('capcode'); 1798 1799 if( capcode != null ) { 1800 for( i = 0, len = capcode.length; i < len; i++ ) { 1801 capcode[i].addEventListener("click", idClick, false); 1802 } 1803 } 1804 1805 if( elems == null ) return; 1806 for( i = 0, len = elems.length; i < len; i++ ) { 1807 elems[i].addEventListener("click", idClick, false); 1808 } 1809 } 1810 1811 function idClick(evt) 1812 { 1813 var i = 0, len = 0, node; 1814 var uid = evt.target.className == 'hand' ? evt.target.parentNode.className.match(/id_([^ $]+)/)[1] : evt.target.className.match(/id_([^ $]+)/)[1]; 1815 1816 // remove all .highlight classes 1817 var hl = document.getElementsByClassName('highlight'); 1818 len = hl.length; 1819 for( i = 0; i < len; i++ ) { 1820 var cn = hl[0].className.toString(); 1821 hl[0].className = cn.replace(/highlight /g, ''); 1822 } 1823 1824 if( currentHighlighted == uid ) { 1825 currentHighlighted = null; 1826 return; 1827 } 1828 currentHighlighted = uid; 1829 1830 var nhl = document.getElementsByClassName('id_' + uid); 1831 len = nhl.length; 1832 for( i = 0; i < len; i++ ) { 1833 node = nhl[i].parentNode.parentNode.parentNode; 1834 if( !node.className.match(/highlight /) ) node.className = "highlight " + node.className; 1835 } 1836 } 1837 1838 function showPostFormError(msg) { 1839 var el = document.getElementById('postFormError'); 1840 1841 if (msg) { 1842 el.innerHTML = msg; 1843 el.style.display = 'block'; 1844 } 1845 else { 1846 el.textContent = ''; 1847 el.style.display = ''; 1848 } 1849 } 1850 1851 function handleFileSelect() { 1852 var fsize, ftype, maxFilesize; 1853 1854 if (this.files) { 1855 maxFilesize = window.maxFilesize; 1856 1857 fsize = this.files[0].size; 1858 ftype = this.files[0].type; 1859 1860 if (ftype.indexOf('video/') !== -1 && window.maxWebmFilesize) { 1861 maxFilesize = window.maxWebmFilesize; 1862 } 1863 1864 if (fsize > maxFilesize) { 1865 showPostFormError('Error: Maximum file size allowed is ' 1866 + Math.floor(maxFilesize / 1048576) + ' MB'); 1867 } 1868 else { 1869 showPostFormError(); 1870 } 1871 } 1872 } 1873 1874 function locationHashChanged(e) 1875 { 1876 var css = document.getElementById('id_css'); 1877 1878 switch( e.id ) 1879 { 1880 case 'refresh_top': 1881 url = window.location.href.replace(/#.+/, '#top'); 1882 if( !/top$/.test(url) ) url += '#top'; 1883 css.innerHTML = '<meta http-equiv="refresh" content="0;URL=' + url + '">'; 1884 document.location.reload(true); 1885 break; 1886 1887 case 'refresh_bottom': 1888 url = window.location.href.replace(/#.+/, '#bottom'); 1889 if( !/bottom$/.test(url) ) url += '#bottom'; 1890 css.innerHTML = '<meta http-equiv="refresh" content="0;URL=' + url + '">'; 1891 document.location.reload(true); 1892 break; 1893 1894 default:break; 1895 } 1896 1897 return true; 1898 1899 } 1900 1901 function setActiveStyleSheet(title, init) { 1902 var a, link, href, i, nodes, fn; 1903 1904 if( document.querySelectorAll('link[title]').length == 1 ) { 1905 return; 1906 } 1907 1908 href = ''; 1909 1910 nodes = document.getElementsByTagName('link'); 1911 1912 for (i = 0; a = nodes[i]; i++) { 1913 if (a.getAttribute("title") == "switch") { 1914 link = a; 1915 } 1916 1917 if (a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) { 1918 if (a.getAttribute("title") == title) { 1919 href = a.href; 1920 } 1921 } 1922 } 1923 1924 link && link.setAttribute("href", href); 1925 1926 if (!init) { 1927 if (title !== '_special') { 1928 createCookie(style_group, title, 365, location.host.indexOf('4channel.org') === -1 ? '4chan.org' : '4channel.org'); 1929 1930 if (window.css_event) { 1931 fn = window['fc_' + window.css_event + '_cleanup']; 1932 localStorage.setItem('4chan_stop_css_event', `${window.css_event}-${window.css_event_v}`); 1933 } 1934 } 1935 else if (window.css_event) { 1936 fn = window['fc_' + window.css_event + '_init']; 1937 localStorage.removeItem('4chan_stop_css_event'); 1938 } 1939 1940 //StorageSync.sync('4chan_stop_css_event'); 1941 1942 activeStyleSheet = title; 1943 1944 fn && fn(); 1945 } 1946 } 1947 1948 function getActiveStyleSheet() { 1949 var i, a; 1950 var link; 1951 1952 if( document.querySelectorAll('link[title]').length == 1 ) { 1953 return 'Yotsuba P'; 1954 } 1955 1956 for (i = 0; (a = document.getElementsByTagName("link")[i]); i++) { 1957 if (a.getAttribute("title") == "switch") 1958 link = a; 1959 else if (a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title") && a.href==link.href) return a.getAttribute("title"); 1960 } 1961 return null; 1962 } 1963 1964 function getPreferredStyleSheet() { 1965 return (style_group == "ws_style") ? "Yotsuba B New" : "Yotsuba New"; 1966 } 1967 1968 function createCookie(name, value, days, domain) { 1969 let expires; 1970 1971 if (days) { 1972 var date = new Date(); 1973 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 1974 expires = "; expires=" + date.toGMTString(); 1975 } 1976 else { 1977 expires = ''; 1978 } 1979 1980 if (domain) { 1981 domain = "; domain=" + domain; 1982 } 1983 else { 1984 domain = ''; 1985 } 1986 1987 document.cookie = name + "=" + value + expires + "; path=/" + domain; 1988 } 1989 1990 function readCookie(name) { 1991 var nameEQ = name + "="; 1992 var ca = document.cookie.split(';'); 1993 for (var i = 0; i < ca.length; i++) { 1994 var c = ca[i]; 1995 while (c.charAt(0) == ' ') c = c.substring(1, c.length); 1996 if (c.indexOf(nameEQ) == 0) { 1997 return decodeURIComponent(c.substring(nameEQ.length, c.length)); 1998 } 1999 } 2000 return ''; 2001 } 2002 2003 // legacy 2004 var get_cookie = readCookie; 2005 2006 function setRetinaIcons() { 2007 var i, j, nodes; 2008 2009 nodes = document.getElementsByClassName('retina'); 2010 2011 for (i = 0; j = nodes[i]; ++i) { 2012 j.src = j.src.replace(/\.(gif|png)$/, "@2x.$1"); 2013 } 2014 } 2015 2016 function onCoreClick(e) { 2017 if (/flag flag-/.test(e.target.className) && e.which == 1) { 2018 window.open('//s.4cdn.org/image/country/' 2019 + e.target.className.match(/flag-([a-z]+)/)[1] 2020 + '.gif', ''); 2021 } 2022 } 2023 2024 function showPostForm(e) { 2025 var el; 2026 2027 e && e.preventDefault(); 2028 2029 if (el = document.getElementById('postForm')) { 2030 $.id('togglePostFormLink').style.display = 'none'; 2031 el.style.display = 'table'; 2032 initRecaptcha(); 2033 initTCaptcha(); 2034 } 2035 } 2036 2037 function oeCanvasPreview(e) { 2038 var t, el, sel; 2039 2040 if (el = document.getElementById('oe-canvas-preview')) { 2041 el.parentNode.removeChild(el); 2042 } 2043 2044 if (e.target.nodeName == 'OPTION' && e.target.value != '0') { 2045 t = document.getElementById('f' + e.target.value); 2046 2047 if (!t) { 2048 return; 2049 } 2050 2051 t = t.getElementsByTagName('img')[0]; 2052 2053 if (!t || !t.hasAttribute('data-md5')) { 2054 return; 2055 } 2056 2057 el = t.cloneNode(); 2058 el.id = 'oe-canvas-preview'; 2059 sel = e.target.parentNode; 2060 sel.parentNode.insertBefore(el, sel.nextSibling); 2061 } 2062 } 2063 2064 function oeClearPreview(e) { 2065 var el; 2066 2067 if (el = document.getElementById('oe-canvas-preview')) { 2068 el.parentNode.removeChild(el); 2069 } 2070 } 2071 2072 var PainterCore = { 2073 init: function() { 2074 var cnt, btns; 2075 2076 if (!document.forms.post) { 2077 return; 2078 } 2079 2080 cnt = document.forms.post.getElementsByClassName('painter-ctrl')[0]; 2081 2082 if (!cnt) { 2083 return; 2084 } 2085 2086 btns = cnt.getElementsByTagName('button'); 2087 2088 if (!btns[1]) { 2089 return; 2090 } 2091 2092 this.data = null; 2093 this.replayBlob = null; 2094 2095 this.time = 0; 2096 2097 this.btnDraw = btns[0]; 2098 this.btnClear = btns[1]; 2099 this.btnFile = document.getElementById('postFile'); 2100 this.btnSubmit = document.forms.post.querySelector('input[type="submit"]'); 2101 this.inputNodes = cnt.getElementsByTagName('input'); 2102 this.replayCb = cnt.getElementsByClassName('oe-r-cb')[0]; 2103 2104 btns[0].addEventListener('click', this.onDrawClick, false); 2105 btns[1].addEventListener('click', this.onCancel, false); 2106 }, 2107 2108 onDrawClick: function() { 2109 var w, h, dims = this.parentNode.getElementsByTagName('input'); 2110 2111 w = +dims[0].value; 2112 h = +dims[1].value; 2113 2114 if (w < 1 || h < 1) { 2115 return; 2116 } 2117 2118 window.Keybinds && (Keybinds.enabled = false); 2119 2120 Tegaki.open({ 2121 onDone: PainterCore.onDone, 2122 onCancel: PainterCore.onCancel, 2123 saveReplay: PainterCore.replayCb && PainterCore.replayCb.checked, 2124 width: w, 2125 height: h 2126 }); 2127 }, 2128 2129 replay: function(id) { 2130 id = +id; 2131 2132 Tegaki.open({ 2133 replayMode: true, 2134 replayURL: '//i.4cdn.org/' + location.pathname.split(/\//)[1] + '/' + id + '.tgkr' 2135 }); 2136 }, 2137 2138 // move this to tegaki.js 2139 b64toBlob: function(data) { 2140 var i, bytes, ary, bary, len; 2141 2142 bytes = atob(data); 2143 len = bytes.length; 2144 2145 ary = new Array(len); 2146 2147 for (i = 0; i < len; ++i) { 2148 ary[i] = bytes.charCodeAt(i); 2149 } 2150 2151 bary = new Uint8Array(ary); 2152 2153 return new Blob([bary]); 2154 }, 2155 2156 onDone: function() { 2157 var self, el; 2158 2159 self = PainterCore; 2160 2161 window.Keybinds && (Keybinds.enabled = true); 2162 2163 self.btnFile.disabled = true; 2164 self.btnClear.disabled = false; 2165 2166 self.data = Tegaki.flatten().toDataURL('image/png'); 2167 2168 if (Tegaki.saveReplay) { 2169 self.replayBlob = Tegaki.replayRecorder.toBlob(); 2170 } 2171 2172 if (!Tegaki.hasCustomCanvas && Tegaki.startTimeStamp) { 2173 self.time = Math.round((Date.now() - Tegaki.startTimeStamp) / 1000); 2174 } 2175 else { 2176 self.time = 0; 2177 } 2178 2179 self.btnFile.style.visibility = 'hidden'; 2180 2181 self.btnDraw.textContent = 'Edit'; 2182 2183 for (el of self.inputNodes) { 2184 el.disabled = true; 2185 } 2186 2187 document.forms.post.addEventListener('submit', self.onSubmit, false); 2188 }, 2189 2190 onCancel: function() { 2191 var self = PainterCore; 2192 2193 window.Keybinds && (Keybinds.enabled = true); 2194 2195 self.data = null; 2196 self.replayBlob = null; 2197 self.time = 0; 2198 2199 self.btnFile.disabled = false; 2200 self.btnClear.disabled = true; 2201 2202 self.btnFile.style.visibility = ''; 2203 2204 self.btnDraw.textContent = 'Draw'; 2205 2206 for (var el of self.inputNodes) { 2207 el.disabled = false; 2208 } 2209 2210 document.forms.post.removeEventListener('submit', self.onSubmit, false); 2211 }, 2212 2213 onSubmit: function(e) { 2214 var formdata, blob, xhr; 2215 2216 e.preventDefault(); 2217 2218 formdata = new FormData(this); 2219 2220 blob = PainterCore.b64toBlob(PainterCore.data.slice(PainterCore.data.indexOf(',') + 1)); 2221 2222 if (blob) { 2223 formdata.append('upfile', blob, 'tegaki.png'); 2224 2225 if (PainterCore.replayBlob) { 2226 formdata.append('oe_replay', PainterCore.replayBlob, 'tegaki.tgkr'); 2227 } 2228 } 2229 2230 formdata.append('oe_time', PainterCore.time); 2231 2232 xhr = new XMLHttpRequest(); 2233 xhr.open('POST', this.action, true); 2234 xhr.withCredentials = true; 2235 xhr.onerror = PainterCore.onSubmitError; 2236 xhr.onload = PainterCore.onSubmitDone; 2237 2238 xhr.send(formdata); 2239 2240 PainterCore.btnSubmit.disabled = true; 2241 }, 2242 2243 onSubmitError: function() { 2244 PainterCore.btnSubmit.disabled = false; 2245 showPostFormError('Connection Error.'); 2246 }, 2247 2248 onSubmitDone: function() { 2249 var resp, ids, tid, pid, board; 2250 2251 PainterCore.btnSubmit.disabled = false; 2252 2253 if (ids = this.responseText.match(/<!-- thread:([0-9]+),no:([0-9]+) -->/)) { 2254 tid = +ids[1]; 2255 pid = +ids[2]; 2256 2257 if (!tid) { 2258 tid = pid; 2259 } 2260 2261 board = location.pathname.split(/\//)[1]; 2262 2263 window.location.href = '/' + board + '/thread/' + tid + '#p' + pid; 2264 2265 PainterCore.onCancel(); 2266 2267 if (tid != pid) { 2268 PainterCore.btnClear.disabled = true; 2269 window.location.reload(); 2270 } 2271 2272 return; 2273 } 2274 2275 if (resp = this.responseText.match(/"errmsg"[^>]*>(.*?)<\/span/)) { 2276 showPostFormError(resp[1]); 2277 } 2278 } 2279 }; 2280 2281 function oeReplay(id) { 2282 PainterCore.replay(id); 2283 } 2284 2285 /*! https://github.com/Joe12387/detectIncognito */ 2286 function checkIncognito() { 2287 if (window.isIncognito !== undefined) { 2288 return; 2289 } 2290 2291 if (!navigator.maxTouchPoints || navigator.vendor === undefined) { 2292 window.isIncognito = false; 2293 return; 2294 } 2295 2296 (new Promise(function(resolve, reject) { 2297 let eh = eval.toString().length; 2298 2299 if (navigator.vendor.indexOf('Apple') === 0 && eh === 37) { 2300 if (navigator.maxTouchPoints === undefined) { 2301 resolve(false); 2302 } 2303 2304 let db_name = Math.random().toString(); 2305 2306 try { 2307 let db = window.indexedDB.open(db_name, 1); 2308 db.onupgradeneeded = function (e) { 2309 let res = e.target.result; 2310 try { 2311 res.createObjectStore('test', { autoIncrement: true }).put(new Blob); 2312 resolve(false); 2313 } 2314 catch(err) { 2315 let msg; 2316 if (err instanceof Error) { 2317 msg = err.message; 2318 } 2319 if (typeof msg !== 'string') { 2320 resolve(false); 2321 } 2322 resolve(/BlobURLs are not yet supported/.test(msg)); 2323 } 2324 finally { 2325 res.close(); 2326 window.indexedDB.deleteDatabase(db_name); 2327 } 2328 }; 2329 } 2330 catch(err) { 2331 resolve(false); 2332 } 2333 } 2334 else if (navigator.vendor.indexOf('Google') === 0 && eh === 33) { 2335 let hsl; 2336 2337 try { 2338 hsl = performance.memory.jsHeapSizeLimit; 2339 } 2340 catch(err) { 2341 hsl = 1073741824; 2342 } 2343 2344 navigator.webkitTemporaryStorage.queryUsageAndQuota(function (_, quota) { 2345 let q = Math.round(quota / (1024 * 1024)); 2346 let q_lim = Math.round(hsl / (1024 * 1024)) * 2; 2347 resolve(q < q_lim); 2348 }, function (err) { 2349 resolve(false); 2350 }); 2351 } 2352 else if (document.body.style.MozAppearance !== undefined && eh === 37) { 2353 resolve(navigator.serviceWorker === undefined); 2354 } 2355 else { 2356 resolve(false); 2357 } 2358 })).then((v) => window.isIncognito = v); 2359 } 2360 2361 function onPostFormSubmit(e) { 2362 let el = $.id('postFile'); 2363 if (el && el.value && window.isIncognito) { 2364 e.stopPropagation() 2365 e.preventDefault(); 2366 el.value = ''; 2367 showPostFormError('Uploading files in incognito mode is not allowed.' 2368 + '<br>The File field has been cleared.'); 2369 return false; 2370 } 2371 } 2372 2373 function contentLoaded() { 2374 var i, el, el2, nodes, len, mobileSelect, params, board, val, fn; 2375 2376 document.removeEventListener('DOMContentLoaded', contentLoaded, true); 2377 2378 initAdsADT(); 2379 2380 initAdsDanbo(); 2381 2382 if (document.post) { 2383 document.post.name.value = get_cookie("4chan_name"); 2384 document.post.email.value = get_cookie("options"); 2385 document.post.addEventListener('submit', onPostFormSubmit, false); 2386 } 2387 2388 cloneTopNav(); 2389 2390 initAnalytics(); 2391 2392 params = location.pathname.split(/\//); 2393 2394 board = params[1]; 2395 2396 if (window.passEnabled) { 2397 setPassMsg(); 2398 } 2399 2400 if (window.Tegaki) { 2401 PainterCore.init(); 2402 } 2403 2404 if (el = document.getElementById('bottomReportBtn')) { 2405 el.addEventListener('click', onReportClick, false); 2406 } 2407 2408 if (el = document.getElementById('styleSelector')) { 2409 el.addEventListener('change', onStyleSheetChange, false); 2410 } 2411 2412 // Post form toggle 2413 if (el = document.getElementById('togglePostFormLink')) { 2414 if (el = el.firstElementChild) { 2415 el.addEventListener('click', showPostForm, false); 2416 } 2417 if (location.hash === '#reply') { 2418 showPostForm(); 2419 } 2420 } 2421 2422 // Selectable flags 2423 if ((el = document.forms.post) && el.flag) { 2424 el.flag.addEventListener('change', onBoardFlagChanged, false); 2425 2426 if ((val = localStorage.getItem('4chan_flag_' + board)) && (el2 = el.querySelector('option[value="' + val + '"]'))) { 2427 el2.setAttribute('selected', 'selected'); 2428 } 2429 } 2430 2431 // Mobile nav menu 2432 buildMobileNav(); 2433 2434 // Mobile global message toggle 2435 if (el = document.getElementById('globalToggle')) { 2436 el.addEventListener('click', toggleGlobalMessage, false); 2437 } 2438 2439 if (localStorage.getItem('4chan_never_show_mobile') == 'true') { 2440 if (el = document.getElementById('disable-mobile')) { 2441 el.style.display = 'none'; 2442 el = document.getElementById('enable-mobile'); 2443 el.parentNode.style.cssText = 'display: inline !important;'; 2444 } 2445 } 2446 2447 if (mobileSelect = document.getElementById('boardSelectMobile')) { 2448 len = mobileSelect.options.length; 2449 for ( i = 0; i < len; i++) { 2450 if (mobileSelect.options[i].value == board) { 2451 mobileSelect.selectedIndex = i; 2452 continue; 2453 } 2454 } 2455 2456 mobileSelect.addEventListener('change', onMobileSelectChange, false); 2457 } 2458 2459 if (document.forms.oeform && (el = document.forms.oeform.oe_src)) { 2460 el.addEventListener('mouseover', oeCanvasPreview, false); 2461 el.addEventListener('mouseout', oeClearPreview, false); 2462 } 2463 2464 if (params[2] != 'catalog') { 2465 // Mobile post form toggle 2466 nodes = document.getElementsByClassName('mobilePostFormToggle'); 2467 2468 for (i = 0; el = nodes[i]; ++i) { 2469 el.addEventListener('click', onMobileFormClick, false); 2470 } 2471 2472 if (el = document.getElementsByName('com')[0]) { 2473 el.addEventListener('keydown', onComKeyDown, false); 2474 el.addEventListener('paste', onComKeyDown, false); 2475 el.addEventListener('cut', onComKeyDown, false); 2476 } 2477 2478 // Mobile refresh buttons 2479 if (el = document.getElementById('refresh_top')) { 2480 el.addEventListener('mouseup', onMobileRefreshClick, false); 2481 } 2482 2483 if (el = document.getElementById('refresh_bottom')) { 2484 el.addEventListener('mouseup', onMobileRefreshClick, false); 2485 } 2486 2487 // Clickable flags 2488 if (board == 'int' || board == 'sp' || board == 'pol') { 2489 el = document.getElementById('delform'); 2490 el.addEventListener('click', onCoreClick, false); 2491 } 2492 2493 // Page switcher + Search field 2494 if (!params[3]) { 2495 nodes = document.getElementsByClassName('pageSwitcherForm'); 2496 2497 for (i = 0; el = nodes[i]; ++i) { 2498 el.addEventListener('submit', onPageSwitch, false); 2499 } 2500 2501 if (el = document.getElementById('search-box')) { 2502 el.addEventListener('keydown', onKeyDownSearch, false); 2503 } 2504 } 2505 2506 if (window.clickable_ids) { 2507 enableClickableIds(); 2508 } 2509 2510 Tip.init(); 2511 } 2512 2513 if (window.devicePixelRatio >= 2) { 2514 setRetinaIcons(); 2515 } 2516 2517 initBlotter(); 2518 2519 loadBannerImage(); 2520 2521 if (window.css_event && activeStyleSheet === '_special') { 2522 fn = window['fc_' + window.css_event + '_init']; 2523 fn && fn(); 2524 } 2525 } 2526 2527 function onBoardFlagChanged() { 2528 var key = '4chan_flag_' + location.pathname.split(/\//)[1]; 2529 2530 if (this.value === '0') { 2531 localStorage.removeItem(key); 2532 } 2533 else { 2534 localStorage.setItem(key, this.value); 2535 } 2536 } 2537 2538 initPass(); 2539 2540 window.onload = init; 2541 2542 if (window.clickable_ids) { 2543 document.addEventListener('4chanParsingDone', onParsingDone, false); 2544 } 2545 2546 document.addEventListener('4chanMainInit', loadExtraScripts, false); 2547 document.addEventListener('DOMContentLoaded', contentLoaded, true); 2548 2549 initStyleSheet();