diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a8132071..b651518fe 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v1.8.9 +*2014-08-10* + Based on v1.8.8.6. **ccd0** diff --git a/LICENSE b/LICENSE index d9ab07d9c..9d269428f 100755 --- a/LICENSE +++ b/LICENSE @@ -1,10 +1,10 @@ /* -* 4chan X - Version 1.8.8.6 - 2014-08-07 +* 4chan X - Version 1.8.9 * * Licensed under the MIT license. * https://github.com/ccd0/4chan-x/blob/master/LICENSE * -* Appchan X Copyright © 2013-2013 Zixaphir +* Appchan X Copyright © 2013-2014 Zixaphir * http://zixaphir.github.io/appchan-x/ * 4chan x Copyright © 2009-2011 James Campos * https://github.com/aeosynth/4chan-x @@ -12,7 +12,7 @@ * https://4chan-x.just-believe.in/ * 4chan x Copyright © 2013-2014 Jordan Bates * http://seaweedchan.github.io/4chan-x/ -* 4chan x Copyright © 2012-2014 ihavenoface +* 4chan x Copyright © 2012-2013 ihavenoface * http://ihavenoface.github.io/4chan-x/ * 4chan SS Copyright © 2011-2013 Ahodesuka * https://github.com/ahodesuka/4chan-Style-Script/ @@ -77,4 +77,4 @@ * All rights reserved. * * license: https://github.com/4chan/4chan-JS/blob/master/LICENSE -*/ \ No newline at end of file +*/ diff --git a/builds/4chan-X-beta.crx b/builds/4chan-X-beta.crx index 2f40ba724..04c044be6 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 ea6b1ab24..4fb6694a5 100644 --- a/builds/4chan-X-beta.meta.js +++ b/builds/4chan-X-beta.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.8.8.6 +// @version 1.8.9 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X diff --git a/builds/4chan-X-beta.user.js b/builds/4chan-X-beta.user.js index 663628570..2e30a3aa6 100644 --- a/builds/4chan-X-beta.user.js +++ b/builds/4chan-X-beta.user.js @@ -1,7 +1,7 @@ // Generated by CoffeeScript // ==UserScript== // @name 4chan X -// @version 1.8.8.6 +// @version 1.8.9 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -24,12 +24,12 @@ // ==/UserScript== /* -* 4chan X - Version 1.8.8.6 - 2014-08-07 +* 4chan X - Version 1.8.9 * * Licensed under the MIT license. * https://github.com/ccd0/4chan-x/blob/master/LICENSE * -* Appchan X Copyright © 2013-2013 Zixaphir +* Appchan X Copyright © 2013-2014 Zixaphir * http://zixaphir.github.io/appchan-x/ * 4chan x Copyright © 2009-2011 James Campos * https://github.com/aeosynth/4chan-x @@ -37,7 +37,7 @@ * https://4chan-x.just-believe.in/ * 4chan x Copyright © 2013-2014 Jordan Bates * http://seaweedchan.github.io/4chan-x/ -* 4chan x Copyright © 2012-2014 ihavenoface +* 4chan x Copyright © 2012-2013 ihavenoface * http://ihavenoface.github.io/4chan-x/ * 4chan SS Copyright © 2011-2013 Ahodesuka * https://github.com/ahodesuka/4chan-Style-Script/ @@ -103,6 +103,7 @@ * * license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ + 'use strict'; (function() { @@ -137,7 +138,8 @@ 'QR Shortcut': [false, 'Adds a small [QR] link in the header.'], 'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'], 'Desktop Notifications': [false, 'Enables desktop notifications across various 4chan X features.'], - '404 Redirect': [true, 'Redirect dead threads and images.'], + '404 Redirect': [true, 'Redirect dead threads and images to the archives.'], + 'Except Archives from Encryption': [false, 'Permit loading content from, and warningless redirects to, HTTP-only archives from HTTPS pages.'], 'Keybinds': [true, 'Bind actions to keyboard shortcuts.'], 'Time Formatting': [true, 'Localize and format timestamps.'], 'Relative Post Dates': [true, 'Display dates like "3 minutes ago". Tooltip shows the timestamp.'], @@ -378,8 +380,11 @@ doc = d.documentElement; g = { - VERSION: '1.8.8.6', + VERSION: '1.8.9', NAMESPACE: '4chan X.', + NAME: '4chan X', + FAQ: 'https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions', + CHANGELOG: 'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md', boards: {} }; @@ -453,15 +458,13 @@ lastModified = {}; blockedURLs = {}; blockedError = function(url) { - var h_message, message; + var message; if (blockedURLs[url]) { return; } blockedURLs[url] = true; - h_message = '4chan X was blocked from loading the following URL:

'; - h_message += '[More info]'; message = $.el('div', { - innerHTML: h_message + innerHTML: E(g.NAME) + " was blocked from loading the following URL:

