Merge branch 'mayhem' into v3

Conflicts:
	CHANGELOG.md
	package.json
	src/General/Build.coffee
	src/General/Index.coffee
	src/General/Main.coffee
	src/General/lib/post.class
	src/Monitoring/ThreadStats.coffee
This commit is contained in:
Zixaphir 2014-05-03 02:45:58 -07:00
commit a1601d8825
15 changed files with 116 additions and 92 deletions

View File

@ -95,10 +95,6 @@ module.exports = (grunt) ->
push: false push: false
shell: shell:
options:
stdout: true
stderr: true
failOnError: true
checkout: checkout:
command: 'git checkout <%= pkg.meta.mainBranch %>' command: 'git checkout <%= pkg.meta.mainBranch %>'
commit: commit:

View File

@ -1175,7 +1175,7 @@
Post.prototype.parseQuote = function(quotelink) { Post.prototype.parseQuote = function(quotelink) {
var fullID, match; var fullID, match;
if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/(?:res|thread)\/\d+#p(\d+)$/))) { if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/(res|thread)\/\d+(.*)?\#p(\d+)$/))) {
return; return;
} }
this.nodes.quotelinks.push(quotelink); this.nodes.quotelinks.push(quotelink);
@ -1189,7 +1189,7 @@
}; };
Post.prototype.parseFile = function(that) { Post.prototype.parseFile = function(that) {
var anchor, fileEl, fileText, nameNode, size, thumb, unit, _ref; var anchor, fileEl, fileText, nameNode, size, thumb, unit;
if (!((fileEl = $('.file', this.nodes.post)) && (thumb = $('img[data-md5]', fileEl)))) { if (!((fileEl = $('.file', this.nodes.post)) && (thumb = $('img[data-md5]', fileEl)))) {
return; return;
} }
@ -1213,7 +1213,7 @@
this.file.isImage = /(jpg|png|gif)$/i.test(this.file.URL); this.file.isImage = /(jpg|png|gif)$/i.test(this.file.URL);
this.file.isVideo = /webm$/i.test(this.file.URL); this.file.isVideo = /webm$/i.test(this.file.URL);
if (this.file.isImage || this.file.isVideo) { if (this.file.isImage || this.file.isVideo) {
this.file.dimensions = (_ref = fileText.childNodes[2].textContent.match(/\d+x\d+/)) != null ? _ref[0] : void 0; this.file.dimensions = fileText.childNodes[2].data.match(/\d+x\d+/)[0];
} }
return this.file.name = !this.file.isSpoiler && (nameNode = $('a', fileText)) ? nameNode.title || nameNode.textContent : fileText.title; return this.file.name = !this.file.isSpoiler && (nameNode = $('a', fileText)) ? nameNode.title || nameNode.textContent : fileText.title;
}; };
@ -1225,7 +1225,7 @@
node = _ref[_i]; node = _ref[_i];
$.rm(node); $.rm(node);
} }
_ref1 = $$('[id]', post); _ref1 = $$('[id]:not(.exif)', post);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
node = _ref1[_j]; node = _ref1[_j];
node.removeAttribute('id'); node.removeAttribute('id');
@ -2926,8 +2926,8 @@
toggleHiddenThreads: function() { toggleHiddenThreads: function() {
$('#hidden-toggle a', Index.navLinks).textContent = (Index.showHiddenThreads = !Index.showHiddenThreads) ? 'Hide' : 'Show'; $('#hidden-toggle a', Index.navLinks).textContent = (Index.showHiddenThreads = !Index.showHiddenThreads) ? 'Hide' : 'Show';
Index.sort(); Index.sort();
if (Conf['Index Mode'] === 'paged' && Index.getCurrentPage() > 0) { if (Conf['Index Mode'] === 'paged' && Index.getCurrentPage() > 1) {
return Index.pageNav(0); return Index.pageNav(1);
} else { } else {
return Index.buildIndex(); return Index.buildIndex();
} }
@ -3010,7 +3010,7 @@
if (Index.cb.indexNav(a, true)) { if (Index.cb.indexNav(a, true)) {
return; return;
} }
return Index.userPageNav(+a.pathname.split('/')[2]); return Index.userPageNav(+a.pathname.split('/')[2] || 1);
}, },
headerNav: function(e) { headerNav: function(e) {
var a, needChange, onSameIndex; var a, needChange, onSameIndex;
@ -3077,7 +3077,7 @@
if (Index.currentPage === pageNum && !Index.root.parentElement) { if (Index.currentPage === pageNum && !Index.root.parentElement) {
return; return;
} }
Navigate.pushState(pageNum === 0 ? './' : pageNum); Navigate.pushState(pageNum === 1 ? './' : pageNum);
return Index.pageLoad(pageNum); return Index.pageLoad(pageNum);
}, },
pageLoad: function(pageNum) { pageLoad: function(pageNum) {
@ -3322,7 +3322,7 @@
var err, thread, threadRoot; var err, thread, threadRoot;
threadRoot = Build.thread(g.BOARD, threadData); threadRoot = Build.thread(g.BOARD, threadData);
if (thread = g.BOARD.threads[threadData.no]) { if (thread = g.BOARD.threads[threadData.no]) {
thread.setPage(Math.floor(i / Index.threadsNumPerPage)); thread.setPage(Math.floor(i / Index.threadsNumPerPage) + 1);
thread.setCount('post', threadData.replies + 1, threadData.bumplimit); thread.setCount('post', threadData.replies + 1, threadData.bumplimit);
thread.setCount('file', threadData.images + !!threadData.ext, threadData.imagelimit); thread.setCount('file', threadData.images + !!threadData.ext, threadData.imagelimit);
thread.setStatus('Sticky', !!threadData.sticky); thread.setStatus('Sticky', !!threadData.sticky);
@ -3517,7 +3517,7 @@
switch (Conf['Index Mode']) { switch (Conf['Index Mode']) {
case 'paged': case 'paged':
case 'infinite': case 'infinite':
pageNum = Index.getCurrentPage(); pageNum = Index.getCurrentPage() - 1;
threadsPerPage = Index.getThreadsNumPerPage(); threadsPerPage = Index.getThreadsNumPerPage();
threads = []; threads = [];
i = threadsPerPage * pageNum; i = threadsPerPage * pageNum;
@ -3645,6 +3645,17 @@
return n = (n + 1) % 3; return n = (n + 1) % 3;
}; };
})(), })(),
path: function(boardID, threadID, postID, fragment) {
var path;
path = "/" + boardID + "/thread/" + threadID;
if ((g.SLUG != null) && threadID === g.THREADID) {
path += "/" + g.SLUG;
}
if (postID) {
path += "#" + (fragment || 'p') + postID;
}
return path;
},
postFromObject: function(data, boardID) { postFromObject: function(data, boardID) {
var o; var o;
o = { o = {
@ -3759,9 +3770,9 @@
sticky = isSticky ? " <img src=" + staticPath + "sticky" + gifIcon + " title=Sticky class=stickyIcon>" : ''; sticky = isSticky ? " <img src=" + staticPath + "sticky" + gifIcon + " title=Sticky class=stickyIcon>" : '';
closed = isClosed ? " <img src=" + staticPath + "closed" + gifIcon + " title=Closed class=closedIcon>" : ''; closed = isClosed ? " <img src=" + staticPath + "closed" + gifIcon + " title=Closed class=closedIcon>" : '';
if (isOP && g.VIEW === 'index') { if (isOP && g.VIEW === 'index') {
pageNum = Math.floor(Index.liveThreadData.keys.indexOf("" + postID) / Index.threadsNumPerPage); pageNum = Math.floor(Index.liveThreadData.keys.indexOf("" + postID) / Index.threadsNumPerPage) + 1;
pageIcon = " <span class=page-num title='This thread is on page " + pageNum + " in the original index.'>Page " + pageNum + "</span>"; pageIcon = " <span class=page-num title='This thread is on page " + pageNum + " in the original index.'>Page " + pageNum + "</span>";
replyLink = " &nbsp; <span>[<a href='/" + boardID + "/thread/" + threadID + "' class=replylink>Reply</a>]</span>"; replyLink = " &nbsp; <span>[<a href='" + (Build.path(boardID, threadID)) + "' class=replylink>Reply</a>]</span>";
} else { } else {
pageIcon = ''; pageIcon = '';
replyLink = ''; replyLink = '';
@ -3769,19 +3780,16 @@
container = $.el('div', { container = $.el('div', {
id: "pc" + postID, id: "pc" + postID,
className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
innerHTML: (isOP ? '' : "<div class=sideArrows>&gt;&gt;</div>") + ("<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'>") + (isOP ? fileHTML : '') + "<div class=postInfo>" + ("<input type=checkbox name=" + postID + " value=delete> ") + ("<span class=subject>" + (subject || '') + "</span> ") + ("<span class='nameBlock" + capcodeClass + "'>") + emailStart + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag + ' </span> ' + ("<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span> ") + "<span class='postNum'>" + ("<a href=" + ("/" + boardID + "/thread/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a>") + ("<a href='" + (g.VIEW === 'thread' && g.THREADID === threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/thread/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a>") + pageIcon + sticky + closed + replyLink + '</span>' + '</div>' + (isOP ? '' : fileHTML) + ("<blockquote class=postMessage>" + (comment || '') + "</blockquote> ") + '</div>' innerHTML: (isOP ? '' : "<div class=sideArrows>&gt;&gt;</div>") + ("<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'>") + (isOP ? fileHTML : '') + "<div class=postInfo>" + ("<input type=checkbox name=" + postID + " value=delete> ") + ("<span class=subject>" + (subject || '') + "</span> ") + ("<span class='nameBlock" + capcodeClass + "'>") + emailStart + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag + ' </span> ' + ("<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span> ") + "<span class='postNum'>" + ("<a href=" + (Build.path(boardID, threadID, postID)) + " title='Highlight this post'>No.</a>") + ("<a href='" + (g.VIEW === 'thread' && g.THREADID === threadID ? "javascript:quote(" + postID + ")" : Build.path(boardID, threadID, postID, 'q')) + "' title='Quote this post'>" + postID + "</a>") + pageIcon + sticky + closed + replyLink + '</span>' + '</div>' + (isOP ? '' : fileHTML) + ("<blockquote class=postMessage>" + (comment || '') + "</blockquote> ") + '</div>'
}); });
_ref = $$('.quotelink', container); _ref = $$('.quotelink', container);
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i]; quote = _ref[_i];
href = quote.getAttribute('href'); href = quote.getAttribute('href');
if (href[0] === '/') { if (href[0] !== '#') {
continue; continue;
} }
if (href[0] === '#') { quote.href = Build.path(boardID, threadID, href.slice(2));
href = "" + threadID + href;
}
quote.href = "/" + boardID + "/thread/" + href;
} }
return container; return container;
}, },
@ -3796,7 +3804,7 @@
return $.el('a', { return $.el('a', {
className: 'summary', className: 'summary',
textContent: text.join(' '), textContent: text.join(' '),
href: "/" + boardID + "/thread/" + threadID href: Build.path(boardID, threadID)
}); });
}, },
thread: function(board, data, full) { thread: function(board, data, full) {
@ -3832,12 +3840,12 @@
data = Index.liveThreadData[thread.ID]; data = Index.liveThreadData[thread.ID];
postCount = data.replies + 1; postCount = data.replies + 1;
fileCount = data.images + !!data.ext; fileCount = data.images + !!data.ext;
pageCount = Math.floor(Index.liveThreadData.keys.indexOf("" + thread.ID) / Index.threadsNumPerPage); pageCount = Math.floor(Index.liveThreadData.keys.indexOf("" + thread.ID) / Index.threadsNumPerPage) + 1;
subject = thread.OP.info.subject ? "<div class='subject'>" + thread.OP.info.subject + "</div>" : ''; subject = thread.OP.info.subject ? "<div class='subject'>" + thread.OP.info.subject + "</div>" : '';
comment = thread.OP.nodes.comment.innerHTML.replace(/(<br>\s*){2,}/g, '<br>'); comment = thread.OP.nodes.comment.innerHTML.replace(/(<br>\s*){2,}/g, '<br>');
root = $.el('div', { root = $.el('div', {
className: 'catalog-thread', className: 'catalog-thread',
innerHTML: "<a href=\"/" + thread.board + "/thread/" + thread.ID + "\" class=\"thumb\"></a><div class=\"thread-stats\" title=\"Post count / File count / Page count\"><span class=\"post-count\">" + postCount + "</span> / <span class=\"file-count\">" + fileCount + "</span> / <span class=\"page-count\">" + pageCount + "</span><span class=\"thread-icons\"></span></div>" + subject + "<div class=\"comment\">" + comment + "</div>" innerHTML: "<a href=\"" + (Build.path(thread.board.ID, thread.ID)) + "\" class=\"thumb\"></a><div class=\"thread-stats\" title=\"Post count / File count / Page count\"><span class=\"post-count\">" + postCount + "</span> / <span class=\"file-count\">" + fileCount + "</span> / <span class=\"page-count\">" + pageCount + "</span><span class=\"thread-icons\"></span></div>" + subject + "<div class=\"comment\">" + comment + "</div>"
}); });
root.dataset.fullID = thread.fullID; root.dataset.fullID = thread.fullID;
if (thread.isPinned) { if (thread.isPinned) {
@ -5316,7 +5324,7 @@
var a, frag, hash, text; var a, frag, hash, text;
frag = QuoteBacklink.frag.cloneNode(true); frag = QuoteBacklink.frag.cloneNode(true);
a = frag.lastElementChild; a = frag.lastElementChild;
a.href = "/" + quoter.board + "/thread/" + quoter.thread + "#p" + quoter; a.href = Build.path(quoter.board.ID, quoter.thread.ID, quoter.ID);
a.textContent = text = QuoteBacklink.funk(quoter.ID); a.textContent = text = QuoteBacklink.funk(quoter.ID);
if (quoter.isDead) { if (quoter.isDead) {
$.addClass(a, 'deadlink'); $.addClass(a, 'deadlink');
@ -5888,7 +5896,7 @@
quoteID = "" + boardID + "." + postID; quoteID = "" + boardID + "." + postID;
if (post = g.posts[quoteID]) { if (post = g.posts[quoteID]) {
a = $.el('a', { a = $.el('a', {
href: "/" + boardID + "/thread/" + post.thread + "#p" + postID, href: Build.path(boardID, post.thread.ID, postID),
className: post.isDead ? 'quotelink deadlink' : 'quotelink', className: post.isDead ? 'quotelink deadlink' : 'quotelink',
textContent: quote textContent: quote
}); });
@ -6222,7 +6230,7 @@
_ref = $$('br', frag); _ref = $$('br', frag);
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i]; node = _ref[_i];
if (node !== frag.lastElementChild) { if (node !== frag.lastChild) {
$.replace(node, $.tn('\n>')); $.replace(node, $.tn('\n>'));
} }
} }
@ -6910,7 +6918,7 @@
isReply: isReply, isReply: isReply,
threadID: threadID threadID: threadID
}); });
URL = threadID === postID ? "/" + g.BOARD + "/thread/" + threadID : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? "/" + g.BOARD + "/thread/" + threadID + "#p" + postID : void 0; URL = threadID === postID ? Build.path(g.BOARD.ID, threadID) : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? Build.path(g.BOARD.ID, threadID, postID) : void 0;
if (URL) { if (URL) {
if (Conf['Open Post in New Tab']) { if (Conf['Open Post in New Tab']) {
$.open(URL); $.open(URL);
@ -11120,7 +11128,7 @@
} }
return Redirect.data = o; return Redirect.data = o;
}, },
archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","vg","vp","vr","wsg"],"files":["a","biz","gd","diy","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i"],"files":["d","i"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":true,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["asp","cm","h","hc","hm","n","p","r","s","soc","y"],"files":["asp","cm","h","hc","hm","n","p","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]}], archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","vg","vp","vr","wsg"],"files":["a","biz","gd","diy","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i"],"files":["d","i"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":true,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["asp","cm","h","hc","hm","n","p","r","s","soc","y"],"files":["asp","cm","h","hc","hm","n","p","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]}],
to: function(dest, data) { to: function(dest, data) {
var archive; var archive;
archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID];
@ -12419,7 +12427,7 @@
if (g.VIEW !== 'index') { if (g.VIEW !== 'index') {
return; return;
} }
url = "/" + thread.board + "/thread/" + thread; url = Build.path(thread.board.ID, thread.ID);
if (tab) { if (tab) {
return $.open(url); return $.open(url);
} else { } else {
@ -13825,7 +13833,10 @@
} }
if (g.VIEW === 'thread') { if (g.VIEW === 'thread') {
g.THREADID = +pathname[3]; g.THREADID = +pathname[3];
if (pathname[2] !== 'thread' || pathname.length > 4) { if (pathname[4] != null) {
g.SLUG = pathname[4];
}
if (pathname[2] !== 'thread') {
pathname[2] = 'thread'; pathname[2] = 'thread';
history.replaceState(null, '', pathname.slice(0, 4).join('/') + location.hash); history.replaceState(null, '', pathname.slice(0, 4).join('/') + location.hash);
} }

View File

@ -1231,7 +1231,7 @@
Post.prototype.parseQuote = function(quotelink) { Post.prototype.parseQuote = function(quotelink) {
var fullID, match; var fullID, match;
if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/(?:res|thread)\/\d+#p(\d+)$/))) { if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/(res|thread)\/\d+(.*)?\#p(\d+)$/))) {
return; return;
} }
this.nodes.quotelinks.push(quotelink); this.nodes.quotelinks.push(quotelink);
@ -1245,7 +1245,7 @@
}; };
Post.prototype.parseFile = function(that) { Post.prototype.parseFile = function(that) {
var anchor, fileEl, fileText, nameNode, size, thumb, unit, _ref, _ref1; var anchor, fileEl, fileText, nameNode, size, thumb, unit, _ref;
if (!((fileEl = $('.file', this.nodes.post)) && (thumb = $('img[data-md5]', fileEl)))) { if (!((fileEl = $('.file', this.nodes.post)) && (thumb = $('img[data-md5]', fileEl)))) {
return; return;
} }
@ -1269,10 +1269,10 @@
this.file.isImage = /(jpg|png|gif)$/i.test(this.file.URL); this.file.isImage = /(jpg|png|gif)$/i.test(this.file.URL);
this.file.isVideo = /webm$/i.test(this.file.URL); this.file.isVideo = /webm$/i.test(this.file.URL);
if (this.file.isImage || this.file.isVideo) { if (this.file.isImage || this.file.isVideo) {
this.file.dimensions = (_ref = fileText.childNodes[2].textContent.match(/\d+x\d+/)) != null ? _ref[0] : void 0; this.file.dimensions = fileText.childNodes[2].data.match(/\d+x\d+/)[0];
} }
this.file.name = !this.file.isSpoiler && (nameNode = $('a', fileText)) ? nameNode.title || nameNode.textContent : fileText.title; this.file.name = !this.file.isSpoiler && (nameNode = $('a', fileText)) ? nameNode.title || nameNode.textContent : fileText.title;
return this.file.name = (_ref1 = this.file.name) != null ? _ref1.replace(/%22/g, '"') : void 0; return this.file.name = (_ref = this.file.name) != null ? _ref.replace(/%22/g, '"') : void 0;
}; };
Post.prototype.cleanup = function(root, post) { Post.prototype.cleanup = function(root, post) {
@ -1282,7 +1282,7 @@
node = _ref[_i]; node = _ref[_i];
$.rm(node); $.rm(node);
} }
_ref1 = $$('[id]', post); _ref1 = $$('[id]:not(.exif)', post);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
node = _ref1[_j]; node = _ref1[_j];
node.removeAttribute('id'); node.removeAttribute('id');
@ -2985,8 +2985,8 @@
toggleHiddenThreads: function() { toggleHiddenThreads: function() {
$('#hidden-toggle a', Index.navLinks).textContent = (Index.showHiddenThreads = !Index.showHiddenThreads) ? 'Hide' : 'Show'; $('#hidden-toggle a', Index.navLinks).textContent = (Index.showHiddenThreads = !Index.showHiddenThreads) ? 'Hide' : 'Show';
Index.sort(); Index.sort();
if (Conf['Index Mode'] === 'paged' && Index.getCurrentPage() > 0) { if (Conf['Index Mode'] === 'paged' && Index.getCurrentPage() > 1) {
return Index.pageNav(0); return Index.pageNav(1);
} else { } else {
return Index.buildIndex(); return Index.buildIndex();
} }
@ -3069,7 +3069,7 @@
if (Index.cb.indexNav(a, true)) { if (Index.cb.indexNav(a, true)) {
return; return;
} }
return Index.userPageNav(+a.pathname.split('/')[2]); return Index.userPageNav(+a.pathname.split('/')[2] || 1);
}, },
headerNav: function(e) { headerNav: function(e) {
var a, needChange, onSameIndex; var a, needChange, onSameIndex;
@ -3136,7 +3136,7 @@
if (Index.currentPage === pageNum && !Index.root.parentElement) { if (Index.currentPage === pageNum && !Index.root.parentElement) {
return; return;
} }
Navigate.pushState(pageNum === 0 ? './' : pageNum); Navigate.pushState(pageNum === 1 ? './' : pageNum);
return Index.pageLoad(pageNum); return Index.pageLoad(pageNum);
}, },
pageLoad: function(pageNum) { pageLoad: function(pageNum) {
@ -3381,7 +3381,7 @@
var err, thread, threadRoot; var err, thread, threadRoot;
threadRoot = Build.thread(g.BOARD, threadData); threadRoot = Build.thread(g.BOARD, threadData);
if (thread = g.BOARD.threads[threadData.no]) { if (thread = g.BOARD.threads[threadData.no]) {
thread.setPage(Math.floor(i / Index.threadsNumPerPage)); thread.setPage(Math.floor(i / Index.threadsNumPerPage) + 1);
thread.setCount('post', threadData.replies + 1, threadData.bumplimit); thread.setCount('post', threadData.replies + 1, threadData.bumplimit);
thread.setCount('file', threadData.images + !!threadData.ext, threadData.imagelimit); thread.setCount('file', threadData.images + !!threadData.ext, threadData.imagelimit);
thread.setStatus('Sticky', !!threadData.sticky); thread.setStatus('Sticky', !!threadData.sticky);
@ -3576,7 +3576,7 @@
switch (Conf['Index Mode']) { switch (Conf['Index Mode']) {
case 'paged': case 'paged':
case 'infinite': case 'infinite':
pageNum = Index.getCurrentPage(); pageNum = Index.getCurrentPage() - 1;
threadsPerPage = Index.getThreadsNumPerPage(); threadsPerPage = Index.getThreadsNumPerPage();
threads = []; threads = [];
i = threadsPerPage * pageNum; i = threadsPerPage * pageNum;
@ -3704,6 +3704,17 @@
return n = (n + 1) % 3; return n = (n + 1) % 3;
}; };
})(), })(),
path: function(boardID, threadID, postID, fragment) {
var path;
path = "/" + boardID + "/thread/" + threadID;
if ((g.SLUG != null) && threadID === g.THREADID) {
path += "/" + g.SLUG;
}
if (postID) {
path += "#" + (fragment || 'p') + postID;
}
return path;
},
postFromObject: function(data, boardID) { postFromObject: function(data, boardID) {
var o; var o;
o = { o = {
@ -3818,9 +3829,9 @@
sticky = isSticky ? " <img src=" + staticPath + "sticky" + gifIcon + " title=Sticky class=stickyIcon>" : ''; sticky = isSticky ? " <img src=" + staticPath + "sticky" + gifIcon + " title=Sticky class=stickyIcon>" : '';
closed = isClosed ? " <img src=" + staticPath + "closed" + gifIcon + " title=Closed class=closedIcon>" : ''; closed = isClosed ? " <img src=" + staticPath + "closed" + gifIcon + " title=Closed class=closedIcon>" : '';
if (isOP && g.VIEW === 'index') { if (isOP && g.VIEW === 'index') {
pageNum = Math.floor(Index.liveThreadData.keys.indexOf("" + postID) / Index.threadsNumPerPage); pageNum = Math.floor(Index.liveThreadData.keys.indexOf("" + postID) / Index.threadsNumPerPage) + 1;
pageIcon = " <span class=page-num title='This thread is on page " + pageNum + " in the original index.'>Page " + pageNum + "</span>"; pageIcon = " <span class=page-num title='This thread is on page " + pageNum + " in the original index.'>Page " + pageNum + "</span>";
replyLink = " &nbsp; <span>[<a href='/" + boardID + "/thread/" + threadID + "' class=replylink>Reply</a>]</span>"; replyLink = " &nbsp; <span>[<a href='" + (Build.path(boardID, threadID)) + "' class=replylink>Reply</a>]</span>";
} else { } else {
pageIcon = ''; pageIcon = '';
replyLink = ''; replyLink = '';
@ -3828,19 +3839,16 @@
container = $.el('div', { container = $.el('div', {
id: "pc" + postID, id: "pc" + postID,
className: "postContainer " + (isOP ? 'op' : 'reply') + "Container", className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
innerHTML: (isOP ? '' : "<div class=sideArrows>&gt;&gt;</div>") + ("<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'>") + (isOP ? fileHTML : '') + "<div class=postInfo>" + ("<input type=checkbox name=" + postID + " value=delete> ") + ("<span class=subject>" + (subject || '') + "</span> ") + ("<span class='nameBlock" + capcodeClass + "'>") + emailStart + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag + ' </span> ' + ("<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span> ") + "<span class='postNum'>" + ("<a href=" + ("/" + boardID + "/thread/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a>") + ("<a href='" + (g.VIEW === 'thread' && g.THREADID === threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/thread/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a>") + pageIcon + sticky + closed + replyLink + '</span>' + '</div>' + (isOP ? '' : fileHTML) + ("<blockquote class=postMessage>" + (comment || '') + "</blockquote> ") + '</div>' innerHTML: (isOP ? '' : "<div class=sideArrows>&gt;&gt;</div>") + ("<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'>") + (isOP ? fileHTML : '') + "<div class=postInfo>" + ("<input type=checkbox name=" + postID + " value=delete> ") + ("<span class=subject>" + (subject || '') + "</span> ") + ("<span class='nameBlock" + capcodeClass + "'>") + emailStart + ("<span class=name>" + (name || '') + "</span>") + tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag + ' </span> ' + ("<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span> ") + "<span class='postNum'>" + ("<a href=" + (Build.path(boardID, threadID, postID)) + " title='Highlight this post'>No.</a>") + ("<a href='" + (g.VIEW === 'thread' && g.THREADID === threadID ? "javascript:quote(" + postID + ")" : Build.path(boardID, threadID, postID, 'q')) + "' title='Quote this post'>" + postID + "</a>") + pageIcon + sticky + closed + replyLink + '</span>' + '</div>' + (isOP ? '' : fileHTML) + ("<blockquote class=postMessage>" + (comment || '') + "</blockquote> ") + '</div>'
}); });
_ref = $$('.quotelink', container); _ref = $$('.quotelink', container);
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quote = _ref[_i]; quote = _ref[_i];
href = quote.getAttribute('href'); href = quote.getAttribute('href');
if (href[0] === '/') { if (href[0] !== '#') {
continue; continue;
} }
if (href[0] === '#') { quote.href = Build.path(boardID, threadID, href.slice(2));
href = "" + threadID + href;
}
quote.href = "/" + boardID + "/thread/" + href;
} }
return container; return container;
}, },
@ -3855,7 +3863,7 @@
return $.el('a', { return $.el('a', {
className: 'summary', className: 'summary',
textContent: text.join(' '), textContent: text.join(' '),
href: "/" + boardID + "/thread/" + threadID href: Build.path(boardID, threadID)
}); });
}, },
thread: function(board, data, full) { thread: function(board, data, full) {
@ -3891,12 +3899,12 @@
data = Index.liveThreadData[thread.ID]; data = Index.liveThreadData[thread.ID];
postCount = data.replies + 1; postCount = data.replies + 1;
fileCount = data.images + !!data.ext; fileCount = data.images + !!data.ext;
pageCount = Math.floor(Index.liveThreadData.keys.indexOf("" + thread.ID) / Index.threadsNumPerPage); pageCount = Math.floor(Index.liveThreadData.keys.indexOf("" + thread.ID) / Index.threadsNumPerPage) + 1;
subject = thread.OP.info.subject ? "<div class='subject'>" + thread.OP.info.subject + "</div>" : ''; subject = thread.OP.info.subject ? "<div class='subject'>" + thread.OP.info.subject + "</div>" : '';
comment = thread.OP.nodes.comment.innerHTML.replace(/(<br>\s*){2,}/g, '<br>'); comment = thread.OP.nodes.comment.innerHTML.replace(/(<br>\s*){2,}/g, '<br>');
root = $.el('div', { root = $.el('div', {
className: 'catalog-thread', className: 'catalog-thread',
innerHTML: "<a href=\"/" + thread.board + "/thread/" + thread.ID + "\" class=\"thumb\"></a><div class=\"thread-stats\" title=\"Post count / File count / Page count\"><span class=\"post-count\">" + postCount + "</span> / <span class=\"file-count\">" + fileCount + "</span> / <span class=\"page-count\">" + pageCount + "</span><span class=\"thread-icons\"></span></div>" + subject + "<div class=\"comment\">" + comment + "</div>" innerHTML: "<a href=\"" + (Build.path(thread.board.ID, thread.ID)) + "\" class=\"thumb\"></a><div class=\"thread-stats\" title=\"Post count / File count / Page count\"><span class=\"post-count\">" + postCount + "</span> / <span class=\"file-count\">" + fileCount + "</span> / <span class=\"page-count\">" + pageCount + "</span><span class=\"thread-icons\"></span></div>" + subject + "<div class=\"comment\">" + comment + "</div>"
}); });
root.dataset.fullID = thread.fullID; root.dataset.fullID = thread.fullID;
if (thread.isPinned) { if (thread.isPinned) {
@ -5368,7 +5376,7 @@
var a, frag, hash, text; var a, frag, hash, text;
frag = QuoteBacklink.frag.cloneNode(true); frag = QuoteBacklink.frag.cloneNode(true);
a = frag.lastElementChild; a = frag.lastElementChild;
a.href = "/" + quoter.board + "/thread/" + quoter.thread + "#p" + quoter; a.href = Build.path(quoter.board.ID, quoter.thread.ID, quoter.ID);
a.textContent = text = QuoteBacklink.funk(quoter.ID); a.textContent = text = QuoteBacklink.funk(quoter.ID);
if (quoter.isDead) { if (quoter.isDead) {
$.addClass(a, 'deadlink'); $.addClass(a, 'deadlink');
@ -5940,7 +5948,7 @@
quoteID = "" + boardID + "." + postID; quoteID = "" + boardID + "." + postID;
if (post = g.posts[quoteID]) { if (post = g.posts[quoteID]) {
a = $.el('a', { a = $.el('a', {
href: "/" + boardID + "/thread/" + post.thread + "#p" + postID, href: Build.path(boardID, post.thread.ID, postID),
className: post.isDead ? 'quotelink deadlink' : 'quotelink', className: post.isDead ? 'quotelink deadlink' : 'quotelink',
textContent: quote textContent: quote
}); });
@ -6284,7 +6292,7 @@
_ref = $$('br', frag); _ref = $$('br', frag);
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i]; node = _ref[_i];
if (node !== frag.lastElementChild) { if (node !== frag.lastChild) {
$.replace(node, $.tn('\n>')); $.replace(node, $.tn('\n>'));
} }
} }
@ -6953,7 +6961,7 @@
isReply: isReply, isReply: isReply,
threadID: threadID threadID: threadID
}); });
URL = threadID === postID ? "/" + g.BOARD + "/thread/" + threadID : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? "/" + g.BOARD + "/thread/" + threadID + "#p" + postID : void 0; URL = threadID === postID ? Build.path(g.BOARD.ID, threadID) : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? Build.path(g.BOARD.ID, threadID, postID) : void 0;
if (URL) { if (URL) {
if (Conf['Open Post in New Tab']) { if (Conf['Open Post in New Tab']) {
$.open(URL); $.open(URL);
@ -11134,7 +11142,7 @@
} }
return Redirect.data = o; return Redirect.data = o;
}, },
archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","vg","vp","vr","wsg"],"files":["a","biz","gd","diy","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i"],"files":["d","i"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":true,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["asp","cm","h","hc","hm","n","p","r","s","soc","y"],"files":["asp","cm","h","hc","hm","n","p","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]}], archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","vg","vp","vr","wsg"],"files":["a","biz","gd","diy","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i"],"files":["d","i"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":true,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["asp","cm","h","hc","hm","n","p","r","s","soc","y"],"files":["asp","cm","h","hc","hm","n","p","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]}],
to: function(dest, data) { to: function(dest, data) {
var archive; var archive;
archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID];
@ -12433,7 +12441,7 @@
if (g.VIEW !== 'index') { if (g.VIEW !== 'index') {
return; return;
} }
url = "/" + thread.board + "/thread/" + thread; url = Build.path(thread.board.ID, thread.ID);
if (tab) { if (tab) {
return $.open(url); return $.open(url);
} else { } else {
@ -13842,7 +13850,10 @@
} }
if (g.VIEW === 'thread') { if (g.VIEW === 'thread') {
g.THREADID = +pathname[3]; g.THREADID = +pathname[3];
if (pathname[2] !== 'thread' || pathname.length > 4) { if (pathname[4] != null) {
g.SLUG = pathname[4];
}
if (pathname[2] !== 'thread') {
pathname[2] = 'thread'; pathname[2] = 'thread';
history.replaceState(null, '', pathname.slice(0, 4).join('/') + location.hash); history.replaceState(null, '', pathname.slice(0, 4).join('/') + location.hash);
} }

View File

@ -105,7 +105,7 @@ a[href="javascript:;"] {
:root.bottom-header body { :root.bottom-header body {
margin-bottom: 2em; margin-bottom: 2em;
} }
body > .desktop:not(#boardNavDesktop):not(#boardNavDesktopFoot), body > .desktop:not(hr):not(.navLinks):not(#boardNavDesktop):not(#boardNavDesktopFoot),
:root.fourchan-x #navtopright, :root.fourchan-x #navtopright,
:root.fourchan-x #navbotright, :root.fourchan-x #navbotright,
:root.fourchan-x:not(.show-original-top-board-list) #boardNavDesktop, :root.fourchan-x:not(.show-original-top-board-list) #boardNavDesktop,

View File

@ -37,7 +37,7 @@
"grunt-contrib-concat": "~0.4.0", "grunt-contrib-concat": "~0.4.0",
"grunt-contrib-copy": "~0.5.0", "grunt-contrib-copy": "~0.5.0",
"grunt-contrib-watch": "~0.6.1", "grunt-contrib-watch": "~0.6.1",
"grunt-shell": "~0.6.4", "grunt-shell": "~0.7.0",
"load-grunt-tasks": "~0.4.0" "load-grunt-tasks": "~0.4.0"
}, },
"repository": { "repository": {

View File

@ -123,6 +123,6 @@
"https": true, "https": true,
"withCredentials": true, "withCredentials": true,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["a", "biz", "co", "d", "diy", "gd", "jp", "m", "s4s", "sci", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], "boards": ["a", "biz", "co", "d", "diy", "gd", "jp", "m", "s4s", "sci", "sp", "tg", "tv", "u", "vg", "vp", "vr", "wsg"],
"files": ["a", "biz", "d", "diy", "gd", "jp", "m", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"] "files": ["a", "biz", "d", "diy", "gd", "jp", "m", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"]
}] }]

View File

@ -15,6 +15,11 @@ Build =
thumbRotate: do -> thumbRotate: do ->
n = 0 n = 0
-> n = (n + 1) % 3 -> n = (n + 1) % 3
path: (boardID, threadID, postID, fragment) ->
path = "/#{boardID}/thread/#{threadID}"
path += "/#{g.SLUG}" if g.SLUG? and threadID is g.THREADID
path += "##{fragment or 'p'}#{postID}" if postID
path
postFromObject: (data, boardID) -> postFromObject: (data, boardID) ->
o = o =
# id # id
@ -178,9 +183,9 @@ Build =
'' ''
if isOP and g.VIEW is 'index' if isOP and g.VIEW is 'index'
pageNum = Index.liveThreadData.keys.indexOf("#{postID}") // Index.threadsNumPerPage pageNum = Index.liveThreadData.keys.indexOf("#{postID}") // Index.threadsNumPerPage + 1
pageIcon = " <span class=page-num title='This thread is on page #{pageNum} in the original index.'>Page #{pageNum}</span>" pageIcon = " <span class=page-num title='This thread is on page #{pageNum} in the original index.'>Page #{pageNum}</span>"
replyLink = " &nbsp; <span>[<a href='/#{boardID}/thread/#{threadID}' class=replylink>Reply</a>]</span>" replyLink = " &nbsp; <span>[<a href='#{Build.path boardID, threadID}' class=replylink>Reply</a>]</span>"
else else
pageIcon = '' pageIcon = ''
replyLink = '' replyLink = ''
@ -209,12 +214,12 @@ Build =
' </span> ' + ' </span> ' +
"<span class=dateTime data-utc=#{dateUTC}>#{date}</span> " + "<span class=dateTime data-utc=#{dateUTC}>#{date}</span> " +
"<span class='postNum'>" + "<span class='postNum'>" +
"<a href=#{"/#{boardID}/thread/#{threadID}#p#{postID}"} title='Highlight this post'>No.</a>" + "<a href=#{Build.path boardID, threadID, postID} title='Highlight this post'>No.</a>" +
"<a href='#{ "<a href='#{
if g.VIEW is 'thread' and g.THREADID is threadID if g.VIEW is 'thread' and g.THREADID is threadID
"javascript:quote(#{postID})" "javascript:quote(#{postID})"
else else
"/#{boardID}/thread/#{threadID}#q#{postID}" Build.path boardID, threadID, postID, 'q'
}' title='Quote this post'>#{postID}</a>" + }' title='Quote this post'>#{postID}</a>" +
pageIcon + sticky + closed + replyLink + pageIcon + sticky + closed + replyLink +
'</span>' + '</span>' +
@ -226,12 +231,11 @@ Build =
'</div>' '</div>'
# Fix pathnames # Fix quote pathnames in index or cross-{board,thread} posts
for quote in $$ '.quotelink', container for quote in $$ '.quotelink', container
href = quote.getAttribute 'href' href = quote.getAttribute 'href'
continue if href[0] is '/' # Cross-board quote, or board link continue unless href[0] is '#'
href = "#{threadID}#{href}" if href[0] is '#' quote.href = Build.path boardID, threadID, href[2..]
quote.href = "/#{boardID}/thread/#{href}" # Fix pathnames
container container
@ -243,7 +247,7 @@ Build =
$.el 'a', $.el 'a',
className: 'summary' className: 'summary'
textContent: text.join ' ' textContent: text.join ' '
href: "/#{boardID}/thread/#{threadID}" href: Build.path boardID, threadID
thread: (board, data, full) -> thread: (board, data, full) ->
Build.spoilerRange[board] = data.custom_spoiler Build.spoilerRange[board] = data.custom_spoiler
@ -277,7 +281,7 @@ Build =
postCount = data.replies + 1 postCount = data.replies + 1
fileCount = data.images + !!data.ext fileCount = data.images + !!data.ext
pageCount = Index.liveThreadData.keys.indexOf("#{thread.ID}") // Index.threadsNumPerPage pageCount = Index.liveThreadData.keys.indexOf("#{thread.ID}") // Index.threadsNumPerPage + 1
subject = if thread.OP.info.subject subject = if thread.OP.info.subject
"<div class='subject'>#{thread.OP.info.subject}</div>" "<div class='subject'>#{thread.OP.info.subject}</div>"

View File

@ -301,8 +301,8 @@ Index =
else else
'Show' 'Show'
Index.sort() Index.sort()
if Conf['Index Mode'] is 'paged' and Index.getCurrentPage() > 0 if Conf['Index Mode'] is 'paged' and Index.getCurrentPage() > 1
Index.pageNav 0 Index.pageNav 1
else else
Index.buildIndex() Index.buildIndex()
@ -360,7 +360,7 @@ Index =
return return
e.preventDefault() e.preventDefault()
return if Index.cb.indexNav a, true return if Index.cb.indexNav a, true
Index.userPageNav +a.pathname.split('/')[2] Index.userPageNav +a.pathname.split('/')[2] or 1
headerNav: (e) -> headerNav: (e) ->
a = e.target a = e.target
@ -411,7 +411,7 @@ Index =
pageNav: (pageNum) -> pageNav: (pageNum) ->
return if Index.currentPage is pageNum and not Index.root.parentElement return if Index.currentPage is pageNum and not Index.root.parentElement
Navigate.pushState if pageNum is 0 then './' else pageNum Navigate.pushState if pageNum is 1 then './' else pageNum
Index.pageLoad pageNum Index.pageLoad pageNum
pageLoad: (pageNum) -> pageLoad: (pageNum) ->
@ -603,7 +603,7 @@ Index =
Index.liveThreadData.forEach (threadData) -> Index.liveThreadData.forEach (threadData) ->
threadRoot = Build.thread g.BOARD, threadData threadRoot = Build.thread g.BOARD, threadData
if thread = g.BOARD.threads[threadData.no] if thread = g.BOARD.threads[threadData.no]
thread.setPage i // Index.threadsNumPerPage thread.setPage i // Index.threadsNumPerPage + 1
thread.setCount 'post', threadData.replies + 1, threadData.bumplimit thread.setCount 'post', threadData.replies + 1, threadData.bumplimit
thread.setCount 'file', threadData.images + !!threadData.ext, threadData.imagelimit thread.setCount 'file', threadData.images + !!threadData.ext, threadData.imagelimit
thread.setStatus 'Sticky', !!threadData.sticky thread.setStatus 'Sticky', !!threadData.sticky
@ -741,7 +741,7 @@ Index =
nodes = [] nodes = []
switch Conf['Index Mode'] switch Conf['Index Mode']
when 'paged', 'infinite' when 'paged', 'infinite'
pageNum = Index.getCurrentPage() pageNum = Index.getCurrentPage() - 1
threadsPerPage = Index.getThreadsNumPerPage() threadsPerPage = Index.getThreadsNumPerPage()
threads = [] threads = []

View File

@ -18,7 +18,8 @@ Main =
return Index.catalogSwitch() return Index.catalogSwitch()
if g.VIEW is 'thread' if g.VIEW is 'thread'
g.THREADID = +pathname[3] g.THREADID = +pathname[3]
if pathname[2] isnt 'thread' or pathname.length > 4 g.SLUG = pathname[4] if pathname[4]?
if pathname[2] isnt 'thread'
pathname[2] = 'thread' pathname[2] = 'thread'
history.replaceState null, '', pathname.slice(0,4).join('/') + location.hash history.replaceState null, '', pathname.slice(0,4).join('/') + location.hash

View File

@ -1,4 +1,4 @@
<a href="/#{thread.board}/thread/#{thread.ID}" class="thumb"></a> <a href="#{Build.path thread.board.ID, thread.ID}" class="thumb"></a>
<div class="thread-stats" title="Post count / File count / Page count"> <div class="thread-stats" title="Post count / File count / Page count">
<span class="post-count">#{postCount}</span> / <span class="file-count">#{fileCount}</span> / <span class="page-count">#{pageCount}</span> <span class="post-count">#{postCount}</span> / <span class="file-count">#{fileCount}</span> / <span class="page-count">#{pageCount}</span>
<span class="thread-icons"></span> <span class="thread-icons"></span>

View File

@ -101,8 +101,9 @@ class Post
return unless match = quotelink.href.match /// return unless match = quotelink.href.match ///
boards\.4chan\.org/ boards\.4chan\.org/
([^/]+) # boardID ([^/]+) # boardID
/(?:res|thread)/\d+#p /(res|thread)/\d+
(\d+) # postID (.*)? # thread slug
\#p(\d+) # postID
$ $
/// ///
@ -138,7 +139,7 @@ class Post
@file.isImage = /(jpg|png|gif)$/i.test @file.URL @file.isImage = /(jpg|png|gif)$/i.test @file.URL
@file.isVideo = /webm$/i.test @file.URL @file.isVideo = /webm$/i.test @file.URL
if @file.isImage or @file.isVideo if @file.isImage or @file.isVideo
@file.dimensions = fileText.childNodes[2].textContent.match(/\d+x\d+/)?[0] @file.dimensions = fileText.childNodes[2].data.match(/\d+x\d+/)[0]
@file.name = if !@file.isSpoiler and nameNode = $ 'a', fileText @file.name = if !@file.isSpoiler and nameNode = $ 'a', fileText
nameNode.title or nameNode.textContent nameNode.title or nameNode.textContent
else else
@ -155,7 +156,7 @@ class Post
cleanup: (root, post) -> cleanup: (root, post) ->
for node in $$ '.mobile', root for node in $$ '.mobile', root
$.rm node $.rm node
for node in $$ '[id]', post for node in $$ '[id]:not(.exif)', post
node.removeAttribute 'id' node.removeAttribute 'id'
for node in $$ '.desktop', root for node in $$ '.desktop', root
$.rmClass node, 'desktop' $.rmClass node, 'desktop'

View File

@ -247,7 +247,7 @@ Keybinds =
open: (thread, tab) -> open: (thread, tab) ->
return if g.VIEW isnt 'index' return if g.VIEW isnt 'index'
url = "/#{thread.board}/thread/#{thread}" url = Build.path thread.board.ID, thread.ID
if tab if tab
$.open url $.open url
else else

View File

@ -228,7 +228,7 @@ QR =
$.prepend frag, $.tn '[code]' $.prepend frag, $.tn '[code]'
$.add frag, $.tn '[/code]' $.add frag, $.tn '[/code]'
for node in $$ 'br', frag for node in $$ 'br', frag
$.replace node, $.tn '\n>' unless node is frag.lastElementChild $.replace node, $.tn '\n>' unless node is frag.lastChild
for node in $$ 's', frag for node in $$ 's', frag
$.replace node, [$.tn('[spoiler]'), node.childNodes..., $.tn '[/spoiler]'] $.replace node, [$.tn('[spoiler]'), node.childNodes..., $.tn '[/spoiler]']
for node in $$ '.prettyprint', frag for node in $$ '.prettyprint', frag
@ -871,9 +871,9 @@ QR =
QR.cooldown.set {req, post, isReply, threadID} QR.cooldown.set {req, post, isReply, threadID}
URL = if threadID is postID # new thread URL = if threadID is postID # new thread
"/#{g.BOARD}/thread/#{threadID}" Build.path g.BOARD.ID, threadID
else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index
"/#{g.BOARD}/thread/#{threadID}#p#{postID}" Build.path g.BOARD.ID, threadID, postID
if URL if URL
if Conf['Open Post in New Tab'] if Conf['Open Post in New Tab']
$.open URL $.open URL

View File

@ -55,7 +55,7 @@ QuoteBacklink =
buildBacklink: (quoted, quoter) -> buildBacklink: (quoted, quoter) ->
frag = QuoteBacklink.frag.cloneNode true frag = QuoteBacklink.frag.cloneNode true
a = frag.lastElementChild a = frag.lastElementChild
a.href = "/#{quoter.board}/thread/#{quoter.thread}#p#{quoter}" a.href = Build.path quoter.board.ID, quoter.thread.ID, quoter.ID
a.textContent = text = QuoteBacklink.funk quoter.ID a.textContent = text = QuoteBacklink.funk quoter.ID
if quoter.isDead if quoter.isDead
$.addClass a, 'deadlink' $.addClass a, 'deadlink'

View File

@ -44,7 +44,7 @@ Quotify =
# Don't add 'deadlink' when quotifying in an archived post, # Don't add 'deadlink' when quotifying in an archived post,
# and we don't know if the post died yet. # and we don't know if the post died yet.
a = $.el 'a', a = $.el 'a',
href: "/#{boardID}/thread/#{post.thread}#p#{postID}" href: Build.path boardID, post.thread.ID, postID
className: if post.isDead then 'quotelink deadlink' else 'quotelink' className: if post.isDead then 'quotelink deadlink' else 'quotelink'
textContent: quote textContent: quote
$.extend a.dataset, {boardID, threadID: post.thread.ID, postID} $.extend a.dataset, {boardID, threadID: post.thread.ID, postID}