diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f558d772..071c183ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ -Sometimes the changelog has notes (not comprehensive) acknowledging people's work. This does not mean the changes are their fault, only that their code was used. All changes to the script are chosen by and the fault of the maintainer (ccd0). +## v1.14.0 + +**v1.14.0.0** *(2018-01-24)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.0.0/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.0.0/builds/4chan-X-noupdate.crx)] +- Based on v1.13.15.5. +- Preliminary support for Tinyboard / vichan based imageboards. Only a subset of features are working. To use 4chan X on a site, use the userscript version of 4chan X and add the site the user `@match` rules. In the instructions below, replace "examplechan.com" with the site you want to add: + - Greasemonkey 4: [Not implemented yet](https://github.com/greasemonkey/greasemonkey/issues/2728). Use Violentmonkey or Tampermonkey for now. + - Greasemonkey 3: Go to the "User Scripts" tab of about:addons, find 4chan X, and click "Options". On the "User Settings" tab, click the "Add" button next to "Matched Pages". Enter `https://examplechan.com/*`. + - Violentmonkey: Open the Violentmonkey settings page and find 4chan X. Click the edit button (looks like ``). Go to the "Settings" tab and enter `https://examplechan.com/*` in the "@match rules" field. Click save. + - Tampermonkey: Open the Tampermonkey settings page, go to the "Installed userscripts" tab, and find 4chan X. Click the edit button (pencil on paper). Go to the "Settings" tab and click the "Add" button below "User matches". Enter `https://examplechan.com/*`. + ### v1.13.15 **v1.13.15.5** *(2018-01-23)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.15.5/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.15.5/builds/4chan-X-noupdate.crx)] diff --git a/builds/4chan-X-beta.crx b/builds/4chan-X-beta.crx index f0db1e5c8..a48660a11 100644 Binary files a/builds/4chan-X-beta.crx and b/builds/4chan-X-beta.crx differ diff --git a/builds/4chan-X-beta.meta.js b/builds/4chan-X-beta.meta.js index 669ab5ff2..4ae2beae9 100644 --- a/builds/4chan-X-beta.meta.js +++ b/builds/4chan-X-beta.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X beta -// @version 1.13.15.5 +// @version 1.14.0.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -24,8 +24,6 @@ // @include https://www.google.com/recaptcha/api2/bframe?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include http://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include http://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include https://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @exclude http://www.4chan.org/pass // @exclude https://www.4chan.org/pass // @exclude http://www.4chan.org/pass?* diff --git a/builds/4chan-X-beta.user.js b/builds/4chan-X-beta.user.js index c71ad42ee..138f07089 100644 --- a/builds/4chan-X-beta.user.js +++ b/builds/4chan-X-beta.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X beta -// @version 1.13.15.5 +// @version 1.14.0.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -24,8 +24,6 @@ // @include https://www.google.com/recaptcha/api2/bframe?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include http://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include http://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include https://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @exclude http://www.4chan.org/pass // @exclude https://www.4chan.org/pass // @exclude http://www.4chan.org/pass?* @@ -144,7 +142,7 @@ 'use strict'; -var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, Build, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume; +var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, Build, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume; var Conf, E, c, d, doc, docSet, g; @@ -159,7 +157,7 @@ docSet = function() { }; g = { - VERSION: '1.13.15.5', + VERSION: '1.14.0.0', NAMESPACE: '4chan X.', boards: {} }; @@ -510,7 +508,8 @@ Config = (function() { 'updater.position': 'bottom: 0px; left: 0px;', 'thread-watcher.position': 'top: 50px; left: 0px;', 'qr.position': 'top: 50px; right: 0px;' - } + }, + siteSoftware: "4chan.org yotsuba" }; return Config; @@ -1359,6 +1358,23 @@ body.is_catalog .thread > a > img {\n\ div.center[style] {\n\ display: none !important;\n\ }\n\ +/* Tinyboard / vichan conflicts */\n\ +#menu > .hide-thread-link {\n\ + width: auto;\n\ + height: auto;\n\ + overflow: visible;\n\ + background-image: none;\n\ +}\n\ +#menu label.entry {\n\ + display: block;\n\ +}\n\ +#fourchanx-settings label {\n\ + display: inline;\n\ +}\n\ +.intro a[href=\"javascript:;\"],\n\ +#menu a {\n\ + margin: 0;\n\ +}\n\ /* Anti-autoplay */\n\ audio.controls-added {\n\ display: block;\n\ @@ -1901,6 +1917,9 @@ div[data-checked=\"false\"] > .suboption-list {\n\ #fourchanx-settings p {\n\ margin: 1em 0px;\n\ }\n\ +#fourchanx-settings table {\n\ + margin: auto;\n\ +}\n\ .unscroll {\n\ overflow: hidden;\n\ }\n\ @@ -2472,7 +2491,7 @@ span.hide-announcement {\n\ .expanded-image > .post > .file > .fileThumb > img[data-md5] {\n\ display: none;\n\ }\n\ -.full-image {\n\ +.full-image[data-full-i-d] {\n\ display: none;\n\ cursor: pointer;\n\ }\n\ @@ -2624,7 +2643,9 @@ input[name=\"Default Volume\"] {\n\ }\n\ /* Spoiler text */\n\ :root.reveal-spoilers s,\n\ -:root.reveal-spoilers s > a {\n\ +:root.reveal-spoilers .spoiler,\n\ +:root.reveal-spoilers s > a,\n\ +:root.reveal-spoilers .spoiler > a {\n\ color: white !important;\n\ }\n\ :root.reveal-spoilers .removed-spoiler::before {\n\ @@ -2674,6 +2695,7 @@ input[name=\"Default Volume\"] {\n\ font-size: 0;\n\ }\n\ :root.anonymize .postertrip,\n\ +:root.anonymize .trip,\n\ :root.anonymize .n-pu {\n\ display: none;\n\ }\n\ @@ -2822,22 +2844,6 @@ input.field.tripped:not(:hover):not(:focus) {\n\ position: relative;\n\ top: 2px;\n\ }\n\ -/* Recaptcha v1 */\n\ -.captcha-img {\n\ - margin: 0px;\n\ - text-align: center;\n\ - background-image: #fff;\n\ - font-size: 0px;\n\ - min-height: 59px;\n\ - min-width: 302px;\n\ -}\n\ -.captcha-input {\n\ - width: 100%;\n\ - margin: 1px 0 0;\n\ -}\n\ -#qr.captcha-v1 #qr-captcha-iframe {\n\ - display: none;\n\ -}\n\ /* Recaptcha v2 */\n\ #qr .captcha-root {\n\ position: relative;\n\ @@ -3207,6 +3213,7 @@ a:only-of-type > .remove {\n\ #menu {\n\ position: fixed;\n\ outline: none;\n\ + font-weight: normal;\n\ }\n\ #menu, .submenu {\n\ border-radius: 3px;\n\ @@ -4994,6 +5001,12 @@ $ = (function() { $.syncing = {}; + $.securityCheck = function(data) { + if (location.protocol !== 'https:') { + return delete data['Redirect to HTTPS']; + } + }; + if (((typeof GM !== "undefined" && GM !== null ? GM.deleteValue : void 0) != null) && window.BroadcastChannel && (typeof GM_addValueChangeListener === "undefined" || GM_addValueChangeListener === null)) { $.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync'); $.on($.syncChannel, 'message', function(e) { @@ -5060,6 +5073,7 @@ $ = (function() { }); $.set = $.oneItemSugar(function(items, cb) { var key, val; + $.securityCheck(items); return Promise.all((function() { var results; results = []; @@ -5248,6 +5262,7 @@ $ = (function() { return cb(items); }; $.set = $.oneItemSugar(function(items, cb) { + $.securityCheck(items); return $.queueTask(function() { var key, value; for (key in items) { @@ -5602,7 +5617,7 @@ DataBoard = (function() { var init; this.key = key1; this.onSync = bind(this.onSync, this); - this.data = Conf[this.key]; + this.initData(Conf[this.key]); $.sync(this.key, this.onSync); if (!dontClean) { this.clean(); @@ -5619,35 +5634,103 @@ DataBoard = (function() { $.on(d, '4chanXInitFinished', init); } - DataBoard.prototype.save = function(cb) { - return $.set(this.key, this.data, cb); + DataBoard.prototype.initData = function(allData) { + var base, name; + this.allData = allData; + if (Site.hostname === '4chan.org' && this.allData.boards) { + return this.data = this.allData; + } else { + return this.data = ((base = this.allData)[name = Site.hostname] || (base[name] = { + boards: {} + })); + } + }; + + DataBoard.prototype.changes = []; + + DataBoard.prototype.save = function(change, cb) { + var changes, snapshot1; + snapshot1 = JSON.stringify(this.allData); + change(); + changes = this.changes; + changes.push(change); + return $.get(this.key, { + boards: {} + }, (function(_this) { + return function(items) { + var c, i, len, snapshot2; + _this.initData(items[_this.key]); + snapshot2 = JSON.stringify(_this.allData); + for (i = 0, len = changes.length; i < len; i++) { + c = changes[i]; + c(); + } + return $.set(_this.key, _this.allData, function() { + _this.changes = []; + if (snapshot1 !== snapshot2) { + if (typeof _this.sync === "function") { + _this.sync(); + } + } + return typeof cb === "function" ? cb() : void 0; + }); + }; + })(this)); + }; + + DataBoard.prototype.forceSync = function(cb) { + var changes, snapshot1; + snapshot1 = JSON.stringify(this.allData); + changes = this.changes; + return $.get(this.key, { + boards: {} + }, (function(_this) { + return function(items) { + var c, i, len, snapshot2; + _this.initData(items[_this.key]); + snapshot2 = JSON.stringify(_this.allData); + for (i = 0, len = changes.length; i < len; i++) { + c = changes[i]; + c(); + } + if (snapshot1 !== snapshot2) { + if (typeof _this.sync === "function") { + _this.sync(); + } + } + return typeof cb === "function" ? cb() : void 0; + }; + })(this)); }; DataBoard.prototype["delete"] = function(arg) { - var boardID, postID, ref, threadID; + var boardID, postID, threadID; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID; - $.forceSync(this.key); - if (postID) { - if (!((ref = this.data.boards[boardID]) != null ? ref[threadID] : void 0)) { - return; - } - delete this.data.boards[boardID][threadID][postID]; - this.deleteIfEmpty({ - boardID: boardID, - threadID: threadID - }); - } else if (threadID) { - if (!this.data.boards[boardID]) { - return; - } - delete this.data.boards[boardID][threadID]; - this.deleteIfEmpty({ - boardID: boardID - }); - } else { - delete this.data.boards[boardID]; - } - return this.save(); + return this.save((function(_this) { + return function() { + var ref; + if (postID) { + if (!((ref = _this.data.boards[boardID]) != null ? ref[threadID] : void 0)) { + return; + } + delete _this.data.boards[boardID][threadID][postID]; + return _this.deleteIfEmpty({ + boardID: boardID, + threadID: threadID + }); + } else if (threadID) { + if (!_this.data.boards[boardID]) { + return; + } + delete _this.data.boards[boardID][threadID]; + return _this.deleteIfEmpty({ + boardID: boardID + }); + } else { + return delete _this.data.boards[boardID]; + } + }; + })(this)); }; DataBoard.prototype.deleteIfEmpty = function(arg) { @@ -5666,45 +5749,59 @@ DataBoard = (function() { }; DataBoard.prototype.set = function(data, cb) { - $.forceSync(this.key); - return this.setUnsafe(data, cb); + return this.save((function(_this) { + return function() { + return _this.setUnsafe(data); + }; + })(this), cb); }; - DataBoard.prototype.setUnsafe = function(arg, cb) { + DataBoard.prototype.setUnsafe = function(arg) { var base, base1, base2, boardID, postID, threadID, val; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, val = arg.val; if (postID !== void 0) { - ((base = ((base1 = this.data.boards)[boardID] || (base1[boardID] = {})))[threadID] || (base[threadID] = {}))[postID] = val; + return ((base = ((base1 = this.data.boards)[boardID] || (base1[boardID] = {})))[threadID] || (base[threadID] = {}))[postID] = val; } else if (threadID !== void 0) { - ((base2 = this.data.boards)[boardID] || (base2[boardID] = {}))[threadID] = val; + return ((base2 = this.data.boards)[boardID] || (base2[boardID] = {}))[threadID] = val; } else { - this.data.boards[boardID] = val; + return this.data.boards[boardID] = val; } - return this.save(cb); }; DataBoard.prototype.extend = function(arg, cb) { - var boardID, i, key, len, oldVal, postID, ref, rm, threadID, val; + var boardID, postID, rm, threadID, val; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, val = arg.val, rm = arg.rm; - $.forceSync(this.key); - oldVal = this.get({ - boardID: boardID, - threadID: threadID, - postID: postID, - val: {} - }); - ref = rm || []; - for (i = 0, len = ref.length; i < len; i++) { - key = ref[i]; - delete oldVal[key]; - } - $.extend(oldVal, val); - return this.setUnsafe({ - boardID: boardID, - threadID: threadID, - postID: postID, - val: oldVal - }, cb); + return this.save((function(_this) { + return function() { + var i, key, len, oldVal, ref; + oldVal = _this.get({ + boardID: boardID, + threadID: threadID, + postID: postID, + val: {} + }); + ref = rm || []; + for (i = 0, len = ref.length; i < len; i++) { + key = ref[i]; + delete oldVal[key]; + } + $.extend(oldVal, val); + return _this.setUnsafe({ + boardID: boardID, + threadID: threadID, + postID: postID, + val: oldVal + }); + }; + })(this), cb); + }; + + DataBoard.prototype.setLastChecked = function() { + return this.save((function(_this) { + return function() { + return _this.data.lastChecked = Date.now(); + }; + })(this)); }; DataBoard.prototype.get = function(arg) { @@ -5730,13 +5827,11 @@ DataBoard = (function() { return val || defaultValue; }; - DataBoard.prototype.forceSync = function() { - return $.forceSync(this.key); - }; - DataBoard.prototype.clean = function() { var boardID, now, ref, ref1, val; - $.forceSync(this.key); + if (Site.software !== 'yotsuba') { + return; + } ref = this.data.boards; for (boardID in ref) { val = ref[boardID]; @@ -5802,13 +5897,11 @@ DataBoard = (function() { this.deleteIfEmpty({ boardID: boardID }); - return this.save(); + return $.set(this.key, this.allData); }; DataBoard.prototype.onSync = function(data) { - this.data = data || { - boards: {} - }; + this.initData(data); return typeof this.sync === "function" ? this.sync() : void 0; }; @@ -6262,7 +6355,6 @@ Notice = (function() { Post = (function() { var Post, - slice = [].slice, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Post = (function() { @@ -6271,31 +6363,25 @@ Post = (function() { }; function Post(root, thread, board) { - var clone, icon, j, k, len, len1, ref, ref1, ref10, ref11, ref12, ref13, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, type; + var clone, j, k, key, len, len1, ref, ref1, ref10, ref11, ref12, ref13, ref14, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, selector; this.thread = thread; this.board = board; - this.ID = +root.id.slice(2); + this.ID = +root.id.match(/\d*$/)[0]; this.threadID = this.thread.ID; this.boardID = this.board.ID; this.fullID = this.board + "." + this.ID; this.context = this; root.dataset.fullID = this.fullID; this.nodes = this.parseNodes(root); - if (!(this.isReply = $.hasClass(this.nodes.post, 'reply'))) { + if (!(this.isReply = this.ID !== this.threadID)) { this.thread.OP = this; - if (this.boardID === 'f') { - ref = ['Sticky', 'Closed']; - for (j = 0, len = ref.length; j < len; j++) { - type = ref[j]; - if ((icon = $("img[alt=" + type + "]", this.nodes.info))) { - $.addClass(icon, (type.toLowerCase()) + "Icon", 'retina'); - } - } + ref = ['isSticky', 'isClosed', 'isArchived']; + for (j = 0, len = ref.length; j < len; j++) { + key = ref[j]; + this.thread[key] = (selector = Site.selectors.icons[key]) ? !!$(selector, this.nodes.info) : false; } - this.thread.isArchived = !!$('.archivedIcon', this.nodes.info); - this.thread.isSticky = !!$('.stickyIcon', this.nodes.info); - this.thread.isClosed = this.thread.isArchived || !!$('.closedIcon', this.nodes.info); if (this.thread.isArchived) { + this.thread.isClosed = true; this.thread.kill(); } } @@ -6309,7 +6395,7 @@ Post = (function() { flagCode: (ref7 = this.nodes.flag) != null ? (ref8 = ref7.className.match(/flag-(\w+)/)) != null ? ref8[1].toUpperCase() : void 0 : void 0, flagCodeTroll: (ref9 = this.nodes.flag) != null ? (ref10 = ref9.src) != null ? (ref11 = ref10.match(/(\w+)\.gif$/)) != null ? ref11[1].toUpperCase() : void 0 : void 0 : void 0, flag: (ref12 = this.nodes.flag) != null ? ref12.title : void 0, - date: this.nodes.date ? new Date(this.nodes.date.dataset.utc * 1000) : void 0 + date: this.nodes.date ? new Date(((ref13 = this.nodes.date.getAttribute('datetime')) != null ? ref13.trim() : void 0) || (this.nodes.date.dataset.utc * 1000)) : void 0 }; if (Conf['Anonymize']) { this.info.nameBlock = 'Anonymous'; @@ -6331,9 +6417,9 @@ Post = (function() { if (g.posts[this.fullID]) { this.isRebuilt = true; this.clones = g.posts[this.fullID].clones; - ref13 = this.clones; - for (k = 0, len1 = ref13.length; k < len1; k++) { - clone = ref13[k]; + ref14 = this.clones; + for (k = 0, len1 = ref14.length; k < len1; k++) { + clone = ref14[k]; clone.origin = this; } } @@ -6343,32 +6429,28 @@ Post = (function() { } Post.prototype.parseNodes = function(root) { - var info, nodes, post; - post = $('.post', root); - info = $('.postInfo', post); + var info, key, nodes, post, ref, s, selector; + s = Site.selectors; + post = $(s.post, root) || root; + info = $(s.infoRoot, post); nodes = { root: root, post: post, info: info, - subject: $('.subject', info), - name: $('.name', info), - email: $('.useremail', info), - tripcode: $('.postertrip', info), - uniqueIDRoot: $('.posteruid', info), - uniqueID: $('.posteruid > .hand', info), - capcode: $('.capcode.hand', info), - pass: $('.n-pu', info), - flag: $('.flag, .countryFlag', info), - date: $('.dateTime', info), - nameBlock: $('.nameBlock', info), - quote: $('.postNum > a:nth-of-type(2)', info), - reply: $('.replylink', info), - fileRoot: $('.file', post), - comment: $('.postMessage', post), + comment: $(s.comment, post), quotelinks: [], archivelinks: [], embedlinks: [] }; + ref = s.info; + for (key in ref) { + selector = ref[key]; + nodes[key] = $(selector, info); + } + if (typeof Site.parseNodes === "function") { + Site.parseNodes(this, nodes); + } + nodes.uniqueIDRoot || (nodes.uniqueIDRoot = nodes.uniqueID); if ($.engine === 'edge') { Object.defineProperty(nodes, 'backlinks', { configurable: true, @@ -6387,7 +6469,9 @@ Post = (function() { var bq; this.nodes.comment.normalize(); this.nodes.commentClean = bq = this.nodes.comment.cloneNode(true); - this.cleanComment(bq); + if (typeof Site.cleanComment === "function") { + Site.cleanComment(bq); + } return this.info.comment = this.nodesToText(bq); }; @@ -6397,14 +6481,18 @@ Post = (function() { if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) { this.cleanSpoilers(bq); } - this.cleanCommentDisplay(bq); + if (typeof Site.cleanCommentDisplay === "function") { + Site.cleanCommentDisplay(bq); + } return this.nodesToText(bq).trim().replace(/\s+$/gm, ''); }; Post.prototype.commentOrig = function() { var bq; bq = this.nodes.commentClean.cloneNode(true); - this.insertTags(bq); + if (typeof Site.insertTags === "function") { + Site.insertTags(bq); + } return this.nodesToText(bq); }; @@ -6419,58 +6507,19 @@ Post = (function() { return text; }; - Post.prototype.cleanComment = function(bq) { - var abbr, br, i, j, k, len, node, ref; - if ((abbr = $('.abbr', bq))) { - ref = $$('.abbr + br, .exif', bq); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - $.rm(node); - } - for (i = k = 0; k < 2; i = ++k) { - if ((br = abbr.previousSibling) && br.nodeName === 'BR') { - $.rm(br); - } - } - return $.rm(abbr); - } - }; - Post.prototype.cleanSpoilers = function(bq) { var j, len, node, spoilers; - spoilers = $$('s', bq); + spoilers = $$(Site.selectors.spoiler, bq); for (j = 0, len = spoilers.length; j < len; j++) { node = spoilers[j]; $.replace(node, $.tn('[spoiler]')); } }; - Post.prototype.cleanCommentDisplay = function(bq) { - var b; - if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) { - $.rm(b); - } - return $.rm($('.fortune', bq)); - }; - - Post.prototype.insertTags = function(bq) { - var j, k, len, len1, node, ref, ref1; - ref = $$('s, .removed-spoiler', bq); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - $.replace(node, [$.tn('[spoiler]')].concat(slice.call(node.childNodes), [$.tn('[/spoiler]')])); - } - ref1 = $$('.prettyprint', bq); - for (k = 0, len1 = ref1.length; k < len1; k++) { - node = ref1[k]; - $.replace(node, [$.tn('[code]')].concat(slice.call(node.childNodes), [$.tn('[/code]')])); - } - }; - Post.prototype.parseQuotes = function() { var j, len, quotelink, ref; this.quotes = []; - ref = $$(':not(pre) > .quotelink', this.nodes.comment); + ref = $$(Site.selectors.quotelink, this.nodes.comment); for (j = 0, len = ref.length; j < len; j++) { quotelink = ref[j]; this.parseQuote(quotelink); @@ -6479,7 +6528,7 @@ Post = (function() { Post.prototype.parseQuote = function(quotelink) { var fullID, match; - match = quotelink.href.match(/^https?:\/\/boards\.4chan\.org\/+([^\/]+)\/+(?:res|thread)\/+\d+(?:[\/?][^#]*)?#p(\d+)$/); + match = quotelink.href.match(Site.regexp.quotelink); if (!(match || (this.isClone && quotelink.dataset.postID))) { return; } @@ -6487,55 +6536,39 @@ Post = (function() { if (this.isClone) { return; } - fullID = match[1] + "." + match[2]; + fullID = match[1] + "." + match[3]; if (indexOf.call(this.quotes, fullID) < 0) { return this.quotes.push(fullID); } }; Post.prototype.parseFile = function() { - var fileRoot, fileText, info, link, m, ref, ref1, ref2, size, thumb, unit; - fileRoot = this.nodes.fileRoot; - if (!fileRoot) { + var file, key, ref, ref1, selector, size, unit; + file = {}; + ref = Site.selectors.file; + for (key in ref) { + selector = ref[key]; + file[key] = $(selector, this.nodes.root); + } + file.thumbLink = (ref1 = file.thumb) != null ? ref1.parentNode : void 0; + if (!(file.text && file.link)) { return; } - if (!(link = $('.fileText > a, .fileText-original > a', fileRoot))) { + if (!Site.parseFile(this, file)) { return; } - if (!(info = (ref = link.nextSibling) != null ? ref.textContent.match(/\(([\d.]+ [KMG]?B).*\)/) : void 0)) { - return; - } - fileText = fileRoot.firstElementChild; - this.file = { - text: fileText, - link: link, - url: link.href, - name: fileText.title || link.title || link.textContent, - size: info[1], - isImage: /(jpg|png|gif)$/i.test(link.href), - isVideo: /webm$/i.test(link.href), - dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, - tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0, - MD5: fileText.dataset.md5 - }; - size = +this.file.size.match(/[\d.]+/)[0]; - unit = ['B', 'KB', 'MB', 'GB'].indexOf(this.file.size.match(/\w+$/)[0]); + $.extend(file, { + url: file.link.href, + isImage: /(jpg|png|gif)$/i.test(file.link.href), + isVideo: /(webm|mp4)$/i.test(file.link.href) + }); + size = +file.size.match(/[\d.]+/)[0]; + unit = ['B', 'KB', 'MB', 'GB'].indexOf(file.size.match(/\w+$/)[0]); while (unit-- > 0) { size *= 1024; } - this.file.sizeInBytes = size; - if ((thumb = $('a.fileThumb > [data-md5]', fileRoot))) { - $.extend(this.file, { - thumb: thumb, - thumbLink: thumb.parentNode, - thumbURL: thumb.src, - MD5: thumb.dataset.md5, - isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') - }); - if (this.file.isSpoiler) { - return this.file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.board + "/" + m[0] + "s.jpg" : void 0; - } - } + file.sizeInBytes = size; + return this.file = file; }; Post.deadMark = $.el('span', { @@ -6665,7 +6698,7 @@ Post = (function() { _Class.prototype.isClone = true; function _Class(origin, context, contractThumb) { - var base, fileRoot, i, inline, inlined, j, k, key, l, len, len1, len2, len3, node, nodes, ref, ref1, ref2, ref3, ref4, ref5, root, val; + var base, i, inline, inlined, j, k, key, l, len, len1, len2, len3, node, nodes, ref, ref1, ref2, ref3, ref4, ref5, ref6, root, selector, val; this.origin = origin; this.context = context; ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']; @@ -6675,13 +6708,13 @@ Post = (function() { } nodes = this.origin.nodes; root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true); - (base = Post.Clone).prefix || (base.prefix = 0); + (base = Post.Clone).suffix || (base.suffix = 0); ref1 = [root].concat(slice.call($$('[id]', root))); for (j = 0, len1 = ref1.length; j < len1; j++) { node = ref1[j]; - node.id = Post.Clone.prefix + node.id; + node.id += "_" + Post.Clone.suffix; } - Post.Clone.prefix++; + Post.Clone.suffix++; ref2 = $$('.inline', root); for (k = 0, len2 = ref2.length; k < len2; k++) { inline = ref2[k]; @@ -6711,12 +6744,15 @@ Post = (function() { val = ref4[key]; this.file[key] = val; } - fileRoot = this.nodes.fileRoot; - this.file.text = fileRoot.firstElementChild; - this.file.link = $('.fileText > a, .fileText-original', fileRoot); - this.file.thumb = $('a.fileThumb > [data-md5]', fileRoot); - this.file.thumbLink = (ref5 = this.file.thumb) != null ? ref5.parentNode : void 0; - this.file.fullImage = $('.full-image', fileRoot); + ref5 = Site.selectors.file; + for (key in ref5) { + selector = ref5[key]; + this.file[key] = $(selector, this.nodes.root); + } + this.file.thumbLink = (ref6 = this.file.thumb) != null ? ref6.parentNode : void 0; + if (this.file.thumbLink) { + this.file.fullImage = $('.full-image', this.file.thumbLink); + } this.file.videoControls = $('.video-controls', this.file.text); if (this.file.videoThumb) { this.file.thumb.muted = true; @@ -7104,6 +7140,348 @@ Thread = (function() { }).call(this); +SW = {}; + +(function() { + var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + SW.tinyboard = { + isOPContainerThread: true, + disabledFeatures: ['Board Configuration', 'Normalize URL', 'Captcha Configuration', 'Image Host Rewriting', 'Index Generator', 'Announcement Hiding', 'Fourchan thingies', 'Resurrect Quotes', 'Quick Reply Personas', 'Quick Reply', 'Cooldown', 'Pass Link', 'Index Generator (Menu)', 'Report Link', 'Delete Link', 'Edit Link', 'Archive Link', 'Quote Inlining', 'Quote Previewing', 'Quote Backlinks', 'File Info Formatting', 'Fappe Tyme', 'Image Expansion', 'Image Expansion (Menu)', 'Comment Expansion', 'Thread Expansion', 'Favicon', 'Unread', 'Quote Threading', 'Thread Stats', 'Thread Updater', 'Mark New IPs', 'Banner', 'Flash Features', 'Reply Pruning'], + detect: function() { + var i, len, ref, script; + ref = $$('script:not([src])', d.head); + for (i = 0, len = ref.length; i < len; i++) { + script = ref[i]; + if (/\bvar configRoot=".*?"/.test(script.textContent)) { + return true; + } + } + return false; + }, + selectors: { + board: 'form[name="postcontrols"]', + thread: 'div[id^="thread_"]', + postContainer: '.reply', + infoRoot: '.intro', + info: { + subject: '.subject', + name: '.name', + email: '.email', + tripcode: '.trip', + uniqueID: '.poster_id', + capcode: '.capcode', + flag: '.flag', + date: 'time', + nameBlock: 'label', + quote: 'a[href*="#q"]', + reply: 'a[href*="/res/"]:not([href*="#"])' + }, + icons: { + isSticky: '.fa-thumb-tack', + isClosed: '.fa-lock' + }, + file: { + text: '.fileinfo', + link: '.fileinfo > a', + thumb: 'a > .post-image' + }, + comment: '.body', + spoiler: '.spoiler', + quotelink: 'a[onclick^="highlightReply("]', + boardList: '.boardlist' + }, + xpath: { + thread: 'div[starts-with(@id,"thread_")]', + postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]' + }, + regexp: { + quotelink: /\/([^\/]+)\/res\/(\d+)\.html#(\d+)$/ + }, + bgColoredEl: function() { + return $.el('div', { + className: 'post reply' + }); + }, + parseNodes: function(post, nodes) { + var m, nextSibling, uniqueID; + if (nodes.uniqueID) { + return; + } + nodes.info.normalize(); + nextSibling = nodes.nameBlock.nextSibling; + if (nextSibling.nodeType === 3 && (m = nextSibling.textContent.match(/(\s*ID:\s*)(\S+)/))) { + nextSibling = nextSibling.splitText(m[1].length); + nextSibling.splitText(m[2].length); + nodes.uniqueID = uniqueID = $.el('span', { + className: 'poster_id' + }); + $.replace(nextSibling, uniqueID); + return $.add(uniqueID, nextSibling); + } + }, + parseFile: function(post, file) { + var info, infoNode, link, nameNode, ref, ref1, text, thumb; + text = file.text, link = file.link, thumb = file.thumb; + if ($.x("ancestor::" + Site.xpath.postContainer + "[1]", text) !== post.nodes.root) { + return false; + } + if (!(infoNode = indexOf.call((ref = link.nextSibling) != null ? ref.textContent : void 0, '(') >= 0 ? link.nextSibling : link.nextElementSibling)) { + return false; + } + if (!(info = infoNode.textContent.match(/\((Spoiler Image, )?([\d.]+ [KMG]?B).*\)/))) { + return false; + } + nameNode = $('.postfilename', text); + $.extend(file, { + name: nameNode ? nameNode.title || nameNode.textContent : link.pathname.match(/[^\/]*$/)[0], + size: info[2], + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0 + }); + if (thumb) { + $.extend(file, { + thumbURL: /\/static\//.test(thumb.src) && /\.(?:gif|jpe?g|png)$/.test(link.href) ? link.href : thumb.src, + isSpoiler: !!info[1] || link.textContent === 'Spoiler Image' + }); + } + return true; + }, + isThumbExpanded: function(file) { + return $.hasClass(file.thumb.parentNode, 'expanded'); + } + }; + +}).call(this); + +(function() { + var slice = [].slice; + + SW.yotsuba = { + isOPContainerThread: false, + selectors: { + board: '.board', + thread: '.thread', + postContainer: '.postContainer', + sideArrows: '.sideArrows', + post: '.post', + infoRoot: '.postInfo', + info: { + subject: '.subject', + name: '.name', + email: '.useremail', + tripcode: '.postertrip', + uniqueIDRoot: '.posteruid', + uniqueID: '.posteruid > .hand', + capcode: '.capcode.hand', + pass: '.n-pu', + flag: '.flag, .countryFlag', + date: '.dateTime', + nameBlock: '.nameBlock', + quote: '.postNum > a:nth-of-type(2)', + reply: '.replylink' + }, + icons: { + isSticky: '.stickyIcon', + isClosed: '.closedIcon', + isArchived: '.archivedIcon' + }, + file: { + text: '.file > :first-child', + link: '.fileText > a', + thumb: 'a.fileThumb > [data-md5]' + }, + comment: '.postMessage', + spoiler: 's', + quotelink: ':not(pre) > .quotelink', + boardList: '#boardNavDesktop > .boardList' + }, + xpath: { + thread: 'div[contains(concat(" ",@class," ")," thread ")]', + postContainer: 'div[contains(@class,"postContainer")]' + }, + regexp: { + quotelink: /^https?:\/\/boards\.4chan\.org\/+([^\/]+)\/+thread\/+(\d+)(?:[\/?][^#]*)?(?:#p(\d+))?$/ + }, + bgColoredEl: function() { + return $.el('div', { + className: 'reply' + }); + }, + isThisPageLegit: function() { + var ref; + return location.hostname === 'boards.4chan.org' && !$('link[href*="favicon-status.ico"]', d.head) && ((ref = d.title) !== '4chan - Temporarily Offline' && ref !== '4chan - Error' && ref !== '504 Gateway Time-out'); + }, + is404: function() { + var ref; + return ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found') || (g.VIEW === 'thread' && $('.board') && !$('.opContainer')); + }, + isIncomplete: function() { + var ref; + return ((ref = g.VIEW) === 'index' || ref === 'thread') && !$('.board + *'); + }, + isAuxiliaryPage: function() { + return location.hostname !== 'boards.4chan.org'; + }, + scriptData: function() { + var j, len, ref, script; + ref = $$('script:not([src])', d.head); + for (j = 0, len = ref.length; j < len; j++) { + script = ref[j]; + if (/\bcooldowns *=/.test(script.textContent)) { + return script.textContent; + } + } + return ''; + }, + parseThreadMetadata: function(thread) { + var file, m, scriptData; + scriptData = this.scriptData(); + thread.postLimit = /\bbumplimit *= *1\b/.test(scriptData); + thread.fileLimit = /\bimagelimit *= *1\b/.test(scriptData); + thread.ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : void 0; + if (g.BOARD.ID === 'f' && thread.OP.file) { + file = thread.OP.file; + return $.ajax(location.protocol + "//a.4cdn.org/f/thread/" + thread + ".json", { + timeout: $.MINUTE, + onloadend: function() { + if (this.response) { + return file.text.dataset.md5 = file.MD5 = this.response.posts[0].md5; + } + } + }); + } + }, + parseNodes: function(post, nodes) { + var icon, j, len, ref, results, type; + if (post.boardID === 'f') { + ref = ['Sticky', 'Closed']; + results = []; + for (j = 0, len = ref.length; j < len; j++) { + type = ref[j]; + if ((icon = $("img[alt=" + type + "]", nodes.info))) { + results.push($.addClass(icon, (type.toLowerCase()) + "Icon", 'retina')); + } + } + return results; + } + }, + parseFile: function(post, file) { + var info, link, m, ref, ref1, ref2, text, thumb; + text = file.text, link = file.link, thumb = file.thumb; + if (!(info = (ref = link.nextSibling) != null ? ref.textContent.match(/\(([\d.]+ [KMG]?B).*\)/) : void 0)) { + return false; + } + $.extend(file, { + name: text.title || link.title || link.textContent, + size: info[1], + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, + tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0, + MD5: text.dataset.md5 + }); + if (thumb) { + $.extend(file, { + thumbURL: thumb.src, + MD5: thumb.dataset.md5, + isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') + }); + if (file.isSpoiler) { + file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//" + (ImageHost.thumbHost()) + "/" + post.board + "/" + m[0] + "s.jpg" : void 0; + } + } + return true; + }, + cleanComment: function(bq) { + var abbr, br, i, j, k, len, node, ref; + if ((abbr = $('.abbr', bq))) { + ref = $$('.abbr + br, .exif', bq); + for (j = 0, len = ref.length; j < len; j++) { + node = ref[j]; + $.rm(node); + } + for (i = k = 0; k < 2; i = ++k) { + if ((br = abbr.previousSibling) && br.nodeName === 'BR') { + $.rm(br); + } + } + return $.rm(abbr); + } + }, + cleanCommentDisplay: function(bq) { + var b; + if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) { + $.rm(b); + } + return $.rm($('.fortune', bq)); + }, + insertTags: function(bq) { + var j, k, len, len1, node, ref, ref1; + ref = $$('s, .removed-spoiler', bq); + for (j = 0, len = ref.length; j < len; j++) { + node = ref[j]; + $.replace(node, [$.tn('[spoiler]')].concat(slice.call(node.childNodes), [$.tn('[/spoiler]')])); + } + ref1 = $$('.prettyprint', bq); + for (k = 0, len1 = ref1.length; k < len1; k++) { + node = ref1[k]; + $.replace(node, [$.tn('[code]')].concat(slice.call(node.childNodes), [$.tn('[/code]')])); + } + } + }; + +}).call(this); + +Site = (function() { + var Site; + + Site = { + init: function(cb) { + var hostname, i, len, line, ref, ref1, software, swDict; + swDict = {}; + ref = Conf['siteSoftware'].split('\n'); + for (i = 0, len = ref.length; i < len; i++) { + line = ref[i]; + if (!(line[0] !== '#')) { + continue; + } + ref1 = line.split(' '), hostname = ref1[0], software = ref1[1]; + if (software in SW) { + swDict[hostname] = software; + } + } + hostname = location.hostname; + while (hostname && !(hostname in swDict)) { + hostname = hostname.replace(/^[^.]*\.?/, ''); + } + if (hostname) { + this.set(hostname, swDict[hostname]); + return cb(); + } else { + return $.onExists(doc, 'body', (function(_this) { + return function() { + var base; + for (software in SW) { + if (typeof (base = SW[software]).detect === "function" ? base.detect() : void 0) { + _this.set(location.hostname.replace(/^www\./, ''), software); + Conf['siteSoftware'] += "\n" + _this.hostname + " " + _this.software; + $.set('siteSoftware', Conf['siteSoftware']); + cb(); + } + } + }; + })(this)); + } + }, + set: function(hostname1, software1) { + this.hostname = hostname1; + this.software = software1; + return $.extend(this, SW[this.software]); + } + }; + + return Site; + +}).call(this); + Redirect = (function() { var Redirect, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -7702,7 +8080,7 @@ Filter = (function() { open: function(post) { var value; value = Filter[type](post); - return (value != null) && !(g.BOARD.ID === 'f' && type === 'MD5'); + return value != null; } }; }, @@ -7760,7 +8138,7 @@ PostHiding = (function() { })); }, node: function() { - var data, sideArrows; + var button, data, sa, sideArrows; if (!this.isReply || this.isClone || this.isFetchedQuote) { return; } @@ -7779,9 +8157,14 @@ PostHiding = (function() { if (!Conf['Reply Hiding Buttons']) { return; } - sideArrows = $('.sideArrows', this.nodes.root); - $.replace(sideArrows.firstChild, PostHiding.makeButton(this, 'hide')); - return sideArrows.removeAttribute('class'); + button = PostHiding.makeButton(this, 'hide'); + if ((sa = Site.selectors.sideArrows)) { + sideArrows = $(sa, this.nodes.root); + $.replace(sideArrows.firstChild, button); + return sideArrows.removeAttribute('class'); + } else { + return $.prepend(this.nodes.root, button); + } }, menu: { init: function() { @@ -8146,7 +8529,7 @@ ThreadHiding = (function() { }, catalogSet: function(board) { var hiddenThreads, threadID; - if (!$.hasStorage) { + if (!($.hasStorage && Site.software === 'yotsuba')) { return; } hiddenThreads = ThreadHiding.db.get({ @@ -8159,7 +8542,7 @@ ThreadHiding = (function() { return localStorage.setItem("4chan-hide-t-" + board, JSON.stringify(hiddenThreads)); }, catalogWatch: function() { - if (!$.hasStorage) { + if (!($.hasStorage && Site.software === 'yotsuba')) { return; } this.hiddenThreads = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; @@ -8504,6 +8887,9 @@ BoardConfig = (function() { }, noAudio: function(boardID) { var boards; + if (Site.software !== 'yotsuba') { + return false; + } boards = this.boards || Conf['boardConfig'].boards; return boards && !boards[boardID].webm_audio; } @@ -8848,10 +9234,10 @@ Get = (function() { if (root == null) { return null; } - return g.threads[g.BOARD + "." + root.id.slice(1)]; + return g.threads[(root.dataset.board || g.BOARD.ID) + "." + (root.id.match(/\d*$/)[0])]; }, threadFromNode: function(node) { - return Get.threadFromRoot($.x('ancestor-or-self::div[contains(concat(" ",@class," ")," thread ")]', node)); + return Get.threadFromRoot($.x("ancestor-or-self::" + Site.xpath.thread, node)); }, postFromRoot: function(root) { var index, post; @@ -8867,18 +9253,17 @@ Get = (function() { } }, postFromNode: function(root) { - return Get.postFromRoot($.x('ancestor-or-self::div[contains(@class,"postContainer")][1]', root)); + return Get.postFromRoot($.x("ancestor-or-self::" + Site.xpath.postContainer + "[1]", root)); }, postDataFromLink: function(link) { - var boardID, path, postID, ref, threadID; - if (link.hostname === 'boards.4chan.org') { - path = link.pathname.split(/\/+/); - boardID = path[1]; - threadID = path[3]; - postID = link.hash ? link.hash.slice(2) : path[3]; - } else { + var boardID, match, postID, ref, ref1, threadID; + if (link.dataset.postID) { ref = link.dataset, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; threadID || (threadID = 0); + } else { + match = link.href.match(Site.regexp.quotelink); + ref1 = match.slice(1), boardID = ref1[0], threadID = ref1[1], postID = ref1[2]; + postID || (postID = threadID); } return { boardID: boardID, @@ -8919,17 +9304,6 @@ Get = (function() { ref1 = Get.postDataFromLink(quotelink), boardID = ref1.boardID, postID = ref1.postID; return boardID === post.board.ID && postID === post.ID; }); - }, - scriptData: function() { - var i, len, ref, script; - ref = $$('script:not([src])', d.head); - for (i = 0, len = ref.length; i < len; i++) { - script = ref[i]; - if (/\bcooldowns *=/.test(script.textContent)) { - return script.textContent; - } - } - return ''; } }; @@ -9024,6 +9398,7 @@ Header = (function() { }); $.on(window, 'load popstate', Header.hashScroll); $.on(d, 'CreateNotification', this.createNotification); + this.setBoardList(); $.onExists(doc, 'body', (function(_this) { return function() { if (!Main.isThisPageLegit()) { @@ -9034,7 +9409,7 @@ Header = (function() { return _this.setBarPosition(Conf['Bottom Header']); }; })(this)); - $.onExists(doc, '#boardNavMobile', Header.setBoardList); + $.onExists(doc, Site.selectors.boardList + " + *", Header.generateFullBoardList); Main.ready(function() { var a, absbot, footer; if (!(footer = $.id('boardNavDesktopFoot'))) { @@ -9088,7 +9463,7 @@ Header = (function() { id: 'scroll-marker' }), setBoardList: function() { - var a, boardList, btn, chr, fullBoardList, i, j, len, len1, node, nodes, ref, ref1, spacer, span; + var boardList, btn; Header.boardList = boardList = $.el('span', { id: 'board-list' }); @@ -9097,20 +9472,28 @@ Header = (function() { }); btn = $('.hide-board-list-button', boardList); $.on(btn, 'click', Header.toggleBoardList); + $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); + Header.setCustomNav(Conf['Custom Board Navigation']); + Header.generateBoardList(Conf['boardnav']); + $.sync('Custom Board Navigation', Header.setCustomNav); + return $.sync('boardnav', Header.generateBoardList); + }, + generateFullBoardList: function() { + var a, chr, fullBoardList, i, items, j, len, node, nodes, ref, spacer, span; nodes = []; spacer = function() { return $.el('span', { className: 'spacer' }); }; - ref = $('#boardNavDesktop > .boardList').childNodes; - for (i = 0, len = ref.length; i < len; i++) { - node = ref[i]; + items = $.X('.//a|.//text()[not(ancestor::a)]', $(Site.selectors.boardList)); + i = 0; + while (node = items.snapshotItem(i++)) { switch (node.nodeName) { case '#text': - ref1 = node.nodeValue; - for (j = 0, len1 = ref1.length; j < len1; j++) { - chr = ref1[j]; + ref = node.nodeValue; + for (j = 0, len = ref.length; j < len; j++) { + chr = ref[j]; span = $.el('span', { textContent: chr }); @@ -9134,14 +9517,9 @@ Header = (function() { nodes.push(a); } } - fullBoardList = $('.boardList', boardList); + fullBoardList = $('.boardList', Header.boardList); $.add(fullBoardList, nodes); - CatalogLinks.setLinks(fullBoardList); - $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); - Header.setCustomNav(Conf['Custom Board Navigation']); - Header.generateBoardList(Conf['boardnav']); - $.sync('Custom Board Navigation', Header.setCustomNav); - return $.sync('boardnav', Header.generateBoardList); + return CatalogLinks.setLinks(fullBoardList); }, generateBoardList: function(boardnav) { var as, list, nodes, re, t; @@ -9154,11 +9532,11 @@ Header = (function() { as = $$('#full-board-list a[title]', Header.boardList); re = /[\w@]+(-(all|title|replace|full|index|catalog|archive|expired|(mode|sort|text):"[^"]+"(,"[^"]+")?))*|[^\w@]+/g; nodes = (function() { - var i, len, ref, results; + var j, len, ref, results; ref = boardnav.match(re); results = []; - for (i = 0, len = ref.length; i < len; i++) { - t = ref[i]; + for (j = 0, len = ref.length; j < len; j++) { + t = ref[j]; results.push(Header.mapCustomNavigation(t, as)); } return results; @@ -9202,10 +9580,22 @@ Header = (function() { } boardID = t.split('-')[0]; if (boardID === 'current') { - boardID = g.BOARD.ID; + if (location.hostname === 'boards.4chan.org') { + boardID = g.BOARD.ID; + } else { + a = $.el('a', { + href: "/" + g.BOARD.ID + "/", + textContent: text || g.BOARD.ID, + className: 'current' + }); + if (/-(catalog|archive|expired)/.test(t)) { + a = a.firstChild; + } + return a; + } } a = (function() { - var i, len, ref; + var j, len, ref; if (boardID === '@') { return $.el('a', { href: 'https://twitter.com/4chan', @@ -9213,25 +9603,25 @@ Header = (function() { textContent: '@' }); } - for (i = 0, len = as.length; i < len; i++) { - a = as[i]; + for (j = 0, len = as.length; j < len; j++) { + a = as[j]; if (a.textContent === boardID) { return a.cloneNode(true); } } a = $.el('a', { - href: "/" + boardID + "/", + href: "//boards.4chan.org/" + boardID + "/", textContent: boardID }); if ((ref = g.VIEW) === 'catalog' || ref === 'archive') { a.href += g.VIEW; } - if (boardID === g.BOARD.ID) { + if (a.hostname === location.hostname && boardID === g.BOARD.ID) { a.className = 'current'; } return a; })(); - a.textContent = /-title/.test(t) || /-replace/.test(t) && boardID === g.BOARD.ID ? a.title || a.textContent : /-full/.test(t) ? ("/" + boardID + "/") + (a.title ? " - " + a.title : '') : text || boardID; + a.textContent = /-title/.test(t) || /-replace/.test(t) && a.hostname === location.hostname && boardID === g.BOARD.ID ? a.title || a.textContent : /-full/.test(t) ? ("/" + boardID + "/") + (a.title ? " - " + a.title : '') : text || boardID; if (m = t.match(/-(index|catalog)/)) { if (!(boardID === 'f' && m[1] === 'catalog')) { a.dataset.only = m[1]; @@ -9260,7 +9650,7 @@ Header = (function() { } if (/-expired/.test(t)) { if (boardID !== 'b' && boardID !== 'f' && boardID !== 'trash' && boardID !== 'bant') { - a.href = "/" + boardID + "/archive"; + a.href = "//boards.4chan.org/" + boardID + "/archive"; } else { return a.firstChild; } @@ -9509,7 +9899,7 @@ Header = (function() { } }, addShortcut: function(id, el, index) { - var i, item, len, ref, shortcut; + var item, j, len, ref, shortcut; shortcut = $.el('span', { id: "shortcut-" + id, className: 'shortcut brackets-wrap' @@ -9517,8 +9907,8 @@ Header = (function() { $.add(shortcut, el); shortcut.dataset.index = index; ref = $$('[data-index]', Header.shortcuts); - for (i = 0, len = ref.length; i < len; i++) { - item = ref[i]; + for (j = 0, len = ref.length; j < len; j++) { + item = ref[j]; if (!(+item.dataset.index > +index)) { continue; } @@ -11159,7 +11549,7 @@ Settings = (function() { if ($.engine !== 'gecko') { $('div[data-name="Remember QR Size"]', section).hidden = true; } - if ($.perProtocolSettings) { + if ($.perProtocolSettings || location.protocol !== 'https:') { $('div[data-name="Redirect to HTTPS"]', section).hidden = true; } $.get(items, function(items) { @@ -12112,7 +12502,7 @@ UI = (function() { $.on(d, 'click CloseMenu', this.close); $.on(d, 'scroll', this.setPosition); $.on(window, 'resize', this.setPosition); - $.add(button, menu); + $.after(button, menu); this.setPosition(); entry = $('.entry', menu); this.focus(entry); @@ -12578,7 +12968,7 @@ Gallery = (function() { Gallery = { init: function() { var el, ref; - if (!(this.enabled = Conf['Gallery'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(this.enabled = Conf['Gallery'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } this.delay = Conf['Slide Delay']; @@ -12609,7 +12999,7 @@ Gallery = (function() { } }, build: function(image) { - var candidate, cb, dialog, entry, file, i, j, key, len, len1, menuButton, nodes, post, ref, ref1, ref2, ref3, thumb, value; + var candidate, cb, dialog, entry, i, j, key, len, len1, menuButton, nodes, post, postThumb, ref, ref1, ref2, ref3, thumb, value; cb = Gallery.cb; if (Conf['Fullscreen Gallery']) { $.one(d, 'fullscreenchange mozfullscreenchange webkitfullscreenchange', function() { @@ -12673,10 +13063,10 @@ Gallery = (function() { $.off(d, 'keydown', Keybinds.keydown); } $.on(window, 'resize', Gallery.cb.setHeight); - ref2 = $$('.post .file'); + ref2 = $$(Site.selectors.file.thumb); for (j = 0, len1 = ref2.length; j < len1; j++) { - file = ref2[j]; - post = Get.postFromNode(file); + postThumb = ref2[j]; + post = Get.postFromNode(postThumb); if (!((ref3 = post.file) != null ? ref3.thumb : void 0)) { continue; } @@ -12734,6 +13124,7 @@ Gallery = (function() { ext = thumb.href.match(/\w*$/); elType = { 'webm': 'video', + 'mp4': 'video', 'pdf': 'iframe' }[ext] || 'img'; file = $.el(elType); @@ -13222,7 +13613,7 @@ ImageExpand = (function() { ImageExpand = { init: function() { var ref; - if (!(this.enabled = Conf['Image Expansion'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(this.enabled = Conf['Image Expansion'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } this.EAI = $.el('a', { @@ -13721,7 +14112,7 @@ ImageHover = (function() { } file = post.file; isVideo = file.isVideo; - if (file.isExpanding || file.isExpanded) { + if (file.isExpanding || file.isExpanded || (typeof Site.isThumbExpanded === "function" ? Site.isThumbExpanded(file) : void 0)) { return; } error = ImageHover.error(post); @@ -13813,7 +14204,7 @@ ImageLoader = (function() { ImageLoader = { init: function() { var prefetch, ref, ref1; - if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && g.BOARD.ID !== 'f')) { + if ((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') { return; } if (!(Conf['Image Prefetching'] || Conf['Replace JPG'] || Conf['Replace PNG'] || Conf['Replace GIF'] || Conf['Replace WEBM'])) { @@ -13964,7 +14355,7 @@ Metadata = (function() { Metadata = { init: function() { var ref; - if (!(Conf['WEBM Metadata'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(Conf['WEBM Metadata'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } return Callbacks.Post.push({ @@ -15241,7 +15632,7 @@ Linkify = (function() { ref = $$('a', this.nodes.comment); for (j = 0, len = ref.length; j < len; j++) { link = ref[j]; - if (!(ImageHost.test(link.hostname))) { + if (!(ImageHost.test(link.hostname) || /\bnofollow\b/.test(link.rel))) { continue; } $.addClass(link, 'linkify'); @@ -15797,8 +16188,7 @@ ReportLink = (function() { order: 10, open: function(post) { ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; - if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) { - ReportLink.url += '&altc=1'; + if (d.cookie.indexOf('pass_enabled=1') >= 0) { ReportLink.dims = 'width=350,height=275'; } else { ReportLink.dims = 'width=400,height=550'; @@ -16046,7 +16436,7 @@ CatalogLinks = (function() { CatalogLinks = { init: function() { var el, input, selector; - if ((Conf['External Catalog'] || Conf['JSON Index']) && !(Conf['JSON Index'] && g.VIEW === 'index')) { + if (Site.software === 'yotsuba' && (Conf['External Catalog'] || Conf['JSON Index']) && !(Conf['JSON Index'] && g.VIEW === 'index')) { selector = (function() { switch (g.VIEW) { case 'thread': @@ -16082,7 +16472,7 @@ CatalogLinks = (function() { } }); } - if (Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { + if (Site.software === 'yotsuba' && Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { Callbacks.Post.push({ name: 'Catalog Link Rewrite', cb: this.node @@ -16133,7 +16523,7 @@ CatalogLinks = (function() { if (((ref2 = a.hostname) !== 'boards.4chan.org' && ref2 !== 'catalog.neet.tv') || !(board = a.pathname.split('/')[1]) || (board === 'f' || board === 'status' || board === '4chan') || a.pathname.split('/')[2] === 'archive' || $.hasClass(a, 'external')) { continue; } - a.href = Conf['Header catalog links'] ? CatalogLinks.catalog(board) : "/" + board + "/"; + a.href = Conf['Header catalog links'] ? CatalogLinks.catalog(board) : "//boards.4chan.org/" + board + "/"; if (a.dataset.indexOptions && a.hostname === 'boards.4chan.org' && a.pathname.split('/')[2] === '') { a.href += (a.hash ? '/' : '#') + a.dataset.indexOptions; } @@ -16146,13 +16536,13 @@ CatalogLinks = (function() { if (Conf['External Catalog'] && (board === 'a' || board === 'c' || board === 'g' || board === 'biz' || board === 'k' || board === 'm' || board === 'o' || board === 'p' || board === 'v' || board === 'vg' || board === 'vr' || board === 'w' || board === 'wg' || board === 'cm' || board === '3' || board === 'adv' || board === 'an' || board === 'asp' || board === 'cgl' || board === 'ck' || board === 'co' || board === 'diy' || board === 'fa' || board === 'fit' || board === 'gd' || board === 'int' || board === 'jp' || board === 'lit' || board === 'mlp' || board === 'mu' || board === 'n' || board === 'out' || board === 'po' || board === 'sci' || board === 'sp' || board === 'tg' || board === 'toy' || board === 'trv' || board === 'tv' || board === 'vp' || board === 'wsg' || board === 'x' || board === 'f' || board === 'pol' || board === 's4s' || board === 'lgbt')) { return "//catalog.neet.tv/" + board + "/"; } else if (Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { - if (g.BOARD.ID === board && g.VIEW === 'index') { + if (location.hostname === 'boards.4chan.org' && g.BOARD.ID === board && g.VIEW === 'index') { return '#catalog'; } else { - return "/" + board + "/#catalog"; + return "//boards.4chan.org/" + board + "/#catalog"; } } else { - return "/" + board + "/catalog"; + return "//boards.4chan.org/" + board + "/catalog"; } }, index: function(board) { @@ -16160,13 +16550,13 @@ CatalogLinks = (function() { board = g.BOARD.ID; } if (Conf['JSON Index'] && board !== 'f') { - if (g.BOARD.ID === board && g.VIEW === 'index') { + if (location.hostname === 'boards.4chan.org' && g.BOARD.ID === board && g.VIEW === 'index') { return '#index'; } else { - return "/" + board + "/#index"; + return "//boards.4chan.org/" + board + "/#index"; } } else { - return "/" + board + "/"; + return "//boards.4chan.org/" + board + "/"; } } }; @@ -17706,7 +18096,7 @@ RelativeDates = (function() { INTERVAL: $.MINUTE / 2, init: function() { var ref; - if (((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || g.VIEW === 'index' && Conf['JSON Index'] && g.BOARD.ID !== 'f') { + if (((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || Index.enabled) { this.flush(); $.on(d, 'visibilitychange ThreadUpdate', this.flush); } @@ -17848,7 +18238,7 @@ RemoveSpoilers = (function() { }, unspoiler: function(el) { var i, len, span, spoiler, spoilers; - spoilers = $$('s', el); + spoilers = $$('s, .spoiler', el); for (i = 0, len = spoilers.length; i < len; i++) { spoiler = spoilers[i]; span = $.el('span', { @@ -19068,11 +19458,14 @@ ThreadWatcher = (function() { this.refreshButton = $('.refresh', this.dialog); this.closeButton = $('.move > .close', this.dialog); this.unreaddb = Unread.db || new DataBoard('lastReadPosts'); - this.unreadEnabled = Conf['Remember Last Read Post']; + this.unreadEnabled = Conf['Remember Last Read Post'] && Site.software === 'yotsuba'; $.on(d, 'QRPostSuccessful', this.cb.post); $.on(sc, 'click', this.toggleWatcher); $.on(this.refreshButton, 'click', this.buttonFetchAll); $.on(this.closeButton, 'click', this.toggleWatcher); + if (Site.software !== 'yotsuba') { + this.refreshButton.hidden = true; + } this.menu.addHeaderMenuEntry(); $.onExists(doc, 'body', this.addDialog); switch (g.VIEW) { @@ -19091,7 +19484,7 @@ ThreadWatcher = (function() { } Header.addShortcut('watcher', sc, 510); ThreadWatcher.fetchAuto(); - if (g.VIEW === 'index' && Conf['JSON Index'] && Conf['Menu'] && g.BOARD.ID !== 'f') { + if (Conf['Menu'] && Index.enabled) { Menu.menu.addEntry({ el: $.el('a', { href: 'javascript:;', @@ -19338,6 +19731,9 @@ ThreadWatcher = (function() { }, fetchAuto: function() { var db, interval, now, ref; + if (Site.software !== 'yotsuba') { + return; + } clearTimeout(ThreadWatcher.timeout); if (!Conf['Auto Update Thread Watcher']) { return; @@ -19347,8 +19743,7 @@ ThreadWatcher = (function() { now = Date.now(); if (!((now - interval < (ref = db.data.lastChecked || 0) && ref <= now))) { ThreadWatcher.fetchAllStatus(); - db.data.lastChecked = now; - db.save(); + db.setLastChecked(); } return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval); }, @@ -19360,22 +19755,35 @@ ThreadWatcher = (function() { } }, fetchAllStatus: function() { - var i, len, ref, thread, threads; - ThreadWatcher.db.forceSync(); - ThreadWatcher.unreaddb.forceSync(); - if ((ref = QuoteYou.db) != null) { - ref.forceSync(); - } - if (!(threads = ThreadWatcher.getAll()).length) { + var db, dbs, i, len, n, results; + if (Site.software !== 'yotsuba') { return; } - for (i = 0, len = threads.length; i < len; i++) { - thread = threads[i]; - ThreadWatcher.fetchStatus(thread); + dbs = [ThreadWatcher.db, ThreadWatcher.unreaddb, QuoteYou.db].filter(function(x) { + return x; + }); + n = 0; + results = []; + for (i = 0, len = dbs.length; i < len; i++) { + db = dbs[i]; + results.push(db.forceSync(function() { + var j, len1, thread, threads; + if ((++n) === dbs.length) { + threads = ThreadWatcher.getAll(); + for (j = 0, len1 = threads.length; j < len1; j++) { + thread = threads[j]; + ThreadWatcher.fetchStatus(thread); + } + } + })); } + return results; }, fetchStatus: function(thread, force) { var boardID, data, req, threadID; + if (Site.software !== 'yotsuba') { + return; + } boardID = thread.boardID, threadID = thread.threadID, data = thread.data; if (data.isDead && !force) { return; @@ -20438,305 +20846,12 @@ Captcha = {}; }).call(this); -(function() { - Captcha.noscript = { - lifetime: 30 * $.MINUTE, - init: function() { - var container, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript'))) { - return; - } - container = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - container: container, - input: input - }; - $.on(input, 'blur', QR.focusout); - $.on(input, 'focus', QR.focusin); - $.on(input, 'keydown', this.keydown.bind(this)); - $.on(input, 'input', function() { - if (!Captcha.cache.getCount()) { - return QR.posts[0].preventAutoPost(); - } - }); - $.on(this.nodes.container, 'click', (function(_this) { - return function() { - _this.reload(); - return _this.nodes.input.focus(); - }; - })(this)); - this.conn = new Connection(null, 'https://www.google.com', { - challenge: this.load.bind(this), - token: this.save.bind(this), - error: this.error.bind(this) - }); - $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v1', 'noscript-captcha'); - $.after(QR.nodes.com.parentNode, [container, input]); - Captcha.cache.init(); - $.on(d, 'CaptchaCount', this.count.bind(this)); - this.beforeSetup(); - return this.setup(); - }, - initFrame: function() { - var cb, conn, img, ref, ref1; - conn = new Connection(window.parent, 'https://boards.4chan.org', { - response: function(response) { - $.id('recaptcha_response_field').value = response; - return HTMLFormElement.prototype.submit.call($('form')); - } - }); - if (location.hash === '#response') { - conn.send({ - token: (ref = $('textarea')) != null ? ref.value : void 0, - error: (ref1 = $('.recaptcha_input_area')) != null ? ref1.textContent.replace(/:$/, '') : void 0 - }); - } - if (!(img = $('img'))) { - return; - } - $('form').action = '#response'; - cb = function() { - var canvas; - canvas = $.el('canvas'); - canvas.width = img.width; - canvas.height = img.height; - canvas.getContext('2d').drawImage(img, 0, 0); - return conn.send({ - challenge: canvas.toDataURL() - }); - }; - if (img.complete) { - return cb(); - } else { - return $.on(img, 'load', cb); - } - }, - timers: {}, - iframeURL: function() { - var lang, url; - url = 'https://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc'; - if (lang = Conf['captchaLanguage'].trim()) { - url += "&hl=" + (encodeURIComponent(lang)); - } - return url; - }, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = true; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - moreNeeded: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (force || Captcha.cache.needed()))) { - return; - } - if (!this.nodes.iframe) { - this.nodes.iframe = $.el('iframe', { - id: 'qr-captcha-iframe', - src: this.iframeURL() - }); - $.add(QR.nodes.el, this.nodes.iframe); - this.conn.target = this.nodes.iframe; - } else if (!this.occupied || force) { - this.nodes.iframe.src = this.iframeURL(); - } - this.occupied = true; - if (focus) { - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = false; - input.placeholder = 'Verification'; - this.count(); - $.off(input, 'focus click', this.cb.focus); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.isEnabled) { - return; - } - $.rm(this.nodes.img); - delete this.nodes.img; - $.rm(this.nodes.iframe); - delete this.nodes.iframe; - delete this.occupied; - return this.beforeSetup(); - }, - getOne: function(isReply) { - var captcha; - if ((captcha = Captcha.cache.getOne(isReply))) { - return captcha; - } else if (/\S/.test(this.nodes.input.value)) { - return (function(_this) { - return function(cb) { - _this.submitCB = cb; - return _this.sendResponse(); - }; - })(this); - } else { - return null; - } - }, - sendResponse: function() { - var response; - response = this.nodes.input.value; - if (/\S/.test(response)) { - return this.conn.send({ - response: response - }); - } - }, - save: function(token) { - var captcha; - delete this.occupied; - this.nodes.input.value = ''; - captcha = { - challenge: token, - response: 'manual_challenge', - timeout: this.timeout - }; - if (this.submitCB) { - this.submitCB(captcha); - delete this.submitCB; - if (Captcha.cache.needed()) { - return this.reload(); - } else { - return this.destroy(); - } - } else { - Captcha.cache.save(captcha); - return this.reload(); - } - }, - error: function(message) { - this.occupied = true; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(); - delete this.submitCB; - } - return QR.error("Captcha Error: " + message); - }, - load: function(src) { - var container, img, input, ref; - ref = this.nodes, container = ref.container, input = ref.input, img = ref.img; - this.occupied = true; - this.timeout = Date.now() + this.lifetime; - if (!img) { - img = this.nodes.img = new Image(); - $.one(img, 'load', this.afterSetup.bind(this)); - $.on(img, 'load', function() { - return this.hidden = false; - }); - $.add(container, img); - } - img.src = src; - input.value = ''; - clearTimeout(this.timers.expire); - return this.timers.expire = setTimeout(this.expire.bind(this), this.lifetime); - }, - count: function() { - var count, placeholder; - count = Captcha.cache.getCount(); - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - return this.nodes.input.alt = count; - }, - expire: function() { - if (!this.nodes.iframe) { - return; - } - if (!d.hidden && (Captcha.cache.needed() || d.activeElement === this.nodes.input)) { - return this.reload(); - } else { - return this.destroy(); - } - }, - reload: function() { - var ref; - this.nodes.iframe.src = this.iframeURL(); - this.occupied = true; - return (ref = this.nodes.img) != null ? ref.hidden = true : void 0; - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - if (this.nodes.iframe) { - this.reload(); - } else { - this.setup(); - } - } else if (e.keyCode === 13 && e.shiftKey) { - this.sendResponse(); - } else { - return; - } - return e.preventDefault(); - } - }; - -}).call(this); - (function() { Captcha.replace = { init: function() { if (!(d.cookie.indexOf('pass_enabled=1') < 0)) { return; } - if (location.hostname === 'sys.4chan.org' && /[?&]altc\b/.test(location.search) && Main.jsEnabled) { - $.onExists(doc, 'script[src="//www.google.com/recaptcha/api/js/recaptcha_ajax.js"]', function() { - $.global(function() { - return window.el.onload = null; - }); - return Captcha.v1.create(); - }); - return; - } - if (location.hostname === 'sys.4chan.org' && Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) { - $.ready(Captcha.replace.v1); - return; - } if (Conf['Force Noscript Captcha'] && Main.jsEnabled) { $.ready(Captcha.replace.noscript); return; @@ -20771,21 +20886,6 @@ Captcha = {}; return insert(); } }, - v1: function() { - var container, old, script; - if (!(old = $.id('g-recaptcha'))) { - return; - } - script = $.el('script', { - src: '//www.google.com/recaptcha/api/js/recaptcha_ajax.js' - }); - $.add(d.head, script); - container = $.el('div', { - id: 'captchaContainerAlt' - }); - $.replace(old, container); - return Captcha.v1.create(); - }, iframe: function(iframe) { var lang, src; if ((lang = Conf['captchaLanguage'].trim())) { @@ -20824,286 +20924,6 @@ Captcha = {}; }).call(this); -(function() { - Captcha.v1 = { - blank: "data:image/svg+xml,", - init: function() { - var container, imgContainer, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript'))) { - return; - } - imgContainer = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' - }); - $.extend(imgContainer, { - innerHTML: "" - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - img: imgContainer.firstChild, - input: input - }; - $.on(input, 'blur', QR.focusout); - $.on(input, 'focus', QR.focusin); - $.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha)); - $.on(input, 'input', function() { - if (!Captcha.cache.getCount()) { - return QR.posts[0].preventAutoPost(); - } - }); - $.on(this.nodes.img.parentNode, 'click', QR.captcha.reload.bind(QR.captcha)); - $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v1'); - $.after(QR.nodes.com.parentNode, [imgContainer, input]); - Captcha.cache.init(); - $.on(d, 'CaptchaCount', this.count.bind(this)); - this.script = $.el('script', { - src: '//www.google.com/recaptcha/api/js/recaptcha_ajax.js' - }); - $.add(d.head, this.script); - container = $.el('div', { - id: 'captchaContainerAlt', - hidden: true - }); - $.add(d.body, container); - this.beforeSetup(); - if (Conf['Auto-load captcha']) { - this.setup(); - } - new MutationObserver(this.afterSetup).observe(container, { - childList: true - }); - return this.afterSetup(); - }, - create: function() { - var cont, lang; - cont = $.id('captchaContainerAlt'); - if (this.occupied) { - return; - } - this.occupied = true; - if ((lang = Conf['captchaLanguage'].trim())) { - cont.dataset.lang = lang; - } - $.onExists(cont, '#recaptcha_image', function(image) { - return $.on(image, 'click', function() { - if ($.id('recaptcha_challenge_image')) { - return $.global(function() { - return window.Recaptcha.reload(); - }); - } - }); - }); - $.onExists(cont, '#recaptcha_response_field', function(field) { - $.on(field, 'keydown', function(e) { - if (e.keyCode === 8 && !field.value) { - return $.global(function() { - return window.Recaptcha.reload(); - }); - } - }); - if (location.hostname === 'sys.4chan.org') { - return field.focus(); - } - }); - return $.global(function() { - var container, options, script; - container = document.getElementById('captchaContainerAlt'); - options = { - theme: 'clean', - lang: container.dataset.lang - }; - if (window.Recaptcha) { - return window.Recaptcha.create('6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', container, options); - } else { - script = document.head.querySelector('script[src="//www.google.com/recaptcha/api/js/recaptcha_ajax.js"]'); - return script.addEventListener('load', function() { - return window.Recaptcha.create('6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', container, options); - }, false); - } - }); - }, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var img, input, ref; - ref = this.nodes, img = ref.img, input = ref.input; - img.parentNode.hidden = true; - img.src = this.blank; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - moreNeeded: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (force || Captcha.cache.needed()))) { - return; - } - this.create(); - if (focus) { - $.addClass(QR.nodes.el, 'focus'); - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var challenge, img, input, ref, setLifetime; - if (!(challenge = $.id('recaptcha_challenge_field_holder'))) { - return; - } - if (challenge === QR.captcha.nodes.challenge) { - return; - } - setLifetime = function(e) { - return QR.captcha.lifetime = e.detail; - }; - $.on(window, 'captcha:timeout', setLifetime); - $.global(function() { - return window.dispatchEvent(new CustomEvent('captcha:timeout', { - detail: window.RecaptchaState.timeout - })); - }); - $.off(window, 'captcha:timeout', setLifetime); - ref = QR.captcha.nodes, img = ref.img, input = ref.input; - img.parentNode.hidden = false; - input.placeholder = 'Verification'; - QR.captcha.count(); - $.off(input, 'focus click', QR.captcha.cb.focus); - QR.captcha.nodes.challenge = challenge; - new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, { - childList: true, - subtree: true, - attributes: true - }); - QR.captcha.load(); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.script) { - return; - } - $.global(function() { - return window.Recaptcha.destroy(); - }); - delete this.occupied; - if (this.nodes) { - return this.beforeSetup(); - } - }, - getOne: function(isReply) { - var captcha, challenge, response, timeout; - if ((captcha = Captcha.cache.getOne(isReply))) { - return captcha; - } else { - challenge = this.nodes.img.alt; - timeout = this.timeout; - if (/\S/.test(response = this.nodes.input.value)) { - this.destroy(); - return { - challenge: challenge, - response: response, - timeout: timeout - }; - } else { - return null; - } - } - }, - save: function() { - var response; - if (!/\S/.test(response = this.nodes.input.value)) { - return; - } - this.nodes.input.value = ''; - Captcha.cache.save({ - challenge: this.nodes.img.alt, - response: response, - timeout: this.timeout - }); - this.destroy(); - return this.setup(false, true); - }, - load: function() { - var challenge, challenge_image; - if ($('#captchaContainerAlt[class~="recaptcha_is_showing_audio"]')) { - this.nodes.img.src = this.blank; - return; - } - if (!this.nodes.challenge.firstChild) { - return; - } - if (!(challenge_image = $.id('recaptcha_challenge_image'))) { - return; - } - this.timeout = Date.now() + this.lifetime * $.SECOND - $.MINUTE; - challenge = this.nodes.challenge.firstChild.value; - this.nodes.img.alt = challenge; - this.nodes.img.src = challenge_image.src; - return this.nodes.input.value = ''; - }, - count: function() { - var count, placeholder; - count = Captcha.cache.getCount(); - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - return this.nodes.input.alt = count; - }, - reload: function(focus) { - $.global(function() { - if (window.Recaptcha.type === 'image') { - window.Recaptcha.reload(); - } else { - window.Recaptcha.switch_type('image'); - } - return window.Recaptcha.should_focus = false; - }); - if (focus) { - return this.nodes.input.focus(); - } - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - this.reload(); - } else if (e.keyCode === 13 && e.shiftKey) { - this.save(); - } else { - return; - } - return e.preventDefault(); - } - }; - -}).call(this); - (function() { var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -21474,7 +21294,7 @@ QR = (function() { 'video/webm': 'webm' }, init: function() { - var info, noscript, sc, version; + var sc; if (!Conf['Quick Reply']) { return; } @@ -21482,8 +21302,7 @@ QR = (function() { if (g.VIEW === 'archive') { return; } - version = Conf[g.VIEW === 'thread' ? 'Use Recaptcha v1' : 'Use Recaptcha v1 on Index'] && (Main.jsEnabled || location.protocol === 'https:') ? (noscript = location.protocol === 'https:' && (Conf['Force Noscript Captcha for v1'] || !Main.jsEnabled), (info = typeof GM !== "undefined" && GM !== null ? GM.info : void 0) && info.scriptHandler === 'Greasemonkey' && /^4\./.test(info.version) ? noscript = false : void 0, noscript ? 'noscript' : 'v1') : 'v2'; - this.captcha = Captcha[version]; + this.captcha = Captcha.v2; $.on(d, '4chanXInitFinished', function() { return BoardConfig.ready(QR.initReady); }); @@ -21836,7 +21655,9 @@ QR = (function() { $.replace(node, $.tn('\n>')); } } - Post.prototype.insertTags(frag); + if (typeof Site.insertTags === "function") { + Site.insertTags(frag); + } ref3 = $$('.linkify[data-original]', frag); for (n = 0, len2 = ref3.length; n < len2; n++) { node = ref3[n]; @@ -24297,9 +24118,11 @@ QuoteYou = (function() { return Conf['Remember Your Posts'] = enabled; }); $.on(d, 'QRPostSuccessful', function(e) { - var boardID, postID, ref, threadID; - $.forceSync('Remember Your Posts'); - if (Conf['Remember Your Posts']) { + return $.get('Remember Your Posts', Conf['Remember Your Posts'], function(items) { + var boardID, postID, ref, threadID; + if (!items['Remember Your Posts']) { + return; + } ref = e.detail, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; return QuoteYou.db.set({ boardID: boardID, @@ -24307,7 +24130,7 @@ QuoteYou = (function() { postID: postID, val: true }); - } + }); }); if ((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') { return; @@ -24619,7 +24442,8 @@ Quotify = (function() { }).call(this); Main = (function() { - var Main; + var Main, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Main = { init: function() { @@ -24638,12 +24462,6 @@ Main = (function() { w['4chan X antidup'] = true; } catch (_error) {} if (location.hostname === 'www.google.com') { - if (location.pathname === '/recaptcha/api/noscript') { - $.ready(function() { - return Captcha.noscript.initFrame(); - }); - return; - } $.get('Captcha Fixes', true, function(arg) { var enabled; enabled = arg['Captcha Fixes']; @@ -24698,9 +24516,7 @@ Main = (function() { ref1 = DataBoard.keys; for (j = 0, len = ref1.length; j < len; j++) { db = ref1[j]; - Conf[db] = { - boards: {} - }; + Conf[db] = {}; } Conf['boardConfig'] = { boards: {} @@ -24719,13 +24535,15 @@ Main = (function() { Conf['QR Shortcut'] = true; Conf['Bottom QR Link'] = true; Conf['Toggleable Thread Watcher'] = true; - ($.getSync || $.get)({ - 'jsWhitelist': Conf['jsWhitelist'] - }, function(arg) { - var jsWhitelist; - jsWhitelist = arg.jsWhitelist; - return $.addCSP("script-src " + (jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim())); - }); + if (/\.4chan\.org$/.test(location.hostname)) { + ($.getSync || $.get)({ + 'jsWhitelist': Conf['jsWhitelist'] + }, function(arg) { + var jsWhitelist; + jsWhitelist = arg.jsWhitelist; + return $.addCSP("script-src " + (jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim())); + }); + } items = {}; for (key in Conf) { items[key] = void 0; @@ -24733,7 +24551,7 @@ Main = (function() { items['previousversion'] = void 0; return ($.getSync || $.get)(items, function(items) { var ref2; - if (!$.perProtocolSettings && ((ref2 = items['Redirect to HTTPS']) != null ? ref2 : Conf['Redirect to HTTPS']) && location.protocol !== 'https:') { + if (!$.perProtocolSettings && /\.4chan\.org$/.test(location.hostname) && ((ref2 = items['Redirect to HTTPS']) != null ? ref2 : Conf['Redirect to HTTPS']) && location.protocol !== 'https:') { location.replace('https:' + location.host + location.pathname + location.search + location.hash); return; } @@ -24753,7 +24571,7 @@ Main = (function() { val = Conf[key]; Conf[key] = (ref3 = items[key]) != null ? ref3 : val; } - return Main.initFeatures(); + return Site.init(Main.initFeatures); }); }); }, @@ -24773,19 +24591,17 @@ Main = (function() { }); }, initFeatures: function() { - var err, feature, hostname, j, len, match, name, pathname, ref, ref1, ref2, ref3, search; + var err, feature, hostname, j, len, match, name, pathname, ref, ref1, ref2, search; hostname = location.hostname, search = location.search; pathname = location.pathname.split(/\/+/); if (hostname !== 'www.4chan.org') { g.BOARD = new Board(pathname[1]); } - if (hostname === 'boards.4chan.org' || hostname === 'sys.4chan.org' || hostname === 'www.4chan.org') { - $.global(function() { - document.documentElement.classList.add('js-enabled'); - return window.FCX = {}; - }); - Main.jsEnabled = $.hasClass(doc, 'js-enabled'); - } + $.global(function() { + document.documentElement.classList.add('js-enabled'); + return window.FCX = {}; + }); + Main.jsEnabled = $.hasClass(doc, 'js-enabled'); switch (hostname) { case 'www.4chan.org': $.onExists(doc, 'body', function() { @@ -24820,8 +24636,8 @@ Main = (function() { $.asap((function() { return d.readyState !== 'loading'; }), function() { - var ref, video; - if (Conf['404 Redirect'] && ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found')) { + var video; + if (Conf['404 Redirect'] && (typeof Site.is404 === "function" ? Site.is404() : void 0)) { return Redirect.navigate('file', { boardID: g.BOARD.ID, filename: pathname[pathname.length - 1] @@ -24840,15 +24656,15 @@ Main = (function() { }); return; } - if (hostname !== 'boards.4chan.org') { + if (typeof Site.isAuxiliaryPage === "function" ? Site.isAuxiliaryPage() : void 0) { return; } if ((ref = pathname[2]) === 'thread' || ref === 'res') { g.VIEW = 'thread'; - g.THREADID = +pathname[3]; - } else if ((ref1 = pathname[2]) === 'catalog' || ref1 === 'archive') { - g.VIEW = pathname[2]; - } else if (pathname[2].match(/^\d*$/)) { + g.THREADID = +pathname[3].replace('.html', ''); + } else if (/^(?:catalog|archive)(?:\.html)?$/.test(pathname[2])) { + g.VIEW = pathname[2].replace('.html', ''); + } else if (/^(?:index|\d*)(?:\.html)?$/.test(pathname[2])) { g.VIEW = 'index'; } else { return; @@ -24856,9 +24672,12 @@ Main = (function() { g.threads = new SimpleDict(); g.posts = new SimpleDict(); $.onExists(doc, 'body', Main.initStyle); - ref2 = Main.features; - for (j = 0, len = ref2.length; j < len; j++) { - ref3 = ref2[j], name = ref3[0], feature = ref3[1]; + ref1 = Main.features; + for (j = 0, len = ref1.length; j < len; j++) { + ref2 = ref1[j], name = ref2[0], feature = ref2[1]; + if (Site.disabledFeatures && indexOf.call(Site.disabledFeatures, name) >= 0) { + continue; + } try { feature.init(); } catch (_error) { @@ -24879,6 +24698,8 @@ Main = (function() { if ((ref = $('link[href*=mobile]', d.head)) != null) { ref.disabled = true; } + doc.dataset.host = location.host; + $.addClass(doc, "sw-" + Site.software); $.addClass(doc, g.VIEW === 'thread' ? 'thread-view' : g.VIEW); $.onExists(doc, '.ad-cnt, .adg-rects > .desktop', function(ad) { return $.onExists(ad, 'img, iframe', function() { @@ -24923,7 +24744,7 @@ Main = (function() { mainStyleSheet = $('link[title=switch]', d.head); styleSheets = $$('link[rel="alternate stylesheet"]', d.head); setStyle = function() { - var bgColor, div, j, len, styleSheet; + var bgColor, div, j, len, s, styleSheet; $.rmClass(doc, style); style = null; for (j = 0, len = styleSheets.length; j < len; j++) { @@ -24943,14 +24764,17 @@ Main = (function() { $.addClass(doc, style); return $.rm(Main.bgColorStyle); } else { - div = $.el('div', { - className: 'reply' - }); - div.style.cssText = 'position: absolute; visibility: hidden;'; + div = Site.bgColoredEl(); + div.style.position = 'absolute'; + div.style.visibility = 'hidden'; $.add(d.body, div); bgColor = window.getComputedStyle(div).backgroundColor; $.rm(div); - Main.bgColorStyle.textContent = ".dialog, .suboption-list > div:last-of-type, :root.catalog-hover-expand .catalog-container:hover > .post {\n background-color: " + bgColor + ";\n}"; + if (!/^rgb\(/.test(bgColor)) { + s = window.getComputedStyle(d.body); + bgColor = s.backgroundColor + " " + s.backgroundImage + " " + s.backgroundRepeat + " " + s.backgroundPosition; + } + Main.bgColorStyle.textContent = ".dialog, .suboption-list > div:last-of-type, :root.catalog-hover-expand .catalog-container:hover > .post {\n background: " + bgColor + ";\n}"; return $.after($.id('fourchanx-css'), Main.bgColorStyle); } }; @@ -24964,23 +24788,22 @@ Main = (function() { }); }, initReady: function() { - var msg, ref, ref1, ref2; - if (g.VIEW === 'thread' && (((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found') || ($('.board') && !$('.opContainer')))) { - ThreadWatcher.set404(g.BOARD.ID, g.THREADID, function() { - if (Conf['404 Redirect']) { - return Redirect.navigate('thread', { - boardID: g.BOARD.ID, - threadID: g.THREADID, - postID: +location.hash.match(/\d+/) - }, "/" + g.BOARD + "/"); - } - }); + var msg; + if (typeof Site.is404 === "function" ? Site.is404() : void 0) { + if (g.VIEW === 'thread') { + ThreadWatcher.set404(g.BOARD.ID, g.THREADID, function() { + if (Conf['404 Redirect']) { + return Redirect.navigate('thread', { + boardID: g.BOARD.ID, + threadID: g.THREADID, + postID: +location.hash.match(/\d+/) + }, "/" + g.BOARD + "/"); + } + }); + } return; } - if ((ref1 = d.title) === '4chan - Temporarily Offline' || ref1 === '4chan - 404 Not Found') { - return; - } - if (((ref2 = g.VIEW) === 'index' || ref2 === 'thread') && !$('.board + *')) { + if (typeof Site.isIncomplete === "function" ? Site.isIncomplete() : void 0) { msg = $.el('div', { innerHTML: "The page didn't load completely.
Some features may not work unless you reload." }); @@ -24989,7 +24812,7 @@ Main = (function() { }); new Notice('warning', msg); } - if (!(Conf['JSON Index'] && g.VIEW === 'index')) { + if (!Index.enabled) { return Main.initThread(); } else { Main.expectInitFinished = true; @@ -24997,22 +24820,27 @@ Main = (function() { } }, initThread: function() { - var board, err, errors, j, k, len, len1, m, postRoot, posts, ref, ref1, scriptData, thread, threadRoot, threads; - if ((board = $('.board'))) { + var board, boardID, boardObj, err, errors, j, k, len, len1, postRoot, postRoots, posts, ref, s, thread, threadRoot, threads; + s = Site.selectors; + if ((board = $(s.board))) { threads = []; posts = []; - ref = $$('.board > .thread', board); + ref = $$(s.thread, board); for (j = 0, len = ref.length; j < len; j++) { threadRoot = ref[j]; - thread = new Thread(+threadRoot.id.slice(1), g.BOARD); + boardObj = (boardID = threadRoot.dataset.board) ? g.boards[boardID] || new Board(boardID) : g.BOARD; + thread = new Thread(+threadRoot.id.match(/\d*$/)[0], boardObj); thread.nodes.root = threadRoot; threads.push(thread); - ref1 = $$('.thread > .postContainer', threadRoot); - for (k = 0, len1 = ref1.length; k < len1; k++) { - postRoot = ref1[k]; - if ($('.postMessage', postRoot)) { + postRoots = $$(s.postContainer, threadRoot); + if (Site.isOPContainerThread) { + postRoots.unshift(threadRoot); + } + for (k = 0, len1 = postRoots.length; k < len1; k++) { + postRoot = postRoots[k]; + if ($(s.comment, postRoot)) { try { - posts.push(new Post(postRoot, thread, g.BOARD)); + posts.push(new Post(postRoot, thread, thread.board)); } catch (_error) { err = _error; if (!errors) { @@ -25030,20 +24858,9 @@ Main = (function() { Main.handleErrors(errors); } if (g.VIEW === 'thread') { - scriptData = Get.scriptData(); - threads[0].postLimit = /\bbumplimit *= *1\b/.test(scriptData); - threads[0].fileLimit = /\bimagelimit *= *1\b/.test(scriptData); - threads[0].ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : void 0; - } - if (g.BOARD.ID === 'f' && g.VIEW === 'thread') { - $.ajax(location.protocol + "//a.4cdn.org/f/thread/" + g.THREADID + ".json", { - timeout: $.MINUTE, - onloadend: function() { - if (this.response && posts[0].file) { - return posts[0].file.text.dataset.md5 = posts[0].file.MD5 = this.response.posts[0].md5; - } - } - }); + if (typeof Site.parseThreadMetadata === "function") { + Site.parseThreadMetadata(threads[0]); + } } Main.callbackNodes('Thread', threads); return Main.callbackNodesDB('Post', posts, function() { @@ -25168,9 +24985,8 @@ Main = (function() { }; }, isThisPageLegit: function() { - var ref; if (!('thisPageIsLegit' in Main)) { - Main.thisPageIsLegit = location.hostname === 'boards.4chan.org' && !$('link[href*="favicon-status.ico"]', d.head) && ((ref = d.title) !== '4chan - Temporarily Offline' && ref !== '4chan - Error' && ref !== '504 Gateway Time-out'); + Main.thisPageIsLegit = Site.isThisPageLegit ? Site.isThisPageLegit() : !/^[45]\d\d\b/.test(document.title); } return Main.thisPageIsLegit; }, diff --git a/builds/4chan-X-noupdate.crx b/builds/4chan-X-noupdate.crx index 9714fad35..6b224277d 100644 Binary files a/builds/4chan-X-noupdate.crx and b/builds/4chan-X-noupdate.crx differ diff --git a/builds/4chan-X-noupdate.user.js b/builds/4chan-X-noupdate.user.js index 5d321aa5f..4e976183b 100644 --- a/builds/4chan-X-noupdate.user.js +++ b/builds/4chan-X-noupdate.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.13.15.5 +// @version 1.14.0.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -24,8 +24,6 @@ // @include https://www.google.com/recaptcha/api2/bframe?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include http://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include http://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include https://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @exclude http://www.4chan.org/pass // @exclude https://www.4chan.org/pass // @exclude http://www.4chan.org/pass?* @@ -144,7 +142,7 @@ 'use strict'; -var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, Build, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume; +var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, Build, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume; var Conf, E, c, d, doc, docSet, g; @@ -159,7 +157,7 @@ docSet = function() { }; g = { - VERSION: '1.13.15.5', + VERSION: '1.14.0.0', NAMESPACE: '4chan X.', boards: {} }; @@ -510,7 +508,8 @@ Config = (function() { 'updater.position': 'bottom: 0px; left: 0px;', 'thread-watcher.position': 'top: 50px; left: 0px;', 'qr.position': 'top: 50px; right: 0px;' - } + }, + siteSoftware: "4chan.org yotsuba" }; return Config; @@ -1359,6 +1358,23 @@ body.is_catalog .thread > a > img {\n\ div.center[style] {\n\ display: none !important;\n\ }\n\ +/* Tinyboard / vichan conflicts */\n\ +#menu > .hide-thread-link {\n\ + width: auto;\n\ + height: auto;\n\ + overflow: visible;\n\ + background-image: none;\n\ +}\n\ +#menu label.entry {\n\ + display: block;\n\ +}\n\ +#fourchanx-settings label {\n\ + display: inline;\n\ +}\n\ +.intro a[href=\"javascript:;\"],\n\ +#menu a {\n\ + margin: 0;\n\ +}\n\ /* Anti-autoplay */\n\ audio.controls-added {\n\ display: block;\n\ @@ -1901,6 +1917,9 @@ div[data-checked=\"false\"] > .suboption-list {\n\ #fourchanx-settings p {\n\ margin: 1em 0px;\n\ }\n\ +#fourchanx-settings table {\n\ + margin: auto;\n\ +}\n\ .unscroll {\n\ overflow: hidden;\n\ }\n\ @@ -2472,7 +2491,7 @@ span.hide-announcement {\n\ .expanded-image > .post > .file > .fileThumb > img[data-md5] {\n\ display: none;\n\ }\n\ -.full-image {\n\ +.full-image[data-full-i-d] {\n\ display: none;\n\ cursor: pointer;\n\ }\n\ @@ -2624,7 +2643,9 @@ input[name=\"Default Volume\"] {\n\ }\n\ /* Spoiler text */\n\ :root.reveal-spoilers s,\n\ -:root.reveal-spoilers s > a {\n\ +:root.reveal-spoilers .spoiler,\n\ +:root.reveal-spoilers s > a,\n\ +:root.reveal-spoilers .spoiler > a {\n\ color: white !important;\n\ }\n\ :root.reveal-spoilers .removed-spoiler::before {\n\ @@ -2674,6 +2695,7 @@ input[name=\"Default Volume\"] {\n\ font-size: 0;\n\ }\n\ :root.anonymize .postertrip,\n\ +:root.anonymize .trip,\n\ :root.anonymize .n-pu {\n\ display: none;\n\ }\n\ @@ -2822,22 +2844,6 @@ input.field.tripped:not(:hover):not(:focus) {\n\ position: relative;\n\ top: 2px;\n\ }\n\ -/* Recaptcha v1 */\n\ -.captcha-img {\n\ - margin: 0px;\n\ - text-align: center;\n\ - background-image: #fff;\n\ - font-size: 0px;\n\ - min-height: 59px;\n\ - min-width: 302px;\n\ -}\n\ -.captcha-input {\n\ - width: 100%;\n\ - margin: 1px 0 0;\n\ -}\n\ -#qr.captcha-v1 #qr-captcha-iframe {\n\ - display: none;\n\ -}\n\ /* Recaptcha v2 */\n\ #qr .captcha-root {\n\ position: relative;\n\ @@ -3207,6 +3213,7 @@ a:only-of-type > .remove {\n\ #menu {\n\ position: fixed;\n\ outline: none;\n\ + font-weight: normal;\n\ }\n\ #menu, .submenu {\n\ border-radius: 3px;\n\ @@ -4994,6 +5001,12 @@ $ = (function() { $.syncing = {}; + $.securityCheck = function(data) { + if (location.protocol !== 'https:') { + return delete data['Redirect to HTTPS']; + } + }; + if (((typeof GM !== "undefined" && GM !== null ? GM.deleteValue : void 0) != null) && window.BroadcastChannel && (typeof GM_addValueChangeListener === "undefined" || GM_addValueChangeListener === null)) { $.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync'); $.on($.syncChannel, 'message', function(e) { @@ -5060,6 +5073,7 @@ $ = (function() { }); $.set = $.oneItemSugar(function(items, cb) { var key, val; + $.securityCheck(items); return Promise.all((function() { var results; results = []; @@ -5248,6 +5262,7 @@ $ = (function() { return cb(items); }; $.set = $.oneItemSugar(function(items, cb) { + $.securityCheck(items); return $.queueTask(function() { var key, value; for (key in items) { @@ -5602,7 +5617,7 @@ DataBoard = (function() { var init; this.key = key1; this.onSync = bind(this.onSync, this); - this.data = Conf[this.key]; + this.initData(Conf[this.key]); $.sync(this.key, this.onSync); if (!dontClean) { this.clean(); @@ -5619,35 +5634,103 @@ DataBoard = (function() { $.on(d, '4chanXInitFinished', init); } - DataBoard.prototype.save = function(cb) { - return $.set(this.key, this.data, cb); + DataBoard.prototype.initData = function(allData) { + var base, name; + this.allData = allData; + if (Site.hostname === '4chan.org' && this.allData.boards) { + return this.data = this.allData; + } else { + return this.data = ((base = this.allData)[name = Site.hostname] || (base[name] = { + boards: {} + })); + } + }; + + DataBoard.prototype.changes = []; + + DataBoard.prototype.save = function(change, cb) { + var changes, snapshot1; + snapshot1 = JSON.stringify(this.allData); + change(); + changes = this.changes; + changes.push(change); + return $.get(this.key, { + boards: {} + }, (function(_this) { + return function(items) { + var c, i, len, snapshot2; + _this.initData(items[_this.key]); + snapshot2 = JSON.stringify(_this.allData); + for (i = 0, len = changes.length; i < len; i++) { + c = changes[i]; + c(); + } + return $.set(_this.key, _this.allData, function() { + _this.changes = []; + if (snapshot1 !== snapshot2) { + if (typeof _this.sync === "function") { + _this.sync(); + } + } + return typeof cb === "function" ? cb() : void 0; + }); + }; + })(this)); + }; + + DataBoard.prototype.forceSync = function(cb) { + var changes, snapshot1; + snapshot1 = JSON.stringify(this.allData); + changes = this.changes; + return $.get(this.key, { + boards: {} + }, (function(_this) { + return function(items) { + var c, i, len, snapshot2; + _this.initData(items[_this.key]); + snapshot2 = JSON.stringify(_this.allData); + for (i = 0, len = changes.length; i < len; i++) { + c = changes[i]; + c(); + } + if (snapshot1 !== snapshot2) { + if (typeof _this.sync === "function") { + _this.sync(); + } + } + return typeof cb === "function" ? cb() : void 0; + }; + })(this)); }; DataBoard.prototype["delete"] = function(arg) { - var boardID, postID, ref, threadID; + var boardID, postID, threadID; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID; - $.forceSync(this.key); - if (postID) { - if (!((ref = this.data.boards[boardID]) != null ? ref[threadID] : void 0)) { - return; - } - delete this.data.boards[boardID][threadID][postID]; - this.deleteIfEmpty({ - boardID: boardID, - threadID: threadID - }); - } else if (threadID) { - if (!this.data.boards[boardID]) { - return; - } - delete this.data.boards[boardID][threadID]; - this.deleteIfEmpty({ - boardID: boardID - }); - } else { - delete this.data.boards[boardID]; - } - return this.save(); + return this.save((function(_this) { + return function() { + var ref; + if (postID) { + if (!((ref = _this.data.boards[boardID]) != null ? ref[threadID] : void 0)) { + return; + } + delete _this.data.boards[boardID][threadID][postID]; + return _this.deleteIfEmpty({ + boardID: boardID, + threadID: threadID + }); + } else if (threadID) { + if (!_this.data.boards[boardID]) { + return; + } + delete _this.data.boards[boardID][threadID]; + return _this.deleteIfEmpty({ + boardID: boardID + }); + } else { + return delete _this.data.boards[boardID]; + } + }; + })(this)); }; DataBoard.prototype.deleteIfEmpty = function(arg) { @@ -5666,45 +5749,59 @@ DataBoard = (function() { }; DataBoard.prototype.set = function(data, cb) { - $.forceSync(this.key); - return this.setUnsafe(data, cb); + return this.save((function(_this) { + return function() { + return _this.setUnsafe(data); + }; + })(this), cb); }; - DataBoard.prototype.setUnsafe = function(arg, cb) { + DataBoard.prototype.setUnsafe = function(arg) { var base, base1, base2, boardID, postID, threadID, val; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, val = arg.val; if (postID !== void 0) { - ((base = ((base1 = this.data.boards)[boardID] || (base1[boardID] = {})))[threadID] || (base[threadID] = {}))[postID] = val; + return ((base = ((base1 = this.data.boards)[boardID] || (base1[boardID] = {})))[threadID] || (base[threadID] = {}))[postID] = val; } else if (threadID !== void 0) { - ((base2 = this.data.boards)[boardID] || (base2[boardID] = {}))[threadID] = val; + return ((base2 = this.data.boards)[boardID] || (base2[boardID] = {}))[threadID] = val; } else { - this.data.boards[boardID] = val; + return this.data.boards[boardID] = val; } - return this.save(cb); }; DataBoard.prototype.extend = function(arg, cb) { - var boardID, i, key, len, oldVal, postID, ref, rm, threadID, val; + var boardID, postID, rm, threadID, val; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, val = arg.val, rm = arg.rm; - $.forceSync(this.key); - oldVal = this.get({ - boardID: boardID, - threadID: threadID, - postID: postID, - val: {} - }); - ref = rm || []; - for (i = 0, len = ref.length; i < len; i++) { - key = ref[i]; - delete oldVal[key]; - } - $.extend(oldVal, val); - return this.setUnsafe({ - boardID: boardID, - threadID: threadID, - postID: postID, - val: oldVal - }, cb); + return this.save((function(_this) { + return function() { + var i, key, len, oldVal, ref; + oldVal = _this.get({ + boardID: boardID, + threadID: threadID, + postID: postID, + val: {} + }); + ref = rm || []; + for (i = 0, len = ref.length; i < len; i++) { + key = ref[i]; + delete oldVal[key]; + } + $.extend(oldVal, val); + return _this.setUnsafe({ + boardID: boardID, + threadID: threadID, + postID: postID, + val: oldVal + }); + }; + })(this), cb); + }; + + DataBoard.prototype.setLastChecked = function() { + return this.save((function(_this) { + return function() { + return _this.data.lastChecked = Date.now(); + }; + })(this)); }; DataBoard.prototype.get = function(arg) { @@ -5730,13 +5827,11 @@ DataBoard = (function() { return val || defaultValue; }; - DataBoard.prototype.forceSync = function() { - return $.forceSync(this.key); - }; - DataBoard.prototype.clean = function() { var boardID, now, ref, ref1, val; - $.forceSync(this.key); + if (Site.software !== 'yotsuba') { + return; + } ref = this.data.boards; for (boardID in ref) { val = ref[boardID]; @@ -5802,13 +5897,11 @@ DataBoard = (function() { this.deleteIfEmpty({ boardID: boardID }); - return this.save(); + return $.set(this.key, this.allData); }; DataBoard.prototype.onSync = function(data) { - this.data = data || { - boards: {} - }; + this.initData(data); return typeof this.sync === "function" ? this.sync() : void 0; }; @@ -6262,7 +6355,6 @@ Notice = (function() { Post = (function() { var Post, - slice = [].slice, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Post = (function() { @@ -6271,31 +6363,25 @@ Post = (function() { }; function Post(root, thread, board) { - var clone, icon, j, k, len, len1, ref, ref1, ref10, ref11, ref12, ref13, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, type; + var clone, j, k, key, len, len1, ref, ref1, ref10, ref11, ref12, ref13, ref14, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, selector; this.thread = thread; this.board = board; - this.ID = +root.id.slice(2); + this.ID = +root.id.match(/\d*$/)[0]; this.threadID = this.thread.ID; this.boardID = this.board.ID; this.fullID = this.board + "." + this.ID; this.context = this; root.dataset.fullID = this.fullID; this.nodes = this.parseNodes(root); - if (!(this.isReply = $.hasClass(this.nodes.post, 'reply'))) { + if (!(this.isReply = this.ID !== this.threadID)) { this.thread.OP = this; - if (this.boardID === 'f') { - ref = ['Sticky', 'Closed']; - for (j = 0, len = ref.length; j < len; j++) { - type = ref[j]; - if ((icon = $("img[alt=" + type + "]", this.nodes.info))) { - $.addClass(icon, (type.toLowerCase()) + "Icon", 'retina'); - } - } + ref = ['isSticky', 'isClosed', 'isArchived']; + for (j = 0, len = ref.length; j < len; j++) { + key = ref[j]; + this.thread[key] = (selector = Site.selectors.icons[key]) ? !!$(selector, this.nodes.info) : false; } - this.thread.isArchived = !!$('.archivedIcon', this.nodes.info); - this.thread.isSticky = !!$('.stickyIcon', this.nodes.info); - this.thread.isClosed = this.thread.isArchived || !!$('.closedIcon', this.nodes.info); if (this.thread.isArchived) { + this.thread.isClosed = true; this.thread.kill(); } } @@ -6309,7 +6395,7 @@ Post = (function() { flagCode: (ref7 = this.nodes.flag) != null ? (ref8 = ref7.className.match(/flag-(\w+)/)) != null ? ref8[1].toUpperCase() : void 0 : void 0, flagCodeTroll: (ref9 = this.nodes.flag) != null ? (ref10 = ref9.src) != null ? (ref11 = ref10.match(/(\w+)\.gif$/)) != null ? ref11[1].toUpperCase() : void 0 : void 0 : void 0, flag: (ref12 = this.nodes.flag) != null ? ref12.title : void 0, - date: this.nodes.date ? new Date(this.nodes.date.dataset.utc * 1000) : void 0 + date: this.nodes.date ? new Date(((ref13 = this.nodes.date.getAttribute('datetime')) != null ? ref13.trim() : void 0) || (this.nodes.date.dataset.utc * 1000)) : void 0 }; if (Conf['Anonymize']) { this.info.nameBlock = 'Anonymous'; @@ -6331,9 +6417,9 @@ Post = (function() { if (g.posts[this.fullID]) { this.isRebuilt = true; this.clones = g.posts[this.fullID].clones; - ref13 = this.clones; - for (k = 0, len1 = ref13.length; k < len1; k++) { - clone = ref13[k]; + ref14 = this.clones; + for (k = 0, len1 = ref14.length; k < len1; k++) { + clone = ref14[k]; clone.origin = this; } } @@ -6343,32 +6429,28 @@ Post = (function() { } Post.prototype.parseNodes = function(root) { - var info, nodes, post; - post = $('.post', root); - info = $('.postInfo', post); + var info, key, nodes, post, ref, s, selector; + s = Site.selectors; + post = $(s.post, root) || root; + info = $(s.infoRoot, post); nodes = { root: root, post: post, info: info, - subject: $('.subject', info), - name: $('.name', info), - email: $('.useremail', info), - tripcode: $('.postertrip', info), - uniqueIDRoot: $('.posteruid', info), - uniqueID: $('.posteruid > .hand', info), - capcode: $('.capcode.hand', info), - pass: $('.n-pu', info), - flag: $('.flag, .countryFlag', info), - date: $('.dateTime', info), - nameBlock: $('.nameBlock', info), - quote: $('.postNum > a:nth-of-type(2)', info), - reply: $('.replylink', info), - fileRoot: $('.file', post), - comment: $('.postMessage', post), + comment: $(s.comment, post), quotelinks: [], archivelinks: [], embedlinks: [] }; + ref = s.info; + for (key in ref) { + selector = ref[key]; + nodes[key] = $(selector, info); + } + if (typeof Site.parseNodes === "function") { + Site.parseNodes(this, nodes); + } + nodes.uniqueIDRoot || (nodes.uniqueIDRoot = nodes.uniqueID); if ($.engine === 'edge') { Object.defineProperty(nodes, 'backlinks', { configurable: true, @@ -6387,7 +6469,9 @@ Post = (function() { var bq; this.nodes.comment.normalize(); this.nodes.commentClean = bq = this.nodes.comment.cloneNode(true); - this.cleanComment(bq); + if (typeof Site.cleanComment === "function") { + Site.cleanComment(bq); + } return this.info.comment = this.nodesToText(bq); }; @@ -6397,14 +6481,18 @@ Post = (function() { if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) { this.cleanSpoilers(bq); } - this.cleanCommentDisplay(bq); + if (typeof Site.cleanCommentDisplay === "function") { + Site.cleanCommentDisplay(bq); + } return this.nodesToText(bq).trim().replace(/\s+$/gm, ''); }; Post.prototype.commentOrig = function() { var bq; bq = this.nodes.commentClean.cloneNode(true); - this.insertTags(bq); + if (typeof Site.insertTags === "function") { + Site.insertTags(bq); + } return this.nodesToText(bq); }; @@ -6419,58 +6507,19 @@ Post = (function() { return text; }; - Post.prototype.cleanComment = function(bq) { - var abbr, br, i, j, k, len, node, ref; - if ((abbr = $('.abbr', bq))) { - ref = $$('.abbr + br, .exif', bq); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - $.rm(node); - } - for (i = k = 0; k < 2; i = ++k) { - if ((br = abbr.previousSibling) && br.nodeName === 'BR') { - $.rm(br); - } - } - return $.rm(abbr); - } - }; - Post.prototype.cleanSpoilers = function(bq) { var j, len, node, spoilers; - spoilers = $$('s', bq); + spoilers = $$(Site.selectors.spoiler, bq); for (j = 0, len = spoilers.length; j < len; j++) { node = spoilers[j]; $.replace(node, $.tn('[spoiler]')); } }; - Post.prototype.cleanCommentDisplay = function(bq) { - var b; - if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) { - $.rm(b); - } - return $.rm($('.fortune', bq)); - }; - - Post.prototype.insertTags = function(bq) { - var j, k, len, len1, node, ref, ref1; - ref = $$('s, .removed-spoiler', bq); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - $.replace(node, [$.tn('[spoiler]')].concat(slice.call(node.childNodes), [$.tn('[/spoiler]')])); - } - ref1 = $$('.prettyprint', bq); - for (k = 0, len1 = ref1.length; k < len1; k++) { - node = ref1[k]; - $.replace(node, [$.tn('[code]')].concat(slice.call(node.childNodes), [$.tn('[/code]')])); - } - }; - Post.prototype.parseQuotes = function() { var j, len, quotelink, ref; this.quotes = []; - ref = $$(':not(pre) > .quotelink', this.nodes.comment); + ref = $$(Site.selectors.quotelink, this.nodes.comment); for (j = 0, len = ref.length; j < len; j++) { quotelink = ref[j]; this.parseQuote(quotelink); @@ -6479,7 +6528,7 @@ Post = (function() { Post.prototype.parseQuote = function(quotelink) { var fullID, match; - match = quotelink.href.match(/^https?:\/\/boards\.4chan\.org\/+([^\/]+)\/+(?:res|thread)\/+\d+(?:[\/?][^#]*)?#p(\d+)$/); + match = quotelink.href.match(Site.regexp.quotelink); if (!(match || (this.isClone && quotelink.dataset.postID))) { return; } @@ -6487,55 +6536,39 @@ Post = (function() { if (this.isClone) { return; } - fullID = match[1] + "." + match[2]; + fullID = match[1] + "." + match[3]; if (indexOf.call(this.quotes, fullID) < 0) { return this.quotes.push(fullID); } }; Post.prototype.parseFile = function() { - var fileRoot, fileText, info, link, m, ref, ref1, ref2, size, thumb, unit; - fileRoot = this.nodes.fileRoot; - if (!fileRoot) { + var file, key, ref, ref1, selector, size, unit; + file = {}; + ref = Site.selectors.file; + for (key in ref) { + selector = ref[key]; + file[key] = $(selector, this.nodes.root); + } + file.thumbLink = (ref1 = file.thumb) != null ? ref1.parentNode : void 0; + if (!(file.text && file.link)) { return; } - if (!(link = $('.fileText > a, .fileText-original > a', fileRoot))) { + if (!Site.parseFile(this, file)) { return; } - if (!(info = (ref = link.nextSibling) != null ? ref.textContent.match(/\(([\d.]+ [KMG]?B).*\)/) : void 0)) { - return; - } - fileText = fileRoot.firstElementChild; - this.file = { - text: fileText, - link: link, - url: link.href, - name: fileText.title || link.title || link.textContent, - size: info[1], - isImage: /(jpg|png|gif)$/i.test(link.href), - isVideo: /webm$/i.test(link.href), - dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, - tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0, - MD5: fileText.dataset.md5 - }; - size = +this.file.size.match(/[\d.]+/)[0]; - unit = ['B', 'KB', 'MB', 'GB'].indexOf(this.file.size.match(/\w+$/)[0]); + $.extend(file, { + url: file.link.href, + isImage: /(jpg|png|gif)$/i.test(file.link.href), + isVideo: /(webm|mp4)$/i.test(file.link.href) + }); + size = +file.size.match(/[\d.]+/)[0]; + unit = ['B', 'KB', 'MB', 'GB'].indexOf(file.size.match(/\w+$/)[0]); while (unit-- > 0) { size *= 1024; } - this.file.sizeInBytes = size; - if ((thumb = $('a.fileThumb > [data-md5]', fileRoot))) { - $.extend(this.file, { - thumb: thumb, - thumbLink: thumb.parentNode, - thumbURL: thumb.src, - MD5: thumb.dataset.md5, - isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') - }); - if (this.file.isSpoiler) { - return this.file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.board + "/" + m[0] + "s.jpg" : void 0; - } - } + file.sizeInBytes = size; + return this.file = file; }; Post.deadMark = $.el('span', { @@ -6665,7 +6698,7 @@ Post = (function() { _Class.prototype.isClone = true; function _Class(origin, context, contractThumb) { - var base, fileRoot, i, inline, inlined, j, k, key, l, len, len1, len2, len3, node, nodes, ref, ref1, ref2, ref3, ref4, ref5, root, val; + var base, i, inline, inlined, j, k, key, l, len, len1, len2, len3, node, nodes, ref, ref1, ref2, ref3, ref4, ref5, ref6, root, selector, val; this.origin = origin; this.context = context; ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']; @@ -6675,13 +6708,13 @@ Post = (function() { } nodes = this.origin.nodes; root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true); - (base = Post.Clone).prefix || (base.prefix = 0); + (base = Post.Clone).suffix || (base.suffix = 0); ref1 = [root].concat(slice.call($$('[id]', root))); for (j = 0, len1 = ref1.length; j < len1; j++) { node = ref1[j]; - node.id = Post.Clone.prefix + node.id; + node.id += "_" + Post.Clone.suffix; } - Post.Clone.prefix++; + Post.Clone.suffix++; ref2 = $$('.inline', root); for (k = 0, len2 = ref2.length; k < len2; k++) { inline = ref2[k]; @@ -6711,12 +6744,15 @@ Post = (function() { val = ref4[key]; this.file[key] = val; } - fileRoot = this.nodes.fileRoot; - this.file.text = fileRoot.firstElementChild; - this.file.link = $('.fileText > a, .fileText-original', fileRoot); - this.file.thumb = $('a.fileThumb > [data-md5]', fileRoot); - this.file.thumbLink = (ref5 = this.file.thumb) != null ? ref5.parentNode : void 0; - this.file.fullImage = $('.full-image', fileRoot); + ref5 = Site.selectors.file; + for (key in ref5) { + selector = ref5[key]; + this.file[key] = $(selector, this.nodes.root); + } + this.file.thumbLink = (ref6 = this.file.thumb) != null ? ref6.parentNode : void 0; + if (this.file.thumbLink) { + this.file.fullImage = $('.full-image', this.file.thumbLink); + } this.file.videoControls = $('.video-controls', this.file.text); if (this.file.videoThumb) { this.file.thumb.muted = true; @@ -7104,6 +7140,348 @@ Thread = (function() { }).call(this); +SW = {}; + +(function() { + var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + SW.tinyboard = { + isOPContainerThread: true, + disabledFeatures: ['Board Configuration', 'Normalize URL', 'Captcha Configuration', 'Image Host Rewriting', 'Index Generator', 'Announcement Hiding', 'Fourchan thingies', 'Resurrect Quotes', 'Quick Reply Personas', 'Quick Reply', 'Cooldown', 'Pass Link', 'Index Generator (Menu)', 'Report Link', 'Delete Link', 'Edit Link', 'Archive Link', 'Quote Inlining', 'Quote Previewing', 'Quote Backlinks', 'File Info Formatting', 'Fappe Tyme', 'Image Expansion', 'Image Expansion (Menu)', 'Comment Expansion', 'Thread Expansion', 'Favicon', 'Unread', 'Quote Threading', 'Thread Stats', 'Thread Updater', 'Mark New IPs', 'Banner', 'Flash Features', 'Reply Pruning'], + detect: function() { + var i, len, ref, script; + ref = $$('script:not([src])', d.head); + for (i = 0, len = ref.length; i < len; i++) { + script = ref[i]; + if (/\bvar configRoot=".*?"/.test(script.textContent)) { + return true; + } + } + return false; + }, + selectors: { + board: 'form[name="postcontrols"]', + thread: 'div[id^="thread_"]', + postContainer: '.reply', + infoRoot: '.intro', + info: { + subject: '.subject', + name: '.name', + email: '.email', + tripcode: '.trip', + uniqueID: '.poster_id', + capcode: '.capcode', + flag: '.flag', + date: 'time', + nameBlock: 'label', + quote: 'a[href*="#q"]', + reply: 'a[href*="/res/"]:not([href*="#"])' + }, + icons: { + isSticky: '.fa-thumb-tack', + isClosed: '.fa-lock' + }, + file: { + text: '.fileinfo', + link: '.fileinfo > a', + thumb: 'a > .post-image' + }, + comment: '.body', + spoiler: '.spoiler', + quotelink: 'a[onclick^="highlightReply("]', + boardList: '.boardlist' + }, + xpath: { + thread: 'div[starts-with(@id,"thread_")]', + postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]' + }, + regexp: { + quotelink: /\/([^\/]+)\/res\/(\d+)\.html#(\d+)$/ + }, + bgColoredEl: function() { + return $.el('div', { + className: 'post reply' + }); + }, + parseNodes: function(post, nodes) { + var m, nextSibling, uniqueID; + if (nodes.uniqueID) { + return; + } + nodes.info.normalize(); + nextSibling = nodes.nameBlock.nextSibling; + if (nextSibling.nodeType === 3 && (m = nextSibling.textContent.match(/(\s*ID:\s*)(\S+)/))) { + nextSibling = nextSibling.splitText(m[1].length); + nextSibling.splitText(m[2].length); + nodes.uniqueID = uniqueID = $.el('span', { + className: 'poster_id' + }); + $.replace(nextSibling, uniqueID); + return $.add(uniqueID, nextSibling); + } + }, + parseFile: function(post, file) { + var info, infoNode, link, nameNode, ref, ref1, text, thumb; + text = file.text, link = file.link, thumb = file.thumb; + if ($.x("ancestor::" + Site.xpath.postContainer + "[1]", text) !== post.nodes.root) { + return false; + } + if (!(infoNode = indexOf.call((ref = link.nextSibling) != null ? ref.textContent : void 0, '(') >= 0 ? link.nextSibling : link.nextElementSibling)) { + return false; + } + if (!(info = infoNode.textContent.match(/\((Spoiler Image, )?([\d.]+ [KMG]?B).*\)/))) { + return false; + } + nameNode = $('.postfilename', text); + $.extend(file, { + name: nameNode ? nameNode.title || nameNode.textContent : link.pathname.match(/[^\/]*$/)[0], + size: info[2], + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0 + }); + if (thumb) { + $.extend(file, { + thumbURL: /\/static\//.test(thumb.src) && /\.(?:gif|jpe?g|png)$/.test(link.href) ? link.href : thumb.src, + isSpoiler: !!info[1] || link.textContent === 'Spoiler Image' + }); + } + return true; + }, + isThumbExpanded: function(file) { + return $.hasClass(file.thumb.parentNode, 'expanded'); + } + }; + +}).call(this); + +(function() { + var slice = [].slice; + + SW.yotsuba = { + isOPContainerThread: false, + selectors: { + board: '.board', + thread: '.thread', + postContainer: '.postContainer', + sideArrows: '.sideArrows', + post: '.post', + infoRoot: '.postInfo', + info: { + subject: '.subject', + name: '.name', + email: '.useremail', + tripcode: '.postertrip', + uniqueIDRoot: '.posteruid', + uniqueID: '.posteruid > .hand', + capcode: '.capcode.hand', + pass: '.n-pu', + flag: '.flag, .countryFlag', + date: '.dateTime', + nameBlock: '.nameBlock', + quote: '.postNum > a:nth-of-type(2)', + reply: '.replylink' + }, + icons: { + isSticky: '.stickyIcon', + isClosed: '.closedIcon', + isArchived: '.archivedIcon' + }, + file: { + text: '.file > :first-child', + link: '.fileText > a', + thumb: 'a.fileThumb > [data-md5]' + }, + comment: '.postMessage', + spoiler: 's', + quotelink: ':not(pre) > .quotelink', + boardList: '#boardNavDesktop > .boardList' + }, + xpath: { + thread: 'div[contains(concat(" ",@class," ")," thread ")]', + postContainer: 'div[contains(@class,"postContainer")]' + }, + regexp: { + quotelink: /^https?:\/\/boards\.4chan\.org\/+([^\/]+)\/+thread\/+(\d+)(?:[\/?][^#]*)?(?:#p(\d+))?$/ + }, + bgColoredEl: function() { + return $.el('div', { + className: 'reply' + }); + }, + isThisPageLegit: function() { + var ref; + return location.hostname === 'boards.4chan.org' && !$('link[href*="favicon-status.ico"]', d.head) && ((ref = d.title) !== '4chan - Temporarily Offline' && ref !== '4chan - Error' && ref !== '504 Gateway Time-out'); + }, + is404: function() { + var ref; + return ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found') || (g.VIEW === 'thread' && $('.board') && !$('.opContainer')); + }, + isIncomplete: function() { + var ref; + return ((ref = g.VIEW) === 'index' || ref === 'thread') && !$('.board + *'); + }, + isAuxiliaryPage: function() { + return location.hostname !== 'boards.4chan.org'; + }, + scriptData: function() { + var j, len, ref, script; + ref = $$('script:not([src])', d.head); + for (j = 0, len = ref.length; j < len; j++) { + script = ref[j]; + if (/\bcooldowns *=/.test(script.textContent)) { + return script.textContent; + } + } + return ''; + }, + parseThreadMetadata: function(thread) { + var file, m, scriptData; + scriptData = this.scriptData(); + thread.postLimit = /\bbumplimit *= *1\b/.test(scriptData); + thread.fileLimit = /\bimagelimit *= *1\b/.test(scriptData); + thread.ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : void 0; + if (g.BOARD.ID === 'f' && thread.OP.file) { + file = thread.OP.file; + return $.ajax(location.protocol + "//a.4cdn.org/f/thread/" + thread + ".json", { + timeout: $.MINUTE, + onloadend: function() { + if (this.response) { + return file.text.dataset.md5 = file.MD5 = this.response.posts[0].md5; + } + } + }); + } + }, + parseNodes: function(post, nodes) { + var icon, j, len, ref, results, type; + if (post.boardID === 'f') { + ref = ['Sticky', 'Closed']; + results = []; + for (j = 0, len = ref.length; j < len; j++) { + type = ref[j]; + if ((icon = $("img[alt=" + type + "]", nodes.info))) { + results.push($.addClass(icon, (type.toLowerCase()) + "Icon", 'retina')); + } + } + return results; + } + }, + parseFile: function(post, file) { + var info, link, m, ref, ref1, ref2, text, thumb; + text = file.text, link = file.link, thumb = file.thumb; + if (!(info = (ref = link.nextSibling) != null ? ref.textContent.match(/\(([\d.]+ [KMG]?B).*\)/) : void 0)) { + return false; + } + $.extend(file, { + name: text.title || link.title || link.textContent, + size: info[1], + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, + tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0, + MD5: text.dataset.md5 + }); + if (thumb) { + $.extend(file, { + thumbURL: thumb.src, + MD5: thumb.dataset.md5, + isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') + }); + if (file.isSpoiler) { + file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//" + (ImageHost.thumbHost()) + "/" + post.board + "/" + m[0] + "s.jpg" : void 0; + } + } + return true; + }, + cleanComment: function(bq) { + var abbr, br, i, j, k, len, node, ref; + if ((abbr = $('.abbr', bq))) { + ref = $$('.abbr + br, .exif', bq); + for (j = 0, len = ref.length; j < len; j++) { + node = ref[j]; + $.rm(node); + } + for (i = k = 0; k < 2; i = ++k) { + if ((br = abbr.previousSibling) && br.nodeName === 'BR') { + $.rm(br); + } + } + return $.rm(abbr); + } + }, + cleanCommentDisplay: function(bq) { + var b; + if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) { + $.rm(b); + } + return $.rm($('.fortune', bq)); + }, + insertTags: function(bq) { + var j, k, len, len1, node, ref, ref1; + ref = $$('s, .removed-spoiler', bq); + for (j = 0, len = ref.length; j < len; j++) { + node = ref[j]; + $.replace(node, [$.tn('[spoiler]')].concat(slice.call(node.childNodes), [$.tn('[/spoiler]')])); + } + ref1 = $$('.prettyprint', bq); + for (k = 0, len1 = ref1.length; k < len1; k++) { + node = ref1[k]; + $.replace(node, [$.tn('[code]')].concat(slice.call(node.childNodes), [$.tn('[/code]')])); + } + } + }; + +}).call(this); + +Site = (function() { + var Site; + + Site = { + init: function(cb) { + var hostname, i, len, line, ref, ref1, software, swDict; + swDict = {}; + ref = Conf['siteSoftware'].split('\n'); + for (i = 0, len = ref.length; i < len; i++) { + line = ref[i]; + if (!(line[0] !== '#')) { + continue; + } + ref1 = line.split(' '), hostname = ref1[0], software = ref1[1]; + if (software in SW) { + swDict[hostname] = software; + } + } + hostname = location.hostname; + while (hostname && !(hostname in swDict)) { + hostname = hostname.replace(/^[^.]*\.?/, ''); + } + if (hostname) { + this.set(hostname, swDict[hostname]); + return cb(); + } else { + return $.onExists(doc, 'body', (function(_this) { + return function() { + var base; + for (software in SW) { + if (typeof (base = SW[software]).detect === "function" ? base.detect() : void 0) { + _this.set(location.hostname.replace(/^www\./, ''), software); + Conf['siteSoftware'] += "\n" + _this.hostname + " " + _this.software; + $.set('siteSoftware', Conf['siteSoftware']); + cb(); + } + } + }; + })(this)); + } + }, + set: function(hostname1, software1) { + this.hostname = hostname1; + this.software = software1; + return $.extend(this, SW[this.software]); + } + }; + + return Site; + +}).call(this); + Redirect = (function() { var Redirect, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -7702,7 +8080,7 @@ Filter = (function() { open: function(post) { var value; value = Filter[type](post); - return (value != null) && !(g.BOARD.ID === 'f' && type === 'MD5'); + return value != null; } }; }, @@ -7760,7 +8138,7 @@ PostHiding = (function() { })); }, node: function() { - var data, sideArrows; + var button, data, sa, sideArrows; if (!this.isReply || this.isClone || this.isFetchedQuote) { return; } @@ -7779,9 +8157,14 @@ PostHiding = (function() { if (!Conf['Reply Hiding Buttons']) { return; } - sideArrows = $('.sideArrows', this.nodes.root); - $.replace(sideArrows.firstChild, PostHiding.makeButton(this, 'hide')); - return sideArrows.removeAttribute('class'); + button = PostHiding.makeButton(this, 'hide'); + if ((sa = Site.selectors.sideArrows)) { + sideArrows = $(sa, this.nodes.root); + $.replace(sideArrows.firstChild, button); + return sideArrows.removeAttribute('class'); + } else { + return $.prepend(this.nodes.root, button); + } }, menu: { init: function() { @@ -8146,7 +8529,7 @@ ThreadHiding = (function() { }, catalogSet: function(board) { var hiddenThreads, threadID; - if (!$.hasStorage) { + if (!($.hasStorage && Site.software === 'yotsuba')) { return; } hiddenThreads = ThreadHiding.db.get({ @@ -8159,7 +8542,7 @@ ThreadHiding = (function() { return localStorage.setItem("4chan-hide-t-" + board, JSON.stringify(hiddenThreads)); }, catalogWatch: function() { - if (!$.hasStorage) { + if (!($.hasStorage && Site.software === 'yotsuba')) { return; } this.hiddenThreads = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; @@ -8504,6 +8887,9 @@ BoardConfig = (function() { }, noAudio: function(boardID) { var boards; + if (Site.software !== 'yotsuba') { + return false; + } boards = this.boards || Conf['boardConfig'].boards; return boards && !boards[boardID].webm_audio; } @@ -8848,10 +9234,10 @@ Get = (function() { if (root == null) { return null; } - return g.threads[g.BOARD + "." + root.id.slice(1)]; + return g.threads[(root.dataset.board || g.BOARD.ID) + "." + (root.id.match(/\d*$/)[0])]; }, threadFromNode: function(node) { - return Get.threadFromRoot($.x('ancestor-or-self::div[contains(concat(" ",@class," ")," thread ")]', node)); + return Get.threadFromRoot($.x("ancestor-or-self::" + Site.xpath.thread, node)); }, postFromRoot: function(root) { var index, post; @@ -8867,18 +9253,17 @@ Get = (function() { } }, postFromNode: function(root) { - return Get.postFromRoot($.x('ancestor-or-self::div[contains(@class,"postContainer")][1]', root)); + return Get.postFromRoot($.x("ancestor-or-self::" + Site.xpath.postContainer + "[1]", root)); }, postDataFromLink: function(link) { - var boardID, path, postID, ref, threadID; - if (link.hostname === 'boards.4chan.org') { - path = link.pathname.split(/\/+/); - boardID = path[1]; - threadID = path[3]; - postID = link.hash ? link.hash.slice(2) : path[3]; - } else { + var boardID, match, postID, ref, ref1, threadID; + if (link.dataset.postID) { ref = link.dataset, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; threadID || (threadID = 0); + } else { + match = link.href.match(Site.regexp.quotelink); + ref1 = match.slice(1), boardID = ref1[0], threadID = ref1[1], postID = ref1[2]; + postID || (postID = threadID); } return { boardID: boardID, @@ -8919,17 +9304,6 @@ Get = (function() { ref1 = Get.postDataFromLink(quotelink), boardID = ref1.boardID, postID = ref1.postID; return boardID === post.board.ID && postID === post.ID; }); - }, - scriptData: function() { - var i, len, ref, script; - ref = $$('script:not([src])', d.head); - for (i = 0, len = ref.length; i < len; i++) { - script = ref[i]; - if (/\bcooldowns *=/.test(script.textContent)) { - return script.textContent; - } - } - return ''; } }; @@ -9024,6 +9398,7 @@ Header = (function() { }); $.on(window, 'load popstate', Header.hashScroll); $.on(d, 'CreateNotification', this.createNotification); + this.setBoardList(); $.onExists(doc, 'body', (function(_this) { return function() { if (!Main.isThisPageLegit()) { @@ -9034,7 +9409,7 @@ Header = (function() { return _this.setBarPosition(Conf['Bottom Header']); }; })(this)); - $.onExists(doc, '#boardNavMobile', Header.setBoardList); + $.onExists(doc, Site.selectors.boardList + " + *", Header.generateFullBoardList); Main.ready(function() { var a, absbot, footer; if (!(footer = $.id('boardNavDesktopFoot'))) { @@ -9088,7 +9463,7 @@ Header = (function() { id: 'scroll-marker' }), setBoardList: function() { - var a, boardList, btn, chr, fullBoardList, i, j, len, len1, node, nodes, ref, ref1, spacer, span; + var boardList, btn; Header.boardList = boardList = $.el('span', { id: 'board-list' }); @@ -9097,20 +9472,28 @@ Header = (function() { }); btn = $('.hide-board-list-button', boardList); $.on(btn, 'click', Header.toggleBoardList); + $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); + Header.setCustomNav(Conf['Custom Board Navigation']); + Header.generateBoardList(Conf['boardnav']); + $.sync('Custom Board Navigation', Header.setCustomNav); + return $.sync('boardnav', Header.generateBoardList); + }, + generateFullBoardList: function() { + var a, chr, fullBoardList, i, items, j, len, node, nodes, ref, spacer, span; nodes = []; spacer = function() { return $.el('span', { className: 'spacer' }); }; - ref = $('#boardNavDesktop > .boardList').childNodes; - for (i = 0, len = ref.length; i < len; i++) { - node = ref[i]; + items = $.X('.//a|.//text()[not(ancestor::a)]', $(Site.selectors.boardList)); + i = 0; + while (node = items.snapshotItem(i++)) { switch (node.nodeName) { case '#text': - ref1 = node.nodeValue; - for (j = 0, len1 = ref1.length; j < len1; j++) { - chr = ref1[j]; + ref = node.nodeValue; + for (j = 0, len = ref.length; j < len; j++) { + chr = ref[j]; span = $.el('span', { textContent: chr }); @@ -9134,14 +9517,9 @@ Header = (function() { nodes.push(a); } } - fullBoardList = $('.boardList', boardList); + fullBoardList = $('.boardList', Header.boardList); $.add(fullBoardList, nodes); - CatalogLinks.setLinks(fullBoardList); - $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); - Header.setCustomNav(Conf['Custom Board Navigation']); - Header.generateBoardList(Conf['boardnav']); - $.sync('Custom Board Navigation', Header.setCustomNav); - return $.sync('boardnav', Header.generateBoardList); + return CatalogLinks.setLinks(fullBoardList); }, generateBoardList: function(boardnav) { var as, list, nodes, re, t; @@ -9154,11 +9532,11 @@ Header = (function() { as = $$('#full-board-list a[title]', Header.boardList); re = /[\w@]+(-(all|title|replace|full|index|catalog|archive|expired|(mode|sort|text):"[^"]+"(,"[^"]+")?))*|[^\w@]+/g; nodes = (function() { - var i, len, ref, results; + var j, len, ref, results; ref = boardnav.match(re); results = []; - for (i = 0, len = ref.length; i < len; i++) { - t = ref[i]; + for (j = 0, len = ref.length; j < len; j++) { + t = ref[j]; results.push(Header.mapCustomNavigation(t, as)); } return results; @@ -9202,10 +9580,22 @@ Header = (function() { } boardID = t.split('-')[0]; if (boardID === 'current') { - boardID = g.BOARD.ID; + if (location.hostname === 'boards.4chan.org') { + boardID = g.BOARD.ID; + } else { + a = $.el('a', { + href: "/" + g.BOARD.ID + "/", + textContent: text || g.BOARD.ID, + className: 'current' + }); + if (/-(catalog|archive|expired)/.test(t)) { + a = a.firstChild; + } + return a; + } } a = (function() { - var i, len, ref; + var j, len, ref; if (boardID === '@') { return $.el('a', { href: 'https://twitter.com/4chan', @@ -9213,25 +9603,25 @@ Header = (function() { textContent: '@' }); } - for (i = 0, len = as.length; i < len; i++) { - a = as[i]; + for (j = 0, len = as.length; j < len; j++) { + a = as[j]; if (a.textContent === boardID) { return a.cloneNode(true); } } a = $.el('a', { - href: "/" + boardID + "/", + href: "//boards.4chan.org/" + boardID + "/", textContent: boardID }); if ((ref = g.VIEW) === 'catalog' || ref === 'archive') { a.href += g.VIEW; } - if (boardID === g.BOARD.ID) { + if (a.hostname === location.hostname && boardID === g.BOARD.ID) { a.className = 'current'; } return a; })(); - a.textContent = /-title/.test(t) || /-replace/.test(t) && boardID === g.BOARD.ID ? a.title || a.textContent : /-full/.test(t) ? ("/" + boardID + "/") + (a.title ? " - " + a.title : '') : text || boardID; + a.textContent = /-title/.test(t) || /-replace/.test(t) && a.hostname === location.hostname && boardID === g.BOARD.ID ? a.title || a.textContent : /-full/.test(t) ? ("/" + boardID + "/") + (a.title ? " - " + a.title : '') : text || boardID; if (m = t.match(/-(index|catalog)/)) { if (!(boardID === 'f' && m[1] === 'catalog')) { a.dataset.only = m[1]; @@ -9260,7 +9650,7 @@ Header = (function() { } if (/-expired/.test(t)) { if (boardID !== 'b' && boardID !== 'f' && boardID !== 'trash' && boardID !== 'bant') { - a.href = "/" + boardID + "/archive"; + a.href = "//boards.4chan.org/" + boardID + "/archive"; } else { return a.firstChild; } @@ -9509,7 +9899,7 @@ Header = (function() { } }, addShortcut: function(id, el, index) { - var i, item, len, ref, shortcut; + var item, j, len, ref, shortcut; shortcut = $.el('span', { id: "shortcut-" + id, className: 'shortcut brackets-wrap' @@ -9517,8 +9907,8 @@ Header = (function() { $.add(shortcut, el); shortcut.dataset.index = index; ref = $$('[data-index]', Header.shortcuts); - for (i = 0, len = ref.length; i < len; i++) { - item = ref[i]; + for (j = 0, len = ref.length; j < len; j++) { + item = ref[j]; if (!(+item.dataset.index > +index)) { continue; } @@ -11159,7 +11549,7 @@ Settings = (function() { if ($.engine !== 'gecko') { $('div[data-name="Remember QR Size"]', section).hidden = true; } - if ($.perProtocolSettings) { + if ($.perProtocolSettings || location.protocol !== 'https:') { $('div[data-name="Redirect to HTTPS"]', section).hidden = true; } $.get(items, function(items) { @@ -12112,7 +12502,7 @@ UI = (function() { $.on(d, 'click CloseMenu', this.close); $.on(d, 'scroll', this.setPosition); $.on(window, 'resize', this.setPosition); - $.add(button, menu); + $.after(button, menu); this.setPosition(); entry = $('.entry', menu); this.focus(entry); @@ -12578,7 +12968,7 @@ Gallery = (function() { Gallery = { init: function() { var el, ref; - if (!(this.enabled = Conf['Gallery'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(this.enabled = Conf['Gallery'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } this.delay = Conf['Slide Delay']; @@ -12609,7 +12999,7 @@ Gallery = (function() { } }, build: function(image) { - var candidate, cb, dialog, entry, file, i, j, key, len, len1, menuButton, nodes, post, ref, ref1, ref2, ref3, thumb, value; + var candidate, cb, dialog, entry, i, j, key, len, len1, menuButton, nodes, post, postThumb, ref, ref1, ref2, ref3, thumb, value; cb = Gallery.cb; if (Conf['Fullscreen Gallery']) { $.one(d, 'fullscreenchange mozfullscreenchange webkitfullscreenchange', function() { @@ -12673,10 +13063,10 @@ Gallery = (function() { $.off(d, 'keydown', Keybinds.keydown); } $.on(window, 'resize', Gallery.cb.setHeight); - ref2 = $$('.post .file'); + ref2 = $$(Site.selectors.file.thumb); for (j = 0, len1 = ref2.length; j < len1; j++) { - file = ref2[j]; - post = Get.postFromNode(file); + postThumb = ref2[j]; + post = Get.postFromNode(postThumb); if (!((ref3 = post.file) != null ? ref3.thumb : void 0)) { continue; } @@ -12734,6 +13124,7 @@ Gallery = (function() { ext = thumb.href.match(/\w*$/); elType = { 'webm': 'video', + 'mp4': 'video', 'pdf': 'iframe' }[ext] || 'img'; file = $.el(elType); @@ -13222,7 +13613,7 @@ ImageExpand = (function() { ImageExpand = { init: function() { var ref; - if (!(this.enabled = Conf['Image Expansion'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(this.enabled = Conf['Image Expansion'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } this.EAI = $.el('a', { @@ -13721,7 +14112,7 @@ ImageHover = (function() { } file = post.file; isVideo = file.isVideo; - if (file.isExpanding || file.isExpanded) { + if (file.isExpanding || file.isExpanded || (typeof Site.isThumbExpanded === "function" ? Site.isThumbExpanded(file) : void 0)) { return; } error = ImageHover.error(post); @@ -13813,7 +14204,7 @@ ImageLoader = (function() { ImageLoader = { init: function() { var prefetch, ref, ref1; - if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && g.BOARD.ID !== 'f')) { + if ((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') { return; } if (!(Conf['Image Prefetching'] || Conf['Replace JPG'] || Conf['Replace PNG'] || Conf['Replace GIF'] || Conf['Replace WEBM'])) { @@ -13964,7 +14355,7 @@ Metadata = (function() { Metadata = { init: function() { var ref; - if (!(Conf['WEBM Metadata'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(Conf['WEBM Metadata'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } return Callbacks.Post.push({ @@ -15241,7 +15632,7 @@ Linkify = (function() { ref = $$('a', this.nodes.comment); for (j = 0, len = ref.length; j < len; j++) { link = ref[j]; - if (!(ImageHost.test(link.hostname))) { + if (!(ImageHost.test(link.hostname) || /\bnofollow\b/.test(link.rel))) { continue; } $.addClass(link, 'linkify'); @@ -15797,8 +16188,7 @@ ReportLink = (function() { order: 10, open: function(post) { ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; - if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) { - ReportLink.url += '&altc=1'; + if (d.cookie.indexOf('pass_enabled=1') >= 0) { ReportLink.dims = 'width=350,height=275'; } else { ReportLink.dims = 'width=400,height=550'; @@ -16046,7 +16436,7 @@ CatalogLinks = (function() { CatalogLinks = { init: function() { var el, input, selector; - if ((Conf['External Catalog'] || Conf['JSON Index']) && !(Conf['JSON Index'] && g.VIEW === 'index')) { + if (Site.software === 'yotsuba' && (Conf['External Catalog'] || Conf['JSON Index']) && !(Conf['JSON Index'] && g.VIEW === 'index')) { selector = (function() { switch (g.VIEW) { case 'thread': @@ -16082,7 +16472,7 @@ CatalogLinks = (function() { } }); } - if (Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { + if (Site.software === 'yotsuba' && Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { Callbacks.Post.push({ name: 'Catalog Link Rewrite', cb: this.node @@ -16133,7 +16523,7 @@ CatalogLinks = (function() { if (((ref2 = a.hostname) !== 'boards.4chan.org' && ref2 !== 'catalog.neet.tv') || !(board = a.pathname.split('/')[1]) || (board === 'f' || board === 'status' || board === '4chan') || a.pathname.split('/')[2] === 'archive' || $.hasClass(a, 'external')) { continue; } - a.href = Conf['Header catalog links'] ? CatalogLinks.catalog(board) : "/" + board + "/"; + a.href = Conf['Header catalog links'] ? CatalogLinks.catalog(board) : "//boards.4chan.org/" + board + "/"; if (a.dataset.indexOptions && a.hostname === 'boards.4chan.org' && a.pathname.split('/')[2] === '') { a.href += (a.hash ? '/' : '#') + a.dataset.indexOptions; } @@ -16146,13 +16536,13 @@ CatalogLinks = (function() { if (Conf['External Catalog'] && (board === 'a' || board === 'c' || board === 'g' || board === 'biz' || board === 'k' || board === 'm' || board === 'o' || board === 'p' || board === 'v' || board === 'vg' || board === 'vr' || board === 'w' || board === 'wg' || board === 'cm' || board === '3' || board === 'adv' || board === 'an' || board === 'asp' || board === 'cgl' || board === 'ck' || board === 'co' || board === 'diy' || board === 'fa' || board === 'fit' || board === 'gd' || board === 'int' || board === 'jp' || board === 'lit' || board === 'mlp' || board === 'mu' || board === 'n' || board === 'out' || board === 'po' || board === 'sci' || board === 'sp' || board === 'tg' || board === 'toy' || board === 'trv' || board === 'tv' || board === 'vp' || board === 'wsg' || board === 'x' || board === 'f' || board === 'pol' || board === 's4s' || board === 'lgbt')) { return "//catalog.neet.tv/" + board + "/"; } else if (Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { - if (g.BOARD.ID === board && g.VIEW === 'index') { + if (location.hostname === 'boards.4chan.org' && g.BOARD.ID === board && g.VIEW === 'index') { return '#catalog'; } else { - return "/" + board + "/#catalog"; + return "//boards.4chan.org/" + board + "/#catalog"; } } else { - return "/" + board + "/catalog"; + return "//boards.4chan.org/" + board + "/catalog"; } }, index: function(board) { @@ -16160,13 +16550,13 @@ CatalogLinks = (function() { board = g.BOARD.ID; } if (Conf['JSON Index'] && board !== 'f') { - if (g.BOARD.ID === board && g.VIEW === 'index') { + if (location.hostname === 'boards.4chan.org' && g.BOARD.ID === board && g.VIEW === 'index') { return '#index'; } else { - return "/" + board + "/#index"; + return "//boards.4chan.org/" + board + "/#index"; } } else { - return "/" + board + "/"; + return "//boards.4chan.org/" + board + "/"; } } }; @@ -17706,7 +18096,7 @@ RelativeDates = (function() { INTERVAL: $.MINUTE / 2, init: function() { var ref; - if (((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || g.VIEW === 'index' && Conf['JSON Index'] && g.BOARD.ID !== 'f') { + if (((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || Index.enabled) { this.flush(); $.on(d, 'visibilitychange ThreadUpdate', this.flush); } @@ -17848,7 +18238,7 @@ RemoveSpoilers = (function() { }, unspoiler: function(el) { var i, len, span, spoiler, spoilers; - spoilers = $$('s', el); + spoilers = $$('s, .spoiler', el); for (i = 0, len = spoilers.length; i < len; i++) { spoiler = spoilers[i]; span = $.el('span', { @@ -19068,11 +19458,14 @@ ThreadWatcher = (function() { this.refreshButton = $('.refresh', this.dialog); this.closeButton = $('.move > .close', this.dialog); this.unreaddb = Unread.db || new DataBoard('lastReadPosts'); - this.unreadEnabled = Conf['Remember Last Read Post']; + this.unreadEnabled = Conf['Remember Last Read Post'] && Site.software === 'yotsuba'; $.on(d, 'QRPostSuccessful', this.cb.post); $.on(sc, 'click', this.toggleWatcher); $.on(this.refreshButton, 'click', this.buttonFetchAll); $.on(this.closeButton, 'click', this.toggleWatcher); + if (Site.software !== 'yotsuba') { + this.refreshButton.hidden = true; + } this.menu.addHeaderMenuEntry(); $.onExists(doc, 'body', this.addDialog); switch (g.VIEW) { @@ -19091,7 +19484,7 @@ ThreadWatcher = (function() { } Header.addShortcut('watcher', sc, 510); ThreadWatcher.fetchAuto(); - if (g.VIEW === 'index' && Conf['JSON Index'] && Conf['Menu'] && g.BOARD.ID !== 'f') { + if (Conf['Menu'] && Index.enabled) { Menu.menu.addEntry({ el: $.el('a', { href: 'javascript:;', @@ -19338,6 +19731,9 @@ ThreadWatcher = (function() { }, fetchAuto: function() { var db, interval, now, ref; + if (Site.software !== 'yotsuba') { + return; + } clearTimeout(ThreadWatcher.timeout); if (!Conf['Auto Update Thread Watcher']) { return; @@ -19347,8 +19743,7 @@ ThreadWatcher = (function() { now = Date.now(); if (!((now - interval < (ref = db.data.lastChecked || 0) && ref <= now))) { ThreadWatcher.fetchAllStatus(); - db.data.lastChecked = now; - db.save(); + db.setLastChecked(); } return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval); }, @@ -19360,22 +19755,35 @@ ThreadWatcher = (function() { } }, fetchAllStatus: function() { - var i, len, ref, thread, threads; - ThreadWatcher.db.forceSync(); - ThreadWatcher.unreaddb.forceSync(); - if ((ref = QuoteYou.db) != null) { - ref.forceSync(); - } - if (!(threads = ThreadWatcher.getAll()).length) { + var db, dbs, i, len, n, results; + if (Site.software !== 'yotsuba') { return; } - for (i = 0, len = threads.length; i < len; i++) { - thread = threads[i]; - ThreadWatcher.fetchStatus(thread); + dbs = [ThreadWatcher.db, ThreadWatcher.unreaddb, QuoteYou.db].filter(function(x) { + return x; + }); + n = 0; + results = []; + for (i = 0, len = dbs.length; i < len; i++) { + db = dbs[i]; + results.push(db.forceSync(function() { + var j, len1, thread, threads; + if ((++n) === dbs.length) { + threads = ThreadWatcher.getAll(); + for (j = 0, len1 = threads.length; j < len1; j++) { + thread = threads[j]; + ThreadWatcher.fetchStatus(thread); + } + } + })); } + return results; }, fetchStatus: function(thread, force) { var boardID, data, req, threadID; + if (Site.software !== 'yotsuba') { + return; + } boardID = thread.boardID, threadID = thread.threadID, data = thread.data; if (data.isDead && !force) { return; @@ -20438,305 +20846,12 @@ Captcha = {}; }).call(this); -(function() { - Captcha.noscript = { - lifetime: 30 * $.MINUTE, - init: function() { - var container, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript'))) { - return; - } - container = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - container: container, - input: input - }; - $.on(input, 'blur', QR.focusout); - $.on(input, 'focus', QR.focusin); - $.on(input, 'keydown', this.keydown.bind(this)); - $.on(input, 'input', function() { - if (!Captcha.cache.getCount()) { - return QR.posts[0].preventAutoPost(); - } - }); - $.on(this.nodes.container, 'click', (function(_this) { - return function() { - _this.reload(); - return _this.nodes.input.focus(); - }; - })(this)); - this.conn = new Connection(null, 'https://www.google.com', { - challenge: this.load.bind(this), - token: this.save.bind(this), - error: this.error.bind(this) - }); - $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v1', 'noscript-captcha'); - $.after(QR.nodes.com.parentNode, [container, input]); - Captcha.cache.init(); - $.on(d, 'CaptchaCount', this.count.bind(this)); - this.beforeSetup(); - return this.setup(); - }, - initFrame: function() { - var cb, conn, img, ref, ref1; - conn = new Connection(window.parent, 'https://boards.4chan.org', { - response: function(response) { - $.id('recaptcha_response_field').value = response; - return HTMLFormElement.prototype.submit.call($('form')); - } - }); - if (location.hash === '#response') { - conn.send({ - token: (ref = $('textarea')) != null ? ref.value : void 0, - error: (ref1 = $('.recaptcha_input_area')) != null ? ref1.textContent.replace(/:$/, '') : void 0 - }); - } - if (!(img = $('img'))) { - return; - } - $('form').action = '#response'; - cb = function() { - var canvas; - canvas = $.el('canvas'); - canvas.width = img.width; - canvas.height = img.height; - canvas.getContext('2d').drawImage(img, 0, 0); - return conn.send({ - challenge: canvas.toDataURL() - }); - }; - if (img.complete) { - return cb(); - } else { - return $.on(img, 'load', cb); - } - }, - timers: {}, - iframeURL: function() { - var lang, url; - url = 'https://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc'; - if (lang = Conf['captchaLanguage'].trim()) { - url += "&hl=" + (encodeURIComponent(lang)); - } - return url; - }, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = true; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - moreNeeded: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (force || Captcha.cache.needed()))) { - return; - } - if (!this.nodes.iframe) { - this.nodes.iframe = $.el('iframe', { - id: 'qr-captcha-iframe', - src: this.iframeURL() - }); - $.add(QR.nodes.el, this.nodes.iframe); - this.conn.target = this.nodes.iframe; - } else if (!this.occupied || force) { - this.nodes.iframe.src = this.iframeURL(); - } - this.occupied = true; - if (focus) { - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = false; - input.placeholder = 'Verification'; - this.count(); - $.off(input, 'focus click', this.cb.focus); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.isEnabled) { - return; - } - $.rm(this.nodes.img); - delete this.nodes.img; - $.rm(this.nodes.iframe); - delete this.nodes.iframe; - delete this.occupied; - return this.beforeSetup(); - }, - getOne: function(isReply) { - var captcha; - if ((captcha = Captcha.cache.getOne(isReply))) { - return captcha; - } else if (/\S/.test(this.nodes.input.value)) { - return (function(_this) { - return function(cb) { - _this.submitCB = cb; - return _this.sendResponse(); - }; - })(this); - } else { - return null; - } - }, - sendResponse: function() { - var response; - response = this.nodes.input.value; - if (/\S/.test(response)) { - return this.conn.send({ - response: response - }); - } - }, - save: function(token) { - var captcha; - delete this.occupied; - this.nodes.input.value = ''; - captcha = { - challenge: token, - response: 'manual_challenge', - timeout: this.timeout - }; - if (this.submitCB) { - this.submitCB(captcha); - delete this.submitCB; - if (Captcha.cache.needed()) { - return this.reload(); - } else { - return this.destroy(); - } - } else { - Captcha.cache.save(captcha); - return this.reload(); - } - }, - error: function(message) { - this.occupied = true; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(); - delete this.submitCB; - } - return QR.error("Captcha Error: " + message); - }, - load: function(src) { - var container, img, input, ref; - ref = this.nodes, container = ref.container, input = ref.input, img = ref.img; - this.occupied = true; - this.timeout = Date.now() + this.lifetime; - if (!img) { - img = this.nodes.img = new Image(); - $.one(img, 'load', this.afterSetup.bind(this)); - $.on(img, 'load', function() { - return this.hidden = false; - }); - $.add(container, img); - } - img.src = src; - input.value = ''; - clearTimeout(this.timers.expire); - return this.timers.expire = setTimeout(this.expire.bind(this), this.lifetime); - }, - count: function() { - var count, placeholder; - count = Captcha.cache.getCount(); - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - return this.nodes.input.alt = count; - }, - expire: function() { - if (!this.nodes.iframe) { - return; - } - if (!d.hidden && (Captcha.cache.needed() || d.activeElement === this.nodes.input)) { - return this.reload(); - } else { - return this.destroy(); - } - }, - reload: function() { - var ref; - this.nodes.iframe.src = this.iframeURL(); - this.occupied = true; - return (ref = this.nodes.img) != null ? ref.hidden = true : void 0; - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - if (this.nodes.iframe) { - this.reload(); - } else { - this.setup(); - } - } else if (e.keyCode === 13 && e.shiftKey) { - this.sendResponse(); - } else { - return; - } - return e.preventDefault(); - } - }; - -}).call(this); - (function() { Captcha.replace = { init: function() { if (!(d.cookie.indexOf('pass_enabled=1') < 0)) { return; } - if (location.hostname === 'sys.4chan.org' && /[?&]altc\b/.test(location.search) && Main.jsEnabled) { - $.onExists(doc, 'script[src="//www.google.com/recaptcha/api/js/recaptcha_ajax.js"]', function() { - $.global(function() { - return window.el.onload = null; - }); - return Captcha.v1.create(); - }); - return; - } - if (location.hostname === 'sys.4chan.org' && Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) { - $.ready(Captcha.replace.v1); - return; - } if (Conf['Force Noscript Captcha'] && Main.jsEnabled) { $.ready(Captcha.replace.noscript); return; @@ -20771,21 +20886,6 @@ Captcha = {}; return insert(); } }, - v1: function() { - var container, old, script; - if (!(old = $.id('g-recaptcha'))) { - return; - } - script = $.el('script', { - src: '//www.google.com/recaptcha/api/js/recaptcha_ajax.js' - }); - $.add(d.head, script); - container = $.el('div', { - id: 'captchaContainerAlt' - }); - $.replace(old, container); - return Captcha.v1.create(); - }, iframe: function(iframe) { var lang, src; if ((lang = Conf['captchaLanguage'].trim())) { @@ -20824,286 +20924,6 @@ Captcha = {}; }).call(this); -(function() { - Captcha.v1 = { - blank: "data:image/svg+xml,", - init: function() { - var container, imgContainer, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript'))) { - return; - } - imgContainer = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' - }); - $.extend(imgContainer, { - innerHTML: "" - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - img: imgContainer.firstChild, - input: input - }; - $.on(input, 'blur', QR.focusout); - $.on(input, 'focus', QR.focusin); - $.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha)); - $.on(input, 'input', function() { - if (!Captcha.cache.getCount()) { - return QR.posts[0].preventAutoPost(); - } - }); - $.on(this.nodes.img.parentNode, 'click', QR.captcha.reload.bind(QR.captcha)); - $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v1'); - $.after(QR.nodes.com.parentNode, [imgContainer, input]); - Captcha.cache.init(); - $.on(d, 'CaptchaCount', this.count.bind(this)); - this.script = $.el('script', { - src: '//www.google.com/recaptcha/api/js/recaptcha_ajax.js' - }); - $.add(d.head, this.script); - container = $.el('div', { - id: 'captchaContainerAlt', - hidden: true - }); - $.add(d.body, container); - this.beforeSetup(); - if (Conf['Auto-load captcha']) { - this.setup(); - } - new MutationObserver(this.afterSetup).observe(container, { - childList: true - }); - return this.afterSetup(); - }, - create: function() { - var cont, lang; - cont = $.id('captchaContainerAlt'); - if (this.occupied) { - return; - } - this.occupied = true; - if ((lang = Conf['captchaLanguage'].trim())) { - cont.dataset.lang = lang; - } - $.onExists(cont, '#recaptcha_image', function(image) { - return $.on(image, 'click', function() { - if ($.id('recaptcha_challenge_image')) { - return $.global(function() { - return window.Recaptcha.reload(); - }); - } - }); - }); - $.onExists(cont, '#recaptcha_response_field', function(field) { - $.on(field, 'keydown', function(e) { - if (e.keyCode === 8 && !field.value) { - return $.global(function() { - return window.Recaptcha.reload(); - }); - } - }); - if (location.hostname === 'sys.4chan.org') { - return field.focus(); - } - }); - return $.global(function() { - var container, options, script; - container = document.getElementById('captchaContainerAlt'); - options = { - theme: 'clean', - lang: container.dataset.lang - }; - if (window.Recaptcha) { - return window.Recaptcha.create('6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', container, options); - } else { - script = document.head.querySelector('script[src="//www.google.com/recaptcha/api/js/recaptcha_ajax.js"]'); - return script.addEventListener('load', function() { - return window.Recaptcha.create('6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', container, options); - }, false); - } - }); - }, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var img, input, ref; - ref = this.nodes, img = ref.img, input = ref.input; - img.parentNode.hidden = true; - img.src = this.blank; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - moreNeeded: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (force || Captcha.cache.needed()))) { - return; - } - this.create(); - if (focus) { - $.addClass(QR.nodes.el, 'focus'); - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var challenge, img, input, ref, setLifetime; - if (!(challenge = $.id('recaptcha_challenge_field_holder'))) { - return; - } - if (challenge === QR.captcha.nodes.challenge) { - return; - } - setLifetime = function(e) { - return QR.captcha.lifetime = e.detail; - }; - $.on(window, 'captcha:timeout', setLifetime); - $.global(function() { - return window.dispatchEvent(new CustomEvent('captcha:timeout', { - detail: window.RecaptchaState.timeout - })); - }); - $.off(window, 'captcha:timeout', setLifetime); - ref = QR.captcha.nodes, img = ref.img, input = ref.input; - img.parentNode.hidden = false; - input.placeholder = 'Verification'; - QR.captcha.count(); - $.off(input, 'focus click', QR.captcha.cb.focus); - QR.captcha.nodes.challenge = challenge; - new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, { - childList: true, - subtree: true, - attributes: true - }); - QR.captcha.load(); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.script) { - return; - } - $.global(function() { - return window.Recaptcha.destroy(); - }); - delete this.occupied; - if (this.nodes) { - return this.beforeSetup(); - } - }, - getOne: function(isReply) { - var captcha, challenge, response, timeout; - if ((captcha = Captcha.cache.getOne(isReply))) { - return captcha; - } else { - challenge = this.nodes.img.alt; - timeout = this.timeout; - if (/\S/.test(response = this.nodes.input.value)) { - this.destroy(); - return { - challenge: challenge, - response: response, - timeout: timeout - }; - } else { - return null; - } - } - }, - save: function() { - var response; - if (!/\S/.test(response = this.nodes.input.value)) { - return; - } - this.nodes.input.value = ''; - Captcha.cache.save({ - challenge: this.nodes.img.alt, - response: response, - timeout: this.timeout - }); - this.destroy(); - return this.setup(false, true); - }, - load: function() { - var challenge, challenge_image; - if ($('#captchaContainerAlt[class~="recaptcha_is_showing_audio"]')) { - this.nodes.img.src = this.blank; - return; - } - if (!this.nodes.challenge.firstChild) { - return; - } - if (!(challenge_image = $.id('recaptcha_challenge_image'))) { - return; - } - this.timeout = Date.now() + this.lifetime * $.SECOND - $.MINUTE; - challenge = this.nodes.challenge.firstChild.value; - this.nodes.img.alt = challenge; - this.nodes.img.src = challenge_image.src; - return this.nodes.input.value = ''; - }, - count: function() { - var count, placeholder; - count = Captcha.cache.getCount(); - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - return this.nodes.input.alt = count; - }, - reload: function(focus) { - $.global(function() { - if (window.Recaptcha.type === 'image') { - window.Recaptcha.reload(); - } else { - window.Recaptcha.switch_type('image'); - } - return window.Recaptcha.should_focus = false; - }); - if (focus) { - return this.nodes.input.focus(); - } - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - this.reload(); - } else if (e.keyCode === 13 && e.shiftKey) { - this.save(); - } else { - return; - } - return e.preventDefault(); - } - }; - -}).call(this); - (function() { var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -21474,7 +21294,7 @@ QR = (function() { 'video/webm': 'webm' }, init: function() { - var info, noscript, sc, version; + var sc; if (!Conf['Quick Reply']) { return; } @@ -21482,8 +21302,7 @@ QR = (function() { if (g.VIEW === 'archive') { return; } - version = Conf[g.VIEW === 'thread' ? 'Use Recaptcha v1' : 'Use Recaptcha v1 on Index'] && (Main.jsEnabled || location.protocol === 'https:') ? (noscript = location.protocol === 'https:' && (Conf['Force Noscript Captcha for v1'] || !Main.jsEnabled), (info = typeof GM !== "undefined" && GM !== null ? GM.info : void 0) && info.scriptHandler === 'Greasemonkey' && /^4\./.test(info.version) ? noscript = false : void 0, noscript ? 'noscript' : 'v1') : 'v2'; - this.captcha = Captcha[version]; + this.captcha = Captcha.v2; $.on(d, '4chanXInitFinished', function() { return BoardConfig.ready(QR.initReady); }); @@ -21836,7 +21655,9 @@ QR = (function() { $.replace(node, $.tn('\n>')); } } - Post.prototype.insertTags(frag); + if (typeof Site.insertTags === "function") { + Site.insertTags(frag); + } ref3 = $$('.linkify[data-original]', frag); for (n = 0, len2 = ref3.length; n < len2; n++) { node = ref3[n]; @@ -24297,9 +24118,11 @@ QuoteYou = (function() { return Conf['Remember Your Posts'] = enabled; }); $.on(d, 'QRPostSuccessful', function(e) { - var boardID, postID, ref, threadID; - $.forceSync('Remember Your Posts'); - if (Conf['Remember Your Posts']) { + return $.get('Remember Your Posts', Conf['Remember Your Posts'], function(items) { + var boardID, postID, ref, threadID; + if (!items['Remember Your Posts']) { + return; + } ref = e.detail, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; return QuoteYou.db.set({ boardID: boardID, @@ -24307,7 +24130,7 @@ QuoteYou = (function() { postID: postID, val: true }); - } + }); }); if ((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') { return; @@ -24619,7 +24442,8 @@ Quotify = (function() { }).call(this); Main = (function() { - var Main; + var Main, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Main = { init: function() { @@ -24638,12 +24462,6 @@ Main = (function() { w['4chan X antidup'] = true; } catch (_error) {} if (location.hostname === 'www.google.com') { - if (location.pathname === '/recaptcha/api/noscript') { - $.ready(function() { - return Captcha.noscript.initFrame(); - }); - return; - } $.get('Captcha Fixes', true, function(arg) { var enabled; enabled = arg['Captcha Fixes']; @@ -24698,9 +24516,7 @@ Main = (function() { ref1 = DataBoard.keys; for (j = 0, len = ref1.length; j < len; j++) { db = ref1[j]; - Conf[db] = { - boards: {} - }; + Conf[db] = {}; } Conf['boardConfig'] = { boards: {} @@ -24719,13 +24535,15 @@ Main = (function() { Conf['QR Shortcut'] = true; Conf['Bottom QR Link'] = true; Conf['Toggleable Thread Watcher'] = true; - ($.getSync || $.get)({ - 'jsWhitelist': Conf['jsWhitelist'] - }, function(arg) { - var jsWhitelist; - jsWhitelist = arg.jsWhitelist; - return $.addCSP("script-src " + (jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim())); - }); + if (/\.4chan\.org$/.test(location.hostname)) { + ($.getSync || $.get)({ + 'jsWhitelist': Conf['jsWhitelist'] + }, function(arg) { + var jsWhitelist; + jsWhitelist = arg.jsWhitelist; + return $.addCSP("script-src " + (jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim())); + }); + } items = {}; for (key in Conf) { items[key] = void 0; @@ -24733,7 +24551,7 @@ Main = (function() { items['previousversion'] = void 0; return ($.getSync || $.get)(items, function(items) { var ref2; - if (!$.perProtocolSettings && ((ref2 = items['Redirect to HTTPS']) != null ? ref2 : Conf['Redirect to HTTPS']) && location.protocol !== 'https:') { + if (!$.perProtocolSettings && /\.4chan\.org$/.test(location.hostname) && ((ref2 = items['Redirect to HTTPS']) != null ? ref2 : Conf['Redirect to HTTPS']) && location.protocol !== 'https:') { location.replace('https:' + location.host + location.pathname + location.search + location.hash); return; } @@ -24753,7 +24571,7 @@ Main = (function() { val = Conf[key]; Conf[key] = (ref3 = items[key]) != null ? ref3 : val; } - return Main.initFeatures(); + return Site.init(Main.initFeatures); }); }); }, @@ -24773,19 +24591,17 @@ Main = (function() { }); }, initFeatures: function() { - var err, feature, hostname, j, len, match, name, pathname, ref, ref1, ref2, ref3, search; + var err, feature, hostname, j, len, match, name, pathname, ref, ref1, ref2, search; hostname = location.hostname, search = location.search; pathname = location.pathname.split(/\/+/); if (hostname !== 'www.4chan.org') { g.BOARD = new Board(pathname[1]); } - if (hostname === 'boards.4chan.org' || hostname === 'sys.4chan.org' || hostname === 'www.4chan.org') { - $.global(function() { - document.documentElement.classList.add('js-enabled'); - return window.FCX = {}; - }); - Main.jsEnabled = $.hasClass(doc, 'js-enabled'); - } + $.global(function() { + document.documentElement.classList.add('js-enabled'); + return window.FCX = {}; + }); + Main.jsEnabled = $.hasClass(doc, 'js-enabled'); switch (hostname) { case 'www.4chan.org': $.onExists(doc, 'body', function() { @@ -24820,8 +24636,8 @@ Main = (function() { $.asap((function() { return d.readyState !== 'loading'; }), function() { - var ref, video; - if (Conf['404 Redirect'] && ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found')) { + var video; + if (Conf['404 Redirect'] && (typeof Site.is404 === "function" ? Site.is404() : void 0)) { return Redirect.navigate('file', { boardID: g.BOARD.ID, filename: pathname[pathname.length - 1] @@ -24840,15 +24656,15 @@ Main = (function() { }); return; } - if (hostname !== 'boards.4chan.org') { + if (typeof Site.isAuxiliaryPage === "function" ? Site.isAuxiliaryPage() : void 0) { return; } if ((ref = pathname[2]) === 'thread' || ref === 'res') { g.VIEW = 'thread'; - g.THREADID = +pathname[3]; - } else if ((ref1 = pathname[2]) === 'catalog' || ref1 === 'archive') { - g.VIEW = pathname[2]; - } else if (pathname[2].match(/^\d*$/)) { + g.THREADID = +pathname[3].replace('.html', ''); + } else if (/^(?:catalog|archive)(?:\.html)?$/.test(pathname[2])) { + g.VIEW = pathname[2].replace('.html', ''); + } else if (/^(?:index|\d*)(?:\.html)?$/.test(pathname[2])) { g.VIEW = 'index'; } else { return; @@ -24856,9 +24672,12 @@ Main = (function() { g.threads = new SimpleDict(); g.posts = new SimpleDict(); $.onExists(doc, 'body', Main.initStyle); - ref2 = Main.features; - for (j = 0, len = ref2.length; j < len; j++) { - ref3 = ref2[j], name = ref3[0], feature = ref3[1]; + ref1 = Main.features; + for (j = 0, len = ref1.length; j < len; j++) { + ref2 = ref1[j], name = ref2[0], feature = ref2[1]; + if (Site.disabledFeatures && indexOf.call(Site.disabledFeatures, name) >= 0) { + continue; + } try { feature.init(); } catch (_error) { @@ -24879,6 +24698,8 @@ Main = (function() { if ((ref = $('link[href*=mobile]', d.head)) != null) { ref.disabled = true; } + doc.dataset.host = location.host; + $.addClass(doc, "sw-" + Site.software); $.addClass(doc, g.VIEW === 'thread' ? 'thread-view' : g.VIEW); $.onExists(doc, '.ad-cnt, .adg-rects > .desktop', function(ad) { return $.onExists(ad, 'img, iframe', function() { @@ -24923,7 +24744,7 @@ Main = (function() { mainStyleSheet = $('link[title=switch]', d.head); styleSheets = $$('link[rel="alternate stylesheet"]', d.head); setStyle = function() { - var bgColor, div, j, len, styleSheet; + var bgColor, div, j, len, s, styleSheet; $.rmClass(doc, style); style = null; for (j = 0, len = styleSheets.length; j < len; j++) { @@ -24943,14 +24764,17 @@ Main = (function() { $.addClass(doc, style); return $.rm(Main.bgColorStyle); } else { - div = $.el('div', { - className: 'reply' - }); - div.style.cssText = 'position: absolute; visibility: hidden;'; + div = Site.bgColoredEl(); + div.style.position = 'absolute'; + div.style.visibility = 'hidden'; $.add(d.body, div); bgColor = window.getComputedStyle(div).backgroundColor; $.rm(div); - Main.bgColorStyle.textContent = ".dialog, .suboption-list > div:last-of-type, :root.catalog-hover-expand .catalog-container:hover > .post {\n background-color: " + bgColor + ";\n}"; + if (!/^rgb\(/.test(bgColor)) { + s = window.getComputedStyle(d.body); + bgColor = s.backgroundColor + " " + s.backgroundImage + " " + s.backgroundRepeat + " " + s.backgroundPosition; + } + Main.bgColorStyle.textContent = ".dialog, .suboption-list > div:last-of-type, :root.catalog-hover-expand .catalog-container:hover > .post {\n background: " + bgColor + ";\n}"; return $.after($.id('fourchanx-css'), Main.bgColorStyle); } }; @@ -24964,23 +24788,22 @@ Main = (function() { }); }, initReady: function() { - var msg, ref, ref1, ref2; - if (g.VIEW === 'thread' && (((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found') || ($('.board') && !$('.opContainer')))) { - ThreadWatcher.set404(g.BOARD.ID, g.THREADID, function() { - if (Conf['404 Redirect']) { - return Redirect.navigate('thread', { - boardID: g.BOARD.ID, - threadID: g.THREADID, - postID: +location.hash.match(/\d+/) - }, "/" + g.BOARD + "/"); - } - }); + var msg; + if (typeof Site.is404 === "function" ? Site.is404() : void 0) { + if (g.VIEW === 'thread') { + ThreadWatcher.set404(g.BOARD.ID, g.THREADID, function() { + if (Conf['404 Redirect']) { + return Redirect.navigate('thread', { + boardID: g.BOARD.ID, + threadID: g.THREADID, + postID: +location.hash.match(/\d+/) + }, "/" + g.BOARD + "/"); + } + }); + } return; } - if ((ref1 = d.title) === '4chan - Temporarily Offline' || ref1 === '4chan - 404 Not Found') { - return; - } - if (((ref2 = g.VIEW) === 'index' || ref2 === 'thread') && !$('.board + *')) { + if (typeof Site.isIncomplete === "function" ? Site.isIncomplete() : void 0) { msg = $.el('div', { innerHTML: "The page didn't load completely.
Some features may not work unless you reload." }); @@ -24989,7 +24812,7 @@ Main = (function() { }); new Notice('warning', msg); } - if (!(Conf['JSON Index'] && g.VIEW === 'index')) { + if (!Index.enabled) { return Main.initThread(); } else { Main.expectInitFinished = true; @@ -24997,22 +24820,27 @@ Main = (function() { } }, initThread: function() { - var board, err, errors, j, k, len, len1, m, postRoot, posts, ref, ref1, scriptData, thread, threadRoot, threads; - if ((board = $('.board'))) { + var board, boardID, boardObj, err, errors, j, k, len, len1, postRoot, postRoots, posts, ref, s, thread, threadRoot, threads; + s = Site.selectors; + if ((board = $(s.board))) { threads = []; posts = []; - ref = $$('.board > .thread', board); + ref = $$(s.thread, board); for (j = 0, len = ref.length; j < len; j++) { threadRoot = ref[j]; - thread = new Thread(+threadRoot.id.slice(1), g.BOARD); + boardObj = (boardID = threadRoot.dataset.board) ? g.boards[boardID] || new Board(boardID) : g.BOARD; + thread = new Thread(+threadRoot.id.match(/\d*$/)[0], boardObj); thread.nodes.root = threadRoot; threads.push(thread); - ref1 = $$('.thread > .postContainer', threadRoot); - for (k = 0, len1 = ref1.length; k < len1; k++) { - postRoot = ref1[k]; - if ($('.postMessage', postRoot)) { + postRoots = $$(s.postContainer, threadRoot); + if (Site.isOPContainerThread) { + postRoots.unshift(threadRoot); + } + for (k = 0, len1 = postRoots.length; k < len1; k++) { + postRoot = postRoots[k]; + if ($(s.comment, postRoot)) { try { - posts.push(new Post(postRoot, thread, g.BOARD)); + posts.push(new Post(postRoot, thread, thread.board)); } catch (_error) { err = _error; if (!errors) { @@ -25030,20 +24858,9 @@ Main = (function() { Main.handleErrors(errors); } if (g.VIEW === 'thread') { - scriptData = Get.scriptData(); - threads[0].postLimit = /\bbumplimit *= *1\b/.test(scriptData); - threads[0].fileLimit = /\bimagelimit *= *1\b/.test(scriptData); - threads[0].ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : void 0; - } - if (g.BOARD.ID === 'f' && g.VIEW === 'thread') { - $.ajax(location.protocol + "//a.4cdn.org/f/thread/" + g.THREADID + ".json", { - timeout: $.MINUTE, - onloadend: function() { - if (this.response && posts[0].file) { - return posts[0].file.text.dataset.md5 = posts[0].file.MD5 = this.response.posts[0].md5; - } - } - }); + if (typeof Site.parseThreadMetadata === "function") { + Site.parseThreadMetadata(threads[0]); + } } Main.callbackNodes('Thread', threads); return Main.callbackNodesDB('Post', posts, function() { @@ -25168,9 +24985,8 @@ Main = (function() { }; }, isThisPageLegit: function() { - var ref; if (!('thisPageIsLegit' in Main)) { - Main.thisPageIsLegit = location.hostname === 'boards.4chan.org' && !$('link[href*="favicon-status.ico"]', d.head) && ((ref = d.title) !== '4chan - Temporarily Offline' && ref !== '4chan - Error' && ref !== '504 Gateway Time-out'); + Main.thisPageIsLegit = Site.isThisPageLegit ? Site.isThisPageLegit() : !/^[45]\d\d\b/.test(document.title); } return Main.thisPageIsLegit; }, diff --git a/builds/4chan-X.crx b/builds/4chan-X.crx index a14c23fa3..fa018b43f 100644 Binary files a/builds/4chan-X.crx and b/builds/4chan-X.crx differ diff --git a/builds/4chan-X.meta.js b/builds/4chan-X.meta.js index 5a4f493b1..f9932d317 100644 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.13.15.5 +// @version 1.14.0.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -24,8 +24,6 @@ // @include https://www.google.com/recaptcha/api2/bframe?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include http://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include http://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include https://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @exclude http://www.4chan.org/pass // @exclude https://www.4chan.org/pass // @exclude http://www.4chan.org/pass?* diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 877ca8fee..e3ead7f0c 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.13.15.5 +// @version 1.14.0.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -24,8 +24,6 @@ // @include https://www.google.com/recaptcha/api2/bframe?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include http://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @include https://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include http://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* -// @include https://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc* // @exclude http://www.4chan.org/pass // @exclude https://www.4chan.org/pass // @exclude http://www.4chan.org/pass?* @@ -144,7 +142,7 @@ 'use strict'; -var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, Build, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume; +var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, Build, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume; var Conf, E, c, d, doc, docSet, g; @@ -159,7 +157,7 @@ docSet = function() { }; g = { - VERSION: '1.13.15.5', + VERSION: '1.14.0.0', NAMESPACE: '4chan X.', boards: {} }; @@ -510,7 +508,8 @@ Config = (function() { 'updater.position': 'bottom: 0px; left: 0px;', 'thread-watcher.position': 'top: 50px; left: 0px;', 'qr.position': 'top: 50px; right: 0px;' - } + }, + siteSoftware: "4chan.org yotsuba" }; return Config; @@ -1359,6 +1358,23 @@ body.is_catalog .thread > a > img {\n\ div.center[style] {\n\ display: none !important;\n\ }\n\ +/* Tinyboard / vichan conflicts */\n\ +#menu > .hide-thread-link {\n\ + width: auto;\n\ + height: auto;\n\ + overflow: visible;\n\ + background-image: none;\n\ +}\n\ +#menu label.entry {\n\ + display: block;\n\ +}\n\ +#fourchanx-settings label {\n\ + display: inline;\n\ +}\n\ +.intro a[href=\"javascript:;\"],\n\ +#menu a {\n\ + margin: 0;\n\ +}\n\ /* Anti-autoplay */\n\ audio.controls-added {\n\ display: block;\n\ @@ -1901,6 +1917,9 @@ div[data-checked=\"false\"] > .suboption-list {\n\ #fourchanx-settings p {\n\ margin: 1em 0px;\n\ }\n\ +#fourchanx-settings table {\n\ + margin: auto;\n\ +}\n\ .unscroll {\n\ overflow: hidden;\n\ }\n\ @@ -2472,7 +2491,7 @@ span.hide-announcement {\n\ .expanded-image > .post > .file > .fileThumb > img[data-md5] {\n\ display: none;\n\ }\n\ -.full-image {\n\ +.full-image[data-full-i-d] {\n\ display: none;\n\ cursor: pointer;\n\ }\n\ @@ -2624,7 +2643,9 @@ input[name=\"Default Volume\"] {\n\ }\n\ /* Spoiler text */\n\ :root.reveal-spoilers s,\n\ -:root.reveal-spoilers s > a {\n\ +:root.reveal-spoilers .spoiler,\n\ +:root.reveal-spoilers s > a,\n\ +:root.reveal-spoilers .spoiler > a {\n\ color: white !important;\n\ }\n\ :root.reveal-spoilers .removed-spoiler::before {\n\ @@ -2674,6 +2695,7 @@ input[name=\"Default Volume\"] {\n\ font-size: 0;\n\ }\n\ :root.anonymize .postertrip,\n\ +:root.anonymize .trip,\n\ :root.anonymize .n-pu {\n\ display: none;\n\ }\n\ @@ -2822,22 +2844,6 @@ input.field.tripped:not(:hover):not(:focus) {\n\ position: relative;\n\ top: 2px;\n\ }\n\ -/* Recaptcha v1 */\n\ -.captcha-img {\n\ - margin: 0px;\n\ - text-align: center;\n\ - background-image: #fff;\n\ - font-size: 0px;\n\ - min-height: 59px;\n\ - min-width: 302px;\n\ -}\n\ -.captcha-input {\n\ - width: 100%;\n\ - margin: 1px 0 0;\n\ -}\n\ -#qr.captcha-v1 #qr-captcha-iframe {\n\ - display: none;\n\ -}\n\ /* Recaptcha v2 */\n\ #qr .captcha-root {\n\ position: relative;\n\ @@ -3207,6 +3213,7 @@ a:only-of-type > .remove {\n\ #menu {\n\ position: fixed;\n\ outline: none;\n\ + font-weight: normal;\n\ }\n\ #menu, .submenu {\n\ border-radius: 3px;\n\ @@ -4994,6 +5001,12 @@ $ = (function() { $.syncing = {}; + $.securityCheck = function(data) { + if (location.protocol !== 'https:') { + return delete data['Redirect to HTTPS']; + } + }; + if (((typeof GM !== "undefined" && GM !== null ? GM.deleteValue : void 0) != null) && window.BroadcastChannel && (typeof GM_addValueChangeListener === "undefined" || GM_addValueChangeListener === null)) { $.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync'); $.on($.syncChannel, 'message', function(e) { @@ -5060,6 +5073,7 @@ $ = (function() { }); $.set = $.oneItemSugar(function(items, cb) { var key, val; + $.securityCheck(items); return Promise.all((function() { var results; results = []; @@ -5248,6 +5262,7 @@ $ = (function() { return cb(items); }; $.set = $.oneItemSugar(function(items, cb) { + $.securityCheck(items); return $.queueTask(function() { var key, value; for (key in items) { @@ -5602,7 +5617,7 @@ DataBoard = (function() { var init; this.key = key1; this.onSync = bind(this.onSync, this); - this.data = Conf[this.key]; + this.initData(Conf[this.key]); $.sync(this.key, this.onSync); if (!dontClean) { this.clean(); @@ -5619,35 +5634,103 @@ DataBoard = (function() { $.on(d, '4chanXInitFinished', init); } - DataBoard.prototype.save = function(cb) { - return $.set(this.key, this.data, cb); + DataBoard.prototype.initData = function(allData) { + var base, name; + this.allData = allData; + if (Site.hostname === '4chan.org' && this.allData.boards) { + return this.data = this.allData; + } else { + return this.data = ((base = this.allData)[name = Site.hostname] || (base[name] = { + boards: {} + })); + } + }; + + DataBoard.prototype.changes = []; + + DataBoard.prototype.save = function(change, cb) { + var changes, snapshot1; + snapshot1 = JSON.stringify(this.allData); + change(); + changes = this.changes; + changes.push(change); + return $.get(this.key, { + boards: {} + }, (function(_this) { + return function(items) { + var c, i, len, snapshot2; + _this.initData(items[_this.key]); + snapshot2 = JSON.stringify(_this.allData); + for (i = 0, len = changes.length; i < len; i++) { + c = changes[i]; + c(); + } + return $.set(_this.key, _this.allData, function() { + _this.changes = []; + if (snapshot1 !== snapshot2) { + if (typeof _this.sync === "function") { + _this.sync(); + } + } + return typeof cb === "function" ? cb() : void 0; + }); + }; + })(this)); + }; + + DataBoard.prototype.forceSync = function(cb) { + var changes, snapshot1; + snapshot1 = JSON.stringify(this.allData); + changes = this.changes; + return $.get(this.key, { + boards: {} + }, (function(_this) { + return function(items) { + var c, i, len, snapshot2; + _this.initData(items[_this.key]); + snapshot2 = JSON.stringify(_this.allData); + for (i = 0, len = changes.length; i < len; i++) { + c = changes[i]; + c(); + } + if (snapshot1 !== snapshot2) { + if (typeof _this.sync === "function") { + _this.sync(); + } + } + return typeof cb === "function" ? cb() : void 0; + }; + })(this)); }; DataBoard.prototype["delete"] = function(arg) { - var boardID, postID, ref, threadID; + var boardID, postID, threadID; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID; - $.forceSync(this.key); - if (postID) { - if (!((ref = this.data.boards[boardID]) != null ? ref[threadID] : void 0)) { - return; - } - delete this.data.boards[boardID][threadID][postID]; - this.deleteIfEmpty({ - boardID: boardID, - threadID: threadID - }); - } else if (threadID) { - if (!this.data.boards[boardID]) { - return; - } - delete this.data.boards[boardID][threadID]; - this.deleteIfEmpty({ - boardID: boardID - }); - } else { - delete this.data.boards[boardID]; - } - return this.save(); + return this.save((function(_this) { + return function() { + var ref; + if (postID) { + if (!((ref = _this.data.boards[boardID]) != null ? ref[threadID] : void 0)) { + return; + } + delete _this.data.boards[boardID][threadID][postID]; + return _this.deleteIfEmpty({ + boardID: boardID, + threadID: threadID + }); + } else if (threadID) { + if (!_this.data.boards[boardID]) { + return; + } + delete _this.data.boards[boardID][threadID]; + return _this.deleteIfEmpty({ + boardID: boardID + }); + } else { + return delete _this.data.boards[boardID]; + } + }; + })(this)); }; DataBoard.prototype.deleteIfEmpty = function(arg) { @@ -5666,45 +5749,59 @@ DataBoard = (function() { }; DataBoard.prototype.set = function(data, cb) { - $.forceSync(this.key); - return this.setUnsafe(data, cb); + return this.save((function(_this) { + return function() { + return _this.setUnsafe(data); + }; + })(this), cb); }; - DataBoard.prototype.setUnsafe = function(arg, cb) { + DataBoard.prototype.setUnsafe = function(arg) { var base, base1, base2, boardID, postID, threadID, val; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, val = arg.val; if (postID !== void 0) { - ((base = ((base1 = this.data.boards)[boardID] || (base1[boardID] = {})))[threadID] || (base[threadID] = {}))[postID] = val; + return ((base = ((base1 = this.data.boards)[boardID] || (base1[boardID] = {})))[threadID] || (base[threadID] = {}))[postID] = val; } else if (threadID !== void 0) { - ((base2 = this.data.boards)[boardID] || (base2[boardID] = {}))[threadID] = val; + return ((base2 = this.data.boards)[boardID] || (base2[boardID] = {}))[threadID] = val; } else { - this.data.boards[boardID] = val; + return this.data.boards[boardID] = val; } - return this.save(cb); }; DataBoard.prototype.extend = function(arg, cb) { - var boardID, i, key, len, oldVal, postID, ref, rm, threadID, val; + var boardID, postID, rm, threadID, val; boardID = arg.boardID, threadID = arg.threadID, postID = arg.postID, val = arg.val, rm = arg.rm; - $.forceSync(this.key); - oldVal = this.get({ - boardID: boardID, - threadID: threadID, - postID: postID, - val: {} - }); - ref = rm || []; - for (i = 0, len = ref.length; i < len; i++) { - key = ref[i]; - delete oldVal[key]; - } - $.extend(oldVal, val); - return this.setUnsafe({ - boardID: boardID, - threadID: threadID, - postID: postID, - val: oldVal - }, cb); + return this.save((function(_this) { + return function() { + var i, key, len, oldVal, ref; + oldVal = _this.get({ + boardID: boardID, + threadID: threadID, + postID: postID, + val: {} + }); + ref = rm || []; + for (i = 0, len = ref.length; i < len; i++) { + key = ref[i]; + delete oldVal[key]; + } + $.extend(oldVal, val); + return _this.setUnsafe({ + boardID: boardID, + threadID: threadID, + postID: postID, + val: oldVal + }); + }; + })(this), cb); + }; + + DataBoard.prototype.setLastChecked = function() { + return this.save((function(_this) { + return function() { + return _this.data.lastChecked = Date.now(); + }; + })(this)); }; DataBoard.prototype.get = function(arg) { @@ -5730,13 +5827,11 @@ DataBoard = (function() { return val || defaultValue; }; - DataBoard.prototype.forceSync = function() { - return $.forceSync(this.key); - }; - DataBoard.prototype.clean = function() { var boardID, now, ref, ref1, val; - $.forceSync(this.key); + if (Site.software !== 'yotsuba') { + return; + } ref = this.data.boards; for (boardID in ref) { val = ref[boardID]; @@ -5802,13 +5897,11 @@ DataBoard = (function() { this.deleteIfEmpty({ boardID: boardID }); - return this.save(); + return $.set(this.key, this.allData); }; DataBoard.prototype.onSync = function(data) { - this.data = data || { - boards: {} - }; + this.initData(data); return typeof this.sync === "function" ? this.sync() : void 0; }; @@ -6262,7 +6355,6 @@ Notice = (function() { Post = (function() { var Post, - slice = [].slice, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Post = (function() { @@ -6271,31 +6363,25 @@ Post = (function() { }; function Post(root, thread, board) { - var clone, icon, j, k, len, len1, ref, ref1, ref10, ref11, ref12, ref13, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, type; + var clone, j, k, key, len, len1, ref, ref1, ref10, ref11, ref12, ref13, ref14, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, selector; this.thread = thread; this.board = board; - this.ID = +root.id.slice(2); + this.ID = +root.id.match(/\d*$/)[0]; this.threadID = this.thread.ID; this.boardID = this.board.ID; this.fullID = this.board + "." + this.ID; this.context = this; root.dataset.fullID = this.fullID; this.nodes = this.parseNodes(root); - if (!(this.isReply = $.hasClass(this.nodes.post, 'reply'))) { + if (!(this.isReply = this.ID !== this.threadID)) { this.thread.OP = this; - if (this.boardID === 'f') { - ref = ['Sticky', 'Closed']; - for (j = 0, len = ref.length; j < len; j++) { - type = ref[j]; - if ((icon = $("img[alt=" + type + "]", this.nodes.info))) { - $.addClass(icon, (type.toLowerCase()) + "Icon", 'retina'); - } - } + ref = ['isSticky', 'isClosed', 'isArchived']; + for (j = 0, len = ref.length; j < len; j++) { + key = ref[j]; + this.thread[key] = (selector = Site.selectors.icons[key]) ? !!$(selector, this.nodes.info) : false; } - this.thread.isArchived = !!$('.archivedIcon', this.nodes.info); - this.thread.isSticky = !!$('.stickyIcon', this.nodes.info); - this.thread.isClosed = this.thread.isArchived || !!$('.closedIcon', this.nodes.info); if (this.thread.isArchived) { + this.thread.isClosed = true; this.thread.kill(); } } @@ -6309,7 +6395,7 @@ Post = (function() { flagCode: (ref7 = this.nodes.flag) != null ? (ref8 = ref7.className.match(/flag-(\w+)/)) != null ? ref8[1].toUpperCase() : void 0 : void 0, flagCodeTroll: (ref9 = this.nodes.flag) != null ? (ref10 = ref9.src) != null ? (ref11 = ref10.match(/(\w+)\.gif$/)) != null ? ref11[1].toUpperCase() : void 0 : void 0 : void 0, flag: (ref12 = this.nodes.flag) != null ? ref12.title : void 0, - date: this.nodes.date ? new Date(this.nodes.date.dataset.utc * 1000) : void 0 + date: this.nodes.date ? new Date(((ref13 = this.nodes.date.getAttribute('datetime')) != null ? ref13.trim() : void 0) || (this.nodes.date.dataset.utc * 1000)) : void 0 }; if (Conf['Anonymize']) { this.info.nameBlock = 'Anonymous'; @@ -6331,9 +6417,9 @@ Post = (function() { if (g.posts[this.fullID]) { this.isRebuilt = true; this.clones = g.posts[this.fullID].clones; - ref13 = this.clones; - for (k = 0, len1 = ref13.length; k < len1; k++) { - clone = ref13[k]; + ref14 = this.clones; + for (k = 0, len1 = ref14.length; k < len1; k++) { + clone = ref14[k]; clone.origin = this; } } @@ -6343,32 +6429,28 @@ Post = (function() { } Post.prototype.parseNodes = function(root) { - var info, nodes, post; - post = $('.post', root); - info = $('.postInfo', post); + var info, key, nodes, post, ref, s, selector; + s = Site.selectors; + post = $(s.post, root) || root; + info = $(s.infoRoot, post); nodes = { root: root, post: post, info: info, - subject: $('.subject', info), - name: $('.name', info), - email: $('.useremail', info), - tripcode: $('.postertrip', info), - uniqueIDRoot: $('.posteruid', info), - uniqueID: $('.posteruid > .hand', info), - capcode: $('.capcode.hand', info), - pass: $('.n-pu', info), - flag: $('.flag, .countryFlag', info), - date: $('.dateTime', info), - nameBlock: $('.nameBlock', info), - quote: $('.postNum > a:nth-of-type(2)', info), - reply: $('.replylink', info), - fileRoot: $('.file', post), - comment: $('.postMessage', post), + comment: $(s.comment, post), quotelinks: [], archivelinks: [], embedlinks: [] }; + ref = s.info; + for (key in ref) { + selector = ref[key]; + nodes[key] = $(selector, info); + } + if (typeof Site.parseNodes === "function") { + Site.parseNodes(this, nodes); + } + nodes.uniqueIDRoot || (nodes.uniqueIDRoot = nodes.uniqueID); if ($.engine === 'edge') { Object.defineProperty(nodes, 'backlinks', { configurable: true, @@ -6387,7 +6469,9 @@ Post = (function() { var bq; this.nodes.comment.normalize(); this.nodes.commentClean = bq = this.nodes.comment.cloneNode(true); - this.cleanComment(bq); + if (typeof Site.cleanComment === "function") { + Site.cleanComment(bq); + } return this.info.comment = this.nodesToText(bq); }; @@ -6397,14 +6481,18 @@ Post = (function() { if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) { this.cleanSpoilers(bq); } - this.cleanCommentDisplay(bq); + if (typeof Site.cleanCommentDisplay === "function") { + Site.cleanCommentDisplay(bq); + } return this.nodesToText(bq).trim().replace(/\s+$/gm, ''); }; Post.prototype.commentOrig = function() { var bq; bq = this.nodes.commentClean.cloneNode(true); - this.insertTags(bq); + if (typeof Site.insertTags === "function") { + Site.insertTags(bq); + } return this.nodesToText(bq); }; @@ -6419,58 +6507,19 @@ Post = (function() { return text; }; - Post.prototype.cleanComment = function(bq) { - var abbr, br, i, j, k, len, node, ref; - if ((abbr = $('.abbr', bq))) { - ref = $$('.abbr + br, .exif', bq); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - $.rm(node); - } - for (i = k = 0; k < 2; i = ++k) { - if ((br = abbr.previousSibling) && br.nodeName === 'BR') { - $.rm(br); - } - } - return $.rm(abbr); - } - }; - Post.prototype.cleanSpoilers = function(bq) { var j, len, node, spoilers; - spoilers = $$('s', bq); + spoilers = $$(Site.selectors.spoiler, bq); for (j = 0, len = spoilers.length; j < len; j++) { node = spoilers[j]; $.replace(node, $.tn('[spoiler]')); } }; - Post.prototype.cleanCommentDisplay = function(bq) { - var b; - if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) { - $.rm(b); - } - return $.rm($('.fortune', bq)); - }; - - Post.prototype.insertTags = function(bq) { - var j, k, len, len1, node, ref, ref1; - ref = $$('s, .removed-spoiler', bq); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - $.replace(node, [$.tn('[spoiler]')].concat(slice.call(node.childNodes), [$.tn('[/spoiler]')])); - } - ref1 = $$('.prettyprint', bq); - for (k = 0, len1 = ref1.length; k < len1; k++) { - node = ref1[k]; - $.replace(node, [$.tn('[code]')].concat(slice.call(node.childNodes), [$.tn('[/code]')])); - } - }; - Post.prototype.parseQuotes = function() { var j, len, quotelink, ref; this.quotes = []; - ref = $$(':not(pre) > .quotelink', this.nodes.comment); + ref = $$(Site.selectors.quotelink, this.nodes.comment); for (j = 0, len = ref.length; j < len; j++) { quotelink = ref[j]; this.parseQuote(quotelink); @@ -6479,7 +6528,7 @@ Post = (function() { Post.prototype.parseQuote = function(quotelink) { var fullID, match; - match = quotelink.href.match(/^https?:\/\/boards\.4chan\.org\/+([^\/]+)\/+(?:res|thread)\/+\d+(?:[\/?][^#]*)?#p(\d+)$/); + match = quotelink.href.match(Site.regexp.quotelink); if (!(match || (this.isClone && quotelink.dataset.postID))) { return; } @@ -6487,55 +6536,39 @@ Post = (function() { if (this.isClone) { return; } - fullID = match[1] + "." + match[2]; + fullID = match[1] + "." + match[3]; if (indexOf.call(this.quotes, fullID) < 0) { return this.quotes.push(fullID); } }; Post.prototype.parseFile = function() { - var fileRoot, fileText, info, link, m, ref, ref1, ref2, size, thumb, unit; - fileRoot = this.nodes.fileRoot; - if (!fileRoot) { + var file, key, ref, ref1, selector, size, unit; + file = {}; + ref = Site.selectors.file; + for (key in ref) { + selector = ref[key]; + file[key] = $(selector, this.nodes.root); + } + file.thumbLink = (ref1 = file.thumb) != null ? ref1.parentNode : void 0; + if (!(file.text && file.link)) { return; } - if (!(link = $('.fileText > a, .fileText-original > a', fileRoot))) { + if (!Site.parseFile(this, file)) { return; } - if (!(info = (ref = link.nextSibling) != null ? ref.textContent.match(/\(([\d.]+ [KMG]?B).*\)/) : void 0)) { - return; - } - fileText = fileRoot.firstElementChild; - this.file = { - text: fileText, - link: link, - url: link.href, - name: fileText.title || link.title || link.textContent, - size: info[1], - isImage: /(jpg|png|gif)$/i.test(link.href), - isVideo: /webm$/i.test(link.href), - dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, - tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0, - MD5: fileText.dataset.md5 - }; - size = +this.file.size.match(/[\d.]+/)[0]; - unit = ['B', 'KB', 'MB', 'GB'].indexOf(this.file.size.match(/\w+$/)[0]); + $.extend(file, { + url: file.link.href, + isImage: /(jpg|png|gif)$/i.test(file.link.href), + isVideo: /(webm|mp4)$/i.test(file.link.href) + }); + size = +file.size.match(/[\d.]+/)[0]; + unit = ['B', 'KB', 'MB', 'GB'].indexOf(file.size.match(/\w+$/)[0]); while (unit-- > 0) { size *= 1024; } - this.file.sizeInBytes = size; - if ((thumb = $('a.fileThumb > [data-md5]', fileRoot))) { - $.extend(this.file, { - thumb: thumb, - thumbLink: thumb.parentNode, - thumbURL: thumb.src, - MD5: thumb.dataset.md5, - isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') - }); - if (this.file.isSpoiler) { - return this.file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.board + "/" + m[0] + "s.jpg" : void 0; - } - } + file.sizeInBytes = size; + return this.file = file; }; Post.deadMark = $.el('span', { @@ -6665,7 +6698,7 @@ Post = (function() { _Class.prototype.isClone = true; function _Class(origin, context, contractThumb) { - var base, fileRoot, i, inline, inlined, j, k, key, l, len, len1, len2, len3, node, nodes, ref, ref1, ref2, ref3, ref4, ref5, root, val; + var base, i, inline, inlined, j, k, key, l, len, len1, len2, len3, node, nodes, ref, ref1, ref2, ref3, ref4, ref5, ref6, root, selector, val; this.origin = origin; this.context = context; ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']; @@ -6675,13 +6708,13 @@ Post = (function() { } nodes = this.origin.nodes; root = contractThumb ? this.cloneWithoutVideo(nodes.root) : nodes.root.cloneNode(true); - (base = Post.Clone).prefix || (base.prefix = 0); + (base = Post.Clone).suffix || (base.suffix = 0); ref1 = [root].concat(slice.call($$('[id]', root))); for (j = 0, len1 = ref1.length; j < len1; j++) { node = ref1[j]; - node.id = Post.Clone.prefix + node.id; + node.id += "_" + Post.Clone.suffix; } - Post.Clone.prefix++; + Post.Clone.suffix++; ref2 = $$('.inline', root); for (k = 0, len2 = ref2.length; k < len2; k++) { inline = ref2[k]; @@ -6711,12 +6744,15 @@ Post = (function() { val = ref4[key]; this.file[key] = val; } - fileRoot = this.nodes.fileRoot; - this.file.text = fileRoot.firstElementChild; - this.file.link = $('.fileText > a, .fileText-original', fileRoot); - this.file.thumb = $('a.fileThumb > [data-md5]', fileRoot); - this.file.thumbLink = (ref5 = this.file.thumb) != null ? ref5.parentNode : void 0; - this.file.fullImage = $('.full-image', fileRoot); + ref5 = Site.selectors.file; + for (key in ref5) { + selector = ref5[key]; + this.file[key] = $(selector, this.nodes.root); + } + this.file.thumbLink = (ref6 = this.file.thumb) != null ? ref6.parentNode : void 0; + if (this.file.thumbLink) { + this.file.fullImage = $('.full-image', this.file.thumbLink); + } this.file.videoControls = $('.video-controls', this.file.text); if (this.file.videoThumb) { this.file.thumb.muted = true; @@ -7104,6 +7140,348 @@ Thread = (function() { }).call(this); +SW = {}; + +(function() { + var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + SW.tinyboard = { + isOPContainerThread: true, + disabledFeatures: ['Board Configuration', 'Normalize URL', 'Captcha Configuration', 'Image Host Rewriting', 'Index Generator', 'Announcement Hiding', 'Fourchan thingies', 'Resurrect Quotes', 'Quick Reply Personas', 'Quick Reply', 'Cooldown', 'Pass Link', 'Index Generator (Menu)', 'Report Link', 'Delete Link', 'Edit Link', 'Archive Link', 'Quote Inlining', 'Quote Previewing', 'Quote Backlinks', 'File Info Formatting', 'Fappe Tyme', 'Image Expansion', 'Image Expansion (Menu)', 'Comment Expansion', 'Thread Expansion', 'Favicon', 'Unread', 'Quote Threading', 'Thread Stats', 'Thread Updater', 'Mark New IPs', 'Banner', 'Flash Features', 'Reply Pruning'], + detect: function() { + var i, len, ref, script; + ref = $$('script:not([src])', d.head); + for (i = 0, len = ref.length; i < len; i++) { + script = ref[i]; + if (/\bvar configRoot=".*?"/.test(script.textContent)) { + return true; + } + } + return false; + }, + selectors: { + board: 'form[name="postcontrols"]', + thread: 'div[id^="thread_"]', + postContainer: '.reply', + infoRoot: '.intro', + info: { + subject: '.subject', + name: '.name', + email: '.email', + tripcode: '.trip', + uniqueID: '.poster_id', + capcode: '.capcode', + flag: '.flag', + date: 'time', + nameBlock: 'label', + quote: 'a[href*="#q"]', + reply: 'a[href*="/res/"]:not([href*="#"])' + }, + icons: { + isSticky: '.fa-thumb-tack', + isClosed: '.fa-lock' + }, + file: { + text: '.fileinfo', + link: '.fileinfo > a', + thumb: 'a > .post-image' + }, + comment: '.body', + spoiler: '.spoiler', + quotelink: 'a[onclick^="highlightReply("]', + boardList: '.boardlist' + }, + xpath: { + thread: 'div[starts-with(@id,"thread_")]', + postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]' + }, + regexp: { + quotelink: /\/([^\/]+)\/res\/(\d+)\.html#(\d+)$/ + }, + bgColoredEl: function() { + return $.el('div', { + className: 'post reply' + }); + }, + parseNodes: function(post, nodes) { + var m, nextSibling, uniqueID; + if (nodes.uniqueID) { + return; + } + nodes.info.normalize(); + nextSibling = nodes.nameBlock.nextSibling; + if (nextSibling.nodeType === 3 && (m = nextSibling.textContent.match(/(\s*ID:\s*)(\S+)/))) { + nextSibling = nextSibling.splitText(m[1].length); + nextSibling.splitText(m[2].length); + nodes.uniqueID = uniqueID = $.el('span', { + className: 'poster_id' + }); + $.replace(nextSibling, uniqueID); + return $.add(uniqueID, nextSibling); + } + }, + parseFile: function(post, file) { + var info, infoNode, link, nameNode, ref, ref1, text, thumb; + text = file.text, link = file.link, thumb = file.thumb; + if ($.x("ancestor::" + Site.xpath.postContainer + "[1]", text) !== post.nodes.root) { + return false; + } + if (!(infoNode = indexOf.call((ref = link.nextSibling) != null ? ref.textContent : void 0, '(') >= 0 ? link.nextSibling : link.nextElementSibling)) { + return false; + } + if (!(info = infoNode.textContent.match(/\((Spoiler Image, )?([\d.]+ [KMG]?B).*\)/))) { + return false; + } + nameNode = $('.postfilename', text); + $.extend(file, { + name: nameNode ? nameNode.title || nameNode.textContent : link.pathname.match(/[^\/]*$/)[0], + size: info[2], + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0 + }); + if (thumb) { + $.extend(file, { + thumbURL: /\/static\//.test(thumb.src) && /\.(?:gif|jpe?g|png)$/.test(link.href) ? link.href : thumb.src, + isSpoiler: !!info[1] || link.textContent === 'Spoiler Image' + }); + } + return true; + }, + isThumbExpanded: function(file) { + return $.hasClass(file.thumb.parentNode, 'expanded'); + } + }; + +}).call(this); + +(function() { + var slice = [].slice; + + SW.yotsuba = { + isOPContainerThread: false, + selectors: { + board: '.board', + thread: '.thread', + postContainer: '.postContainer', + sideArrows: '.sideArrows', + post: '.post', + infoRoot: '.postInfo', + info: { + subject: '.subject', + name: '.name', + email: '.useremail', + tripcode: '.postertrip', + uniqueIDRoot: '.posteruid', + uniqueID: '.posteruid > .hand', + capcode: '.capcode.hand', + pass: '.n-pu', + flag: '.flag, .countryFlag', + date: '.dateTime', + nameBlock: '.nameBlock', + quote: '.postNum > a:nth-of-type(2)', + reply: '.replylink' + }, + icons: { + isSticky: '.stickyIcon', + isClosed: '.closedIcon', + isArchived: '.archivedIcon' + }, + file: { + text: '.file > :first-child', + link: '.fileText > a', + thumb: 'a.fileThumb > [data-md5]' + }, + comment: '.postMessage', + spoiler: 's', + quotelink: ':not(pre) > .quotelink', + boardList: '#boardNavDesktop > .boardList' + }, + xpath: { + thread: 'div[contains(concat(" ",@class," ")," thread ")]', + postContainer: 'div[contains(@class,"postContainer")]' + }, + regexp: { + quotelink: /^https?:\/\/boards\.4chan\.org\/+([^\/]+)\/+thread\/+(\d+)(?:[\/?][^#]*)?(?:#p(\d+))?$/ + }, + bgColoredEl: function() { + return $.el('div', { + className: 'reply' + }); + }, + isThisPageLegit: function() { + var ref; + return location.hostname === 'boards.4chan.org' && !$('link[href*="favicon-status.ico"]', d.head) && ((ref = d.title) !== '4chan - Temporarily Offline' && ref !== '4chan - Error' && ref !== '504 Gateway Time-out'); + }, + is404: function() { + var ref; + return ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found') || (g.VIEW === 'thread' && $('.board') && !$('.opContainer')); + }, + isIncomplete: function() { + var ref; + return ((ref = g.VIEW) === 'index' || ref === 'thread') && !$('.board + *'); + }, + isAuxiliaryPage: function() { + return location.hostname !== 'boards.4chan.org'; + }, + scriptData: function() { + var j, len, ref, script; + ref = $$('script:not([src])', d.head); + for (j = 0, len = ref.length; j < len; j++) { + script = ref[j]; + if (/\bcooldowns *=/.test(script.textContent)) { + return script.textContent; + } + } + return ''; + }, + parseThreadMetadata: function(thread) { + var file, m, scriptData; + scriptData = this.scriptData(); + thread.postLimit = /\bbumplimit *= *1\b/.test(scriptData); + thread.fileLimit = /\bimagelimit *= *1\b/.test(scriptData); + thread.ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : void 0; + if (g.BOARD.ID === 'f' && thread.OP.file) { + file = thread.OP.file; + return $.ajax(location.protocol + "//a.4cdn.org/f/thread/" + thread + ".json", { + timeout: $.MINUTE, + onloadend: function() { + if (this.response) { + return file.text.dataset.md5 = file.MD5 = this.response.posts[0].md5; + } + } + }); + } + }, + parseNodes: function(post, nodes) { + var icon, j, len, ref, results, type; + if (post.boardID === 'f') { + ref = ['Sticky', 'Closed']; + results = []; + for (j = 0, len = ref.length; j < len; j++) { + type = ref[j]; + if ((icon = $("img[alt=" + type + "]", nodes.info))) { + results.push($.addClass(icon, (type.toLowerCase()) + "Icon", 'retina')); + } + } + return results; + } + }, + parseFile: function(post, file) { + var info, link, m, ref, ref1, ref2, text, thumb; + text = file.text, link = file.link, thumb = file.thumb; + if (!(info = (ref = link.nextSibling) != null ? ref.textContent.match(/\(([\d.]+ [KMG]?B).*\)/) : void 0)) { + return false; + } + $.extend(file, { + name: text.title || link.title || link.textContent, + size: info[1], + dimensions: (ref1 = info[0].match(/\d+x\d+/)) != null ? ref1[0] : void 0, + tag: (ref2 = info[0].match(/,[^,]*, ([a-z]+)\)/i)) != null ? ref2[1] : void 0, + MD5: text.dataset.md5 + }); + if (thumb) { + $.extend(file, { + thumbURL: thumb.src, + MD5: thumb.dataset.md5, + isSpoiler: $.hasClass(thumb.parentNode, 'imgspoiler') + }); + if (file.isSpoiler) { + file.thumbURL = (m = link.href.match(/\d+(?=\.\w+$)/)) ? location.protocol + "//" + (ImageHost.thumbHost()) + "/" + post.board + "/" + m[0] + "s.jpg" : void 0; + } + } + return true; + }, + cleanComment: function(bq) { + var abbr, br, i, j, k, len, node, ref; + if ((abbr = $('.abbr', bq))) { + ref = $$('.abbr + br, .exif', bq); + for (j = 0, len = ref.length; j < len; j++) { + node = ref[j]; + $.rm(node); + } + for (i = k = 0; k < 2; i = ++k) { + if ((br = abbr.previousSibling) && br.nodeName === 'BR') { + $.rm(br); + } + } + return $.rm(abbr); + } + }, + cleanCommentDisplay: function(bq) { + var b; + if ((b = $('b', bq)) && /^Rolled /.test(b.textContent)) { + $.rm(b); + } + return $.rm($('.fortune', bq)); + }, + insertTags: function(bq) { + var j, k, len, len1, node, ref, ref1; + ref = $$('s, .removed-spoiler', bq); + for (j = 0, len = ref.length; j < len; j++) { + node = ref[j]; + $.replace(node, [$.tn('[spoiler]')].concat(slice.call(node.childNodes), [$.tn('[/spoiler]')])); + } + ref1 = $$('.prettyprint', bq); + for (k = 0, len1 = ref1.length; k < len1; k++) { + node = ref1[k]; + $.replace(node, [$.tn('[code]')].concat(slice.call(node.childNodes), [$.tn('[/code]')])); + } + } + }; + +}).call(this); + +Site = (function() { + var Site; + + Site = { + init: function(cb) { + var hostname, i, len, line, ref, ref1, software, swDict; + swDict = {}; + ref = Conf['siteSoftware'].split('\n'); + for (i = 0, len = ref.length; i < len; i++) { + line = ref[i]; + if (!(line[0] !== '#')) { + continue; + } + ref1 = line.split(' '), hostname = ref1[0], software = ref1[1]; + if (software in SW) { + swDict[hostname] = software; + } + } + hostname = location.hostname; + while (hostname && !(hostname in swDict)) { + hostname = hostname.replace(/^[^.]*\.?/, ''); + } + if (hostname) { + this.set(hostname, swDict[hostname]); + return cb(); + } else { + return $.onExists(doc, 'body', (function(_this) { + return function() { + var base; + for (software in SW) { + if (typeof (base = SW[software]).detect === "function" ? base.detect() : void 0) { + _this.set(location.hostname.replace(/^www\./, ''), software); + Conf['siteSoftware'] += "\n" + _this.hostname + " " + _this.software; + $.set('siteSoftware', Conf['siteSoftware']); + cb(); + } + } + }; + })(this)); + } + }, + set: function(hostname1, software1) { + this.hostname = hostname1; + this.software = software1; + return $.extend(this, SW[this.software]); + } + }; + + return Site; + +}).call(this); + Redirect = (function() { var Redirect, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -7702,7 +8080,7 @@ Filter = (function() { open: function(post) { var value; value = Filter[type](post); - return (value != null) && !(g.BOARD.ID === 'f' && type === 'MD5'); + return value != null; } }; }, @@ -7760,7 +8138,7 @@ PostHiding = (function() { })); }, node: function() { - var data, sideArrows; + var button, data, sa, sideArrows; if (!this.isReply || this.isClone || this.isFetchedQuote) { return; } @@ -7779,9 +8157,14 @@ PostHiding = (function() { if (!Conf['Reply Hiding Buttons']) { return; } - sideArrows = $('.sideArrows', this.nodes.root); - $.replace(sideArrows.firstChild, PostHiding.makeButton(this, 'hide')); - return sideArrows.removeAttribute('class'); + button = PostHiding.makeButton(this, 'hide'); + if ((sa = Site.selectors.sideArrows)) { + sideArrows = $(sa, this.nodes.root); + $.replace(sideArrows.firstChild, button); + return sideArrows.removeAttribute('class'); + } else { + return $.prepend(this.nodes.root, button); + } }, menu: { init: function() { @@ -8146,7 +8529,7 @@ ThreadHiding = (function() { }, catalogSet: function(board) { var hiddenThreads, threadID; - if (!$.hasStorage) { + if (!($.hasStorage && Site.software === 'yotsuba')) { return; } hiddenThreads = ThreadHiding.db.get({ @@ -8159,7 +8542,7 @@ ThreadHiding = (function() { return localStorage.setItem("4chan-hide-t-" + board, JSON.stringify(hiddenThreads)); }, catalogWatch: function() { - if (!$.hasStorage) { + if (!($.hasStorage && Site.software === 'yotsuba')) { return; } this.hiddenThreads = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; @@ -8504,6 +8887,9 @@ BoardConfig = (function() { }, noAudio: function(boardID) { var boards; + if (Site.software !== 'yotsuba') { + return false; + } boards = this.boards || Conf['boardConfig'].boards; return boards && !boards[boardID].webm_audio; } @@ -8848,10 +9234,10 @@ Get = (function() { if (root == null) { return null; } - return g.threads[g.BOARD + "." + root.id.slice(1)]; + return g.threads[(root.dataset.board || g.BOARD.ID) + "." + (root.id.match(/\d*$/)[0])]; }, threadFromNode: function(node) { - return Get.threadFromRoot($.x('ancestor-or-self::div[contains(concat(" ",@class," ")," thread ")]', node)); + return Get.threadFromRoot($.x("ancestor-or-self::" + Site.xpath.thread, node)); }, postFromRoot: function(root) { var index, post; @@ -8867,18 +9253,17 @@ Get = (function() { } }, postFromNode: function(root) { - return Get.postFromRoot($.x('ancestor-or-self::div[contains(@class,"postContainer")][1]', root)); + return Get.postFromRoot($.x("ancestor-or-self::" + Site.xpath.postContainer + "[1]", root)); }, postDataFromLink: function(link) { - var boardID, path, postID, ref, threadID; - if (link.hostname === 'boards.4chan.org') { - path = link.pathname.split(/\/+/); - boardID = path[1]; - threadID = path[3]; - postID = link.hash ? link.hash.slice(2) : path[3]; - } else { + var boardID, match, postID, ref, ref1, threadID; + if (link.dataset.postID) { ref = link.dataset, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; threadID || (threadID = 0); + } else { + match = link.href.match(Site.regexp.quotelink); + ref1 = match.slice(1), boardID = ref1[0], threadID = ref1[1], postID = ref1[2]; + postID || (postID = threadID); } return { boardID: boardID, @@ -8919,17 +9304,6 @@ Get = (function() { ref1 = Get.postDataFromLink(quotelink), boardID = ref1.boardID, postID = ref1.postID; return boardID === post.board.ID && postID === post.ID; }); - }, - scriptData: function() { - var i, len, ref, script; - ref = $$('script:not([src])', d.head); - for (i = 0, len = ref.length; i < len; i++) { - script = ref[i]; - if (/\bcooldowns *=/.test(script.textContent)) { - return script.textContent; - } - } - return ''; } }; @@ -9024,6 +9398,7 @@ Header = (function() { }); $.on(window, 'load popstate', Header.hashScroll); $.on(d, 'CreateNotification', this.createNotification); + this.setBoardList(); $.onExists(doc, 'body', (function(_this) { return function() { if (!Main.isThisPageLegit()) { @@ -9034,7 +9409,7 @@ Header = (function() { return _this.setBarPosition(Conf['Bottom Header']); }; })(this)); - $.onExists(doc, '#boardNavMobile', Header.setBoardList); + $.onExists(doc, Site.selectors.boardList + " + *", Header.generateFullBoardList); Main.ready(function() { var a, absbot, footer; if (!(footer = $.id('boardNavDesktopFoot'))) { @@ -9088,7 +9463,7 @@ Header = (function() { id: 'scroll-marker' }), setBoardList: function() { - var a, boardList, btn, chr, fullBoardList, i, j, len, len1, node, nodes, ref, ref1, spacer, span; + var boardList, btn; Header.boardList = boardList = $.el('span', { id: 'board-list' }); @@ -9097,20 +9472,28 @@ Header = (function() { }); btn = $('.hide-board-list-button', boardList); $.on(btn, 'click', Header.toggleBoardList); + $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); + Header.setCustomNav(Conf['Custom Board Navigation']); + Header.generateBoardList(Conf['boardnav']); + $.sync('Custom Board Navigation', Header.setCustomNav); + return $.sync('boardnav', Header.generateBoardList); + }, + generateFullBoardList: function() { + var a, chr, fullBoardList, i, items, j, len, node, nodes, ref, spacer, span; nodes = []; spacer = function() { return $.el('span', { className: 'spacer' }); }; - ref = $('#boardNavDesktop > .boardList').childNodes; - for (i = 0, len = ref.length; i < len; i++) { - node = ref[i]; + items = $.X('.//a|.//text()[not(ancestor::a)]', $(Site.selectors.boardList)); + i = 0; + while (node = items.snapshotItem(i++)) { switch (node.nodeName) { case '#text': - ref1 = node.nodeValue; - for (j = 0, len1 = ref1.length; j < len1; j++) { - chr = ref1[j]; + ref = node.nodeValue; + for (j = 0, len = ref.length; j < len; j++) { + chr = ref[j]; span = $.el('span', { textContent: chr }); @@ -9134,14 +9517,9 @@ Header = (function() { nodes.push(a); } } - fullBoardList = $('.boardList', boardList); + fullBoardList = $('.boardList', Header.boardList); $.add(fullBoardList, nodes); - CatalogLinks.setLinks(fullBoardList); - $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); - Header.setCustomNav(Conf['Custom Board Navigation']); - Header.generateBoardList(Conf['boardnav']); - $.sync('Custom Board Navigation', Header.setCustomNav); - return $.sync('boardnav', Header.generateBoardList); + return CatalogLinks.setLinks(fullBoardList); }, generateBoardList: function(boardnav) { var as, list, nodes, re, t; @@ -9154,11 +9532,11 @@ Header = (function() { as = $$('#full-board-list a[title]', Header.boardList); re = /[\w@]+(-(all|title|replace|full|index|catalog|archive|expired|(mode|sort|text):"[^"]+"(,"[^"]+")?))*|[^\w@]+/g; nodes = (function() { - var i, len, ref, results; + var j, len, ref, results; ref = boardnav.match(re); results = []; - for (i = 0, len = ref.length; i < len; i++) { - t = ref[i]; + for (j = 0, len = ref.length; j < len; j++) { + t = ref[j]; results.push(Header.mapCustomNavigation(t, as)); } return results; @@ -9202,10 +9580,22 @@ Header = (function() { } boardID = t.split('-')[0]; if (boardID === 'current') { - boardID = g.BOARD.ID; + if (location.hostname === 'boards.4chan.org') { + boardID = g.BOARD.ID; + } else { + a = $.el('a', { + href: "/" + g.BOARD.ID + "/", + textContent: text || g.BOARD.ID, + className: 'current' + }); + if (/-(catalog|archive|expired)/.test(t)) { + a = a.firstChild; + } + return a; + } } a = (function() { - var i, len, ref; + var j, len, ref; if (boardID === '@') { return $.el('a', { href: 'https://twitter.com/4chan', @@ -9213,25 +9603,25 @@ Header = (function() { textContent: '@' }); } - for (i = 0, len = as.length; i < len; i++) { - a = as[i]; + for (j = 0, len = as.length; j < len; j++) { + a = as[j]; if (a.textContent === boardID) { return a.cloneNode(true); } } a = $.el('a', { - href: "/" + boardID + "/", + href: "//boards.4chan.org/" + boardID + "/", textContent: boardID }); if ((ref = g.VIEW) === 'catalog' || ref === 'archive') { a.href += g.VIEW; } - if (boardID === g.BOARD.ID) { + if (a.hostname === location.hostname && boardID === g.BOARD.ID) { a.className = 'current'; } return a; })(); - a.textContent = /-title/.test(t) || /-replace/.test(t) && boardID === g.BOARD.ID ? a.title || a.textContent : /-full/.test(t) ? ("/" + boardID + "/") + (a.title ? " - " + a.title : '') : text || boardID; + a.textContent = /-title/.test(t) || /-replace/.test(t) && a.hostname === location.hostname && boardID === g.BOARD.ID ? a.title || a.textContent : /-full/.test(t) ? ("/" + boardID + "/") + (a.title ? " - " + a.title : '') : text || boardID; if (m = t.match(/-(index|catalog)/)) { if (!(boardID === 'f' && m[1] === 'catalog')) { a.dataset.only = m[1]; @@ -9260,7 +9650,7 @@ Header = (function() { } if (/-expired/.test(t)) { if (boardID !== 'b' && boardID !== 'f' && boardID !== 'trash' && boardID !== 'bant') { - a.href = "/" + boardID + "/archive"; + a.href = "//boards.4chan.org/" + boardID + "/archive"; } else { return a.firstChild; } @@ -9509,7 +9899,7 @@ Header = (function() { } }, addShortcut: function(id, el, index) { - var i, item, len, ref, shortcut; + var item, j, len, ref, shortcut; shortcut = $.el('span', { id: "shortcut-" + id, className: 'shortcut brackets-wrap' @@ -9517,8 +9907,8 @@ Header = (function() { $.add(shortcut, el); shortcut.dataset.index = index; ref = $$('[data-index]', Header.shortcuts); - for (i = 0, len = ref.length; i < len; i++) { - item = ref[i]; + for (j = 0, len = ref.length; j < len; j++) { + item = ref[j]; if (!(+item.dataset.index > +index)) { continue; } @@ -11159,7 +11549,7 @@ Settings = (function() { if ($.engine !== 'gecko') { $('div[data-name="Remember QR Size"]', section).hidden = true; } - if ($.perProtocolSettings) { + if ($.perProtocolSettings || location.protocol !== 'https:') { $('div[data-name="Redirect to HTTPS"]', section).hidden = true; } $.get(items, function(items) { @@ -12112,7 +12502,7 @@ UI = (function() { $.on(d, 'click CloseMenu', this.close); $.on(d, 'scroll', this.setPosition); $.on(window, 'resize', this.setPosition); - $.add(button, menu); + $.after(button, menu); this.setPosition(); entry = $('.entry', menu); this.focus(entry); @@ -12578,7 +12968,7 @@ Gallery = (function() { Gallery = { init: function() { var el, ref; - if (!(this.enabled = Conf['Gallery'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(this.enabled = Conf['Gallery'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } this.delay = Conf['Slide Delay']; @@ -12609,7 +12999,7 @@ Gallery = (function() { } }, build: function(image) { - var candidate, cb, dialog, entry, file, i, j, key, len, len1, menuButton, nodes, post, ref, ref1, ref2, ref3, thumb, value; + var candidate, cb, dialog, entry, i, j, key, len, len1, menuButton, nodes, post, postThumb, ref, ref1, ref2, ref3, thumb, value; cb = Gallery.cb; if (Conf['Fullscreen Gallery']) { $.one(d, 'fullscreenchange mozfullscreenchange webkitfullscreenchange', function() { @@ -12673,10 +13063,10 @@ Gallery = (function() { $.off(d, 'keydown', Keybinds.keydown); } $.on(window, 'resize', Gallery.cb.setHeight); - ref2 = $$('.post .file'); + ref2 = $$(Site.selectors.file.thumb); for (j = 0, len1 = ref2.length; j < len1; j++) { - file = ref2[j]; - post = Get.postFromNode(file); + postThumb = ref2[j]; + post = Get.postFromNode(postThumb); if (!((ref3 = post.file) != null ? ref3.thumb : void 0)) { continue; } @@ -12734,6 +13124,7 @@ Gallery = (function() { ext = thumb.href.match(/\w*$/); elType = { 'webm': 'video', + 'mp4': 'video', 'pdf': 'iframe' }[ext] || 'img'; file = $.el(elType); @@ -13222,7 +13613,7 @@ ImageExpand = (function() { ImageExpand = { init: function() { var ref; - if (!(this.enabled = Conf['Image Expansion'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(this.enabled = Conf['Image Expansion'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } this.EAI = $.el('a', { @@ -13721,7 +14112,7 @@ ImageHover = (function() { } file = post.file; isVideo = file.isVideo; - if (file.isExpanding || file.isExpanded) { + if (file.isExpanding || file.isExpanded || (typeof Site.isThumbExpanded === "function" ? Site.isThumbExpanded(file) : void 0)) { return; } error = ImageHover.error(post); @@ -13813,7 +14204,7 @@ ImageLoader = (function() { ImageLoader = { init: function() { var prefetch, ref, ref1; - if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && g.BOARD.ID !== 'f')) { + if ((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') { return; } if (!(Conf['Image Prefetching'] || Conf['Replace JPG'] || Conf['Replace PNG'] || Conf['Replace GIF'] || Conf['Replace WEBM'])) { @@ -13964,7 +14355,7 @@ Metadata = (function() { Metadata = { init: function() { var ref; - if (!(Conf['WEBM Metadata'] && ((ref = g.VIEW) === 'index' || ref === 'thread') && g.BOARD.ID !== 'f')) { + if (!(Conf['WEBM Metadata'] && ((ref = g.VIEW) === 'index' || ref === 'thread'))) { return; } return Callbacks.Post.push({ @@ -15241,7 +15632,7 @@ Linkify = (function() { ref = $$('a', this.nodes.comment); for (j = 0, len = ref.length; j < len; j++) { link = ref[j]; - if (!(ImageHost.test(link.hostname))) { + if (!(ImageHost.test(link.hostname) || /\bnofollow\b/.test(link.rel))) { continue; } $.addClass(link, 'linkify'); @@ -15797,8 +16188,7 @@ ReportLink = (function() { order: 10, open: function(post) { ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; - if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) { - ReportLink.url += '&altc=1'; + if (d.cookie.indexOf('pass_enabled=1') >= 0) { ReportLink.dims = 'width=350,height=275'; } else { ReportLink.dims = 'width=400,height=550'; @@ -16046,7 +16436,7 @@ CatalogLinks = (function() { CatalogLinks = { init: function() { var el, input, selector; - if ((Conf['External Catalog'] || Conf['JSON Index']) && !(Conf['JSON Index'] && g.VIEW === 'index')) { + if (Site.software === 'yotsuba' && (Conf['External Catalog'] || Conf['JSON Index']) && !(Conf['JSON Index'] && g.VIEW === 'index')) { selector = (function() { switch (g.VIEW) { case 'thread': @@ -16082,7 +16472,7 @@ CatalogLinks = (function() { } }); } - if (Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { + if (Site.software === 'yotsuba' && Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { Callbacks.Post.push({ name: 'Catalog Link Rewrite', cb: this.node @@ -16133,7 +16523,7 @@ CatalogLinks = (function() { if (((ref2 = a.hostname) !== 'boards.4chan.org' && ref2 !== 'catalog.neet.tv') || !(board = a.pathname.split('/')[1]) || (board === 'f' || board === 'status' || board === '4chan') || a.pathname.split('/')[2] === 'archive' || $.hasClass(a, 'external')) { continue; } - a.href = Conf['Header catalog links'] ? CatalogLinks.catalog(board) : "/" + board + "/"; + a.href = Conf['Header catalog links'] ? CatalogLinks.catalog(board) : "//boards.4chan.org/" + board + "/"; if (a.dataset.indexOptions && a.hostname === 'boards.4chan.org' && a.pathname.split('/')[2] === '') { a.href += (a.hash ? '/' : '#') + a.dataset.indexOptions; } @@ -16146,13 +16536,13 @@ CatalogLinks = (function() { if (Conf['External Catalog'] && (board === 'a' || board === 'c' || board === 'g' || board === 'biz' || board === 'k' || board === 'm' || board === 'o' || board === 'p' || board === 'v' || board === 'vg' || board === 'vr' || board === 'w' || board === 'wg' || board === 'cm' || board === '3' || board === 'adv' || board === 'an' || board === 'asp' || board === 'cgl' || board === 'ck' || board === 'co' || board === 'diy' || board === 'fa' || board === 'fit' || board === 'gd' || board === 'int' || board === 'jp' || board === 'lit' || board === 'mlp' || board === 'mu' || board === 'n' || board === 'out' || board === 'po' || board === 'sci' || board === 'sp' || board === 'tg' || board === 'toy' || board === 'trv' || board === 'tv' || board === 'vp' || board === 'wsg' || board === 'x' || board === 'f' || board === 'pol' || board === 's4s' || board === 'lgbt')) { return "//catalog.neet.tv/" + board + "/"; } else if (Conf['JSON Index'] && Conf['Use 4chan X Catalog']) { - if (g.BOARD.ID === board && g.VIEW === 'index') { + if (location.hostname === 'boards.4chan.org' && g.BOARD.ID === board && g.VIEW === 'index') { return '#catalog'; } else { - return "/" + board + "/#catalog"; + return "//boards.4chan.org/" + board + "/#catalog"; } } else { - return "/" + board + "/catalog"; + return "//boards.4chan.org/" + board + "/catalog"; } }, index: function(board) { @@ -16160,13 +16550,13 @@ CatalogLinks = (function() { board = g.BOARD.ID; } if (Conf['JSON Index'] && board !== 'f') { - if (g.BOARD.ID === board && g.VIEW === 'index') { + if (location.hostname === 'boards.4chan.org' && g.BOARD.ID === board && g.VIEW === 'index') { return '#index'; } else { - return "/" + board + "/#index"; + return "//boards.4chan.org/" + board + "/#index"; } } else { - return "/" + board + "/"; + return "//boards.4chan.org/" + board + "/"; } } }; @@ -17706,7 +18096,7 @@ RelativeDates = (function() { INTERVAL: $.MINUTE / 2, init: function() { var ref; - if (((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || g.VIEW === 'index' && Conf['JSON Index'] && g.BOARD.ID !== 'f') { + if (((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'archive') && Conf['Relative Post Dates'] && !Conf['Relative Date Title'] || Index.enabled) { this.flush(); $.on(d, 'visibilitychange ThreadUpdate', this.flush); } @@ -17848,7 +18238,7 @@ RemoveSpoilers = (function() { }, unspoiler: function(el) { var i, len, span, spoiler, spoilers; - spoilers = $$('s', el); + spoilers = $$('s, .spoiler', el); for (i = 0, len = spoilers.length; i < len; i++) { spoiler = spoilers[i]; span = $.el('span', { @@ -19068,11 +19458,14 @@ ThreadWatcher = (function() { this.refreshButton = $('.refresh', this.dialog); this.closeButton = $('.move > .close', this.dialog); this.unreaddb = Unread.db || new DataBoard('lastReadPosts'); - this.unreadEnabled = Conf['Remember Last Read Post']; + this.unreadEnabled = Conf['Remember Last Read Post'] && Site.software === 'yotsuba'; $.on(d, 'QRPostSuccessful', this.cb.post); $.on(sc, 'click', this.toggleWatcher); $.on(this.refreshButton, 'click', this.buttonFetchAll); $.on(this.closeButton, 'click', this.toggleWatcher); + if (Site.software !== 'yotsuba') { + this.refreshButton.hidden = true; + } this.menu.addHeaderMenuEntry(); $.onExists(doc, 'body', this.addDialog); switch (g.VIEW) { @@ -19091,7 +19484,7 @@ ThreadWatcher = (function() { } Header.addShortcut('watcher', sc, 510); ThreadWatcher.fetchAuto(); - if (g.VIEW === 'index' && Conf['JSON Index'] && Conf['Menu'] && g.BOARD.ID !== 'f') { + if (Conf['Menu'] && Index.enabled) { Menu.menu.addEntry({ el: $.el('a', { href: 'javascript:;', @@ -19338,6 +19731,9 @@ ThreadWatcher = (function() { }, fetchAuto: function() { var db, interval, now, ref; + if (Site.software !== 'yotsuba') { + return; + } clearTimeout(ThreadWatcher.timeout); if (!Conf['Auto Update Thread Watcher']) { return; @@ -19347,8 +19743,7 @@ ThreadWatcher = (function() { now = Date.now(); if (!((now - interval < (ref = db.data.lastChecked || 0) && ref <= now))) { ThreadWatcher.fetchAllStatus(); - db.data.lastChecked = now; - db.save(); + db.setLastChecked(); } return ThreadWatcher.timeout = setTimeout(ThreadWatcher.fetchAuto, interval); }, @@ -19360,22 +19755,35 @@ ThreadWatcher = (function() { } }, fetchAllStatus: function() { - var i, len, ref, thread, threads; - ThreadWatcher.db.forceSync(); - ThreadWatcher.unreaddb.forceSync(); - if ((ref = QuoteYou.db) != null) { - ref.forceSync(); - } - if (!(threads = ThreadWatcher.getAll()).length) { + var db, dbs, i, len, n, results; + if (Site.software !== 'yotsuba') { return; } - for (i = 0, len = threads.length; i < len; i++) { - thread = threads[i]; - ThreadWatcher.fetchStatus(thread); + dbs = [ThreadWatcher.db, ThreadWatcher.unreaddb, QuoteYou.db].filter(function(x) { + return x; + }); + n = 0; + results = []; + for (i = 0, len = dbs.length; i < len; i++) { + db = dbs[i]; + results.push(db.forceSync(function() { + var j, len1, thread, threads; + if ((++n) === dbs.length) { + threads = ThreadWatcher.getAll(); + for (j = 0, len1 = threads.length; j < len1; j++) { + thread = threads[j]; + ThreadWatcher.fetchStatus(thread); + } + } + })); } + return results; }, fetchStatus: function(thread, force) { var boardID, data, req, threadID; + if (Site.software !== 'yotsuba') { + return; + } boardID = thread.boardID, threadID = thread.threadID, data = thread.data; if (data.isDead && !force) { return; @@ -20438,305 +20846,12 @@ Captcha = {}; }).call(this); -(function() { - Captcha.noscript = { - lifetime: 30 * $.MINUTE, - init: function() { - var container, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript'))) { - return; - } - container = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - container: container, - input: input - }; - $.on(input, 'blur', QR.focusout); - $.on(input, 'focus', QR.focusin); - $.on(input, 'keydown', this.keydown.bind(this)); - $.on(input, 'input', function() { - if (!Captcha.cache.getCount()) { - return QR.posts[0].preventAutoPost(); - } - }); - $.on(this.nodes.container, 'click', (function(_this) { - return function() { - _this.reload(); - return _this.nodes.input.focus(); - }; - })(this)); - this.conn = new Connection(null, 'https://www.google.com', { - challenge: this.load.bind(this), - token: this.save.bind(this), - error: this.error.bind(this) - }); - $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v1', 'noscript-captcha'); - $.after(QR.nodes.com.parentNode, [container, input]); - Captcha.cache.init(); - $.on(d, 'CaptchaCount', this.count.bind(this)); - this.beforeSetup(); - return this.setup(); - }, - initFrame: function() { - var cb, conn, img, ref, ref1; - conn = new Connection(window.parent, 'https://boards.4chan.org', { - response: function(response) { - $.id('recaptcha_response_field').value = response; - return HTMLFormElement.prototype.submit.call($('form')); - } - }); - if (location.hash === '#response') { - conn.send({ - token: (ref = $('textarea')) != null ? ref.value : void 0, - error: (ref1 = $('.recaptcha_input_area')) != null ? ref1.textContent.replace(/:$/, '') : void 0 - }); - } - if (!(img = $('img'))) { - return; - } - $('form').action = '#response'; - cb = function() { - var canvas; - canvas = $.el('canvas'); - canvas.width = img.width; - canvas.height = img.height; - canvas.getContext('2d').drawImage(img, 0, 0); - return conn.send({ - challenge: canvas.toDataURL() - }); - }; - if (img.complete) { - return cb(); - } else { - return $.on(img, 'load', cb); - } - }, - timers: {}, - iframeURL: function() { - var lang, url; - url = 'https://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc'; - if (lang = Conf['captchaLanguage'].trim()) { - url += "&hl=" + (encodeURIComponent(lang)); - } - return url; - }, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = true; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - moreNeeded: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (force || Captcha.cache.needed()))) { - return; - } - if (!this.nodes.iframe) { - this.nodes.iframe = $.el('iframe', { - id: 'qr-captcha-iframe', - src: this.iframeURL() - }); - $.add(QR.nodes.el, this.nodes.iframe); - this.conn.target = this.nodes.iframe; - } else if (!this.occupied || force) { - this.nodes.iframe.src = this.iframeURL(); - } - this.occupied = true; - if (focus) { - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var container, input, ref; - ref = this.nodes, container = ref.container, input = ref.input; - container.hidden = false; - input.placeholder = 'Verification'; - this.count(); - $.off(input, 'focus click', this.cb.focus); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.isEnabled) { - return; - } - $.rm(this.nodes.img); - delete this.nodes.img; - $.rm(this.nodes.iframe); - delete this.nodes.iframe; - delete this.occupied; - return this.beforeSetup(); - }, - getOne: function(isReply) { - var captcha; - if ((captcha = Captcha.cache.getOne(isReply))) { - return captcha; - } else if (/\S/.test(this.nodes.input.value)) { - return (function(_this) { - return function(cb) { - _this.submitCB = cb; - return _this.sendResponse(); - }; - })(this); - } else { - return null; - } - }, - sendResponse: function() { - var response; - response = this.nodes.input.value; - if (/\S/.test(response)) { - return this.conn.send({ - response: response - }); - } - }, - save: function(token) { - var captcha; - delete this.occupied; - this.nodes.input.value = ''; - captcha = { - challenge: token, - response: 'manual_challenge', - timeout: this.timeout - }; - if (this.submitCB) { - this.submitCB(captcha); - delete this.submitCB; - if (Captcha.cache.needed()) { - return this.reload(); - } else { - return this.destroy(); - } - } else { - Captcha.cache.save(captcha); - return this.reload(); - } - }, - error: function(message) { - this.occupied = true; - this.nodes.input.value = ''; - if (this.submitCB) { - this.submitCB(); - delete this.submitCB; - } - return QR.error("Captcha Error: " + message); - }, - load: function(src) { - var container, img, input, ref; - ref = this.nodes, container = ref.container, input = ref.input, img = ref.img; - this.occupied = true; - this.timeout = Date.now() + this.lifetime; - if (!img) { - img = this.nodes.img = new Image(); - $.one(img, 'load', this.afterSetup.bind(this)); - $.on(img, 'load', function() { - return this.hidden = false; - }); - $.add(container, img); - } - img.src = src; - input.value = ''; - clearTimeout(this.timers.expire); - return this.timers.expire = setTimeout(this.expire.bind(this), this.lifetime); - }, - count: function() { - var count, placeholder; - count = Captcha.cache.getCount(); - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - return this.nodes.input.alt = count; - }, - expire: function() { - if (!this.nodes.iframe) { - return; - } - if (!d.hidden && (Captcha.cache.needed() || d.activeElement === this.nodes.input)) { - return this.reload(); - } else { - return this.destroy(); - } - }, - reload: function() { - var ref; - this.nodes.iframe.src = this.iframeURL(); - this.occupied = true; - return (ref = this.nodes.img) != null ? ref.hidden = true : void 0; - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - if (this.nodes.iframe) { - this.reload(); - } else { - this.setup(); - } - } else if (e.keyCode === 13 && e.shiftKey) { - this.sendResponse(); - } else { - return; - } - return e.preventDefault(); - } - }; - -}).call(this); - (function() { Captcha.replace = { init: function() { if (!(d.cookie.indexOf('pass_enabled=1') < 0)) { return; } - if (location.hostname === 'sys.4chan.org' && /[?&]altc\b/.test(location.search) && Main.jsEnabled) { - $.onExists(doc, 'script[src="//www.google.com/recaptcha/api/js/recaptcha_ajax.js"]', function() { - $.global(function() { - return window.el.onload = null; - }); - return Captcha.v1.create(); - }); - return; - } - if (location.hostname === 'sys.4chan.org' && Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) { - $.ready(Captcha.replace.v1); - return; - } if (Conf['Force Noscript Captcha'] && Main.jsEnabled) { $.ready(Captcha.replace.noscript); return; @@ -20771,21 +20886,6 @@ Captcha = {}; return insert(); } }, - v1: function() { - var container, old, script; - if (!(old = $.id('g-recaptcha'))) { - return; - } - script = $.el('script', { - src: '//www.google.com/recaptcha/api/js/recaptcha_ajax.js' - }); - $.add(d.head, script); - container = $.el('div', { - id: 'captchaContainerAlt' - }); - $.replace(old, container); - return Captcha.v1.create(); - }, iframe: function(iframe) { var lang, src; if ((lang = Conf['captchaLanguage'].trim())) { @@ -20824,286 +20924,6 @@ Captcha = {}; }).call(this); -(function() { - Captcha.v1 = { - blank: "data:image/svg+xml,", - init: function() { - var container, imgContainer, input; - if (d.cookie.indexOf('pass_enabled=1') >= 0) { - return; - } - if (!(this.isEnabled = !!$('#g-recaptcha, #captcha-forced-noscript'))) { - return; - } - imgContainer = $.el('div', { - className: 'captcha-img', - title: 'Reload reCAPTCHA' - }); - $.extend(imgContainer, { - innerHTML: "" - }); - input = $.el('input', { - className: 'captcha-input field', - title: 'Verification', - autocomplete: 'off', - spellcheck: false - }); - this.nodes = { - img: imgContainer.firstChild, - input: input - }; - $.on(input, 'blur', QR.focusout); - $.on(input, 'focus', QR.focusin); - $.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha)); - $.on(input, 'input', function() { - if (!Captcha.cache.getCount()) { - return QR.posts[0].preventAutoPost(); - } - }); - $.on(this.nodes.img.parentNode, 'click', QR.captcha.reload.bind(QR.captcha)); - $.addClass(QR.nodes.el, 'has-captcha', 'captcha-v1'); - $.after(QR.nodes.com.parentNode, [imgContainer, input]); - Captcha.cache.init(); - $.on(d, 'CaptchaCount', this.count.bind(this)); - this.script = $.el('script', { - src: '//www.google.com/recaptcha/api/js/recaptcha_ajax.js' - }); - $.add(d.head, this.script); - container = $.el('div', { - id: 'captchaContainerAlt', - hidden: true - }); - $.add(d.body, container); - this.beforeSetup(); - if (Conf['Auto-load captcha']) { - this.setup(); - } - new MutationObserver(this.afterSetup).observe(container, { - childList: true - }); - return this.afterSetup(); - }, - create: function() { - var cont, lang; - cont = $.id('captchaContainerAlt'); - if (this.occupied) { - return; - } - this.occupied = true; - if ((lang = Conf['captchaLanguage'].trim())) { - cont.dataset.lang = lang; - } - $.onExists(cont, '#recaptcha_image', function(image) { - return $.on(image, 'click', function() { - if ($.id('recaptcha_challenge_image')) { - return $.global(function() { - return window.Recaptcha.reload(); - }); - } - }); - }); - $.onExists(cont, '#recaptcha_response_field', function(field) { - $.on(field, 'keydown', function(e) { - if (e.keyCode === 8 && !field.value) { - return $.global(function() { - return window.Recaptcha.reload(); - }); - } - }); - if (location.hostname === 'sys.4chan.org') { - return field.focus(); - } - }); - return $.global(function() { - var container, options, script; - container = document.getElementById('captchaContainerAlt'); - options = { - theme: 'clean', - lang: container.dataset.lang - }; - if (window.Recaptcha) { - return window.Recaptcha.create('6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', container, options); - } else { - script = document.head.querySelector('script[src="//www.google.com/recaptcha/api/js/recaptcha_ajax.js"]'); - return script.addEventListener('load', function() { - return window.Recaptcha.create('6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc', container, options); - }, false); - } - }); - }, - cb: { - focus: function() { - return QR.captcha.setup(false, true); - } - }, - beforeSetup: function() { - var img, input, ref; - ref = this.nodes, img = ref.img, input = ref.input; - img.parentNode.hidden = true; - img.src = this.blank; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - this.count(); - return $.on(input, 'focus click', this.cb.focus); - }, - moreNeeded: function() {}, - setup: function(focus, force) { - if (!(this.isEnabled && (force || Captcha.cache.needed()))) { - return; - } - this.create(); - if (focus) { - $.addClass(QR.nodes.el, 'focus'); - return this.nodes.input.focus(); - } - }, - afterSetup: function() { - var challenge, img, input, ref, setLifetime; - if (!(challenge = $.id('recaptcha_challenge_field_holder'))) { - return; - } - if (challenge === QR.captcha.nodes.challenge) { - return; - } - setLifetime = function(e) { - return QR.captcha.lifetime = e.detail; - }; - $.on(window, 'captcha:timeout', setLifetime); - $.global(function() { - return window.dispatchEvent(new CustomEvent('captcha:timeout', { - detail: window.RecaptchaState.timeout - })); - }); - $.off(window, 'captcha:timeout', setLifetime); - ref = QR.captcha.nodes, img = ref.img, input = ref.input; - img.parentNode.hidden = false; - input.placeholder = 'Verification'; - QR.captcha.count(); - $.off(input, 'focus click', QR.captcha.cb.focus); - QR.captcha.nodes.challenge = challenge; - new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, { - childList: true, - subtree: true, - attributes: true - }); - QR.captcha.load(); - if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) { - QR.nodes.el.style.top = ''; - return QR.nodes.el.style.bottom = '0px'; - } - }, - destroy: function() { - if (!this.script) { - return; - } - $.global(function() { - return window.Recaptcha.destroy(); - }); - delete this.occupied; - if (this.nodes) { - return this.beforeSetup(); - } - }, - getOne: function(isReply) { - var captcha, challenge, response, timeout; - if ((captcha = Captcha.cache.getOne(isReply))) { - return captcha; - } else { - challenge = this.nodes.img.alt; - timeout = this.timeout; - if (/\S/.test(response = this.nodes.input.value)) { - this.destroy(); - return { - challenge: challenge, - response: response, - timeout: timeout - }; - } else { - return null; - } - } - }, - save: function() { - var response; - if (!/\S/.test(response = this.nodes.input.value)) { - return; - } - this.nodes.input.value = ''; - Captcha.cache.save({ - challenge: this.nodes.img.alt, - response: response, - timeout: this.timeout - }); - this.destroy(); - return this.setup(false, true); - }, - load: function() { - var challenge, challenge_image; - if ($('#captchaContainerAlt[class~="recaptcha_is_showing_audio"]')) { - this.nodes.img.src = this.blank; - return; - } - if (!this.nodes.challenge.firstChild) { - return; - } - if (!(challenge_image = $.id('recaptcha_challenge_image'))) { - return; - } - this.timeout = Date.now() + this.lifetime * $.SECOND - $.MINUTE; - challenge = this.nodes.challenge.firstChild.value; - this.nodes.img.alt = challenge; - this.nodes.img.src = challenge_image.src; - return this.nodes.input.value = ''; - }, - count: function() { - var count, placeholder; - count = Captcha.cache.getCount(); - placeholder = this.nodes.input.placeholder.replace(/\ \(.*\)$/, ''); - placeholder += (function() { - switch (count) { - case 0: - if (placeholder === 'Verification') { - return ' (Shift + Enter to cache)'; - } else { - return ''; - } - break; - case 1: - return ' (1 cached captcha)'; - default: - return " (" + count + " cached captchas)"; - } - })(); - this.nodes.input.placeholder = placeholder; - return this.nodes.input.alt = count; - }, - reload: function(focus) { - $.global(function() { - if (window.Recaptcha.type === 'image') { - window.Recaptcha.reload(); - } else { - window.Recaptcha.switch_type('image'); - } - return window.Recaptcha.should_focus = false; - }); - if (focus) { - return this.nodes.input.focus(); - } - }, - keydown: function(e) { - if (e.keyCode === 8 && !this.nodes.input.value) { - this.reload(); - } else if (e.keyCode === 13 && e.shiftKey) { - this.save(); - } else { - return; - } - return e.preventDefault(); - } - }; - -}).call(this); - (function() { var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -21474,7 +21294,7 @@ QR = (function() { 'video/webm': 'webm' }, init: function() { - var info, noscript, sc, version; + var sc; if (!Conf['Quick Reply']) { return; } @@ -21482,8 +21302,7 @@ QR = (function() { if (g.VIEW === 'archive') { return; } - version = Conf[g.VIEW === 'thread' ? 'Use Recaptcha v1' : 'Use Recaptcha v1 on Index'] && (Main.jsEnabled || location.protocol === 'https:') ? (noscript = location.protocol === 'https:' && (Conf['Force Noscript Captcha for v1'] || !Main.jsEnabled), (info = typeof GM !== "undefined" && GM !== null ? GM.info : void 0) && info.scriptHandler === 'Greasemonkey' && /^4\./.test(info.version) ? noscript = false : void 0, noscript ? 'noscript' : 'v1') : 'v2'; - this.captcha = Captcha[version]; + this.captcha = Captcha.v2; $.on(d, '4chanXInitFinished', function() { return BoardConfig.ready(QR.initReady); }); @@ -21836,7 +21655,9 @@ QR = (function() { $.replace(node, $.tn('\n>')); } } - Post.prototype.insertTags(frag); + if (typeof Site.insertTags === "function") { + Site.insertTags(frag); + } ref3 = $$('.linkify[data-original]', frag); for (n = 0, len2 = ref3.length; n < len2; n++) { node = ref3[n]; @@ -24297,9 +24118,11 @@ QuoteYou = (function() { return Conf['Remember Your Posts'] = enabled; }); $.on(d, 'QRPostSuccessful', function(e) { - var boardID, postID, ref, threadID; - $.forceSync('Remember Your Posts'); - if (Conf['Remember Your Posts']) { + return $.get('Remember Your Posts', Conf['Remember Your Posts'], function(items) { + var boardID, postID, ref, threadID; + if (!items['Remember Your Posts']) { + return; + } ref = e.detail, boardID = ref.boardID, threadID = ref.threadID, postID = ref.postID; return QuoteYou.db.set({ boardID: boardID, @@ -24307,7 +24130,7 @@ QuoteYou = (function() { postID: postID, val: true }); - } + }); }); if ((ref = g.VIEW) !== 'index' && ref !== 'thread' && ref !== 'archive') { return; @@ -24619,7 +24442,8 @@ Quotify = (function() { }).call(this); Main = (function() { - var Main; + var Main, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Main = { init: function() { @@ -24638,12 +24462,6 @@ Main = (function() { w['4chan X antidup'] = true; } catch (_error) {} if (location.hostname === 'www.google.com') { - if (location.pathname === '/recaptcha/api/noscript') { - $.ready(function() { - return Captcha.noscript.initFrame(); - }); - return; - } $.get('Captcha Fixes', true, function(arg) { var enabled; enabled = arg['Captcha Fixes']; @@ -24698,9 +24516,7 @@ Main = (function() { ref1 = DataBoard.keys; for (j = 0, len = ref1.length; j < len; j++) { db = ref1[j]; - Conf[db] = { - boards: {} - }; + Conf[db] = {}; } Conf['boardConfig'] = { boards: {} @@ -24719,13 +24535,15 @@ Main = (function() { Conf['QR Shortcut'] = true; Conf['Bottom QR Link'] = true; Conf['Toggleable Thread Watcher'] = true; - ($.getSync || $.get)({ - 'jsWhitelist': Conf['jsWhitelist'] - }, function(arg) { - var jsWhitelist; - jsWhitelist = arg.jsWhitelist; - return $.addCSP("script-src " + (jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim())); - }); + if (/\.4chan\.org$/.test(location.hostname)) { + ($.getSync || $.get)({ + 'jsWhitelist': Conf['jsWhitelist'] + }, function(arg) { + var jsWhitelist; + jsWhitelist = arg.jsWhitelist; + return $.addCSP("script-src " + (jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim())); + }); + } items = {}; for (key in Conf) { items[key] = void 0; @@ -24733,7 +24551,7 @@ Main = (function() { items['previousversion'] = void 0; return ($.getSync || $.get)(items, function(items) { var ref2; - if (!$.perProtocolSettings && ((ref2 = items['Redirect to HTTPS']) != null ? ref2 : Conf['Redirect to HTTPS']) && location.protocol !== 'https:') { + if (!$.perProtocolSettings && /\.4chan\.org$/.test(location.hostname) && ((ref2 = items['Redirect to HTTPS']) != null ? ref2 : Conf['Redirect to HTTPS']) && location.protocol !== 'https:') { location.replace('https:' + location.host + location.pathname + location.search + location.hash); return; } @@ -24753,7 +24571,7 @@ Main = (function() { val = Conf[key]; Conf[key] = (ref3 = items[key]) != null ? ref3 : val; } - return Main.initFeatures(); + return Site.init(Main.initFeatures); }); }); }, @@ -24773,19 +24591,17 @@ Main = (function() { }); }, initFeatures: function() { - var err, feature, hostname, j, len, match, name, pathname, ref, ref1, ref2, ref3, search; + var err, feature, hostname, j, len, match, name, pathname, ref, ref1, ref2, search; hostname = location.hostname, search = location.search; pathname = location.pathname.split(/\/+/); if (hostname !== 'www.4chan.org') { g.BOARD = new Board(pathname[1]); } - if (hostname === 'boards.4chan.org' || hostname === 'sys.4chan.org' || hostname === 'www.4chan.org') { - $.global(function() { - document.documentElement.classList.add('js-enabled'); - return window.FCX = {}; - }); - Main.jsEnabled = $.hasClass(doc, 'js-enabled'); - } + $.global(function() { + document.documentElement.classList.add('js-enabled'); + return window.FCX = {}; + }); + Main.jsEnabled = $.hasClass(doc, 'js-enabled'); switch (hostname) { case 'www.4chan.org': $.onExists(doc, 'body', function() { @@ -24820,8 +24636,8 @@ Main = (function() { $.asap((function() { return d.readyState !== 'loading'; }), function() { - var ref, video; - if (Conf['404 Redirect'] && ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found')) { + var video; + if (Conf['404 Redirect'] && (typeof Site.is404 === "function" ? Site.is404() : void 0)) { return Redirect.navigate('file', { boardID: g.BOARD.ID, filename: pathname[pathname.length - 1] @@ -24840,15 +24656,15 @@ Main = (function() { }); return; } - if (hostname !== 'boards.4chan.org') { + if (typeof Site.isAuxiliaryPage === "function" ? Site.isAuxiliaryPage() : void 0) { return; } if ((ref = pathname[2]) === 'thread' || ref === 'res') { g.VIEW = 'thread'; - g.THREADID = +pathname[3]; - } else if ((ref1 = pathname[2]) === 'catalog' || ref1 === 'archive') { - g.VIEW = pathname[2]; - } else if (pathname[2].match(/^\d*$/)) { + g.THREADID = +pathname[3].replace('.html', ''); + } else if (/^(?:catalog|archive)(?:\.html)?$/.test(pathname[2])) { + g.VIEW = pathname[2].replace('.html', ''); + } else if (/^(?:index|\d*)(?:\.html)?$/.test(pathname[2])) { g.VIEW = 'index'; } else { return; @@ -24856,9 +24672,12 @@ Main = (function() { g.threads = new SimpleDict(); g.posts = new SimpleDict(); $.onExists(doc, 'body', Main.initStyle); - ref2 = Main.features; - for (j = 0, len = ref2.length; j < len; j++) { - ref3 = ref2[j], name = ref3[0], feature = ref3[1]; + ref1 = Main.features; + for (j = 0, len = ref1.length; j < len; j++) { + ref2 = ref1[j], name = ref2[0], feature = ref2[1]; + if (Site.disabledFeatures && indexOf.call(Site.disabledFeatures, name) >= 0) { + continue; + } try { feature.init(); } catch (_error) { @@ -24879,6 +24698,8 @@ Main = (function() { if ((ref = $('link[href*=mobile]', d.head)) != null) { ref.disabled = true; } + doc.dataset.host = location.host; + $.addClass(doc, "sw-" + Site.software); $.addClass(doc, g.VIEW === 'thread' ? 'thread-view' : g.VIEW); $.onExists(doc, '.ad-cnt, .adg-rects > .desktop', function(ad) { return $.onExists(ad, 'img, iframe', function() { @@ -24923,7 +24744,7 @@ Main = (function() { mainStyleSheet = $('link[title=switch]', d.head); styleSheets = $$('link[rel="alternate stylesheet"]', d.head); setStyle = function() { - var bgColor, div, j, len, styleSheet; + var bgColor, div, j, len, s, styleSheet; $.rmClass(doc, style); style = null; for (j = 0, len = styleSheets.length; j < len; j++) { @@ -24943,14 +24764,17 @@ Main = (function() { $.addClass(doc, style); return $.rm(Main.bgColorStyle); } else { - div = $.el('div', { - className: 'reply' - }); - div.style.cssText = 'position: absolute; visibility: hidden;'; + div = Site.bgColoredEl(); + div.style.position = 'absolute'; + div.style.visibility = 'hidden'; $.add(d.body, div); bgColor = window.getComputedStyle(div).backgroundColor; $.rm(div); - Main.bgColorStyle.textContent = ".dialog, .suboption-list > div:last-of-type, :root.catalog-hover-expand .catalog-container:hover > .post {\n background-color: " + bgColor + ";\n}"; + if (!/^rgb\(/.test(bgColor)) { + s = window.getComputedStyle(d.body); + bgColor = s.backgroundColor + " " + s.backgroundImage + " " + s.backgroundRepeat + " " + s.backgroundPosition; + } + Main.bgColorStyle.textContent = ".dialog, .suboption-list > div:last-of-type, :root.catalog-hover-expand .catalog-container:hover > .post {\n background: " + bgColor + ";\n}"; return $.after($.id('fourchanx-css'), Main.bgColorStyle); } }; @@ -24964,23 +24788,22 @@ Main = (function() { }); }, initReady: function() { - var msg, ref, ref1, ref2; - if (g.VIEW === 'thread' && (((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found') || ($('.board') && !$('.opContainer')))) { - ThreadWatcher.set404(g.BOARD.ID, g.THREADID, function() { - if (Conf['404 Redirect']) { - return Redirect.navigate('thread', { - boardID: g.BOARD.ID, - threadID: g.THREADID, - postID: +location.hash.match(/\d+/) - }, "/" + g.BOARD + "/"); - } - }); + var msg; + if (typeof Site.is404 === "function" ? Site.is404() : void 0) { + if (g.VIEW === 'thread') { + ThreadWatcher.set404(g.BOARD.ID, g.THREADID, function() { + if (Conf['404 Redirect']) { + return Redirect.navigate('thread', { + boardID: g.BOARD.ID, + threadID: g.THREADID, + postID: +location.hash.match(/\d+/) + }, "/" + g.BOARD + "/"); + } + }); + } return; } - if ((ref1 = d.title) === '4chan - Temporarily Offline' || ref1 === '4chan - 404 Not Found') { - return; - } - if (((ref2 = g.VIEW) === 'index' || ref2 === 'thread') && !$('.board + *')) { + if (typeof Site.isIncomplete === "function" ? Site.isIncomplete() : void 0) { msg = $.el('div', { innerHTML: "The page didn't load completely.
Some features may not work unless you reload." }); @@ -24989,7 +24812,7 @@ Main = (function() { }); new Notice('warning', msg); } - if (!(Conf['JSON Index'] && g.VIEW === 'index')) { + if (!Index.enabled) { return Main.initThread(); } else { Main.expectInitFinished = true; @@ -24997,22 +24820,27 @@ Main = (function() { } }, initThread: function() { - var board, err, errors, j, k, len, len1, m, postRoot, posts, ref, ref1, scriptData, thread, threadRoot, threads; - if ((board = $('.board'))) { + var board, boardID, boardObj, err, errors, j, k, len, len1, postRoot, postRoots, posts, ref, s, thread, threadRoot, threads; + s = Site.selectors; + if ((board = $(s.board))) { threads = []; posts = []; - ref = $$('.board > .thread', board); + ref = $$(s.thread, board); for (j = 0, len = ref.length; j < len; j++) { threadRoot = ref[j]; - thread = new Thread(+threadRoot.id.slice(1), g.BOARD); + boardObj = (boardID = threadRoot.dataset.board) ? g.boards[boardID] || new Board(boardID) : g.BOARD; + thread = new Thread(+threadRoot.id.match(/\d*$/)[0], boardObj); thread.nodes.root = threadRoot; threads.push(thread); - ref1 = $$('.thread > .postContainer', threadRoot); - for (k = 0, len1 = ref1.length; k < len1; k++) { - postRoot = ref1[k]; - if ($('.postMessage', postRoot)) { + postRoots = $$(s.postContainer, threadRoot); + if (Site.isOPContainerThread) { + postRoots.unshift(threadRoot); + } + for (k = 0, len1 = postRoots.length; k < len1; k++) { + postRoot = postRoots[k]; + if ($(s.comment, postRoot)) { try { - posts.push(new Post(postRoot, thread, g.BOARD)); + posts.push(new Post(postRoot, thread, thread.board)); } catch (_error) { err = _error; if (!errors) { @@ -25030,20 +24858,9 @@ Main = (function() { Main.handleErrors(errors); } if (g.VIEW === 'thread') { - scriptData = Get.scriptData(); - threads[0].postLimit = /\bbumplimit *= *1\b/.test(scriptData); - threads[0].fileLimit = /\bimagelimit *= *1\b/.test(scriptData); - threads[0].ipCount = (m = scriptData.match(/\bunique_ips *= *(\d+)\b/)) ? +m[1] : void 0; - } - if (g.BOARD.ID === 'f' && g.VIEW === 'thread') { - $.ajax(location.protocol + "//a.4cdn.org/f/thread/" + g.THREADID + ".json", { - timeout: $.MINUTE, - onloadend: function() { - if (this.response && posts[0].file) { - return posts[0].file.text.dataset.md5 = posts[0].file.MD5 = this.response.posts[0].md5; - } - } - }); + if (typeof Site.parseThreadMetadata === "function") { + Site.parseThreadMetadata(threads[0]); + } } Main.callbackNodes('Thread', threads); return Main.callbackNodesDB('Post', posts, function() { @@ -25168,9 +24985,8 @@ Main = (function() { }; }, isThisPageLegit: function() { - var ref; if (!('thisPageIsLegit' in Main)) { - Main.thisPageIsLegit = location.hostname === 'boards.4chan.org' && !$('link[href*="favicon-status.ico"]', d.head) && ((ref = d.title) !== '4chan - Temporarily Offline' && ref !== '4chan - Error' && ref !== '504 Gateway Time-out'); + Main.thisPageIsLegit = Site.isThisPageLegit ? Site.isThisPageLegit() : !/^[45]\d\d\b/.test(document.title); } return Main.thisPageIsLegit; }, diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip index 479797007..66c395aba 100644 Binary files a/builds/4chan-X.zip and b/builds/4chan-X.zip differ diff --git a/builds/updates-beta.json b/builds/updates-beta.json index 413b6bb6a..e23fdbafa 100644 --- a/builds/updates-beta.json +++ b/builds/updates-beta.json @@ -3,7 +3,7 @@ "4chan-x@4chan-x.net": { "updates": [ { - "version": "1.13.15.5", + "version": "1.14.0.0", "update_link": "https://www.4chan-x.net/builds/4chan-X-beta.crx" } ] diff --git a/builds/updates-beta.xml b/builds/updates-beta.xml index 6f812dfb6..b09617958 100644 --- a/builds/updates-beta.xml +++ b/builds/updates-beta.xml @@ -1,7 +1,7 @@ - + diff --git a/builds/updates.json b/builds/updates.json index dffa620f5..9ce10fadb 100644 --- a/builds/updates.json +++ b/builds/updates.json @@ -3,7 +3,7 @@ "4chan-x@4chan-x.net": { "updates": [ { - "version": "1.13.15.5", + "version": "1.14.0.0", "update_link": "https://www.4chan-x.net/builds/4chan-X.crx" } ] diff --git a/builds/updates.xml b/builds/updates.xml index 5e48c2308..1531f10e4 100644 --- a/builds/updates.xml +++ b/builds/updates.xml @@ -1,7 +1,7 @@ - + diff --git a/version.json b/version.json index db07d6bb7..b6ac32c22 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "version": "1.13.15.5", - "date": "2018-01-23T21:09:54.187Z" + "version": "1.14.0.0", + "date": "2018-01-24T16:27:06.567Z" } \ No newline at end of file