[More info]" }); $('span', message).textContent = (/^\/\//.test(url) ? location.protocol : '') + url; return new Notice('error', message, 30, function() { @@ -1551,7 +1554,7 @@ this.close = __bind(this.close, this); this.add = __bind(this.add, this); this.el = $.el('div', { - innerHTML: '
' + innerHTML: "
" }); this.el.style.opacity = 0; this.setType(type); @@ -1818,8 +1821,10 @@ var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler; this.menu = new UI.Menu('header'); menuButton = $.el('span', { - className: 'menu-button', - innerHTML: '' + className: 'menu-button' + }); + $.extend(menuButton, { + innerHTML: "" }); barFixedToggler = UI.checkbox('Fixed Header', ' Fixed Header'); headerToggler = UI.checkbox('Header auto-hide', ' Auto-hide header'); @@ -1955,8 +1960,10 @@ var a, boardList, btn, fourchannav, fullBoardList, _i, _len, _ref; fourchannav = $.id('boardNavDesktop'); Header.boardList = boardList = $.el('span', { - id: 'board-list', - innerHTML: "" + id: 'board-list' + }); + $.extend(boardList, { + innerHTML: "" }); _ref = $$('a', boardList); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -2293,7 +2300,7 @@ return; } el = $.el('span', { - innerHTML: '4chan X needs your permission to show desktop notifications.\n[FAQ]
\n or ' + innerHTML: E(g.NAME) + " needs your permission to show desktop notifications. [FAQ]
or " }); _ref = $$('button', el), authorize = _ref[0], disable = _ref[1]; $.on(authorize, 'click', function() { @@ -2335,15 +2342,15 @@ subEntries: [ { el: $.el('label', { - innerHTML: ' Paged' + innerHTML: " Paged" }) }, { el: $.el('label', { - innerHTML: ' Infinite scrolling' + innerHTML: " Infinite scrolling" }) }, { el: $.el('label', { - innerHTML: ' All threads' + innerHTML: " All threads" }) } ] @@ -2363,23 +2370,23 @@ subEntries: [ { el: $.el('label', { - innerHTML: ' Bump order' + innerHTML: " Bump order" }) }, { el: $.el('label', { - innerHTML: ' Last reply' + innerHTML: " Last reply" }) }, { el: $.el('label', { - innerHTML: ' Creation date' + innerHTML: " Creation date" }) }, { el: $.el('label', { - innerHTML: ' Reply count' + innerHTML: " Reply count" }) }, { el: $.el('label', { - innerHTML: ' File count' + innerHTML: " File count" }) } ] @@ -2430,12 +2437,16 @@ }); this.pagelist = $.el('div', { className: 'pagelist', - hidden: true, - innerHTML: '
' + hidden: true + }); + $.extend(this.pagelist, { + innerHTML: "
" }); this.navLinks = $.el('div', { - className: 'navLinks', - innerHTML: 'Return Catalog Bottom ×' + className: 'navLinks' + }); + $.extend(this.navLinks, { + innerHTML: "Return Catalog Bottom ×" }); $('.returnlink a', this.navLinks).href = "//boards.4chan.org/" + g.BOARD + "/"; $('.cataloglink a', this.navLinks).href = "//boards.4chan.org/" + g.BOARD + "/catalog"; @@ -3093,7 +3104,9 @@ flagName: Build.unescape(data.country_name), date: data.now, dateUTC: data.time, - h_comment: data.com || '', + comment: { + innerHTML: data.com || '' + }, isSticky: !!data.sticky, isClosed: !!data.closed }; @@ -3126,200 +3139,158 @@ This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ - var boardID, capcode, container, date, dateUTC, email, emailProcessed, file, fileSize, fileThumb, flagCode, flagCodeLC, flagName, h_capcodeClass, h_capcodeIcon, h_capcodeStart, h_closed, h_comment, h_desktop2, h_email, h_emailCont, h_file, h_fileCont, h_fileDims, h_fileTitle1, h_fileTitle2, h_flag, h_highlightPost, h_message, h_nameBlock, h_nameClass, h_pageIcon, h_post, h_postInfo, h_replyLink, h_retina, h_spoilerClass, h_sticky, h_subject, h_tripcode, h_userID, href, isClosed, isOP, isSticky, match, name, pageNum, postID, postLink, quote, quoteLink, shortFilename, spoilerRange, subject, threadID, tripcode, uniqueID, _i, _len, _ref; - postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, file = o.file; + var boardID, capcode, capcodeClass, capcodeIcon, capcodeStart, closed, comment, container, date, dateUTC, desktop2, email, emailField, emailProcessed, file, fileBlock, fileCont, fileDims, fileLink, fileSize, fileText, fileThumb, flag, flagCode, flagName, highlightPost, href, isClosed, isOP, isSticky, match, message, name, nameBlock, nameClass, pageIcon, pageNum, postID, postInfo, postLink, quote, quoteLink, replyLink, retina, shortFilename, spoilerRange, sticky, subject, subjectField, threadID, tripcode, tripcodeField, uniqueID, userID, wholePost, _i, _len, _ref; + postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file; name || (name = ''); subject || (subject = ''); - h_comment = o.h_comment; isOP = postID === threadID; - if (Build.initPixelRatio >= 2) { - h_retina = '@2x'; - } else { - h_retina = ''; - } + retina = Build.initPixelRatio >= 2 ? '@2x' : ''; /* Name Block */ switch (capcode) { case 'admin': case 'admin_highlight': - h_capcodeClass = ' capcodeAdmin'; - h_capcodeStart = ' ## Admin'; - h_capcodeIcon = "Admin Icon"; + capcodeClass = ' capcodeAdmin'; + capcodeStart = { + innerHTML: " ## Admin" + }; + capcodeIcon = { + innerHTML: "\"Admin" + }; break; case 'mod': - h_capcodeClass = ' capcodeMod'; - h_capcodeStart = ' ## Mod'; - h_capcodeIcon = "Mod Icon"; + capcodeClass = ' capcodeMod'; + capcodeStart = { + innerHTML: " ## Mod" + }; + capcodeIcon = { + innerHTML: "\"Mod" + }; break; case 'developer': - h_capcodeClass = ' capcodeDeveloper'; - h_capcodeStart = ' ## Developer'; - h_capcodeIcon = "Developer Icon"; + capcodeClass = ' capcodeDeveloper'; + capcodeStart = { + innerHTML: " ## Developer" + }; + capcodeIcon = { + innerHTML: "\"Developer" + }; break; default: - h_capcodeClass = ''; - h_capcodeStart = ''; - h_capcodeIcon = ''; + capcodeClass = ''; + capcodeStart = { + innerHTML: "" + }; + capcodeIcon = { + innerHTML: "" + }; } - if (capcode) { - h_nameClass = ' capcode'; - } else { - h_nameClass = ''; - } - if (tripcode) { - h_tripcode = " " + (E(tripcode)) + ""; - } else { - h_tripcode = ''; - } - h_emailCont = "" + (E(name)) + "" + h_tripcode + h_capcodeStart; + nameClass = capcode ? ' capcode' : ''; + tripcodeField = tripcode ? { + innerHTML: " " + E(tripcode) + "" + } : { + innerHTML: "" + }; + emailField = { + innerHTML: "" + E(name) + "" + tripcodeField.innerHTML + capcodeStart.innerHTML + }; if (email) { emailProcessed = encodeURIComponent(email).replace(/%40/g, '@'); - h_email = "" + h_emailCont + ""; - } else { - h_email = h_emailCont; + emailField = { + innerHTML: "" + emailField.innerHTML + "" + }; } if (!(isOP && boardID === 'f')) { - h_email += ' '; + emailField = { + innerHTML: emailField.innerHTML + " " + }; } - if (!capcode && uniqueID) { - h_userID = " (ID: " + (E(uniqueID)) + ")"; - } else { - h_userID = ''; - } - if (!flagCode) { - h_flag = ''; - } else { - flagCodeLC = flagCode.toLowerCase(); - if (boardID === 'pol') { - h_flag = "" + (E(flagCode)) + ""; - } else { - h_flag = ""; - } - } - h_nameBlock = ""; - h_nameBlock += "" + h_email + h_capcodeIcon + h_userID + h_flag; - h_nameBlock += ' '; + userID = !capcode && uniqueID ? { + innerHTML: " (ID: " + E(uniqueID) + ")" + } : { + innerHTML: "" + }; + flag = !flagCode ? { + innerHTML: "" + } : boardID === 'pol' ? { + innerHTML: "\""" + } : { + innerHTML: "" + }; + nameBlock = { + innerHTML: "" + emailField.innerHTML + capcodeIcon.innerHTML + userID.innerHTML + flag.innerHTML + " " + }; /* Post Info */ - if (isOP || boardID === 'f') { - h_subject = "" + (E(subject)) + " "; - } else { - h_subject = ''; - } - if (isOP && boardID === 'f') { - h_desktop2 = ''; - } else { - h_desktop2 = ' desktop'; - } + subjectField = isOP || boardID === 'f' ? { + innerHTML: "" + E(subject) + " " + } : { + innerHTML: "" + }; + desktop2 = isOP && boardID === 'f' ? '' : ' desktop'; postLink = Build.postURL(boardID, threadID, postID); quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + postID + "');" : "/" + boardID + "/thread/" + threadID + "\#q" + postID; - if (isOP && g.VIEW === 'index' && Conf['JSON Navigation']) { - pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage) + 1; - h_pageIcon = " [" + (+pageNum) + "]"; - } else { - h_pageIcon = ''; - } - if (isSticky) { - h_sticky = " Sticky"; - } else { - h_sticky = ''; - } - if (isClosed) { - h_closed = " Closed"; - } else { - h_closed = ''; - } - if (isOP && g.VIEW === 'index') { - h_replyLink = "   [Reply]"; - } else { - h_replyLink = ''; - } - h_postInfo = "'; + pageIcon = isOP && g.VIEW === 'index' && Conf['JSON Navigation'] ? (pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage) + 1, { + innerHTML: " [" + E(pageNum) + "]" + }) : { + innerHTML: "" + }; + sticky = isSticky ? { + innerHTML: " \"Sticky\"" + } : { + innerHTML: "" + }; + closed = isClosed ? { + innerHTML: " \"Closed\"" + } : { + innerHTML: "" + }; + replyLink = isOP && g.VIEW === 'index' ? { + innerHTML: "   [Reply]" + } : { + innerHTML: "" + }; + postInfo = { + innerHTML: "
" + subjectField.innerHTML + nameBlock.innerHTML + "" + E(date) + " No." + E(postID) + "" + pageIcon.innerHTML + sticky.innerHTML + closed.innerHTML + replyLink.innerHTML + "
" + }; /* File Info */ - if (file != null ? file.isDeleted : void 0) { - h_fileCont = ''; - h_fileCont += "File deleted."; - h_fileCont += ''; - } else if (file && boardID === 'f') { - fileSize = $.bytesToString(file.size); - h_fileCont = "
"; - h_fileCont += "File: " + (E(file.name)) + ""; - h_fileCont += "-(" + (E(fileSize)) + ", " + (+file.width) + "x" + (+file.height) + ", " + (E(file.tag)) + ")"; - h_fileCont += '
'; - } else if (file) { - if (file.isSpoiler) { - h_fileTitle1 = "title='" + (E(file.name)) + "'"; - shortFilename = 'Spoiler Image'; - h_spoilerClass = ' imgspoiler'; - if (spoilerRange = Build.spoilerRange[boardID]) { - fileThumb = "//s.4cdn.org/image/spoiler-" + boardID + (Math.floor(1 + spoilerRange * Math.random())) + ".png"; - } else { - fileThumb = '//s.4cdn.org/image/spoiler.png'; - } - file.twidth = file.theight = 100; - } else { - h_fileTitle1 = ''; - shortFilename = Build.shortFilename(file.name, !isOP); - h_spoilerClass = ''; - fileThumb = file.turl; - } - if (file.isSpoiler || file.name === shortFilename) { - h_fileTitle2 = ''; - } else { - h_fileTitle2 = "title='" + (E(file.name)) + "'"; - } - fileSize = $.bytesToString(file.size); - if (file.url.slice(-4) === '.pdf') { - h_fileDims = 'PDF'; - } else { - h_fileDims = "" + (+file.width) + "x" + (+file.height); - } - h_fileCont = "
"; - h_fileCont += "File: " + (E(shortFilename)) + " (" + (E(fileSize)) + ", " + h_fileDims + ")"; - h_fileCont += '
'; - h_fileCont += ""; - h_fileCont += "" + (E(fileSize)) + ""; - h_fileCont += ''; - } - if (file) { - h_file = "
" + h_fileCont + "
"; - } else { - h_file = ''; - } + fileCont = (file != null ? file.isDeleted : void 0) ? { + innerHTML: "\"File" + } : file && boardID === 'f' ? { + innerHTML: "
File: " + E(file.name) + "-(" + E($.bytesToString(file.size)) + ", " + E(file.width) + "x" + E(file.height) + ", " + E(file.tag) + ")
" + } : file ? (file.isSpoiler ? (shortFilename = 'Spoiler Image', (spoilerRange = Build.spoilerRange[boardID]) ? fileThumb = "//s.4cdn.org/image/spoiler-" + boardID + (Math.floor(1 + spoilerRange * Math.random())) + ".png" : fileThumb = '//s.4cdn.org/image/spoiler.png', file.twidth = file.theight = 100) : (shortFilename = Build.shortFilename(file.name, !isOP), fileThumb = file.turl), fileSize = $.bytesToString(file.size), fileDims = file.url.slice(-4) === '.pdf' ? 'PDF' : "" + file.width + "x" + file.height, fileLink = file.isSpoiler || file.name === shortFilename ? { + innerHTML: "" + E(shortFilename) + "" + } : { + innerHTML: "" + E(shortFilename) + "" + }, fileText = file.isSpoiler ? { + innerHTML: "
File: " + fileLink.innerHTML + " (" + E(fileSize) + ", " + E(fileDims) + ")
" + } : { + innerHTML: "
File: " + fileLink.innerHTML + " (" + E(fileSize) + ", " + E(fileDims) + ")
" + }, { + innerHTML: fileText.innerHTML + "\""" + }) : void 0; + fileBlock = file ? { + innerHTML: "
" + fileCont.innerHTML + "
" + } : { + innerHTML: "" + }; /* Whole Post */ - if (capcode === 'admin_highlight') { - h_highlightPost = ' highlightPost'; - } else { - h_highlightPost = ''; - } - h_message = "
" + h_comment + "
"; - if (isOP) { - h_post = "
"; - h_post += "" + h_file + h_postInfo + h_message; - h_post += '
'; - } else { - h_post = "
>>
"; - h_post += "
"; - h_post += "" + h_postInfo + h_file + h_message; - h_post += '
'; - } + highlightPost = capcode === 'admin_highlight' ? ' highlightPost' : ''; + message = { + innerHTML: "
" + comment.innerHTML + "
" + }; + wholePost = isOP ? { + innerHTML: "
" + fileBlock.innerHTML + postInfo.innerHTML + message.innerHTML + "
" + } : { + innerHTML: "
>>
" + postInfo.innerHTML + fileBlock.innerHTML + message.innerHTML + "
" + }; container = $.el('div', { className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", - id: "pc" + postID, - innerHTML: h_post + id: "pc" + postID }); + $.extend(container, wholePost); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; @@ -3467,7 +3438,7 @@ }); }, postClone: function(boardID, threadID, postID, root, context) { - var post, url; + var post; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; @@ -3477,16 +3448,8 @@ return $.cache("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", function() { return Get.fetchedPost(this, boardID, threadID, postID, root, context); }); - } else if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - return $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); + } else { + return Get.archivedPost(boardID, postID, root, context); } }, insert: function(post, root, context) { @@ -3503,24 +3466,14 @@ return $.add(root, nodes.root); }, fetchedPost: function(req, boardID, threadID, postID, root, context) { - var board, post, posts, status, thread, url, _i, _len; + var board, post, posts, status, thread, _i, _len; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; } status = req.status; if (status !== 200 && status !== 304) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); - } else { + if (!Get.archivedPost(boardID, postID, root, context)) { $.addClass(root, 'warning'); root.textContent = status === 404 ? "Thread No." + threadID + " 404'd." : "Error " + req.statusText + " (" + req.status + ")."; } @@ -3535,17 +3488,7 @@ } } if (post.no !== postID) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); - } else { + if (!Get.archivedPost(boardID, postID, root, context)) { $.addClass(root, 'warning'); root.textContent = "Post No." + postID + " was not found."; } @@ -3558,21 +3501,93 @@ Main.callbackNodes(Post, [post]); return Get.insert(post, root, context); }, - archivedPost: function(req, boardID, postID, root, context) { - var board, data, h_comment, o, post, thread, threadID, _ref; + archivedPost: function(boardID, postID, root, context) { + var url; + if (!Conf['Resurrect Quotes']) { + return false; + } + if (!(url = Redirect.to('post', { + boardID: boardID, + postID: postID + }))) { + return false; + } + if (/^https:\/\//.test(URL) || location.protocol === 'http:') { + $.cache(url, function() { + return Get.parseArchivedPost(this.response, boardID, postID, root, context); + }, { + responseType: 'json', + withCredentials: url.archive.withCredentials + }); + return true; + } else if (Conf['Except Archives from Encryption']) { + CrossOrigin.json(url, function(response) { + return Get.parseArchivedPost(response, boardID, postID, root, context); + }); + return true; + } + return false; + }, + parseArchivedPost: function(data, boardID, postID, root, context) { + var board, comment, greentext, i, j, o, post, text, text2, thread, threadID, _ref; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; } - data = req.response; if (data.error) { $.addClass(root, 'warning'); root.textContent = data.error; return; } - h_comment = E(data.comment || ''); - h_comment = h_comment.replace(/\n|\[\/?[a-z]+(:lit)?\]/g, Get.parseMarkup); - h_comment = h_comment.replace(/(^|>)(>[^<$]*)(<|$)/g, '$1$2$3').replace(/((>){2}(>\/[a-z\d]+\/)?\d+)/g, '$1'); + comment = (data.comment || '').split(/(\n|\[\/?(?:b|spoiler|code|moot|banned)\])/); + comment = (function() { + var _i, _len, _results; + _results = []; + for (i = _i = 0, _len = comment.length; _i < _len; i = ++_i) { + text = comment[i]; + if (i % 2 === 1) { + _results.push(Get.archiveTags[text]); + } else { + greentext = text[0] === '>'; + text = text.replace(/(\[\/?[a-z]+):lit(\])/, '$1$2'); + text = (function() { + var _j, _len1, _ref, _results1; + _ref = text.split(/(>>(?:>\/[a-z\d]+\/)?\d+)/g); + _results1 = []; + for (j = _j = 0, _len1 = _ref.length; _j < _len1; j = ++_j) { + text2 = _ref[j]; + if (j % 2 === 1) { + _results1.push({ + innerHTML: "" + E(text2) + "" + }); + } else { + _results1.push({ + innerHTML: E(text2) + }); + } + } + return _results1; + })(); + text = { + innerHTML: text.map(function(x) { + return x.innerHTML; + }).join('') + }; + if (greentext) { + text = { + innerHTML: "" + text.innerHTML + "" + }; + } + _results.push(text); + } + } + return _results; + })(); + comment = { + innerHTML: comment.map(function(x) { + return x.innerHTML; + }).join('') + }; threadID = +data.thread_num; o = { postID: postID, @@ -3597,7 +3612,7 @@ flagName: data.poster_country_name, date: data.fourchan_date, dateUTC: data.timestamp, - h_comment: h_comment + comment: comment }; if ((_ref = data.media) != null ? _ref.media_filename : void 0) { o.file = { @@ -3629,20 +3644,40 @@ Main.callbackNodes(Post, [post]); return Get.insert(post, root, context); }, - parseMarkup: function(text) { - return { - '\n': '
', - '[b]': '', - '[/b]': '', - '[spoiler]': '', - '[/spoiler]': '', - '[code]': '
',
-        '[/code]': '
', - '[moot]': '
', - '[/moot]': '
', - '[banned]': '', - '[/banned]': '' - }[text] || text.replace(':lit', ''); + archiveTags: { + '\n': { + innerHTML: "
" + }, + '[b]': { + innerHTML: "" + }, + '[/b]': { + innerHTML: "" + }, + '[spoiler]': { + innerHTML: "" + }, + '[/spoiler]': { + innerHTML: "" + }, + '[code]': { + innerHTML: "
"
+      },
+      '[/code]': {
+        innerHTML: "
" + }, + '[moot]': { + innerHTML: "
" + }, + '[/moot]': { + innerHTML: "
" + }, + '[banned]': { + innerHTML: "" + }, + '[/banned]': { + innerHTML: "" + } } }; @@ -4082,49 +4117,85 @@ }; })(); - CrossOrigin = (function() { - var handleBlob, handleUrl; - handleBlob = function(urlBlob, contentType, contentDisposition, url, cb) { - var blob, match, mime, name, _ref, _ref1, _ref2; - name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0; - mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream'; - match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0); - if (match) { - name = match.replace(/\\"/g, '"'); - } - blob = new Blob([urlBlob], { - type: mime - }); - blob.name = name; - return cb(blob); - }; - handleUrl = function(url, cb) { - return GM_xmlhttpRequest({ - method: "GET", - url: url, - overrideMimeType: "text/plain; charset=x-user-defined", - onload: function(xhr) { - var contentDisposition, contentType, data, i, r, _ref, _ref1; - r = xhr.responseText; - data = new Uint8Array(r.length); - i = 0; - while (i < r.length) { - data[i] = r.charCodeAt(i); - i++; - } - contentType = (_ref = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)) != null ? _ref[1] : void 0; - contentDisposition = (_ref1 = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)) != null ? _ref1[1] : void 0; - return handleBlob(data, contentType, contentDisposition, url, cb); - }, - onerror: function(xhr) { - return cb(null); + CrossOrigin = { + file: (function() { + var makeBlob; + makeBlob = function(urlBlob, contentType, contentDisposition, url) { + var blob, match, mime, name, _ref, _ref1, _ref2; + name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0; + mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream'; + match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0); + if (match) { + name = match.replace(/\\"/g, '"'); } - }); - }; - return { - request: handleUrl - }; - })(); + blob = new Blob([urlBlob], { + type: mime + }); + blob.name = name; + return blob; + }; + return function(url, cb) { + return GM_xmlhttpRequest({ + method: "GET", + url: url, + overrideMimeType: "text/plain; charset=x-user-defined", + onload: function(xhr) { + var contentDisposition, contentType, data, i, r, _ref, _ref1; + r = xhr.responseText; + data = new Uint8Array(r.length); + i = 0; + while (i < r.length) { + data[i] = r.charCodeAt(i); + i++; + } + contentType = (_ref = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)) != null ? _ref[1] : void 0; + contentDisposition = (_ref1 = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)) != null ? _ref1[1] : void 0; + return cb(makeBlob(data, contentType, contentDisposition, url)); + }, + onerror: function() { + return cb(null); + } + }); + }; + })(), + json: (function() { + var callbacks, responses; + callbacks = {}; + responses = {}; + return function(url, cb) { + if (responses[url]) { + cb(responses[url]); + return; + } + if (callbacks[url]) { + callbacks[url].push(cb); + return; + } + callbacks[url] = [cb]; + return GM_xmlhttpRequest({ + method: "GET", + url: url, + onload: function(xhr) { + var response, _i, _len, _ref; + response = JSON.parse(xhr.responseText); + _ref = callbacks[url]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + cb = _ref[_i]; + cb(response); + } + delete callbacks[url]; + return responses[url] = response; + }, + onerror: function() { + return delete callbacks[url]; + }, + onabort: function() { + return delete callbacks[url]; + } + }); + }; + })() + }; Anonymize = { init: function() { @@ -4963,9 +5034,11 @@ var a; a = $.el('a', { className: "" + type + "-thread-button", - innerHTML: "", href: 'javascript:;' }); + $.extend(a, { + innerHTML: "" + }); a.dataset.fullID = thread.fullID; $.on(a, 'click', ThreadHiding.toggle); return a; @@ -5454,7 +5527,7 @@ } this.enabled = true; this.controls = $.el('span', { - innerHTML: '' + innerHTML: "" }); input = $('input', this.controls); $.on(input, 'change', this.toggle); @@ -5707,7 +5780,7 @@ } }, parseDeadlink: function(deadlink) { - var a, boardID, m, post, postID, quote, quoteID, redirect, _ref; + var a, boardID, fetchable, m, post, postID, quote, quoteID, redirect, _ref; if ($.hasClass(deadlink.parentNode, 'prettyprint')) { Quotify.fixDeadlink(deadlink); return; @@ -5742,26 +5815,30 @@ postID: postID }); } - } else if (redirect = Redirect.to('thread', { - boardID: boardID, - threadID: 0, - postID: postID - })) { - a = $.el('a', { - href: redirect, - className: 'deadlink', - target: '_blank', - textContent: "" + quote + "\u00A0(Dead)" + } else { + redirect = Redirect.to('thread', { + boardID: boardID, + threadID: 0, + postID: postID }); - if (Redirect.to('post', { + fetchable = Redirect.to('post', { boardID: boardID, postID: postID - })) { - $.addClass(a, 'quotelink'); - $.extend(a.dataset, { - boardID: boardID, - postID: postID + }); + if (redirect || fetchable) { + a = $.el('a', { + href: redirect || 'javascript:;', + className: 'deadlink', + target: '_blank', + textContent: "" + quote + "\u00A0(Dead)" }); + if (fetchable) { + $.addClass(a, 'quotelink'); + $.extend(a.dataset, { + boardID: boardID, + postID: postID + }); + } } } if (__indexOf.call(this.quotes, quoteID) < 0) { @@ -5844,9 +5921,11 @@ return; } link = $.el('h1', { - innerHTML: "" + (g.VIEW === 'thread' ? 'Reply to Thread' : 'Start a Thread') + "", className: "qr-link-container" }); + $.extend(link, { + innerHTML: "" + E((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + "" + }); QR.link = link.firstElementChild; $.on(link.firstChild, 'click', function() { $.event('CloseMenu'); @@ -5858,9 +5937,11 @@ }); if (Conf['Bottom QR Link'] && g.VIEW === 'thread') { linkBot = $.el('div', { - innerHTML: 'Reply to Thread', className: "brackets-wrap qr-link-container-bottom" }); + $.extend(linkBot, { + innerHTML: "Reply to Thread" + }); $.on(linkBot.firstElementChild, 'click', function() { $.event('CloseMenu'); QR.open(); @@ -6137,7 +6218,7 @@ if (url === null) { return; } - return CrossOrigin.request(url, function(blob) { + return CrossOrigin.file(url, function(blob) { if (blob) { return QR.handleFiles([blob]); } else { @@ -6326,7 +6407,7 @@ var dialog, elm, event, i, items, name, node, nodes, prop, rules, save, setNode, _, _i, _len, _ref, _ref1, _ref2; QR.nodes = nodes = { el: dialog = UI.dialog('qr', 'top:0;right:0;', { - innerHTML: '
×
No selected file
' + innerHTML: "
×
No selected file
" }) }; setNode = function(name, query) { @@ -6390,8 +6471,10 @@ } if (g.BOARD.ID === 'f' && g.VIEW !== 'thread') { nodes.flashTag = $.el('select', { - name: 'filetag', - innerHTML: '\n\n\n\n\n\n' + name: 'filetag' + }); + $.extend(nodes.flashTag, { + innerHTML: "" }); nodes.flashTag.dataset["default"] = '4'; $.add(nodes.form, nodes.flashTag); @@ -6575,7 +6658,7 @@ QR.cooldown.auto = false; QR.status(); return QR.error($.el('span', { - innerHTML: '4chan X encountered an error while posting. \n[Banned?] [More info]' + innerHTML: "4chan X encountered an error while posting. [Banned?] [More info]" })); } }; @@ -6608,9 +6691,9 @@ resDoc = req.response; if (ban = $('.banType', resDoc)) { err = $.el('span', ban.textContent.toLowerCase() === 'banned' ? { - innerHTML: "You are banned on " + ($('.board', resDoc).innerHTML) + "! ;_;
Click here to see the reason." + innerHTML: "You are banned on " + $(".board", resDoc).innerHTML + "! ;_;
Click here to see the reason." } : { - innerHTML: "You were issued a warning on " + ($('.board', resDoc).innerHTML) + " as " + ($('.nameBlock', resDoc).innerHTML) + ".
Reason: " + ($('.reason', resDoc).innerHTML) + innerHTML: "You were issued a warning on " + $(".board", resDoc).innerHTML + " as " + $(".nameBlock", resDoc).innerHTML + ".
Reason: " + $(".reason", resDoc).innerHTML }); } else if (err = resDoc.getElementById('errmsg')) { if ((_ref = $('a', err)) != null) { @@ -6739,8 +6822,10 @@ } imgContainer = $.el('div', { className: 'captcha-img', - title: 'Reload reCAPTCHA', - innerHTML: '' + title: 'Reload reCAPTCHA' + }); + $.extend(imgContainer, { + innerHTML: "" }); input = $.el('input', { className: 'captcha-input field', @@ -7168,8 +7253,10 @@ el = $.el('a', { className: 'qr-preview', draggable: true, - href: 'javascript:;', - innerHTML: '' + href: 'javascript:;' + }); + $.extend(el, { + innerHTML: "" }); this.nodes = { el: el, @@ -7634,8 +7721,10 @@ Gallery.fullIDs = {}; Gallery.slideshow = false; nodes.el = dialog = $.el('div', { - id: 'a-gallery', - innerHTML: '
× /
' + id: 'a-gallery' + }); + $.extend(dialog, { + innerHTML: "
× /
" }); _ref = { buttons: '.gal-buttons', @@ -7967,7 +8056,7 @@ var delayInput, delayLabel, subEntries; subEntries = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Scroll to Post'].map(Gallery.menu.createSubEntry); delayLabel = $.el('label', { - innerHTML: 'Slide Delay: ' + innerHTML: "Slide Delay: " }); delayInput = delayLabel.firstElementChild; delayInput.value = Gallery.delay; @@ -8003,7 +8092,7 @@ boardID: post.board.ID, filename: src[src.length - 1] }); - if (!(URL && (/^https:\/\//.test(URL) || location.protocol === 'http:'))) { + if (!(Conf['404 Redirect'] && URL && Redirect.securityCheck(URL))) { URL = null; } if ((post.isDead || post.file.isDead) && file.src.split('/')[2] === 'i.4cdn.org') { @@ -8083,6 +8172,12 @@ $.on(this.EAI, 'click', this.cb.toggleAll); Header.addShortcut(this.EAI, 3); $.on(d, 'scroll visibilitychange', this.cb.playVideos); + this.videoControls = $.el('span', { + className: 'video-controls' + }); + $.extend(this.videoControls, { + innerHTML: " contract" + }); return Post.callbacks.push({ name: 'Image Expansion', cb: this.node @@ -8337,10 +8432,6 @@ return ImageCommon.addControls(fullImage); } }, - videoControls: $.el('span', { - className: 'video-controls', - innerHTML: '\u00A0contract' - }), videoCB: (function() { var mousedown; mousedown = false; @@ -8527,11 +8618,13 @@ return function(URL) { if (URL) { return _this.src = URL + (_this.src === URL ? '?' + Date.now() : ''); + } else { + $.rm(_this); + return delete post.file.fullImage; } }; })(this)); } else { - $.off(this, 'error', ImageHover.error); $.rm(this); return delete post.file.fullImage; } @@ -8559,7 +8652,7 @@ return; } prefetch = $.el('label', { - innerHTML: ' Prefetch Images' + innerHTML: " Prefetch Images" }); this.el = prefetch.firstElementChild; $.on(this.el, 'change', this.toggle); @@ -8946,8 +9039,9 @@ }); } catch (_error) { err = _error; - link.innerHTML = 'Title Link Blocked (are you using NoScript?)'; - $.prepend(link, $.tn("[" + key + "] ")); + $.extend(link, { + innerHTML: "[" + E(key) + "] Title Link Blocked (are you using NoScript?)" + }); } } }, @@ -9021,10 +9115,14 @@ key: 'gist', regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, el: function(a) { - var div; - return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + a.dataset.uid + ".js" - }); + var content, el; + el = $.el('iframe'); + el.setAttribute('sandbox', 'allow-scripts'); + content = { + innerHTML: "" + E(a.dataset.uid) + "" + }; + el.src = "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + return el; }, title: { api: function(uid) { @@ -9045,11 +9143,9 @@ regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, style: 'border: 0; width: auto; height: auto;', el: function(a) { - var el; - el = $.el('div'); - el.innerHTML = ''; - el.firstChild.href = el.firstChild.firstChild.src = a.href; - return el; + return $.el('div', { + innerHTML: "" + }); } }, { key: 'InstallGentoo', @@ -9089,7 +9185,7 @@ var el; el = $.el('div'); $.cache("https://mediacru.sh/" + a.dataset.uid + ".json", function() { - var embed, ext, file, files, i, status, type, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _results, _results1; + var embed, ext, file, files, i, status, type, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _results; status = this.status; if (status !== 200 && status !== 304) { return el.textContent = "ERROR " + status; @@ -9116,8 +9212,10 @@ case 'video/mp4': case 'video/webm': case 'video/ogv': - el.innerHTML = ''; - _ref1 = ['mp4', 'webm', 'ogv']; + $.extend(el, { + innerHTML: "" + }); + _ref1 = ['mp4', 'webm']; _results = []; for (i = _k = 0, _len2 = _ref1.length; _k < _len2; i = ++_k) { ext = _ref1[i]; @@ -9129,20 +9227,14 @@ case 'image/png': case 'image/gif': case 'image/jpeg': - el.innerHTML = ''; - el.firstChild.href = a.href; - return el.firstChild.firstChild.src = "https://mediacru.sh/" + file.file; + return $.extend(el, { + innerHTML: "" + }); case 'audio/mpeg': case 'audio/ogg': - el.innerHTML = ''; - _ref2 = ['ogg', 'mp3']; - _results1 = []; - for (i = _l = 0, _len3 = _ref2.length; _l < _len3; i = ++_l) { - ext = _ref2[i]; - _results1.push(el.firstChild.children[i].src = "https://mediacru.sh/" + a.dataset.uid + "." + ext); - } - return _results1; - break; + return $.extend(el, { + innerHTML: "" + }); default: return el.textContent = "ERROR: No valid filetype."; } @@ -9208,7 +9300,9 @@ obj = $.el('object', { data: 'http://www.twitch.tv/widgets/archive_embed_player.swf' }); - obj.innerHTML = ''; + $.extend(obj, { + innerHTML: "" + }); obj.children[1].value = "channel=" + channel + "&start_volume=25&auto_play=false&" + idparam[type] + "=" + id; return obj; } else { @@ -9216,7 +9310,9 @@ obj = $.el('object', { data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel }); - obj.innerHTML = ''; + $.extend(obj, { + innerHTML: "" + }); obj.children[1].value = "hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25"; return obj; } @@ -9525,7 +9621,7 @@ return true; } e.preventDefault(); - return CrossOrigin.request(this.href, (function(_this) { + return CrossOrigin.file(this.href, (function(_this) { return function(blob) { if (blob) { _this.href = URL.createObjectURL(blob); @@ -9575,9 +9671,11 @@ var a; a = $.el('a', { className: 'menu-button', - innerHTML: '', href: 'javascript:;' }); + $.extend(a, { + innerHTML: "" + }); return function() { var button; button = a.cloneNode(true); @@ -9686,22 +9784,30 @@ ThreadStats = { init: function() { - var sc; + var countHTML, sc; if (g.VIEW !== 'thread' || !Conf['Thread Stats']) { return; } + countHTML = { + innerHTML: "0 / 0" + }; + if (Conf['Page Count in Stats']) { + countHTML = { + innerHTML: countHTML.innerHTML + " / 0" + }; + } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: "0 / 0" + (Conf['Page Count in Stats'] ? ' / 0' : ''), id: 'thread-stats', title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "") }); + $.extend(sc, countHTML); $.ready(function() { return Header.addShortcut(sc); }); } else { this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', { - innerHTML: "
0 / 0" + (Conf['Page Count in Stats'] ? ' / 0' : '') + "
" + innerHTML: "
" + countHTML.innerHTML + "
" }); $.ready((function(_this) { return function() { @@ -9794,15 +9900,17 @@ } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: '', id: 'updater' }); + $.extend(sc, { + innerHTML: "" + }); $.ready(function() { return Header.addShortcut(sc); }); } else { this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', { - innerHTML: '
' + innerHTML: "
" }); $.addClass(doc, 'float'); $.ready((function(_this) { @@ -9819,9 +9927,11 @@ $.on(this.timer, 'click', this.update); $.on(this.status, 'click', this.update); updateLink = $.el('span', { - innerHTML: 'Update', className: 'brackets-wrap updatelink' }); + $.extend(updateLink, { + innerHTML: "Update" + }); $.ready(function() { return $.add($('.navLinksBot'), [$.tn(' '), updateLink]); }); @@ -9845,7 +9955,7 @@ }); } this.settings = $.el('span', { - innerHTML: 'Interval' + innerHTML: "Interval" }); $.on(this.settings, 'click', this.intervalShortcut); subEntries.push({ @@ -10165,7 +10275,7 @@ }); this.db = new DataBoard('watchedThreads', this.refresh, true); this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', { - innerHTML: '
Thread Watcher ×
' + innerHTML: "
Thread Watcher ×
" }); this.status = $('#watcher-status', this.dialog); this.list = this.dialog.lastElementChild; @@ -10409,7 +10519,7 @@ href: 'javascript:;' }); $.on(x, 'click', ThreadWatcher.cb.rm); - if (data.isDead) { + if (Conf['404 Redirect'] && data.isDead) { href = Redirect.to('thread', { boardID: boardID, threadID: threadID @@ -10938,14 +11048,14 @@ record = _ref1[boardID]; for (type in record) { id = record[type]; - if (!((archive = archives[id]))) { - continue; + if (id === 'disabled') { + delete o[type][boardID]; + } else if (archive = archives[id]) { + boards = type === 'file' ? archive.files : archive.boards; + if (__indexOf.call(boards, boardID) >= 0) { + o[type][boardID] = archive; + } } - boards = type === 'file' ? archive.files : archive.boards; - if (__indexOf.call(boards, boardID) < 0) { - continue; - } - o[type][boardID] = archive; } } return Redirect.data = o; @@ -10983,10 +11093,10 @@ var URL, boardID, postID, protocol; boardID = _arg.boardID, postID = _arg.postID; protocol = Redirect.protocol(archive); - if (!(protocol === 'https://' || location.protocol === 'http:')) { + URL = new String("" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID); + if (!Redirect.securityCheck(URL)) { return ''; } - URL = new String("" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID); URL.archive = archive; return URL; }, @@ -11003,8 +11113,11 @@ path = archive.software === 'foolfuuka' ? "" + boardID + "/search/" + type + "/" + value : "" + boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value; return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path; }, + securityCheck: function(URL) { + return /^https:\/\//.test(URL) || location.protocol === 'http:' || Conf['Except Archives from Encryption']; + }, navigate: function(URL, alternative) { - if (URL && (/^https:\/\//.test(URL) || location.protocol === 'http:' || confirm("Redirect to " + URL + "?\n\nYour connection will not be encrypted."))) { + if (URL && (Redirect.securityCheck(URL) || confirm("Redirect to " + URL + "?\n\nYour connection will not be encrypted."))) { return location.replace(URL); } else if (alternative) { return location.replace(alternative); @@ -11041,10 +11154,11 @@ Header.menu.addEntry(entry); $.on(entry.el, 'click', PSAHiding.toggle); PSAHiding.btn = btn = $.el('span', { - innerHTML: '[Dismiss]', title: 'Mark announcement as read and hide.', - className: 'hide-announcement', - href: 'javascript:;' + className: 'hide-announcement' + }); + $.extend(btn, { + innerHTML: "[Dismiss]" }); $.on(btn, 'click', PSAHiding.toggle); $.get('hiddenPSA', 0, function(_arg) { @@ -11611,85 +11725,106 @@ if (!this.file || this.isClone) { return; } - this.file.text.innerHTML = ''; + $.extend(this.file.text, { + innerHTML: "" + }); return FileInfo.format(Conf['fileInfo'], this, this.file.text.firstElementChild); }, format: function(formatString, post, outputNode) { - FileInfo.innerHTML = ''; + var output; + output = []; formatString.replace(/%(.)|[^%]+/g, function(s, c) { - if (c in FileInfo.formatters) { - FileInfo.formatters[c].call(post); - } else { - FileInfo.innerHTML += E(s); - } + output.push(c in FileInfo.formatters ? FileInfo.formatters[c].call(post) : { + innerHTML: E(s) + }); return ''; }); - return outputNode.innerHTML = FileInfo.innerHTML; + return $.extend(outputNode, { + innerHTML: output.map(function(x) { + return x.innerHTML; + }).join('') + }); }, formatters: { t: function() { - var timestamp; - timestamp = this.file.URL.match(/\d+\..+$/)[0]; - return FileInfo.innerHTML += E(timestamp); + return { + innerHTML: E(this.file.URL.match(/\d+\..+$/)[0]) + }; }, T: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.t.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" + }; }, l: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.n.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" + }; }, L: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.N.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" + }; }, n: function() { var fullname, shortname; fullname = this.file.name; shortname = Build.shortFilename(this.file.name, this.isReply); if (fullname === shortname) { - return FileInfo.innerHTML += E(fullname); + return { + innerHTML: E(fullname) + }; } else { - return FileInfo.innerHTML += "" + (E(shortname)) + "" + (E(fullname)) + ""; + return { + innerHTML: "" + E(shortname) + "" + E(fullname) + "" + }; } }, N: function() { - return FileInfo.innerHTML += E(this.file.name); + return { + innerHTML: E(this.file.name) + }; }, p: function() { if (this.file.isSpoiler) { - return FileInfo.innerHTML += 'Spoiler, '; + return { + innerHTML: "Spoiler, " + }; + } else { + return { + innerHTML: "" + }; } }, s: function() { - return FileInfo.innerHTML += E(this.file.size); + return { + innerHTML: E(this.file.size) + }; }, B: function() { - var sizeB; - sizeB = Math.round(this.file.sizeInBytes); - return FileInfo.innerHTML += "" + (+sizeB) + " Bytes"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes" + }; }, K: function() { - var sizeKB; - sizeKB = Math.round(this.file.sizeInBytes / 1024); - return FileInfo.innerHTML += "" + (+sizeKB) + " KB"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB" + }; }, M: function() { - var sizeMB; - sizeMB = Math.round(this.file.sizeInBytes / 1048576 * 100) / 100; - return FileInfo.innerHTML += "" + (+sizeMB) + " MB"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB" + }; }, r: function() { - var dim; - dim = this.file.dimensions || 'PDF'; - return FileInfo.innerHTML += E(dim); + return { + innerHTML: E(this.file.dimensions || "PDF") + }; }, '%': function() { - return FileInfo.innerHTML += '%'; + return { + innerHTML: "%" + }; } } }; @@ -12593,8 +12728,10 @@ }); Settings.dialog = dialog = $.el('div', { id: 'fourchanx-settings', - className: 'dialog', - innerHTML: '
' + className: 'dialog' + }); + $.extend(dialog, { + innerHTML: "
" }); $.on($('.export', Settings.dialog), 'click', Settings["export"]); $.on($('.import', Settings.dialog), 'click', Settings["import"]); @@ -12666,9 +12803,8 @@ for (key in _ref) { obj = _ref[key]; fs = $.el('fieldset', { - innerHTML: '' + innerHTML: "" + E(key) + "" }); - fs.firstElementChild.textContent = key; for (key in obj) { arr = obj[key]; description = arr[1]; @@ -12695,7 +12831,7 @@ } }); div = $.el('div', { - innerHTML: ': Clear manually-hidden threads and posts on all boards. Reload the page to apply.' + innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." }); button = $('button', div); $.get({ @@ -12892,7 +13028,9 @@ }, filter: function(section) { var select; - section.innerHTML = '
'; + $.extend(section, { + innerHTML: "
" + }); select = $('select', section); $.on(select, 'change', Settings.selectFilter); return Settings.selectFilter.call(select); @@ -12914,12 +13052,16 @@ $.add(div, ta); return; } - div.innerHTML = '
Filter is disabled.

Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions.

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.
'; + $.extend(div, { + innerHTML: "
Filter is disabled.

Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions.

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.
" + }); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; - section.innerHTML = '
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
'; + $.extend(section, { + innerHTML: "
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" + }); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { @@ -12929,7 +13071,9 @@ }, advanced: function(section) { var archBoards, boardID, boardOptions, boardSelect, boards, customCSS, event, files, i, input, inputs, interval, item, items, name, o, row, rows, software, ta, table, warning, withCredentials, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _len6, _m, _n, _o, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; - section.innerHTML = '
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Disabled selections indicate that only one archive is available for that board and redirection type.
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Quote Backlinks formatting is disabled.
:
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays \'PDF\' for PDF files)
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input\'s auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, email, subject and password.
  • Wrap values of items with quotes, like this: email:"sage".
  • Force values as defaults with the always keyword, for example: email:"sage";always.
  • Select specific boards for an item, separated with commas, for example: email:"sage";boards:jp;always.
Unread Favicon is disabled.
Emoji is disabled.
Sage Icon:
Position:
Thread Updater is disabled.
Interval:
'; + $.extend(section, { + innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:\"Install Gentoo\"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:\"Google\",\"http://www.google.com\"
Combinations are possible: g-index-text:\"Technology Index\"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:\"Piracy\"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Quote Backlinks formatting is disabled.
:
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, email, subject and password.
  • Wrap values of items with quotes, like this: email:\"sage\".
  • Force values as defaults with the always keyword, for example: email:\"sage\";always.
  • Select specific boards for an item, separated with commas, for example: email:\"sage\";boards:jp;always.
Unread Favicon is disabled.
Emoji is disabled.
Sage Icon:
Position:
Thread Updater is disabled.
Interval:
" + }); _ref = $$('.warning', section); for (_i = 0, _len = _ref.length; _i < _len; _i++) { warning = _ref[_i]; @@ -13001,9 +13145,7 @@ _ref4 = ['thread', 'post', 'file']; for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { item = _ref4[_m]; - if (o[item][0].length === 0 && o[item][1].length !== 0) { - o[item][0].push('disabled'); - } + o[item][0].push('disabled'); o[item] = o[item][0].concat(o[item][1]); } } @@ -13073,7 +13215,9 @@ value: archive })); } - td.innerHTML = ''; + $.extend(td, { + innerHTML: "" + }); select = td.firstElementChild; if (!(select.disabled = length === 1)) { select.setAttribute('data-boardid', boardID); @@ -13151,7 +13295,9 @@ }, keybinds: function(section) { var arr, input, inputs, items, key, tbody, tr, _ref; - section.innerHTML = '
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
'; + $.extend(section, { + innerHTML: "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
" + }); $('.warning', section).hidden = Conf['Keybinds']; tbody = $('tbody', section); items = {}; @@ -13160,9 +13306,8 @@ for (key in _ref) { arr = _ref[key]; tr = $.el('tr', { - innerHTML: '' + innerHTML: "" + E(arr[1]) + "" }); - tr.firstElementChild.textContent = arr[1]; input = $('input', tr); input.name = key; input.spellcheck = false; @@ -13436,7 +13581,7 @@ } if (previousversion) { el = $.el('span', { - innerHTML: '4chan X has been updated to version 1.8.8.6.' + innerHTML: E(g.NAME) + " has been updated to version " + E(g.VERSION) + "." }); new Notice('info', el, 15); } else { @@ -13491,7 +13636,7 @@ return; } div = $.el('div', { - innerHTML: "" + (+errors.length) + " errors occurred. [show]" + innerHTML: E(errors.length) + " errors occurred. [show]" }); $.on(div.lastElementChild, 'click', function() { var _ref; diff --git a/builds/4chan-X-noupdate.crx b/builds/4chan-X-noupdate.crx index 6445e61db..7176a47c5 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 1edc1eecc..97973a721 100644 --- a/builds/4chan-X-noupdate.user.js +++ b/builds/4chan-X-noupdate.user.js @@ -1,7 +1,7 @@ // Generated by CoffeeScript // ==UserScript== // @name 4chan X -// @version 1.8.8.6 +// @version 1.8.9 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -23,12 +23,12 @@ // ==/UserScript== /* -* 4chan X - Version 1.8.8.6 - 2014-08-07 +* 4chan X - Version 1.8.9 * * Licensed under the MIT license. * https://github.com/ccd0/4chan-x/blob/master/LICENSE * -* Appchan X Copyright © 2013-2013 Zixaphir +* Appchan X Copyright © 2013-2014 Zixaphir * http://zixaphir.github.io/appchan-x/ * 4chan x Copyright © 2009-2011 James Campos * https://github.com/aeosynth/4chan-x @@ -36,7 +36,7 @@ * https://4chan-x.just-believe.in/ * 4chan x Copyright © 2013-2014 Jordan Bates * http://seaweedchan.github.io/4chan-x/ -* 4chan x Copyright © 2012-2014 ihavenoface +* 4chan x Copyright © 2012-2013 ihavenoface * http://ihavenoface.github.io/4chan-x/ * 4chan SS Copyright © 2011-2013 Ahodesuka * https://github.com/ahodesuka/4chan-Style-Script/ @@ -102,6 +102,7 @@ * * license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ + 'use strict'; (function() { @@ -136,7 +137,8 @@ 'QR Shortcut': [false, 'Adds a small [QR] link in the header.'], 'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'], 'Desktop Notifications': [false, 'Enables desktop notifications across various 4chan X features.'], - '404 Redirect': [true, 'Redirect dead threads and images.'], + '404 Redirect': [true, 'Redirect dead threads and images to the archives.'], + 'Except Archives from Encryption': [false, 'Permit loading content from, and warningless redirects to, HTTP-only archives from HTTPS pages.'], 'Keybinds': [true, 'Bind actions to keyboard shortcuts.'], 'Time Formatting': [true, 'Localize and format timestamps.'], 'Relative Post Dates': [true, 'Display dates like "3 minutes ago". Tooltip shows the timestamp.'], @@ -377,8 +379,11 @@ doc = d.documentElement; g = { - VERSION: '1.8.8.6', + VERSION: '1.8.9', NAMESPACE: '4chan X.', + NAME: '4chan X', + FAQ: 'https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions', + CHANGELOG: 'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md', boards: {} }; @@ -452,15 +457,13 @@ lastModified = {}; blockedURLs = {}; blockedError = function(url) { - var h_message, message; + var message; if (blockedURLs[url]) { return; } blockedURLs[url] = true; - h_message = '4chan X was blocked from loading the following URL:

'; - h_message += '[More info]'; message = $.el('div', { - innerHTML: h_message + innerHTML: E(g.NAME) + " was blocked from loading the following URL:

[More info]" }); $('span', message).textContent = (/^\/\//.test(url) ? location.protocol : '') + url; return new Notice('error', message, 30, function() { @@ -1550,7 +1553,7 @@ this.close = __bind(this.close, this); this.add = __bind(this.add, this); this.el = $.el('div', { - innerHTML: '
' + innerHTML: "
" }); this.el.style.opacity = 0; this.setType(type); @@ -1817,8 +1820,10 @@ var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler; this.menu = new UI.Menu('header'); menuButton = $.el('span', { - className: 'menu-button', - innerHTML: '' + className: 'menu-button' + }); + $.extend(menuButton, { + innerHTML: "" }); barFixedToggler = UI.checkbox('Fixed Header', ' Fixed Header'); headerToggler = UI.checkbox('Header auto-hide', ' Auto-hide header'); @@ -1954,8 +1959,10 @@ var a, boardList, btn, fourchannav, fullBoardList, _i, _len, _ref; fourchannav = $.id('boardNavDesktop'); Header.boardList = boardList = $.el('span', { - id: 'board-list', - innerHTML: "" + id: 'board-list' + }); + $.extend(boardList, { + innerHTML: "" }); _ref = $$('a', boardList); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -2292,7 +2299,7 @@ return; } el = $.el('span', { - innerHTML: '4chan X needs your permission to show desktop notifications.\n[FAQ]
\n or ' + innerHTML: E(g.NAME) + " needs your permission to show desktop notifications. [FAQ]
or " }); _ref = $$('button', el), authorize = _ref[0], disable = _ref[1]; $.on(authorize, 'click', function() { @@ -2334,15 +2341,15 @@ subEntries: [ { el: $.el('label', { - innerHTML: ' Paged' + innerHTML: " Paged" }) }, { el: $.el('label', { - innerHTML: ' Infinite scrolling' + innerHTML: " Infinite scrolling" }) }, { el: $.el('label', { - innerHTML: ' All threads' + innerHTML: " All threads" }) } ] @@ -2362,23 +2369,23 @@ subEntries: [ { el: $.el('label', { - innerHTML: ' Bump order' + innerHTML: " Bump order" }) }, { el: $.el('label', { - innerHTML: ' Last reply' + innerHTML: " Last reply" }) }, { el: $.el('label', { - innerHTML: ' Creation date' + innerHTML: " Creation date" }) }, { el: $.el('label', { - innerHTML: ' Reply count' + innerHTML: " Reply count" }) }, { el: $.el('label', { - innerHTML: ' File count' + innerHTML: " File count" }) } ] @@ -2429,12 +2436,16 @@ }); this.pagelist = $.el('div', { className: 'pagelist', - hidden: true, - innerHTML: '
' + hidden: true + }); + $.extend(this.pagelist, { + innerHTML: "
" }); this.navLinks = $.el('div', { - className: 'navLinks', - innerHTML: 'Return Catalog Bottom ×' + className: 'navLinks' + }); + $.extend(this.navLinks, { + innerHTML: "Return Catalog Bottom ×" }); $('.returnlink a', this.navLinks).href = "//boards.4chan.org/" + g.BOARD + "/"; $('.cataloglink a', this.navLinks).href = "//boards.4chan.org/" + g.BOARD + "/catalog"; @@ -3092,7 +3103,9 @@ flagName: Build.unescape(data.country_name), date: data.now, dateUTC: data.time, - h_comment: data.com || '', + comment: { + innerHTML: data.com || '' + }, isSticky: !!data.sticky, isClosed: !!data.closed }; @@ -3125,200 +3138,158 @@ This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ - var boardID, capcode, container, date, dateUTC, email, emailProcessed, file, fileSize, fileThumb, flagCode, flagCodeLC, flagName, h_capcodeClass, h_capcodeIcon, h_capcodeStart, h_closed, h_comment, h_desktop2, h_email, h_emailCont, h_file, h_fileCont, h_fileDims, h_fileTitle1, h_fileTitle2, h_flag, h_highlightPost, h_message, h_nameBlock, h_nameClass, h_pageIcon, h_post, h_postInfo, h_replyLink, h_retina, h_spoilerClass, h_sticky, h_subject, h_tripcode, h_userID, href, isClosed, isOP, isSticky, match, name, pageNum, postID, postLink, quote, quoteLink, shortFilename, spoilerRange, subject, threadID, tripcode, uniqueID, _i, _len, _ref; - postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, file = o.file; + var boardID, capcode, capcodeClass, capcodeIcon, capcodeStart, closed, comment, container, date, dateUTC, desktop2, email, emailField, emailProcessed, file, fileBlock, fileCont, fileDims, fileLink, fileSize, fileText, fileThumb, flag, flagCode, flagName, highlightPost, href, isClosed, isOP, isSticky, match, message, name, nameBlock, nameClass, pageIcon, pageNum, postID, postInfo, postLink, quote, quoteLink, replyLink, retina, shortFilename, spoilerRange, sticky, subject, subjectField, threadID, tripcode, tripcodeField, uniqueID, userID, wholePost, _i, _len, _ref; + postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file; name || (name = ''); subject || (subject = ''); - h_comment = o.h_comment; isOP = postID === threadID; - if (Build.initPixelRatio >= 2) { - h_retina = '@2x'; - } else { - h_retina = ''; - } + retina = Build.initPixelRatio >= 2 ? '@2x' : ''; /* Name Block */ switch (capcode) { case 'admin': case 'admin_highlight': - h_capcodeClass = ' capcodeAdmin'; - h_capcodeStart = ' ## Admin'; - h_capcodeIcon = "Admin Icon"; + capcodeClass = ' capcodeAdmin'; + capcodeStart = { + innerHTML: " ## Admin" + }; + capcodeIcon = { + innerHTML: "\"Admin" + }; break; case 'mod': - h_capcodeClass = ' capcodeMod'; - h_capcodeStart = ' ## Mod'; - h_capcodeIcon = "Mod Icon"; + capcodeClass = ' capcodeMod'; + capcodeStart = { + innerHTML: " ## Mod" + }; + capcodeIcon = { + innerHTML: "\"Mod" + }; break; case 'developer': - h_capcodeClass = ' capcodeDeveloper'; - h_capcodeStart = ' ## Developer'; - h_capcodeIcon = "Developer Icon"; + capcodeClass = ' capcodeDeveloper'; + capcodeStart = { + innerHTML: " ## Developer" + }; + capcodeIcon = { + innerHTML: "\"Developer" + }; break; default: - h_capcodeClass = ''; - h_capcodeStart = ''; - h_capcodeIcon = ''; + capcodeClass = ''; + capcodeStart = { + innerHTML: "" + }; + capcodeIcon = { + innerHTML: "" + }; } - if (capcode) { - h_nameClass = ' capcode'; - } else { - h_nameClass = ''; - } - if (tripcode) { - h_tripcode = " " + (E(tripcode)) + ""; - } else { - h_tripcode = ''; - } - h_emailCont = "" + (E(name)) + "" + h_tripcode + h_capcodeStart; + nameClass = capcode ? ' capcode' : ''; + tripcodeField = tripcode ? { + innerHTML: " " + E(tripcode) + "" + } : { + innerHTML: "" + }; + emailField = { + innerHTML: "" + E(name) + "" + tripcodeField.innerHTML + capcodeStart.innerHTML + }; if (email) { emailProcessed = encodeURIComponent(email).replace(/%40/g, '@'); - h_email = "" + h_emailCont + ""; - } else { - h_email = h_emailCont; + emailField = { + innerHTML: "" + emailField.innerHTML + "" + }; } if (!(isOP && boardID === 'f')) { - h_email += ' '; + emailField = { + innerHTML: emailField.innerHTML + " " + }; } - if (!capcode && uniqueID) { - h_userID = " (ID: " + (E(uniqueID)) + ")"; - } else { - h_userID = ''; - } - if (!flagCode) { - h_flag = ''; - } else { - flagCodeLC = flagCode.toLowerCase(); - if (boardID === 'pol') { - h_flag = "" + (E(flagCode)) + ""; - } else { - h_flag = ""; - } - } - h_nameBlock = ""; - h_nameBlock += "" + h_email + h_capcodeIcon + h_userID + h_flag; - h_nameBlock += ' '; + userID = !capcode && uniqueID ? { + innerHTML: " (ID: " + E(uniqueID) + ")" + } : { + innerHTML: "" + }; + flag = !flagCode ? { + innerHTML: "" + } : boardID === 'pol' ? { + innerHTML: "\""" + } : { + innerHTML: "" + }; + nameBlock = { + innerHTML: "" + emailField.innerHTML + capcodeIcon.innerHTML + userID.innerHTML + flag.innerHTML + " " + }; /* Post Info */ - if (isOP || boardID === 'f') { - h_subject = "" + (E(subject)) + " "; - } else { - h_subject = ''; - } - if (isOP && boardID === 'f') { - h_desktop2 = ''; - } else { - h_desktop2 = ' desktop'; - } + subjectField = isOP || boardID === 'f' ? { + innerHTML: "" + E(subject) + " " + } : { + innerHTML: "" + }; + desktop2 = isOP && boardID === 'f' ? '' : ' desktop'; postLink = Build.postURL(boardID, threadID, postID); quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + postID + "');" : "/" + boardID + "/thread/" + threadID + "\#q" + postID; - if (isOP && g.VIEW === 'index' && Conf['JSON Navigation']) { - pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage) + 1; - h_pageIcon = " [" + (+pageNum) + "]"; - } else { - h_pageIcon = ''; - } - if (isSticky) { - h_sticky = " Sticky"; - } else { - h_sticky = ''; - } - if (isClosed) { - h_closed = " Closed"; - } else { - h_closed = ''; - } - if (isOP && g.VIEW === 'index') { - h_replyLink = "   [Reply]"; - } else { - h_replyLink = ''; - } - h_postInfo = "'; + pageIcon = isOP && g.VIEW === 'index' && Conf['JSON Navigation'] ? (pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage) + 1, { + innerHTML: " [" + E(pageNum) + "]" + }) : { + innerHTML: "" + }; + sticky = isSticky ? { + innerHTML: " \"Sticky\"" + } : { + innerHTML: "" + }; + closed = isClosed ? { + innerHTML: " \"Closed\"" + } : { + innerHTML: "" + }; + replyLink = isOP && g.VIEW === 'index' ? { + innerHTML: "   [Reply]" + } : { + innerHTML: "" + }; + postInfo = { + innerHTML: "
" + subjectField.innerHTML + nameBlock.innerHTML + "" + E(date) + " No." + E(postID) + "" + pageIcon.innerHTML + sticky.innerHTML + closed.innerHTML + replyLink.innerHTML + "
" + }; /* File Info */ - if (file != null ? file.isDeleted : void 0) { - h_fileCont = ''; - h_fileCont += "File deleted."; - h_fileCont += ''; - } else if (file && boardID === 'f') { - fileSize = $.bytesToString(file.size); - h_fileCont = "
"; - h_fileCont += "File: " + (E(file.name)) + ""; - h_fileCont += "-(" + (E(fileSize)) + ", " + (+file.width) + "x" + (+file.height) + ", " + (E(file.tag)) + ")"; - h_fileCont += '
'; - } else if (file) { - if (file.isSpoiler) { - h_fileTitle1 = "title='" + (E(file.name)) + "'"; - shortFilename = 'Spoiler Image'; - h_spoilerClass = ' imgspoiler'; - if (spoilerRange = Build.spoilerRange[boardID]) { - fileThumb = "//s.4cdn.org/image/spoiler-" + boardID + (Math.floor(1 + spoilerRange * Math.random())) + ".png"; - } else { - fileThumb = '//s.4cdn.org/image/spoiler.png'; - } - file.twidth = file.theight = 100; - } else { - h_fileTitle1 = ''; - shortFilename = Build.shortFilename(file.name, !isOP); - h_spoilerClass = ''; - fileThumb = file.turl; - } - if (file.isSpoiler || file.name === shortFilename) { - h_fileTitle2 = ''; - } else { - h_fileTitle2 = "title='" + (E(file.name)) + "'"; - } - fileSize = $.bytesToString(file.size); - if (file.url.slice(-4) === '.pdf') { - h_fileDims = 'PDF'; - } else { - h_fileDims = "" + (+file.width) + "x" + (+file.height); - } - h_fileCont = "
"; - h_fileCont += "File: " + (E(shortFilename)) + " (" + (E(fileSize)) + ", " + h_fileDims + ")"; - h_fileCont += '
'; - h_fileCont += ""; - h_fileCont += "" + (E(fileSize)) + ""; - h_fileCont += ''; - } - if (file) { - h_file = "
" + h_fileCont + "
"; - } else { - h_file = ''; - } + fileCont = (file != null ? file.isDeleted : void 0) ? { + innerHTML: "\"File" + } : file && boardID === 'f' ? { + innerHTML: "
File: " + E(file.name) + "-(" + E($.bytesToString(file.size)) + ", " + E(file.width) + "x" + E(file.height) + ", " + E(file.tag) + ")
" + } : file ? (file.isSpoiler ? (shortFilename = 'Spoiler Image', (spoilerRange = Build.spoilerRange[boardID]) ? fileThumb = "//s.4cdn.org/image/spoiler-" + boardID + (Math.floor(1 + spoilerRange * Math.random())) + ".png" : fileThumb = '//s.4cdn.org/image/spoiler.png', file.twidth = file.theight = 100) : (shortFilename = Build.shortFilename(file.name, !isOP), fileThumb = file.turl), fileSize = $.bytesToString(file.size), fileDims = file.url.slice(-4) === '.pdf' ? 'PDF' : "" + file.width + "x" + file.height, fileLink = file.isSpoiler || file.name === shortFilename ? { + innerHTML: "" + E(shortFilename) + "" + } : { + innerHTML: "" + E(shortFilename) + "" + }, fileText = file.isSpoiler ? { + innerHTML: "
File: " + fileLink.innerHTML + " (" + E(fileSize) + ", " + E(fileDims) + ")
" + } : { + innerHTML: "
File: " + fileLink.innerHTML + " (" + E(fileSize) + ", " + E(fileDims) + ")
" + }, { + innerHTML: fileText.innerHTML + "\""" + }) : void 0; + fileBlock = file ? { + innerHTML: "
" + fileCont.innerHTML + "
" + } : { + innerHTML: "" + }; /* Whole Post */ - if (capcode === 'admin_highlight') { - h_highlightPost = ' highlightPost'; - } else { - h_highlightPost = ''; - } - h_message = "
" + h_comment + "
"; - if (isOP) { - h_post = "
"; - h_post += "" + h_file + h_postInfo + h_message; - h_post += '
'; - } else { - h_post = "
>>
"; - h_post += "
"; - h_post += "" + h_postInfo + h_file + h_message; - h_post += '
'; - } + highlightPost = capcode === 'admin_highlight' ? ' highlightPost' : ''; + message = { + innerHTML: "
" + comment.innerHTML + "
" + }; + wholePost = isOP ? { + innerHTML: "
" + fileBlock.innerHTML + postInfo.innerHTML + message.innerHTML + "
" + } : { + innerHTML: "
>>
" + postInfo.innerHTML + fileBlock.innerHTML + message.innerHTML + "
" + }; container = $.el('div', { className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", - id: "pc" + postID, - innerHTML: h_post + id: "pc" + postID }); + $.extend(container, wholePost); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; @@ -3466,7 +3437,7 @@ }); }, postClone: function(boardID, threadID, postID, root, context) { - var post, url; + var post; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; @@ -3476,16 +3447,8 @@ return $.cache("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", function() { return Get.fetchedPost(this, boardID, threadID, postID, root, context); }); - } else if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - return $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); + } else { + return Get.archivedPost(boardID, postID, root, context); } }, insert: function(post, root, context) { @@ -3502,24 +3465,14 @@ return $.add(root, nodes.root); }, fetchedPost: function(req, boardID, threadID, postID, root, context) { - var board, post, posts, status, thread, url, _i, _len; + var board, post, posts, status, thread, _i, _len; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; } status = req.status; if (status !== 200 && status !== 304) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); - } else { + if (!Get.archivedPost(boardID, postID, root, context)) { $.addClass(root, 'warning'); root.textContent = status === 404 ? "Thread No." + threadID + " 404'd." : "Error " + req.statusText + " (" + req.status + ")."; } @@ -3534,17 +3487,7 @@ } } if (post.no !== postID) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); - } else { + if (!Get.archivedPost(boardID, postID, root, context)) { $.addClass(root, 'warning'); root.textContent = "Post No." + postID + " was not found."; } @@ -3557,21 +3500,93 @@ Main.callbackNodes(Post, [post]); return Get.insert(post, root, context); }, - archivedPost: function(req, boardID, postID, root, context) { - var board, data, h_comment, o, post, thread, threadID, _ref; + archivedPost: function(boardID, postID, root, context) { + var url; + if (!Conf['Resurrect Quotes']) { + return false; + } + if (!(url = Redirect.to('post', { + boardID: boardID, + postID: postID + }))) { + return false; + } + if (/^https:\/\//.test(URL) || location.protocol === 'http:') { + $.cache(url, function() { + return Get.parseArchivedPost(this.response, boardID, postID, root, context); + }, { + responseType: 'json', + withCredentials: url.archive.withCredentials + }); + return true; + } else if (Conf['Except Archives from Encryption']) { + CrossOrigin.json(url, function(response) { + return Get.parseArchivedPost(response, boardID, postID, root, context); + }); + return true; + } + return false; + }, + parseArchivedPost: function(data, boardID, postID, root, context) { + var board, comment, greentext, i, j, o, post, text, text2, thread, threadID, _ref; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; } - data = req.response; if (data.error) { $.addClass(root, 'warning'); root.textContent = data.error; return; } - h_comment = E(data.comment || ''); - h_comment = h_comment.replace(/\n|\[\/?[a-z]+(:lit)?\]/g, Get.parseMarkup); - h_comment = h_comment.replace(/(^|>)(>[^<$]*)(<|$)/g, '$1$2$3').replace(/((>){2}(>\/[a-z\d]+\/)?\d+)/g, '$1'); + comment = (data.comment || '').split(/(\n|\[\/?(?:b|spoiler|code|moot|banned)\])/); + comment = (function() { + var _i, _len, _results; + _results = []; + for (i = _i = 0, _len = comment.length; _i < _len; i = ++_i) { + text = comment[i]; + if (i % 2 === 1) { + _results.push(Get.archiveTags[text]); + } else { + greentext = text[0] === '>'; + text = text.replace(/(\[\/?[a-z]+):lit(\])/, '$1$2'); + text = (function() { + var _j, _len1, _ref, _results1; + _ref = text.split(/(>>(?:>\/[a-z\d]+\/)?\d+)/g); + _results1 = []; + for (j = _j = 0, _len1 = _ref.length; _j < _len1; j = ++_j) { + text2 = _ref[j]; + if (j % 2 === 1) { + _results1.push({ + innerHTML: "" + E(text2) + "" + }); + } else { + _results1.push({ + innerHTML: E(text2) + }); + } + } + return _results1; + })(); + text = { + innerHTML: text.map(function(x) { + return x.innerHTML; + }).join('') + }; + if (greentext) { + text = { + innerHTML: "" + text.innerHTML + "" + }; + } + _results.push(text); + } + } + return _results; + })(); + comment = { + innerHTML: comment.map(function(x) { + return x.innerHTML; + }).join('') + }; threadID = +data.thread_num; o = { postID: postID, @@ -3596,7 +3611,7 @@ flagName: data.poster_country_name, date: data.fourchan_date, dateUTC: data.timestamp, - h_comment: h_comment + comment: comment }; if ((_ref = data.media) != null ? _ref.media_filename : void 0) { o.file = { @@ -3628,20 +3643,40 @@ Main.callbackNodes(Post, [post]); return Get.insert(post, root, context); }, - parseMarkup: function(text) { - return { - '\n': '
', - '[b]': '', - '[/b]': '', - '[spoiler]': '', - '[/spoiler]': '', - '[code]': '
',
-        '[/code]': '
', - '[moot]': '
', - '[/moot]': '
', - '[banned]': '', - '[/banned]': '' - }[text] || text.replace(':lit', ''); + archiveTags: { + '\n': { + innerHTML: "
" + }, + '[b]': { + innerHTML: "" + }, + '[/b]': { + innerHTML: "" + }, + '[spoiler]': { + innerHTML: "" + }, + '[/spoiler]': { + innerHTML: "" + }, + '[code]': { + innerHTML: "
"
+      },
+      '[/code]': {
+        innerHTML: "
" + }, + '[moot]': { + innerHTML: "
" + }, + '[/moot]': { + innerHTML: "
" + }, + '[banned]': { + innerHTML: "" + }, + '[/banned]': { + innerHTML: "" + } } }; @@ -4081,49 +4116,85 @@ }; })(); - CrossOrigin = (function() { - var handleBlob, handleUrl; - handleBlob = function(urlBlob, contentType, contentDisposition, url, cb) { - var blob, match, mime, name, _ref, _ref1, _ref2; - name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0; - mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream'; - match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0); - if (match) { - name = match.replace(/\\"/g, '"'); - } - blob = new Blob([urlBlob], { - type: mime - }); - blob.name = name; - return cb(blob); - }; - handleUrl = function(url, cb) { - return GM_xmlhttpRequest({ - method: "GET", - url: url, - overrideMimeType: "text/plain; charset=x-user-defined", - onload: function(xhr) { - var contentDisposition, contentType, data, i, r, _ref, _ref1; - r = xhr.responseText; - data = new Uint8Array(r.length); - i = 0; - while (i < r.length) { - data[i] = r.charCodeAt(i); - i++; - } - contentType = (_ref = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)) != null ? _ref[1] : void 0; - contentDisposition = (_ref1 = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)) != null ? _ref1[1] : void 0; - return handleBlob(data, contentType, contentDisposition, url, cb); - }, - onerror: function(xhr) { - return cb(null); + CrossOrigin = { + file: (function() { + var makeBlob; + makeBlob = function(urlBlob, contentType, contentDisposition, url) { + var blob, match, mime, name, _ref, _ref1, _ref2; + name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0; + mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream'; + match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0); + if (match) { + name = match.replace(/\\"/g, '"'); } - }); - }; - return { - request: handleUrl - }; - })(); + blob = new Blob([urlBlob], { + type: mime + }); + blob.name = name; + return blob; + }; + return function(url, cb) { + return GM_xmlhttpRequest({ + method: "GET", + url: url, + overrideMimeType: "text/plain; charset=x-user-defined", + onload: function(xhr) { + var contentDisposition, contentType, data, i, r, _ref, _ref1; + r = xhr.responseText; + data = new Uint8Array(r.length); + i = 0; + while (i < r.length) { + data[i] = r.charCodeAt(i); + i++; + } + contentType = (_ref = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)) != null ? _ref[1] : void 0; + contentDisposition = (_ref1 = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)) != null ? _ref1[1] : void 0; + return cb(makeBlob(data, contentType, contentDisposition, url)); + }, + onerror: function() { + return cb(null); + } + }); + }; + })(), + json: (function() { + var callbacks, responses; + callbacks = {}; + responses = {}; + return function(url, cb) { + if (responses[url]) { + cb(responses[url]); + return; + } + if (callbacks[url]) { + callbacks[url].push(cb); + return; + } + callbacks[url] = [cb]; + return GM_xmlhttpRequest({ + method: "GET", + url: url, + onload: function(xhr) { + var response, _i, _len, _ref; + response = JSON.parse(xhr.responseText); + _ref = callbacks[url]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + cb = _ref[_i]; + cb(response); + } + delete callbacks[url]; + return responses[url] = response; + }, + onerror: function() { + return delete callbacks[url]; + }, + onabort: function() { + return delete callbacks[url]; + } + }); + }; + })() + }; Anonymize = { init: function() { @@ -4962,9 +5033,11 @@ var a; a = $.el('a', { className: "" + type + "-thread-button", - innerHTML: "", href: 'javascript:;' }); + $.extend(a, { + innerHTML: "" + }); a.dataset.fullID = thread.fullID; $.on(a, 'click', ThreadHiding.toggle); return a; @@ -5453,7 +5526,7 @@ } this.enabled = true; this.controls = $.el('span', { - innerHTML: '' + innerHTML: "" }); input = $('input', this.controls); $.on(input, 'change', this.toggle); @@ -5706,7 +5779,7 @@ } }, parseDeadlink: function(deadlink) { - var a, boardID, m, post, postID, quote, quoteID, redirect, _ref; + var a, boardID, fetchable, m, post, postID, quote, quoteID, redirect, _ref; if ($.hasClass(deadlink.parentNode, 'prettyprint')) { Quotify.fixDeadlink(deadlink); return; @@ -5741,26 +5814,30 @@ postID: postID }); } - } else if (redirect = Redirect.to('thread', { - boardID: boardID, - threadID: 0, - postID: postID - })) { - a = $.el('a', { - href: redirect, - className: 'deadlink', - target: '_blank', - textContent: "" + quote + "\u00A0(Dead)" + } else { + redirect = Redirect.to('thread', { + boardID: boardID, + threadID: 0, + postID: postID }); - if (Redirect.to('post', { + fetchable = Redirect.to('post', { boardID: boardID, postID: postID - })) { - $.addClass(a, 'quotelink'); - $.extend(a.dataset, { - boardID: boardID, - postID: postID + }); + if (redirect || fetchable) { + a = $.el('a', { + href: redirect || 'javascript:;', + className: 'deadlink', + target: '_blank', + textContent: "" + quote + "\u00A0(Dead)" }); + if (fetchable) { + $.addClass(a, 'quotelink'); + $.extend(a.dataset, { + boardID: boardID, + postID: postID + }); + } } } if (__indexOf.call(this.quotes, quoteID) < 0) { @@ -5843,9 +5920,11 @@ return; } link = $.el('h1', { - innerHTML: "" + (g.VIEW === 'thread' ? 'Reply to Thread' : 'Start a Thread') + "", className: "qr-link-container" }); + $.extend(link, { + innerHTML: "" + E((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + "" + }); QR.link = link.firstElementChild; $.on(link.firstChild, 'click', function() { $.event('CloseMenu'); @@ -5857,9 +5936,11 @@ }); if (Conf['Bottom QR Link'] && g.VIEW === 'thread') { linkBot = $.el('div', { - innerHTML: 'Reply to Thread', className: "brackets-wrap qr-link-container-bottom" }); + $.extend(linkBot, { + innerHTML: "Reply to Thread" + }); $.on(linkBot.firstElementChild, 'click', function() { $.event('CloseMenu'); QR.open(); @@ -6136,7 +6217,7 @@ if (url === null) { return; } - return CrossOrigin.request(url, function(blob) { + return CrossOrigin.file(url, function(blob) { if (blob) { return QR.handleFiles([blob]); } else { @@ -6325,7 +6406,7 @@ var dialog, elm, event, i, items, name, node, nodes, prop, rules, save, setNode, _, _i, _len, _ref, _ref1, _ref2; QR.nodes = nodes = { el: dialog = UI.dialog('qr', 'top:0;right:0;', { - innerHTML: '
×
No selected file
' + innerHTML: "
×
No selected file
" }) }; setNode = function(name, query) { @@ -6389,8 +6470,10 @@ } if (g.BOARD.ID === 'f' && g.VIEW !== 'thread') { nodes.flashTag = $.el('select', { - name: 'filetag', - innerHTML: '\n\n\n\n\n\n' + name: 'filetag' + }); + $.extend(nodes.flashTag, { + innerHTML: "" }); nodes.flashTag.dataset["default"] = '4'; $.add(nodes.form, nodes.flashTag); @@ -6574,7 +6657,7 @@ QR.cooldown.auto = false; QR.status(); return QR.error($.el('span', { - innerHTML: '4chan X encountered an error while posting. \n[Banned?] [More info]' + innerHTML: "4chan X encountered an error while posting. [Banned?] [More info]" })); } }; @@ -6607,9 +6690,9 @@ resDoc = req.response; if (ban = $('.banType', resDoc)) { err = $.el('span', ban.textContent.toLowerCase() === 'banned' ? { - innerHTML: "You are banned on " + ($('.board', resDoc).innerHTML) + "! ;_;
Click here to see the reason." + innerHTML: "You are banned on " + $(".board", resDoc).innerHTML + "! ;_;
Click here to see the reason." } : { - innerHTML: "You were issued a warning on " + ($('.board', resDoc).innerHTML) + " as " + ($('.nameBlock', resDoc).innerHTML) + ".
Reason: " + ($('.reason', resDoc).innerHTML) + innerHTML: "You were issued a warning on " + $(".board", resDoc).innerHTML + " as " + $(".nameBlock", resDoc).innerHTML + ".
Reason: " + $(".reason", resDoc).innerHTML }); } else if (err = resDoc.getElementById('errmsg')) { if ((_ref = $('a', err)) != null) { @@ -6738,8 +6821,10 @@ } imgContainer = $.el('div', { className: 'captcha-img', - title: 'Reload reCAPTCHA', - innerHTML: '' + title: 'Reload reCAPTCHA' + }); + $.extend(imgContainer, { + innerHTML: "" }); input = $.el('input', { className: 'captcha-input field', @@ -7167,8 +7252,10 @@ el = $.el('a', { className: 'qr-preview', draggable: true, - href: 'javascript:;', - innerHTML: '' + href: 'javascript:;' + }); + $.extend(el, { + innerHTML: "" }); this.nodes = { el: el, @@ -7633,8 +7720,10 @@ Gallery.fullIDs = {}; Gallery.slideshow = false; nodes.el = dialog = $.el('div', { - id: 'a-gallery', - innerHTML: '
× /
' + id: 'a-gallery' + }); + $.extend(dialog, { + innerHTML: "
× /
" }); _ref = { buttons: '.gal-buttons', @@ -7966,7 +8055,7 @@ var delayInput, delayLabel, subEntries; subEntries = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Scroll to Post'].map(Gallery.menu.createSubEntry); delayLabel = $.el('label', { - innerHTML: 'Slide Delay: ' + innerHTML: "Slide Delay: " }); delayInput = delayLabel.firstElementChild; delayInput.value = Gallery.delay; @@ -8002,7 +8091,7 @@ boardID: post.board.ID, filename: src[src.length - 1] }); - if (!(URL && (/^https:\/\//.test(URL) || location.protocol === 'http:'))) { + if (!(Conf['404 Redirect'] && URL && Redirect.securityCheck(URL))) { URL = null; } if ((post.isDead || post.file.isDead) && file.src.split('/')[2] === 'i.4cdn.org') { @@ -8082,6 +8171,12 @@ $.on(this.EAI, 'click', this.cb.toggleAll); Header.addShortcut(this.EAI, 3); $.on(d, 'scroll visibilitychange', this.cb.playVideos); + this.videoControls = $.el('span', { + className: 'video-controls' + }); + $.extend(this.videoControls, { + innerHTML: " contract" + }); return Post.callbacks.push({ name: 'Image Expansion', cb: this.node @@ -8336,10 +8431,6 @@ return ImageCommon.addControls(fullImage); } }, - videoControls: $.el('span', { - className: 'video-controls', - innerHTML: '\u00A0contract' - }), videoCB: (function() { var mousedown; mousedown = false; @@ -8526,11 +8617,13 @@ return function(URL) { if (URL) { return _this.src = URL + (_this.src === URL ? '?' + Date.now() : ''); + } else { + $.rm(_this); + return delete post.file.fullImage; } }; })(this)); } else { - $.off(this, 'error', ImageHover.error); $.rm(this); return delete post.file.fullImage; } @@ -8558,7 +8651,7 @@ return; } prefetch = $.el('label', { - innerHTML: ' Prefetch Images' + innerHTML: " Prefetch Images" }); this.el = prefetch.firstElementChild; $.on(this.el, 'change', this.toggle); @@ -8945,8 +9038,9 @@ }); } catch (_error) { err = _error; - link.innerHTML = 'Title Link Blocked (are you using NoScript?)'; - $.prepend(link, $.tn("[" + key + "] ")); + $.extend(link, { + innerHTML: "[" + E(key) + "] Title Link Blocked (are you using NoScript?)" + }); } } }, @@ -9020,10 +9114,14 @@ key: 'gist', regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, el: function(a) { - var div; - return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + a.dataset.uid + ".js" - }); + var content, el; + el = $.el('iframe'); + el.setAttribute('sandbox', 'allow-scripts'); + content = { + innerHTML: "" + E(a.dataset.uid) + "" + }; + el.src = "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + return el; }, title: { api: function(uid) { @@ -9044,11 +9142,9 @@ regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, style: 'border: 0; width: auto; height: auto;', el: function(a) { - var el; - el = $.el('div'); - el.innerHTML = ''; - el.firstChild.href = el.firstChild.firstChild.src = a.href; - return el; + return $.el('div', { + innerHTML: "" + }); } }, { key: 'InstallGentoo', @@ -9088,7 +9184,7 @@ var el; el = $.el('div'); $.cache("https://mediacru.sh/" + a.dataset.uid + ".json", function() { - var embed, ext, file, files, i, status, type, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _results, _results1; + var embed, ext, file, files, i, status, type, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _results; status = this.status; if (status !== 200 && status !== 304) { return el.textContent = "ERROR " + status; @@ -9115,8 +9211,10 @@ case 'video/mp4': case 'video/webm': case 'video/ogv': - el.innerHTML = ''; - _ref1 = ['mp4', 'webm', 'ogv']; + $.extend(el, { + innerHTML: "" + }); + _ref1 = ['mp4', 'webm']; _results = []; for (i = _k = 0, _len2 = _ref1.length; _k < _len2; i = ++_k) { ext = _ref1[i]; @@ -9128,20 +9226,14 @@ case 'image/png': case 'image/gif': case 'image/jpeg': - el.innerHTML = ''; - el.firstChild.href = a.href; - return el.firstChild.firstChild.src = "https://mediacru.sh/" + file.file; + return $.extend(el, { + innerHTML: "" + }); case 'audio/mpeg': case 'audio/ogg': - el.innerHTML = ''; - _ref2 = ['ogg', 'mp3']; - _results1 = []; - for (i = _l = 0, _len3 = _ref2.length; _l < _len3; i = ++_l) { - ext = _ref2[i]; - _results1.push(el.firstChild.children[i].src = "https://mediacru.sh/" + a.dataset.uid + "." + ext); - } - return _results1; - break; + return $.extend(el, { + innerHTML: "" + }); default: return el.textContent = "ERROR: No valid filetype."; } @@ -9207,7 +9299,9 @@ obj = $.el('object', { data: 'http://www.twitch.tv/widgets/archive_embed_player.swf' }); - obj.innerHTML = ''; + $.extend(obj, { + innerHTML: "" + }); obj.children[1].value = "channel=" + channel + "&start_volume=25&auto_play=false&" + idparam[type] + "=" + id; return obj; } else { @@ -9215,7 +9309,9 @@ obj = $.el('object', { data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel }); - obj.innerHTML = ''; + $.extend(obj, { + innerHTML: "" + }); obj.children[1].value = "hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25"; return obj; } @@ -9524,7 +9620,7 @@ return true; } e.preventDefault(); - return CrossOrigin.request(this.href, (function(_this) { + return CrossOrigin.file(this.href, (function(_this) { return function(blob) { if (blob) { _this.href = URL.createObjectURL(blob); @@ -9574,9 +9670,11 @@ var a; a = $.el('a', { className: 'menu-button', - innerHTML: '', href: 'javascript:;' }); + $.extend(a, { + innerHTML: "" + }); return function() { var button; button = a.cloneNode(true); @@ -9685,22 +9783,30 @@ ThreadStats = { init: function() { - var sc; + var countHTML, sc; if (g.VIEW !== 'thread' || !Conf['Thread Stats']) { return; } + countHTML = { + innerHTML: "0 / 0" + }; + if (Conf['Page Count in Stats']) { + countHTML = { + innerHTML: countHTML.innerHTML + " / 0" + }; + } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: "0 / 0" + (Conf['Page Count in Stats'] ? ' / 0' : ''), id: 'thread-stats', title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "") }); + $.extend(sc, countHTML); $.ready(function() { return Header.addShortcut(sc); }); } else { this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', { - innerHTML: "
0 / 0" + (Conf['Page Count in Stats'] ? ' / 0' : '') + "
" + innerHTML: "
" + countHTML.innerHTML + "
" }); $.ready((function(_this) { return function() { @@ -9793,15 +9899,17 @@ } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: '', id: 'updater' }); + $.extend(sc, { + innerHTML: "" + }); $.ready(function() { return Header.addShortcut(sc); }); } else { this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', { - innerHTML: '
' + innerHTML: "
" }); $.addClass(doc, 'float'); $.ready((function(_this) { @@ -9818,9 +9926,11 @@ $.on(this.timer, 'click', this.update); $.on(this.status, 'click', this.update); updateLink = $.el('span', { - innerHTML: 'Update', className: 'brackets-wrap updatelink' }); + $.extend(updateLink, { + innerHTML: "Update" + }); $.ready(function() { return $.add($('.navLinksBot'), [$.tn(' '), updateLink]); }); @@ -9844,7 +9954,7 @@ }); } this.settings = $.el('span', { - innerHTML: 'Interval' + innerHTML: "Interval" }); $.on(this.settings, 'click', this.intervalShortcut); subEntries.push({ @@ -10164,7 +10274,7 @@ }); this.db = new DataBoard('watchedThreads', this.refresh, true); this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', { - innerHTML: '
Thread Watcher ×
' + innerHTML: "
Thread Watcher ×
" }); this.status = $('#watcher-status', this.dialog); this.list = this.dialog.lastElementChild; @@ -10408,7 +10518,7 @@ href: 'javascript:;' }); $.on(x, 'click', ThreadWatcher.cb.rm); - if (data.isDead) { + if (Conf['404 Redirect'] && data.isDead) { href = Redirect.to('thread', { boardID: boardID, threadID: threadID @@ -10937,14 +11047,14 @@ record = _ref1[boardID]; for (type in record) { id = record[type]; - if (!((archive = archives[id]))) { - continue; + if (id === 'disabled') { + delete o[type][boardID]; + } else if (archive = archives[id]) { + boards = type === 'file' ? archive.files : archive.boards; + if (__indexOf.call(boards, boardID) >= 0) { + o[type][boardID] = archive; + } } - boards = type === 'file' ? archive.files : archive.boards; - if (__indexOf.call(boards, boardID) < 0) { - continue; - } - o[type][boardID] = archive; } } return Redirect.data = o; @@ -10982,10 +11092,10 @@ var URL, boardID, postID, protocol; boardID = _arg.boardID, postID = _arg.postID; protocol = Redirect.protocol(archive); - if (!(protocol === 'https://' || location.protocol === 'http:')) { + URL = new String("" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID); + if (!Redirect.securityCheck(URL)) { return ''; } - URL = new String("" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID); URL.archive = archive; return URL; }, @@ -11002,8 +11112,11 @@ path = archive.software === 'foolfuuka' ? "" + boardID + "/search/" + type + "/" + value : "" + boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value; return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path; }, + securityCheck: function(URL) { + return /^https:\/\//.test(URL) || location.protocol === 'http:' || Conf['Except Archives from Encryption']; + }, navigate: function(URL, alternative) { - if (URL && (/^https:\/\//.test(URL) || location.protocol === 'http:' || confirm("Redirect to " + URL + "?\n\nYour connection will not be encrypted."))) { + if (URL && (Redirect.securityCheck(URL) || confirm("Redirect to " + URL + "?\n\nYour connection will not be encrypted."))) { return location.replace(URL); } else if (alternative) { return location.replace(alternative); @@ -11040,10 +11153,11 @@ Header.menu.addEntry(entry); $.on(entry.el, 'click', PSAHiding.toggle); PSAHiding.btn = btn = $.el('span', { - innerHTML: '[Dismiss]', title: 'Mark announcement as read and hide.', - className: 'hide-announcement', - href: 'javascript:;' + className: 'hide-announcement' + }); + $.extend(btn, { + innerHTML: "[Dismiss]" }); $.on(btn, 'click', PSAHiding.toggle); $.get('hiddenPSA', 0, function(_arg) { @@ -11610,85 +11724,106 @@ if (!this.file || this.isClone) { return; } - this.file.text.innerHTML = ''; + $.extend(this.file.text, { + innerHTML: "" + }); return FileInfo.format(Conf['fileInfo'], this, this.file.text.firstElementChild); }, format: function(formatString, post, outputNode) { - FileInfo.innerHTML = ''; + var output; + output = []; formatString.replace(/%(.)|[^%]+/g, function(s, c) { - if (c in FileInfo.formatters) { - FileInfo.formatters[c].call(post); - } else { - FileInfo.innerHTML += E(s); - } + output.push(c in FileInfo.formatters ? FileInfo.formatters[c].call(post) : { + innerHTML: E(s) + }); return ''; }); - return outputNode.innerHTML = FileInfo.innerHTML; + return $.extend(outputNode, { + innerHTML: output.map(function(x) { + return x.innerHTML; + }).join('') + }); }, formatters: { t: function() { - var timestamp; - timestamp = this.file.URL.match(/\d+\..+$/)[0]; - return FileInfo.innerHTML += E(timestamp); + return { + innerHTML: E(this.file.URL.match(/\d+\..+$/)[0]) + }; }, T: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.t.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" + }; }, l: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.n.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" + }; }, L: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.N.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" + }; }, n: function() { var fullname, shortname; fullname = this.file.name; shortname = Build.shortFilename(this.file.name, this.isReply); if (fullname === shortname) { - return FileInfo.innerHTML += E(fullname); + return { + innerHTML: E(fullname) + }; } else { - return FileInfo.innerHTML += "" + (E(shortname)) + "" + (E(fullname)) + ""; + return { + innerHTML: "" + E(shortname) + "" + E(fullname) + "" + }; } }, N: function() { - return FileInfo.innerHTML += E(this.file.name); + return { + innerHTML: E(this.file.name) + }; }, p: function() { if (this.file.isSpoiler) { - return FileInfo.innerHTML += 'Spoiler, '; + return { + innerHTML: "Spoiler, " + }; + } else { + return { + innerHTML: "" + }; } }, s: function() { - return FileInfo.innerHTML += E(this.file.size); + return { + innerHTML: E(this.file.size) + }; }, B: function() { - var sizeB; - sizeB = Math.round(this.file.sizeInBytes); - return FileInfo.innerHTML += "" + (+sizeB) + " Bytes"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes" + }; }, K: function() { - var sizeKB; - sizeKB = Math.round(this.file.sizeInBytes / 1024); - return FileInfo.innerHTML += "" + (+sizeKB) + " KB"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB" + }; }, M: function() { - var sizeMB; - sizeMB = Math.round(this.file.sizeInBytes / 1048576 * 100) / 100; - return FileInfo.innerHTML += "" + (+sizeMB) + " MB"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB" + }; }, r: function() { - var dim; - dim = this.file.dimensions || 'PDF'; - return FileInfo.innerHTML += E(dim); + return { + innerHTML: E(this.file.dimensions || "PDF") + }; }, '%': function() { - return FileInfo.innerHTML += '%'; + return { + innerHTML: "%" + }; } } }; @@ -12592,8 +12727,10 @@ }); Settings.dialog = dialog = $.el('div', { id: 'fourchanx-settings', - className: 'dialog', - innerHTML: '
' + className: 'dialog' + }); + $.extend(dialog, { + innerHTML: "
" }); $.on($('.export', Settings.dialog), 'click', Settings["export"]); $.on($('.import', Settings.dialog), 'click', Settings["import"]); @@ -12665,9 +12802,8 @@ for (key in _ref) { obj = _ref[key]; fs = $.el('fieldset', { - innerHTML: '' + innerHTML: "" + E(key) + "" }); - fs.firstElementChild.textContent = key; for (key in obj) { arr = obj[key]; description = arr[1]; @@ -12694,7 +12830,7 @@ } }); div = $.el('div', { - innerHTML: ': Clear manually-hidden threads and posts on all boards. Reload the page to apply.' + innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." }); button = $('button', div); $.get({ @@ -12891,7 +13027,9 @@ }, filter: function(section) { var select; - section.innerHTML = '
'; + $.extend(section, { + innerHTML: "
" + }); select = $('select', section); $.on(select, 'change', Settings.selectFilter); return Settings.selectFilter.call(select); @@ -12913,12 +13051,16 @@ $.add(div, ta); return; } - div.innerHTML = '
Filter is disabled.

Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions.

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.
'; + $.extend(div, { + innerHTML: "
Filter is disabled.

Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions.

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.
" + }); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; - section.innerHTML = '
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
'; + $.extend(section, { + innerHTML: "
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" + }); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { @@ -12928,7 +13070,9 @@ }, advanced: function(section) { var archBoards, boardID, boardOptions, boardSelect, boards, customCSS, event, files, i, input, inputs, interval, item, items, name, o, row, rows, software, ta, table, warning, withCredentials, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _len6, _m, _n, _o, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; - section.innerHTML = '
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Disabled selections indicate that only one archive is available for that board and redirection type.
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Quote Backlinks formatting is disabled.
:
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays \'PDF\' for PDF files)
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input\'s auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, email, subject and password.
  • Wrap values of items with quotes, like this: email:"sage".
  • Force values as defaults with the always keyword, for example: email:"sage";always.
  • Select specific boards for an item, separated with commas, for example: email:"sage";boards:jp;always.
Unread Favicon is disabled.
Emoji is disabled.
Sage Icon:
Position:
Thread Updater is disabled.
Interval:
'; + $.extend(section, { + innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:\"Install Gentoo\"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:\"Google\",\"http://www.google.com\"
Combinations are possible: g-index-text:\"Technology Index\"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:\"Piracy\"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Quote Backlinks formatting is disabled.
:
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, email, subject and password.
  • Wrap values of items with quotes, like this: email:\"sage\".
  • Force values as defaults with the always keyword, for example: email:\"sage\";always.
  • Select specific boards for an item, separated with commas, for example: email:\"sage\";boards:jp;always.
Unread Favicon is disabled.
Emoji is disabled.
Sage Icon:
Position:
Thread Updater is disabled.
Interval:
" + }); _ref = $$('.warning', section); for (_i = 0, _len = _ref.length; _i < _len; _i++) { warning = _ref[_i]; @@ -13000,9 +13144,7 @@ _ref4 = ['thread', 'post', 'file']; for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { item = _ref4[_m]; - if (o[item][0].length === 0 && o[item][1].length !== 0) { - o[item][0].push('disabled'); - } + o[item][0].push('disabled'); o[item] = o[item][0].concat(o[item][1]); } } @@ -13072,7 +13214,9 @@ value: archive })); } - td.innerHTML = ''; + $.extend(td, { + innerHTML: "" + }); select = td.firstElementChild; if (!(select.disabled = length === 1)) { select.setAttribute('data-boardid', boardID); @@ -13150,7 +13294,9 @@ }, keybinds: function(section) { var arr, input, inputs, items, key, tbody, tr, _ref; - section.innerHTML = '
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
'; + $.extend(section, { + innerHTML: "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
" + }); $('.warning', section).hidden = Conf['Keybinds']; tbody = $('tbody', section); items = {}; @@ -13159,9 +13305,8 @@ for (key in _ref) { arr = _ref[key]; tr = $.el('tr', { - innerHTML: '' + innerHTML: "" + E(arr[1]) + "" }); - tr.firstElementChild.textContent = arr[1]; input = $('input', tr); input.name = key; input.spellcheck = false; @@ -13435,7 +13580,7 @@ } if (previousversion) { el = $.el('span', { - innerHTML: '4chan X has been updated to version 1.8.8.6.' + innerHTML: E(g.NAME) + " has been updated to version " + E(g.VERSION) + "." }); new Notice('info', el, 15); } else { @@ -13490,7 +13635,7 @@ return; } div = $.el('div', { - innerHTML: "" + (+errors.length) + " errors occurred. [show]" + innerHTML: E(errors.length) + " errors occurred. [show]" }); $.on(div.lastElementChild, 'click', function() { var _ref; diff --git a/builds/4chan-X.crx b/builds/4chan-X.crx index 6985a4bca..2e9267650 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 bd1b74128..b1115014d 100644 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.8.8.6 +// @version 1.8.9 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index c9ba45349..fb26a75a6 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1,7 +1,7 @@ // Generated by CoffeeScript // ==UserScript== // @name 4chan X -// @version 1.8.8.6 +// @version 1.8.9 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -24,12 +24,12 @@ // ==/UserScript== /* -* 4chan X - Version 1.8.8.6 - 2014-08-07 +* 4chan X - Version 1.8.9 * * Licensed under the MIT license. * https://github.com/ccd0/4chan-x/blob/master/LICENSE * -* Appchan X Copyright © 2013-2013 Zixaphir +* Appchan X Copyright © 2013-2014 Zixaphir * http://zixaphir.github.io/appchan-x/ * 4chan x Copyright © 2009-2011 James Campos * https://github.com/aeosynth/4chan-x @@ -37,7 +37,7 @@ * https://4chan-x.just-believe.in/ * 4chan x Copyright © 2013-2014 Jordan Bates * http://seaweedchan.github.io/4chan-x/ -* 4chan x Copyright © 2012-2014 ihavenoface +* 4chan x Copyright © 2012-2013 ihavenoface * http://ihavenoface.github.io/4chan-x/ * 4chan SS Copyright © 2011-2013 Ahodesuka * https://github.com/ahodesuka/4chan-Style-Script/ @@ -103,6 +103,7 @@ * * license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ + 'use strict'; (function() { @@ -137,7 +138,8 @@ 'QR Shortcut': [false, 'Adds a small [QR] link in the header.'], 'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'], 'Desktop Notifications': [false, 'Enables desktop notifications across various 4chan X features.'], - '404 Redirect': [true, 'Redirect dead threads and images.'], + '404 Redirect': [true, 'Redirect dead threads and images to the archives.'], + 'Except Archives from Encryption': [false, 'Permit loading content from, and warningless redirects to, HTTP-only archives from HTTPS pages.'], 'Keybinds': [true, 'Bind actions to keyboard shortcuts.'], 'Time Formatting': [true, 'Localize and format timestamps.'], 'Relative Post Dates': [true, 'Display dates like "3 minutes ago". Tooltip shows the timestamp.'], @@ -378,8 +380,11 @@ doc = d.documentElement; g = { - VERSION: '1.8.8.6', + VERSION: '1.8.9', NAMESPACE: '4chan X.', + NAME: '4chan X', + FAQ: 'https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions', + CHANGELOG: 'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md', boards: {} }; @@ -453,15 +458,13 @@ lastModified = {}; blockedURLs = {}; blockedError = function(url) { - var h_message, message; + var message; if (blockedURLs[url]) { return; } blockedURLs[url] = true; - h_message = '4chan X was blocked from loading the following URL:

'; - h_message += '[More info]'; message = $.el('div', { - innerHTML: h_message + innerHTML: E(g.NAME) + " was blocked from loading the following URL:

[More info]" }); $('span', message).textContent = (/^\/\//.test(url) ? location.protocol : '') + url; return new Notice('error', message, 30, function() { @@ -1551,7 +1554,7 @@ this.close = __bind(this.close, this); this.add = __bind(this.add, this); this.el = $.el('div', { - innerHTML: '
' + innerHTML: "
" }); this.el.style.opacity = 0; this.setType(type); @@ -1818,8 +1821,10 @@ var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler; this.menu = new UI.Menu('header'); menuButton = $.el('span', { - className: 'menu-button', - innerHTML: '' + className: 'menu-button' + }); + $.extend(menuButton, { + innerHTML: "" }); barFixedToggler = UI.checkbox('Fixed Header', ' Fixed Header'); headerToggler = UI.checkbox('Header auto-hide', ' Auto-hide header'); @@ -1955,8 +1960,10 @@ var a, boardList, btn, fourchannav, fullBoardList, _i, _len, _ref; fourchannav = $.id('boardNavDesktop'); Header.boardList = boardList = $.el('span', { - id: 'board-list', - innerHTML: "" + id: 'board-list' + }); + $.extend(boardList, { + innerHTML: "" }); _ref = $$('a', boardList); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -2293,7 +2300,7 @@ return; } el = $.el('span', { - innerHTML: '4chan X needs your permission to show desktop notifications.\n[FAQ]
\n or ' + innerHTML: E(g.NAME) + " needs your permission to show desktop notifications. [FAQ]
or " }); _ref = $$('button', el), authorize = _ref[0], disable = _ref[1]; $.on(authorize, 'click', function() { @@ -2335,15 +2342,15 @@ subEntries: [ { el: $.el('label', { - innerHTML: ' Paged' + innerHTML: " Paged" }) }, { el: $.el('label', { - innerHTML: ' Infinite scrolling' + innerHTML: " Infinite scrolling" }) }, { el: $.el('label', { - innerHTML: ' All threads' + innerHTML: " All threads" }) } ] @@ -2363,23 +2370,23 @@ subEntries: [ { el: $.el('label', { - innerHTML: ' Bump order' + innerHTML: " Bump order" }) }, { el: $.el('label', { - innerHTML: ' Last reply' + innerHTML: " Last reply" }) }, { el: $.el('label', { - innerHTML: ' Creation date' + innerHTML: " Creation date" }) }, { el: $.el('label', { - innerHTML: ' Reply count' + innerHTML: " Reply count" }) }, { el: $.el('label', { - innerHTML: ' File count' + innerHTML: " File count" }) } ] @@ -2430,12 +2437,16 @@ }); this.pagelist = $.el('div', { className: 'pagelist', - hidden: true, - innerHTML: '
' + hidden: true + }); + $.extend(this.pagelist, { + innerHTML: "
" }); this.navLinks = $.el('div', { - className: 'navLinks', - innerHTML: 'Return Catalog Bottom ×' + className: 'navLinks' + }); + $.extend(this.navLinks, { + innerHTML: "Return Catalog Bottom ×" }); $('.returnlink a', this.navLinks).href = "//boards.4chan.org/" + g.BOARD + "/"; $('.cataloglink a', this.navLinks).href = "//boards.4chan.org/" + g.BOARD + "/catalog"; @@ -3093,7 +3104,9 @@ flagName: Build.unescape(data.country_name), date: data.now, dateUTC: data.time, - h_comment: data.com || '', + comment: { + innerHTML: data.com || '' + }, isSticky: !!data.sticky, isClosed: !!data.closed }; @@ -3126,200 +3139,158 @@ This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE */ - var boardID, capcode, container, date, dateUTC, email, emailProcessed, file, fileSize, fileThumb, flagCode, flagCodeLC, flagName, h_capcodeClass, h_capcodeIcon, h_capcodeStart, h_closed, h_comment, h_desktop2, h_email, h_emailCont, h_file, h_fileCont, h_fileDims, h_fileTitle1, h_fileTitle2, h_flag, h_highlightPost, h_message, h_nameBlock, h_nameClass, h_pageIcon, h_post, h_postInfo, h_replyLink, h_retina, h_spoilerClass, h_sticky, h_subject, h_tripcode, h_userID, href, isClosed, isOP, isSticky, match, name, pageNum, postID, postLink, quote, quoteLink, shortFilename, spoilerRange, subject, threadID, tripcode, uniqueID, _i, _len, _ref; - postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, file = o.file; + var boardID, capcode, capcodeClass, capcodeIcon, capcodeStart, closed, comment, container, date, dateUTC, desktop2, email, emailField, emailProcessed, file, fileBlock, fileCont, fileDims, fileLink, fileSize, fileText, fileThumb, flag, flagCode, flagName, highlightPost, href, isClosed, isOP, isSticky, match, message, name, nameBlock, nameClass, pageIcon, pageNum, postID, postInfo, postLink, quote, quoteLink, replyLink, retina, shortFilename, spoilerRange, sticky, subject, subjectField, threadID, tripcode, tripcodeField, uniqueID, userID, wholePost, _i, _len, _ref; + postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file; name || (name = ''); subject || (subject = ''); - h_comment = o.h_comment; isOP = postID === threadID; - if (Build.initPixelRatio >= 2) { - h_retina = '@2x'; - } else { - h_retina = ''; - } + retina = Build.initPixelRatio >= 2 ? '@2x' : ''; /* Name Block */ switch (capcode) { case 'admin': case 'admin_highlight': - h_capcodeClass = ' capcodeAdmin'; - h_capcodeStart = ' ## Admin'; - h_capcodeIcon = "Admin Icon"; + capcodeClass = ' capcodeAdmin'; + capcodeStart = { + innerHTML: " ## Admin" + }; + capcodeIcon = { + innerHTML: "\"Admin" + }; break; case 'mod': - h_capcodeClass = ' capcodeMod'; - h_capcodeStart = ' ## Mod'; - h_capcodeIcon = "Mod Icon"; + capcodeClass = ' capcodeMod'; + capcodeStart = { + innerHTML: " ## Mod" + }; + capcodeIcon = { + innerHTML: "\"Mod" + }; break; case 'developer': - h_capcodeClass = ' capcodeDeveloper'; - h_capcodeStart = ' ## Developer'; - h_capcodeIcon = "Developer Icon"; + capcodeClass = ' capcodeDeveloper'; + capcodeStart = { + innerHTML: " ## Developer" + }; + capcodeIcon = { + innerHTML: "\"Developer" + }; break; default: - h_capcodeClass = ''; - h_capcodeStart = ''; - h_capcodeIcon = ''; + capcodeClass = ''; + capcodeStart = { + innerHTML: "" + }; + capcodeIcon = { + innerHTML: "" + }; } - if (capcode) { - h_nameClass = ' capcode'; - } else { - h_nameClass = ''; - } - if (tripcode) { - h_tripcode = " " + (E(tripcode)) + ""; - } else { - h_tripcode = ''; - } - h_emailCont = "" + (E(name)) + "" + h_tripcode + h_capcodeStart; + nameClass = capcode ? ' capcode' : ''; + tripcodeField = tripcode ? { + innerHTML: " " + E(tripcode) + "" + } : { + innerHTML: "" + }; + emailField = { + innerHTML: "" + E(name) + "" + tripcodeField.innerHTML + capcodeStart.innerHTML + }; if (email) { emailProcessed = encodeURIComponent(email).replace(/%40/g, '@'); - h_email = "" + h_emailCont + ""; - } else { - h_email = h_emailCont; + emailField = { + innerHTML: "" + emailField.innerHTML + "" + }; } if (!(isOP && boardID === 'f')) { - h_email += ' '; + emailField = { + innerHTML: emailField.innerHTML + " " + }; } - if (!capcode && uniqueID) { - h_userID = " (ID: " + (E(uniqueID)) + ")"; - } else { - h_userID = ''; - } - if (!flagCode) { - h_flag = ''; - } else { - flagCodeLC = flagCode.toLowerCase(); - if (boardID === 'pol') { - h_flag = "" + (E(flagCode)) + ""; - } else { - h_flag = ""; - } - } - h_nameBlock = ""; - h_nameBlock += "" + h_email + h_capcodeIcon + h_userID + h_flag; - h_nameBlock += ' '; + userID = !capcode && uniqueID ? { + innerHTML: " (ID: " + E(uniqueID) + ")" + } : { + innerHTML: "" + }; + flag = !flagCode ? { + innerHTML: "" + } : boardID === 'pol' ? { + innerHTML: "\""" + } : { + innerHTML: "" + }; + nameBlock = { + innerHTML: "" + emailField.innerHTML + capcodeIcon.innerHTML + userID.innerHTML + flag.innerHTML + " " + }; /* Post Info */ - if (isOP || boardID === 'f') { - h_subject = "" + (E(subject)) + " "; - } else { - h_subject = ''; - } - if (isOP && boardID === 'f') { - h_desktop2 = ''; - } else { - h_desktop2 = ' desktop'; - } + subjectField = isOP || boardID === 'f' ? { + innerHTML: "" + E(subject) + " " + } : { + innerHTML: "" + }; + desktop2 = isOP && boardID === 'f' ? '' : ' desktop'; postLink = Build.postURL(boardID, threadID, postID); quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + postID + "');" : "/" + boardID + "/thread/" + threadID + "\#q" + postID; - if (isOP && g.VIEW === 'index' && Conf['JSON Navigation']) { - pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage) + 1; - h_pageIcon = " [" + (+pageNum) + "]"; - } else { - h_pageIcon = ''; - } - if (isSticky) { - h_sticky = " Sticky"; - } else { - h_sticky = ''; - } - if (isClosed) { - h_closed = " Closed"; - } else { - h_closed = ''; - } - if (isOP && g.VIEW === 'index') { - h_replyLink = "   [Reply]"; - } else { - h_replyLink = ''; - } - h_postInfo = "'; + pageIcon = isOP && g.VIEW === 'index' && Conf['JSON Navigation'] ? (pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage) + 1, { + innerHTML: " [" + E(pageNum) + "]" + }) : { + innerHTML: "" + }; + sticky = isSticky ? { + innerHTML: " \"Sticky\"" + } : { + innerHTML: "" + }; + closed = isClosed ? { + innerHTML: " \"Closed\"" + } : { + innerHTML: "" + }; + replyLink = isOP && g.VIEW === 'index' ? { + innerHTML: "   [Reply]" + } : { + innerHTML: "" + }; + postInfo = { + innerHTML: "
" + subjectField.innerHTML + nameBlock.innerHTML + "" + E(date) + " No." + E(postID) + "" + pageIcon.innerHTML + sticky.innerHTML + closed.innerHTML + replyLink.innerHTML + "
" + }; /* File Info */ - if (file != null ? file.isDeleted : void 0) { - h_fileCont = ''; - h_fileCont += "File deleted."; - h_fileCont += ''; - } else if (file && boardID === 'f') { - fileSize = $.bytesToString(file.size); - h_fileCont = "
"; - h_fileCont += "File: " + (E(file.name)) + ""; - h_fileCont += "-(" + (E(fileSize)) + ", " + (+file.width) + "x" + (+file.height) + ", " + (E(file.tag)) + ")"; - h_fileCont += '
'; - } else if (file) { - if (file.isSpoiler) { - h_fileTitle1 = "title='" + (E(file.name)) + "'"; - shortFilename = 'Spoiler Image'; - h_spoilerClass = ' imgspoiler'; - if (spoilerRange = Build.spoilerRange[boardID]) { - fileThumb = "//s.4cdn.org/image/spoiler-" + boardID + (Math.floor(1 + spoilerRange * Math.random())) + ".png"; - } else { - fileThumb = '//s.4cdn.org/image/spoiler.png'; - } - file.twidth = file.theight = 100; - } else { - h_fileTitle1 = ''; - shortFilename = Build.shortFilename(file.name, !isOP); - h_spoilerClass = ''; - fileThumb = file.turl; - } - if (file.isSpoiler || file.name === shortFilename) { - h_fileTitle2 = ''; - } else { - h_fileTitle2 = "title='" + (E(file.name)) + "'"; - } - fileSize = $.bytesToString(file.size); - if (file.url.slice(-4) === '.pdf') { - h_fileDims = 'PDF'; - } else { - h_fileDims = "" + (+file.width) + "x" + (+file.height); - } - h_fileCont = "
"; - h_fileCont += "File: " + (E(shortFilename)) + " (" + (E(fileSize)) + ", " + h_fileDims + ")"; - h_fileCont += '
'; - h_fileCont += ""; - h_fileCont += "" + (E(fileSize)) + ""; - h_fileCont += ''; - } - if (file) { - h_file = "
" + h_fileCont + "
"; - } else { - h_file = ''; - } + fileCont = (file != null ? file.isDeleted : void 0) ? { + innerHTML: "\"File" + } : file && boardID === 'f' ? { + innerHTML: "
File: " + E(file.name) + "-(" + E($.bytesToString(file.size)) + ", " + E(file.width) + "x" + E(file.height) + ", " + E(file.tag) + ")
" + } : file ? (file.isSpoiler ? (shortFilename = 'Spoiler Image', (spoilerRange = Build.spoilerRange[boardID]) ? fileThumb = "//s.4cdn.org/image/spoiler-" + boardID + (Math.floor(1 + spoilerRange * Math.random())) + ".png" : fileThumb = '//s.4cdn.org/image/spoiler.png', file.twidth = file.theight = 100) : (shortFilename = Build.shortFilename(file.name, !isOP), fileThumb = file.turl), fileSize = $.bytesToString(file.size), fileDims = file.url.slice(-4) === '.pdf' ? 'PDF' : "" + file.width + "x" + file.height, fileLink = file.isSpoiler || file.name === shortFilename ? { + innerHTML: "" + E(shortFilename) + "" + } : { + innerHTML: "" + E(shortFilename) + "" + }, fileText = file.isSpoiler ? { + innerHTML: "
File: " + fileLink.innerHTML + " (" + E(fileSize) + ", " + E(fileDims) + ")
" + } : { + innerHTML: "
File: " + fileLink.innerHTML + " (" + E(fileSize) + ", " + E(fileDims) + ")
" + }, { + innerHTML: fileText.innerHTML + "\""" + }) : void 0; + fileBlock = file ? { + innerHTML: "
" + fileCont.innerHTML + "
" + } : { + innerHTML: "" + }; /* Whole Post */ - if (capcode === 'admin_highlight') { - h_highlightPost = ' highlightPost'; - } else { - h_highlightPost = ''; - } - h_message = "
" + h_comment + "
"; - if (isOP) { - h_post = "
"; - h_post += "" + h_file + h_postInfo + h_message; - h_post += '
'; - } else { - h_post = "
>>
"; - h_post += "
"; - h_post += "" + h_postInfo + h_file + h_message; - h_post += '
'; - } + highlightPost = capcode === 'admin_highlight' ? ' highlightPost' : ''; + message = { + innerHTML: "
" + comment.innerHTML + "
" + }; + wholePost = isOP ? { + innerHTML: "
" + fileBlock.innerHTML + postInfo.innerHTML + message.innerHTML + "
" + } : { + innerHTML: "
>>
" + postInfo.innerHTML + fileBlock.innerHTML + message.innerHTML + "
" + }; container = $.el('div', { className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", - id: "pc" + postID, - innerHTML: h_post + id: "pc" + postID }); + $.extend(container, wholePost); _ref = $$('.quotelink', container); for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; @@ -3467,7 +3438,7 @@ }); }, postClone: function(boardID, threadID, postID, root, context) { - var post, url; + var post; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; @@ -3477,16 +3448,8 @@ return $.cache("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", function() { return Get.fetchedPost(this, boardID, threadID, postID, root, context); }); - } else if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - return $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); + } else { + return Get.archivedPost(boardID, postID, root, context); } }, insert: function(post, root, context) { @@ -3503,24 +3466,14 @@ return $.add(root, nodes.root); }, fetchedPost: function(req, boardID, threadID, postID, root, context) { - var board, post, posts, status, thread, url, _i, _len; + var board, post, posts, status, thread, _i, _len; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; } status = req.status; if (status !== 200 && status !== 304) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); - } else { + if (!Get.archivedPost(boardID, postID, root, context)) { $.addClass(root, 'warning'); root.textContent = status === 404 ? "Thread No." + threadID + " 404'd." : "Error " + req.statusText + " (" + req.status + ")."; } @@ -3535,17 +3488,7 @@ } } if (post.no !== postID) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - responseType: 'json', - withCredentials: url.archive.withCredentials - }); - } else { + if (!Get.archivedPost(boardID, postID, root, context)) { $.addClass(root, 'warning'); root.textContent = "Post No." + postID + " was not found."; } @@ -3558,21 +3501,93 @@ Main.callbackNodes(Post, [post]); return Get.insert(post, root, context); }, - archivedPost: function(req, boardID, postID, root, context) { - var board, data, h_comment, o, post, thread, threadID, _ref; + archivedPost: function(boardID, postID, root, context) { + var url; + if (!Conf['Resurrect Quotes']) { + return false; + } + if (!(url = Redirect.to('post', { + boardID: boardID, + postID: postID + }))) { + return false; + } + if (/^https:\/\//.test(URL) || location.protocol === 'http:') { + $.cache(url, function() { + return Get.parseArchivedPost(this.response, boardID, postID, root, context); + }, { + responseType: 'json', + withCredentials: url.archive.withCredentials + }); + return true; + } else if (Conf['Except Archives from Encryption']) { + CrossOrigin.json(url, function(response) { + return Get.parseArchivedPost(response, boardID, postID, root, context); + }); + return true; + } + return false; + }, + parseArchivedPost: function(data, boardID, postID, root, context) { + var board, comment, greentext, i, j, o, post, text, text2, thread, threadID, _ref; if (post = g.posts["" + boardID + "." + postID]) { Get.insert(post, root, context); return; } - data = req.response; if (data.error) { $.addClass(root, 'warning'); root.textContent = data.error; return; } - h_comment = E(data.comment || ''); - h_comment = h_comment.replace(/\n|\[\/?[a-z]+(:lit)?\]/g, Get.parseMarkup); - h_comment = h_comment.replace(/(^|>)(>[^<$]*)(<|$)/g, '$1$2$3').replace(/((>){2}(>\/[a-z\d]+\/)?\d+)/g, '$1'); + comment = (data.comment || '').split(/(\n|\[\/?(?:b|spoiler|code|moot|banned)\])/); + comment = (function() { + var _i, _len, _results; + _results = []; + for (i = _i = 0, _len = comment.length; _i < _len; i = ++_i) { + text = comment[i]; + if (i % 2 === 1) { + _results.push(Get.archiveTags[text]); + } else { + greentext = text[0] === '>'; + text = text.replace(/(\[\/?[a-z]+):lit(\])/, '$1$2'); + text = (function() { + var _j, _len1, _ref, _results1; + _ref = text.split(/(>>(?:>\/[a-z\d]+\/)?\d+)/g); + _results1 = []; + for (j = _j = 0, _len1 = _ref.length; _j < _len1; j = ++_j) { + text2 = _ref[j]; + if (j % 2 === 1) { + _results1.push({ + innerHTML: "" + E(text2) + "" + }); + } else { + _results1.push({ + innerHTML: E(text2) + }); + } + } + return _results1; + })(); + text = { + innerHTML: text.map(function(x) { + return x.innerHTML; + }).join('') + }; + if (greentext) { + text = { + innerHTML: "" + text.innerHTML + "" + }; + } + _results.push(text); + } + } + return _results; + })(); + comment = { + innerHTML: comment.map(function(x) { + return x.innerHTML; + }).join('') + }; threadID = +data.thread_num; o = { postID: postID, @@ -3597,7 +3612,7 @@ flagName: data.poster_country_name, date: data.fourchan_date, dateUTC: data.timestamp, - h_comment: h_comment + comment: comment }; if ((_ref = data.media) != null ? _ref.media_filename : void 0) { o.file = { @@ -3629,20 +3644,40 @@ Main.callbackNodes(Post, [post]); return Get.insert(post, root, context); }, - parseMarkup: function(text) { - return { - '\n': '
', - '[b]': '', - '[/b]': '', - '[spoiler]': '', - '[/spoiler]': '', - '[code]': '
',
-        '[/code]': '
', - '[moot]': '
', - '[/moot]': '
', - '[banned]': '', - '[/banned]': '' - }[text] || text.replace(':lit', ''); + archiveTags: { + '\n': { + innerHTML: "
" + }, + '[b]': { + innerHTML: "" + }, + '[/b]': { + innerHTML: "" + }, + '[spoiler]': { + innerHTML: "" + }, + '[/spoiler]': { + innerHTML: "" + }, + '[code]': { + innerHTML: "
"
+      },
+      '[/code]': {
+        innerHTML: "
" + }, + '[moot]': { + innerHTML: "
" + }, + '[/moot]': { + innerHTML: "
" + }, + '[banned]': { + innerHTML: "" + }, + '[/banned]': { + innerHTML: "" + } } }; @@ -4082,49 +4117,85 @@ }; })(); - CrossOrigin = (function() { - var handleBlob, handleUrl; - handleBlob = function(urlBlob, contentType, contentDisposition, url, cb) { - var blob, match, mime, name, _ref, _ref1, _ref2; - name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0; - mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream'; - match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0); - if (match) { - name = match.replace(/\\"/g, '"'); - } - blob = new Blob([urlBlob], { - type: mime - }); - blob.name = name; - return cb(blob); - }; - handleUrl = function(url, cb) { - return GM_xmlhttpRequest({ - method: "GET", - url: url, - overrideMimeType: "text/plain; charset=x-user-defined", - onload: function(xhr) { - var contentDisposition, contentType, data, i, r, _ref, _ref1; - r = xhr.responseText; - data = new Uint8Array(r.length); - i = 0; - while (i < r.length) { - data[i] = r.charCodeAt(i); - i++; - } - contentType = (_ref = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)) != null ? _ref[1] : void 0; - contentDisposition = (_ref1 = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)) != null ? _ref1[1] : void 0; - return handleBlob(data, contentType, contentDisposition, url, cb); - }, - onerror: function(xhr) { - return cb(null); + CrossOrigin = { + file: (function() { + var makeBlob; + makeBlob = function(urlBlob, contentType, contentDisposition, url) { + var blob, match, mime, name, _ref, _ref1, _ref2; + name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0; + mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream'; + match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0); + if (match) { + name = match.replace(/\\"/g, '"'); } - }); - }; - return { - request: handleUrl - }; - })(); + blob = new Blob([urlBlob], { + type: mime + }); + blob.name = name; + return blob; + }; + return function(url, cb) { + return GM_xmlhttpRequest({ + method: "GET", + url: url, + overrideMimeType: "text/plain; charset=x-user-defined", + onload: function(xhr) { + var contentDisposition, contentType, data, i, r, _ref, _ref1; + r = xhr.responseText; + data = new Uint8Array(r.length); + i = 0; + while (i < r.length) { + data[i] = r.charCodeAt(i); + i++; + } + contentType = (_ref = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)) != null ? _ref[1] : void 0; + contentDisposition = (_ref1 = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)) != null ? _ref1[1] : void 0; + return cb(makeBlob(data, contentType, contentDisposition, url)); + }, + onerror: function() { + return cb(null); + } + }); + }; + })(), + json: (function() { + var callbacks, responses; + callbacks = {}; + responses = {}; + return function(url, cb) { + if (responses[url]) { + cb(responses[url]); + return; + } + if (callbacks[url]) { + callbacks[url].push(cb); + return; + } + callbacks[url] = [cb]; + return GM_xmlhttpRequest({ + method: "GET", + url: url, + onload: function(xhr) { + var response, _i, _len, _ref; + response = JSON.parse(xhr.responseText); + _ref = callbacks[url]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + cb = _ref[_i]; + cb(response); + } + delete callbacks[url]; + return responses[url] = response; + }, + onerror: function() { + return delete callbacks[url]; + }, + onabort: function() { + return delete callbacks[url]; + } + }); + }; + })() + }; Anonymize = { init: function() { @@ -4963,9 +5034,11 @@ var a; a = $.el('a', { className: "" + type + "-thread-button", - innerHTML: "", href: 'javascript:;' }); + $.extend(a, { + innerHTML: "" + }); a.dataset.fullID = thread.fullID; $.on(a, 'click', ThreadHiding.toggle); return a; @@ -5454,7 +5527,7 @@ } this.enabled = true; this.controls = $.el('span', { - innerHTML: '' + innerHTML: "" }); input = $('input', this.controls); $.on(input, 'change', this.toggle); @@ -5707,7 +5780,7 @@ } }, parseDeadlink: function(deadlink) { - var a, boardID, m, post, postID, quote, quoteID, redirect, _ref; + var a, boardID, fetchable, m, post, postID, quote, quoteID, redirect, _ref; if ($.hasClass(deadlink.parentNode, 'prettyprint')) { Quotify.fixDeadlink(deadlink); return; @@ -5742,26 +5815,30 @@ postID: postID }); } - } else if (redirect = Redirect.to('thread', { - boardID: boardID, - threadID: 0, - postID: postID - })) { - a = $.el('a', { - href: redirect, - className: 'deadlink', - target: '_blank', - textContent: "" + quote + "\u00A0(Dead)" + } else { + redirect = Redirect.to('thread', { + boardID: boardID, + threadID: 0, + postID: postID }); - if (Redirect.to('post', { + fetchable = Redirect.to('post', { boardID: boardID, postID: postID - })) { - $.addClass(a, 'quotelink'); - $.extend(a.dataset, { - boardID: boardID, - postID: postID + }); + if (redirect || fetchable) { + a = $.el('a', { + href: redirect || 'javascript:;', + className: 'deadlink', + target: '_blank', + textContent: "" + quote + "\u00A0(Dead)" }); + if (fetchable) { + $.addClass(a, 'quotelink'); + $.extend(a.dataset, { + boardID: boardID, + postID: postID + }); + } } } if (__indexOf.call(this.quotes, quoteID) < 0) { @@ -5844,9 +5921,11 @@ return; } link = $.el('h1', { - innerHTML: "" + (g.VIEW === 'thread' ? 'Reply to Thread' : 'Start a Thread') + "", className: "qr-link-container" }); + $.extend(link, { + innerHTML: "" + E((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + "" + }); QR.link = link.firstElementChild; $.on(link.firstChild, 'click', function() { $.event('CloseMenu'); @@ -5858,9 +5937,11 @@ }); if (Conf['Bottom QR Link'] && g.VIEW === 'thread') { linkBot = $.el('div', { - innerHTML: 'Reply to Thread', className: "brackets-wrap qr-link-container-bottom" }); + $.extend(linkBot, { + innerHTML: "Reply to Thread" + }); $.on(linkBot.firstElementChild, 'click', function() { $.event('CloseMenu'); QR.open(); @@ -6137,7 +6218,7 @@ if (url === null) { return; } - return CrossOrigin.request(url, function(blob) { + return CrossOrigin.file(url, function(blob) { if (blob) { return QR.handleFiles([blob]); } else { @@ -6326,7 +6407,7 @@ var dialog, elm, event, i, items, name, node, nodes, prop, rules, save, setNode, _, _i, _len, _ref, _ref1, _ref2; QR.nodes = nodes = { el: dialog = UI.dialog('qr', 'top:0;right:0;', { - innerHTML: '
×
No selected file
' + innerHTML: "
×
No selected file
" }) }; setNode = function(name, query) { @@ -6390,8 +6471,10 @@ } if (g.BOARD.ID === 'f' && g.VIEW !== 'thread') { nodes.flashTag = $.el('select', { - name: 'filetag', - innerHTML: '\n\n\n\n\n\n' + name: 'filetag' + }); + $.extend(nodes.flashTag, { + innerHTML: "" }); nodes.flashTag.dataset["default"] = '4'; $.add(nodes.form, nodes.flashTag); @@ -6575,7 +6658,7 @@ QR.cooldown.auto = false; QR.status(); return QR.error($.el('span', { - innerHTML: '4chan X encountered an error while posting. \n[Banned?] [More info]' + innerHTML: "4chan X encountered an error while posting. [Banned?] [More info]" })); } }; @@ -6608,9 +6691,9 @@ resDoc = req.response; if (ban = $('.banType', resDoc)) { err = $.el('span', ban.textContent.toLowerCase() === 'banned' ? { - innerHTML: "You are banned on " + ($('.board', resDoc).innerHTML) + "! ;_;
Click here to see the reason." + innerHTML: "You are banned on " + $(".board", resDoc).innerHTML + "! ;_;
Click here to see the reason." } : { - innerHTML: "You were issued a warning on " + ($('.board', resDoc).innerHTML) + " as " + ($('.nameBlock', resDoc).innerHTML) + ".
Reason: " + ($('.reason', resDoc).innerHTML) + innerHTML: "You were issued a warning on " + $(".board", resDoc).innerHTML + " as " + $(".nameBlock", resDoc).innerHTML + ".
Reason: " + $(".reason", resDoc).innerHTML }); } else if (err = resDoc.getElementById('errmsg')) { if ((_ref = $('a', err)) != null) { @@ -6739,8 +6822,10 @@ } imgContainer = $.el('div', { className: 'captcha-img', - title: 'Reload reCAPTCHA', - innerHTML: '' + title: 'Reload reCAPTCHA' + }); + $.extend(imgContainer, { + innerHTML: "" }); input = $.el('input', { className: 'captcha-input field', @@ -7168,8 +7253,10 @@ el = $.el('a', { className: 'qr-preview', draggable: true, - href: 'javascript:;', - innerHTML: '' + href: 'javascript:;' + }); + $.extend(el, { + innerHTML: "" }); this.nodes = { el: el, @@ -7634,8 +7721,10 @@ Gallery.fullIDs = {}; Gallery.slideshow = false; nodes.el = dialog = $.el('div', { - id: 'a-gallery', - innerHTML: '
× /
' + id: 'a-gallery' + }); + $.extend(dialog, { + innerHTML: "
× /
" }); _ref = { buttons: '.gal-buttons', @@ -7967,7 +8056,7 @@ var delayInput, delayLabel, subEntries; subEntries = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Scroll to Post'].map(Gallery.menu.createSubEntry); delayLabel = $.el('label', { - innerHTML: 'Slide Delay: ' + innerHTML: "Slide Delay: " }); delayInput = delayLabel.firstElementChild; delayInput.value = Gallery.delay; @@ -8003,7 +8092,7 @@ boardID: post.board.ID, filename: src[src.length - 1] }); - if (!(URL && (/^https:\/\//.test(URL) || location.protocol === 'http:'))) { + if (!(Conf['404 Redirect'] && URL && Redirect.securityCheck(URL))) { URL = null; } if ((post.isDead || post.file.isDead) && file.src.split('/')[2] === 'i.4cdn.org') { @@ -8083,6 +8172,12 @@ $.on(this.EAI, 'click', this.cb.toggleAll); Header.addShortcut(this.EAI, 3); $.on(d, 'scroll visibilitychange', this.cb.playVideos); + this.videoControls = $.el('span', { + className: 'video-controls' + }); + $.extend(this.videoControls, { + innerHTML: " contract" + }); return Post.callbacks.push({ name: 'Image Expansion', cb: this.node @@ -8337,10 +8432,6 @@ return ImageCommon.addControls(fullImage); } }, - videoControls: $.el('span', { - className: 'video-controls', - innerHTML: '\u00A0contract' - }), videoCB: (function() { var mousedown; mousedown = false; @@ -8527,11 +8618,13 @@ return function(URL) { if (URL) { return _this.src = URL + (_this.src === URL ? '?' + Date.now() : ''); + } else { + $.rm(_this); + return delete post.file.fullImage; } }; })(this)); } else { - $.off(this, 'error', ImageHover.error); $.rm(this); return delete post.file.fullImage; } @@ -8559,7 +8652,7 @@ return; } prefetch = $.el('label', { - innerHTML: ' Prefetch Images' + innerHTML: " Prefetch Images" }); this.el = prefetch.firstElementChild; $.on(this.el, 'change', this.toggle); @@ -8946,8 +9039,9 @@ }); } catch (_error) { err = _error; - link.innerHTML = 'Title Link Blocked (are you using NoScript?)'; - $.prepend(link, $.tn("[" + key + "] ")); + $.extend(link, { + innerHTML: "[" + E(key) + "] Title Link Blocked (are you using NoScript?)" + }); } } }, @@ -9021,10 +9115,14 @@ key: 'gist', regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/, el: function(a) { - var div; - return div = $.el('iframe', { - src: "http://www.purplegene.com/script?url=https://gist.github.com/" + a.dataset.uid + ".js" - }); + var content, el; + el = $.el('iframe'); + el.setAttribute('sandbox', 'allow-scripts'); + content = { + innerHTML: "" + E(a.dataset.uid) + "" + }; + el.src = "data:text/html;charset=utf-8," + (encodeURIComponent(content.innerHTML)); + return el; }, title: { api: function(uid) { @@ -9045,11 +9143,9 @@ regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/, style: 'border: 0; width: auto; height: auto;', el: function(a) { - var el; - el = $.el('div'); - el.innerHTML = ''; - el.firstChild.href = el.firstChild.firstChild.src = a.href; - return el; + return $.el('div', { + innerHTML: "" + }); } }, { key: 'InstallGentoo', @@ -9089,7 +9185,7 @@ var el; el = $.el('div'); $.cache("https://mediacru.sh/" + a.dataset.uid + ".json", function() { - var embed, ext, file, files, i, status, type, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _results, _results1; + var embed, ext, file, files, i, status, type, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _results; status = this.status; if (status !== 200 && status !== 304) { return el.textContent = "ERROR " + status; @@ -9116,8 +9212,10 @@ case 'video/mp4': case 'video/webm': case 'video/ogv': - el.innerHTML = ''; - _ref1 = ['mp4', 'webm', 'ogv']; + $.extend(el, { + innerHTML: "" + }); + _ref1 = ['mp4', 'webm']; _results = []; for (i = _k = 0, _len2 = _ref1.length; _k < _len2; i = ++_k) { ext = _ref1[i]; @@ -9129,20 +9227,14 @@ case 'image/png': case 'image/gif': case 'image/jpeg': - el.innerHTML = ''; - el.firstChild.href = a.href; - return el.firstChild.firstChild.src = "https://mediacru.sh/" + file.file; + return $.extend(el, { + innerHTML: "" + }); case 'audio/mpeg': case 'audio/ogg': - el.innerHTML = ''; - _ref2 = ['ogg', 'mp3']; - _results1 = []; - for (i = _l = 0, _len3 = _ref2.length; _l < _len3; i = ++_l) { - ext = _ref2[i]; - _results1.push(el.firstChild.children[i].src = "https://mediacru.sh/" + a.dataset.uid + "." + ext); - } - return _results1; - break; + return $.extend(el, { + innerHTML: "" + }); default: return el.textContent = "ERROR: No valid filetype."; } @@ -9208,7 +9300,9 @@ obj = $.el('object', { data: 'http://www.twitch.tv/widgets/archive_embed_player.swf' }); - obj.innerHTML = ''; + $.extend(obj, { + innerHTML: "" + }); obj.children[1].value = "channel=" + channel + "&start_volume=25&auto_play=false&" + idparam[type] + "=" + id; return obj; } else { @@ -9216,7 +9310,9 @@ obj = $.el('object', { data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel }); - obj.innerHTML = ''; + $.extend(obj, { + innerHTML: "" + }); obj.children[1].value = "hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25"; return obj; } @@ -9525,7 +9621,7 @@ return true; } e.preventDefault(); - return CrossOrigin.request(this.href, (function(_this) { + return CrossOrigin.file(this.href, (function(_this) { return function(blob) { if (blob) { _this.href = URL.createObjectURL(blob); @@ -9575,9 +9671,11 @@ var a; a = $.el('a', { className: 'menu-button', - innerHTML: '', href: 'javascript:;' }); + $.extend(a, { + innerHTML: "" + }); return function() { var button; button = a.cloneNode(true); @@ -9686,22 +9784,30 @@ ThreadStats = { init: function() { - var sc; + var countHTML, sc; if (g.VIEW !== 'thread' || !Conf['Thread Stats']) { return; } + countHTML = { + innerHTML: "0 / 0" + }; + if (Conf['Page Count in Stats']) { + countHTML = { + innerHTML: countHTML.innerHTML + " / 0" + }; + } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: "0 / 0" + (Conf['Page Count in Stats'] ? ' / 0' : ''), id: 'thread-stats', title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "") }); + $.extend(sc, countHTML); $.ready(function() { return Header.addShortcut(sc); }); } else { this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', { - innerHTML: "
0 / 0" + (Conf['Page Count in Stats'] ? ' / 0' : '') + "
" + innerHTML: "
" + countHTML.innerHTML + "
" }); $.ready((function(_this) { return function() { @@ -9794,15 +9900,17 @@ } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: '', id: 'updater' }); + $.extend(sc, { + innerHTML: "" + }); $.ready(function() { return Header.addShortcut(sc); }); } else { this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', { - innerHTML: '
' + innerHTML: "
" }); $.addClass(doc, 'float'); $.ready((function(_this) { @@ -9819,9 +9927,11 @@ $.on(this.timer, 'click', this.update); $.on(this.status, 'click', this.update); updateLink = $.el('span', { - innerHTML: 'Update', className: 'brackets-wrap updatelink' }); + $.extend(updateLink, { + innerHTML: "Update" + }); $.ready(function() { return $.add($('.navLinksBot'), [$.tn(' '), updateLink]); }); @@ -9845,7 +9955,7 @@ }); } this.settings = $.el('span', { - innerHTML: 'Interval' + innerHTML: "Interval" }); $.on(this.settings, 'click', this.intervalShortcut); subEntries.push({ @@ -10165,7 +10275,7 @@ }); this.db = new DataBoard('watchedThreads', this.refresh, true); this.dialog = UI.dialog('thread-watcher', 'top: 50px; left: 0px;', { - innerHTML: '
Thread Watcher ×
' + innerHTML: "
Thread Watcher ×
" }); this.status = $('#watcher-status', this.dialog); this.list = this.dialog.lastElementChild; @@ -10409,7 +10519,7 @@ href: 'javascript:;' }); $.on(x, 'click', ThreadWatcher.cb.rm); - if (data.isDead) { + if (Conf['404 Redirect'] && data.isDead) { href = Redirect.to('thread', { boardID: boardID, threadID: threadID @@ -10938,14 +11048,14 @@ record = _ref1[boardID]; for (type in record) { id = record[type]; - if (!((archive = archives[id]))) { - continue; + if (id === 'disabled') { + delete o[type][boardID]; + } else if (archive = archives[id]) { + boards = type === 'file' ? archive.files : archive.boards; + if (__indexOf.call(boards, boardID) >= 0) { + o[type][boardID] = archive; + } } - boards = type === 'file' ? archive.files : archive.boards; - if (__indexOf.call(boards, boardID) < 0) { - continue; - } - o[type][boardID] = archive; } } return Redirect.data = o; @@ -10983,10 +11093,10 @@ var URL, boardID, postID, protocol; boardID = _arg.boardID, postID = _arg.postID; protocol = Redirect.protocol(archive); - if (!(protocol === 'https://' || location.protocol === 'http:')) { + URL = new String("" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID); + if (!Redirect.securityCheck(URL)) { return ''; } - URL = new String("" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID); URL.archive = archive; return URL; }, @@ -11003,8 +11113,11 @@ path = archive.software === 'foolfuuka' ? "" + boardID + "/search/" + type + "/" + value : "" + boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value; return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path; }, + securityCheck: function(URL) { + return /^https:\/\//.test(URL) || location.protocol === 'http:' || Conf['Except Archives from Encryption']; + }, navigate: function(URL, alternative) { - if (URL && (/^https:\/\//.test(URL) || location.protocol === 'http:' || confirm("Redirect to " + URL + "?\n\nYour connection will not be encrypted."))) { + if (URL && (Redirect.securityCheck(URL) || confirm("Redirect to " + URL + "?\n\nYour connection will not be encrypted."))) { return location.replace(URL); } else if (alternative) { return location.replace(alternative); @@ -11041,10 +11154,11 @@ Header.menu.addEntry(entry); $.on(entry.el, 'click', PSAHiding.toggle); PSAHiding.btn = btn = $.el('span', { - innerHTML: '[Dismiss]', title: 'Mark announcement as read and hide.', - className: 'hide-announcement', - href: 'javascript:;' + className: 'hide-announcement' + }); + $.extend(btn, { + innerHTML: "[Dismiss]" }); $.on(btn, 'click', PSAHiding.toggle); $.get('hiddenPSA', 0, function(_arg) { @@ -11611,85 +11725,106 @@ if (!this.file || this.isClone) { return; } - this.file.text.innerHTML = ''; + $.extend(this.file.text, { + innerHTML: "" + }); return FileInfo.format(Conf['fileInfo'], this, this.file.text.firstElementChild); }, format: function(formatString, post, outputNode) { - FileInfo.innerHTML = ''; + var output; + output = []; formatString.replace(/%(.)|[^%]+/g, function(s, c) { - if (c in FileInfo.formatters) { - FileInfo.formatters[c].call(post); - } else { - FileInfo.innerHTML += E(s); - } + output.push(c in FileInfo.formatters ? FileInfo.formatters[c].call(post) : { + innerHTML: E(s) + }); return ''; }); - return outputNode.innerHTML = FileInfo.innerHTML; + return $.extend(outputNode, { + innerHTML: output.map(function(x) { + return x.innerHTML; + }).join('') + }); }, formatters: { t: function() { - var timestamp; - timestamp = this.file.URL.match(/\d+\..+$/)[0]; - return FileInfo.innerHTML += E(timestamp); + return { + innerHTML: E(this.file.URL.match(/\d+\..+$/)[0]) + }; }, T: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.t.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.t.call(this).innerHTML + "" + }; }, l: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.n.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.n.call(this).innerHTML + "" + }; }, L: function() { - FileInfo.innerHTML += ""; - FileInfo.formatters.N.call(this); - return FileInfo.innerHTML += ''; + return { + innerHTML: "" + FileInfo.formatters.N.call(this).innerHTML + "" + }; }, n: function() { var fullname, shortname; fullname = this.file.name; shortname = Build.shortFilename(this.file.name, this.isReply); if (fullname === shortname) { - return FileInfo.innerHTML += E(fullname); + return { + innerHTML: E(fullname) + }; } else { - return FileInfo.innerHTML += "" + (E(shortname)) + "" + (E(fullname)) + ""; + return { + innerHTML: "" + E(shortname) + "" + E(fullname) + "" + }; } }, N: function() { - return FileInfo.innerHTML += E(this.file.name); + return { + innerHTML: E(this.file.name) + }; }, p: function() { if (this.file.isSpoiler) { - return FileInfo.innerHTML += 'Spoiler, '; + return { + innerHTML: "Spoiler, " + }; + } else { + return { + innerHTML: "" + }; } }, s: function() { - return FileInfo.innerHTML += E(this.file.size); + return { + innerHTML: E(this.file.size) + }; }, B: function() { - var sizeB; - sizeB = Math.round(this.file.sizeInBytes); - return FileInfo.innerHTML += "" + (+sizeB) + " Bytes"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes" + }; }, K: function() { - var sizeKB; - sizeKB = Math.round(this.file.sizeInBytes / 1024); - return FileInfo.innerHTML += "" + (+sizeKB) + " KB"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB" + }; }, M: function() { - var sizeMB; - sizeMB = Math.round(this.file.sizeInBytes / 1048576 * 100) / 100; - return FileInfo.innerHTML += "" + (+sizeMB) + " MB"; + return { + innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB" + }; }, r: function() { - var dim; - dim = this.file.dimensions || 'PDF'; - return FileInfo.innerHTML += E(dim); + return { + innerHTML: E(this.file.dimensions || "PDF") + }; }, '%': function() { - return FileInfo.innerHTML += '%'; + return { + innerHTML: "%" + }; } } }; @@ -12593,8 +12728,10 @@ }); Settings.dialog = dialog = $.el('div', { id: 'fourchanx-settings', - className: 'dialog', - innerHTML: '
' + className: 'dialog' + }); + $.extend(dialog, { + innerHTML: "
" }); $.on($('.export', Settings.dialog), 'click', Settings["export"]); $.on($('.import', Settings.dialog), 'click', Settings["import"]); @@ -12666,9 +12803,8 @@ for (key in _ref) { obj = _ref[key]; fs = $.el('fieldset', { - innerHTML: '' + innerHTML: "" + E(key) + "" }); - fs.firstElementChild.textContent = key; for (key in obj) { arr = obj[key]; description = arr[1]; @@ -12695,7 +12831,7 @@ } }); div = $.el('div', { - innerHTML: ': Clear manually-hidden threads and posts on all boards. Reload the page to apply.' + innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." }); button = $('button', div); $.get({ @@ -12892,7 +13028,9 @@ }, filter: function(section) { var select; - section.innerHTML = '
'; + $.extend(section, { + innerHTML: "
" + }); select = $('select', section); $.on(select, 'change', Settings.selectFilter); return Settings.selectFilter.call(select); @@ -12914,12 +13052,16 @@ $.add(div, ta); return; } - div.innerHTML = '
Filter is disabled.

Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions.

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.
'; + $.extend(div, { + innerHTML: "
Filter is disabled.

Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions.

    You can use these settings with each regular expression, separate them with semicolons:
  • Per boards, separate them with commas. It is global if not specified.
    For example: boards:a,jp;.
  • Filter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    For example: op:only;, op:no; or op:yes;.
  • Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    For example: stub:yes; or stub:no;.
  • Highlight instead of hiding. You can specify a class name to use with a userstyle.
    For example: highlight; or highlight:wallpaper;.
  • Highlighted OPs will have their threads put on top of the board index by default.
    For example: top:yes; or top:no;.
" + }); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; - section.innerHTML = '
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
'; + $.extend(section, { + innerHTML: "
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
    These parameters will be replaced by their corresponding values:
  • %TURL: Thumbnail URL.
  • %URL: Full image URL.
  • %MD5: MD5 hash.
  • %name: Original file name.
  • %board: Current board.
  • %%, %semi: Literal % and ;.
" + }); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { @@ -12929,7 +13071,9 @@ }, advanced: function(section) { var archBoards, boardID, boardOptions, boardSelect, boards, customCSS, event, files, i, input, inputs, interval, item, items, name, o, row, rows, software, ta, table, warning, withCredentials, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _len6, _m, _n, _o, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; - section.innerHTML = '
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Disabled selections indicate that only one archive is available for that board and redirection type.
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:"Google","http://www.google.com"
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Quote Backlinks formatting is disabled.
:
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays \'PDF\' for PDF files)
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input\'s auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, email, subject and password.
  • Wrap values of items with quotes, like this: email:"sage".
  • Force values as defaults with the always keyword, for example: email:"sage";always.
  • Select specific boards for an item, separated with commas, for example: email:"sage";boards:jp;always.
Unread Favicon is disabled.
Emoji is disabled.
Sage Icon:
Position:
Thread Updater is disabled.
Interval:
'; + $.extend(section, { + innerHTML: "
Archiver
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:\"Install Gentoo\"
Index-only link: g-index
Catalog-only link: g-catalog
External link: external-text:\"Google\",\"http://www.google.com\"
Combinations are possible: g-index-text:\"Technology Index\"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:\"Piracy\"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Quote Backlinks formatting is disabled.
:
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, email, subject and password.
  • Wrap values of items with quotes, like this: email:\"sage\".
  • Force values as defaults with the always keyword, for example: email:\"sage\";always.
  • Select specific boards for an item, separated with commas, for example: email:\"sage\";boards:jp;always.
Unread Favicon is disabled.
Emoji is disabled.
Sage Icon:
Position:
Thread Updater is disabled.
Interval:
" + }); _ref = $$('.warning', section); for (_i = 0, _len = _ref.length; _i < _len; _i++) { warning = _ref[_i]; @@ -13001,9 +13145,7 @@ _ref4 = ['thread', 'post', 'file']; for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { item = _ref4[_m]; - if (o[item][0].length === 0 && o[item][1].length !== 0) { - o[item][0].push('disabled'); - } + o[item][0].push('disabled'); o[item] = o[item][0].concat(o[item][1]); } } @@ -13073,7 +13215,9 @@ value: archive })); } - td.innerHTML = ''; + $.extend(td, { + innerHTML: "" + }); select = td.firstElementChild; if (!(select.disabled = length === 1)) { select.setAttribute('data-boardid', boardID); @@ -13151,7 +13295,9 @@ }, keybinds: function(section) { var arr, input, inputs, items, key, tbody, tr, _ref; - section.innerHTML = '
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
'; + $.extend(section, { + innerHTML: "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
" + }); $('.warning', section).hidden = Conf['Keybinds']; tbody = $('tbody', section); items = {}; @@ -13160,9 +13306,8 @@ for (key in _ref) { arr = _ref[key]; tr = $.el('tr', { - innerHTML: '' + innerHTML: "" + E(arr[1]) + "" }); - tr.firstElementChild.textContent = arr[1]; input = $('input', tr); input.name = key; input.spellcheck = false; @@ -13436,7 +13581,7 @@ } if (previousversion) { el = $.el('span', { - innerHTML: '4chan X has been updated to version 1.8.8.6.' + innerHTML: E(g.NAME) + " has been updated to version " + E(g.VERSION) + "." }); new Notice('info', el, 15); } else { @@ -13491,7 +13636,7 @@ return; } div = $.el('div', { - innerHTML: "" + (+errors.length) + " errors occurred. [show]" + innerHTML: E(errors.length) + " errors occurred. [show]" }); $.on(div.lastElementChild, 'click', function() { var _ref; diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip index aa1f907a1..a8916756b 100644 Binary files a/builds/4chan-X.zip and b/builds/4chan-X.zip differ diff --git a/builds/updates-beta.xml b/builds/updates-beta.xml index 42ab69e8d..5d434db82 100644 --- a/builds/updates-beta.xml +++ b/builds/updates-beta.xml @@ -1,7 +1,7 @@ - + diff --git a/builds/updates.xml b/builds/updates.xml index 7aacfb5cd..c40bcb56e 100644 --- a/builds/updates.xml +++ b/builds/updates.xml @@ -1,7 +1,7 @@ - + diff --git a/package.json b/package.json index 0ba7bc882..dab8a83db 100755 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "Cross-browser userscript for maximum lurking on 4chan.", "meta": { "name": "4chan X", - "version": "1.8.8.6", + "version": "1.8.9", "repo": "https://github.com/ccd0/4chan-x/", "page": "https://github.com/ccd0/4chan-x", "downloads": "https://ccd0.github.io/4chan-x/builds/",