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