diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b3adb662..7c6345ca3 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+**MayhemYDG**
+- Update 4chan namespaces support.
+- Better handling of webm playback errors.
+
### v1.7.8
*2014-04-12*
diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js
index e6ba2cb3f..19f814579 100644
--- a/builds/4chan-X.user.js
+++ b/builds/4chan-X.user.js
@@ -1175,7 +1175,7 @@
Post.prototype.parseQuote = function(quotelink) {
var fullID, match;
- if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/res\/\d+#p(\d+)$/))) {
+ if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/thread\/\d+#p(\d+)$/))) {
return;
}
this.nodes.quotelinks.push(quotelink);
@@ -1209,7 +1209,7 @@
size *= 1024;
}
this.file.sizeInBytes = size;
- this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//t.4cdn.org/" + this.board + "/thumb/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg";
+ this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//t.4cdn.org/" + this.board + "/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg";
this.file.name = (nameNode = $('span', fileText)) ? nameNode.title || nameNode.textContent : fileText.title;
this.file.isImage = /(jpg|png|gif)$/i.test(this.file.name);
this.file.isVideo = /webm$/i.test(this.file.name);
@@ -1941,23 +1941,6 @@
Polyfill = {
init: function() {},
- notificationPermission: function() {
- if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) {
- return;
- }
- return Object.defineProperty(Notification, 'permission', {
- get: function() {
- switch (webkitNotifications.checkPermission()) {
- case 0:
- return 'granted';
- case 1:
- return 'default';
- case 2:
- return 'denied';
- }
- }
- });
- },
toBlob: function() {
var _base;
return (_base = HTMLCanvasElement.prototype).toBlob || (_base.toBlob = function(cb) {
@@ -1972,26 +1955,6 @@
type: 'image/png'
}));
});
- },
- visibility: function() {
- if ('visibilityState' in d) {
- return;
- }
- Object.defineProperties(HTMLDocument.prototype, {
- visibilityState: {
- get: function() {
- return this.webkitVisibilityState;
- }
- },
- hidden: {
- get: function() {
- return this.webkitHidden;
- }
- }
- });
- return $.on(d, 'webkitvisibilitychange', function() {
- return $.event('visibilitychange');
- });
}
};
@@ -3694,12 +3657,12 @@
o.file = {
name: data.filename + data.ext,
timestamp: "" + data.tim + data.ext,
- url: boardID === 'f' ? "//i.4cdn.org/" + boardID + "/src/" + data.filename + data.ext : "//i.4cdn.org/" + boardID + "/src/" + data.tim + data.ext,
+ url: boardID === 'f' ? "//i.4cdn.org/" + boardID + "/" + data.filename + data.ext : "//i.4cdn.org/" + boardID + "/" + data.tim + data.ext,
height: data.h,
width: data.w,
MD5: data.md5,
size: data.fsize,
- turl: "//" + (Build.thumbRotate()) + ".t.4cdn.org/" + boardID + "/thumb/" + data.tim + "s.jpg",
+ turl: "//" + (Build.thumbRotate()) + ".t.4cdn.org/" + boardID + "/" + data.tim + "s.jpg",
theight: data.tn_h,
twidth: data.tn_w,
isSpoiler: !!data.spoiler,
@@ -3786,14 +3749,14 @@
if (isOP && g.VIEW === 'index') {
pageNum = Math.floor(Index.liveThreadData.keys.indexOf("" + postID) / Index.threadsNumPerPage);
pageIcon = " Page " + pageNum + "";
- replyLink = " [Reply]";
+ replyLink = " [Reply]";
} else {
pageIcon = replyLink = '';
}
container = $.el('div', {
id: "pc" + postID,
className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
- innerHTML: "" + (isOP ? '' : "
>>
") + "" + (isOP ? fileHTML : '') + "
" + ' ' + "
" + (subject || '') + "" + ' ' + "
" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag) + "" + " " + "
" + date + "" + ' ' + "
No." + postID + "" + (pageIcon + sticky + closed + replyLink) + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + ' ' + "
"
+ innerHTML: (isOP ? '' : ">>
") + ("") + (isOP ? fileHTML : '') + "
" + ("
") + ("
" + (subject || '') + " ") + ("
") + emailStart + ("" + (name || '') + "") + tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag + ' ' + ("
" + date + " ") + "
" + ("No.") + ("" + postID + "") + pageIcon + sticky + closed + replyLink + '' + '
' + (isOP ? '' : fileHTML) + ("
" + (comment || '') + "
") + '
'
});
_ref = $$('.quotelink', container);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -3802,7 +3765,7 @@
if (href[0] === '/') {
continue;
}
- quote.href = "/" + boardID + "/res/" + href;
+ quote.href = "/" + boardID + "/thread/" + href;
}
return container;
},
@@ -3817,7 +3780,7 @@
return $.el('a', {
className: 'summary',
textContent: text.join(' '),
- href: "/" + boardID + "/res/" + threadID
+ href: "/" + boardID + "/thread/" + threadID
});
},
thread: function(board, data, full) {
@@ -3858,7 +3821,7 @@
comment = thread.OP.nodes.comment.innerHTML.replace(/(
\s*){2,}/g, '
');
root = $.el('div', {
className: 'catalog-thread',
- innerHTML: "" + postCount + " / " + fileCount + " / " + pageCount + "
" + subject + ""
+ innerHTML: "" + postCount + " / " + fileCount + " / " + pageCount + "
" + subject + ""
});
root.dataset.fullID = thread.fullID;
if (thread.isPinned) {
@@ -4016,7 +3979,7 @@
}
root.textContent = "Loading post No." + postID + "...";
if (threadID) {
- return $.cache("//a.4cdn.org/" + boardID + "/res/" + threadID + ".json", function() {
+ 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', {
@@ -4150,7 +4113,7 @@
width: data.media.media_w,
MD5: data.media.media_hash,
size: data.media.media_size,
- turl: data.media.thumb_link || ("//t.4cdn.org/" + boardID + "/thumb/" + data.media.preview_orig),
+ turl: data.media.thumb_link || ("//t.4cdn.org/" + boardID + "/" + data.media.preview_orig),
theight: data.media.preview_h,
twidth: data.media.preview_w,
isSpoiler: data.media.spoiler === '1'
@@ -4933,27 +4896,10 @@
className: 'show-post-button fa fa-plus-square-o',
href: 'javascript:;'
});
- Post.callbacks.push({
+ return Post.callbacks.push({
name: 'Post Hiding',
cb: this.node
});
- return $.get('hiddenThreads', null, function(_arg) {
- var board, boardID, hiddenThreads, threadID, val, _base, _base1, _ref;
- hiddenThreads = _arg.hiddenThreads;
- if (!hiddenThreads) {
- return;
- }
- _ref = hiddenThreads.boards;
- for (boardID in _ref) {
- board = _ref[boardID];
- for (threadID in board) {
- val = board[threadID];
- ((_base = ((_base1 = PostHiding.db.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[threadID] = val;
- }
- }
- PostHiding.db.save();
- return $["delete"]('hiddenThreads');
- });
},
node: function() {
var a, data, label;
@@ -5354,7 +5300,7 @@
var a, frag, hash, text;
frag = QuoteBacklink.frag.cloneNode(true);
a = frag.lastElementChild;
- a.href = "/" + quoter.board + "/res/" + quoter.thread + "#p" + quoter;
+ a.href = "/" + quoter.board + "/thread/" + quoter.thread + "#p" + quoter;
a.textContent = text = QuoteBacklink.funk(quoter.ID);
if (quoter.isDead) {
$.addClass(a, 'deadlink');
@@ -5926,7 +5872,7 @@
quoteID = "" + boardID + "." + postID;
if (post = g.posts[quoteID]) {
a = $.el('a', {
- href: "/" + boardID + "/res/" + post.thread + "#p" + postID,
+ href: "/" + boardID + "/thread/" + post.thread + "#p" + postID,
className: post.isDead ? 'quotelink deadlink' : 'quotelink',
textContent: quote
});
@@ -6254,7 +6200,9 @@
_ref = $$('br', frag);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
- $.replace(node, $.tn('\n>'));
+ if (node !== frag.lastElementChild) {
+ $.replace(node, $.tn('\n>'));
+ }
}
_ref1 = $$('s', frag);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
@@ -6866,7 +6814,7 @@
isReply: isReply,
threadID: threadID
});
- URL = threadID === postID ? "/" + g.BOARD + "/res/" + threadID : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? "/" + g.BOARD + "/res/" + threadID + "#p" + postID : void 0;
+ 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;
if (URL) {
if (Conf['Open Post in New Tab']) {
$.open(URL);
@@ -8092,8 +8040,12 @@
delete post.file.videoControls;
}
$.rmClass(post.nodes.root, 'expanded-image');
- $.rmClass(thumb, 'expanding');
- return post.file.isExpanded = false;
+ $.rmClass(post.file.thumb, 'expanding');
+ delete post.file.isExpanding;
+ delete post.file.isExpanded;
+ if (post.file.isVideo && post.file.fullImage) {
+ return post.file.fullImage.pause();
+ }
},
expand: function(post, src, disableAutoplay) {
var el, isVideo, thumb, _ref;
@@ -8209,19 +8161,40 @@
return $.add(file.text, file.videoControls);
},
error: function() {
- var URL, post, src, timeoutID;
+ var URL, error, post, src, timeoutID;
post = Get.postFromNode(this);
$.rm(this);
+ delete post.file.isReady;
delete post.file.fullImage;
if (!($.hasClass(post.file.thumb, 'expanding') || $.hasClass(post.nodes.root, 'expanded-image'))) {
return;
}
ImageExpand.contract(post);
+ if (this.error && this.error.code !== this.error.MEDIA_ERR_NETWORK) {
+ error = (function() {
+ switch (this.error.code) {
+ case 1:
+ return 'MEDIA_ERR_ABORTED';
+ case 3:
+ return 'MEDIA_ERR_DECODE';
+ case 4:
+ return 'MEDIA_ERR_SRC_NOT_SUPPORTED';
+ case 5:
+ return 'MEDIA_ERR_ENCRYPTED';
+ }
+ }).call(this);
+ post.file.error = $.el('div', {
+ textContent: "Playback error: " + error,
+ className: 'warning'
+ });
+ $.after(post.file.thumb, post.file.error);
+ return;
+ }
src = this.src.split('/');
if (src[2] === 'i.4cdn.org') {
URL = Redirect.to('file', {
boardID: src[3],
- filename: src[5]
+ filename: src[4].replace(/\?.+$/, '')
});
if (URL) {
setTimeout(ImageExpand.expand, 10000, post, URL);
@@ -8232,7 +8205,7 @@
}
}
timeoutID = setTimeout(ImageExpand.expand, 10000, post);
- return $.ajax("//a.4cdn.org/" + post.board + "/res/" + post.thread + ".json", {
+ return $.ajax("//a.4cdn.org/" + post.board + "/thread/" + post.thread + ".json", {
onload: function() {
var postObj, _i, _len, _ref;
if (this.status !== 200) {
@@ -8367,10 +8340,11 @@
el: el,
latestEvent: e,
endEvents: 'mouseout click',
- asapTest: function() {
- return el.videoHeight || el.naturalHeight;
+ asapTest: post.file.isImage ? function() {
+ return el.naturalHeight;
+ } : function() {
+ return el.readyState >= el.HAVE_CURRENT_DATA;
},
- noRemove: true,
cb: function() {
if (isVideo) {
el.pause();
@@ -8391,7 +8365,7 @@
if (src[2] === 'i.4cdn.org') {
URL = Redirect.to('file', {
boardID: src[3],
- filename: src[5].replace(/\?.+$/, '')
+ filename: src[4].replace(/\?.+$/, '')
});
if (URL) {
this.src = URL;
@@ -8406,7 +8380,7 @@
return _this.src = post.file.URL + '?' + Date.now();
};
})(this)), 3000);
- return $.ajax("//a.4cdn.org/" + post.board + "/res/" + post.thread + ".json", {
+ return $.ajax("//a.4cdn.org/" + post.board + "/thread/" + post.thread + ".json", {
onload: function() {
var postObj, _i, _len, _ref;
if (this.status !== 200) {
@@ -10007,9 +9981,12 @@
if ((_ref = ThreadUpdater.req) != null) {
_ref.abort();
}
- url = "//a.4cdn.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json";
+ url = "//a.4cdn.org/" + ThreadUpdater.thread.board + "/thread/" + ThreadUpdater.thread + ".json";
return ThreadUpdater.req = $.ajax(url, {
- onloadend: ThreadUpdater.cb.load
+ onabort: ThreadUpdater.cb.load,
+ onloadend: ThreadUpdater.cb.load,
+ ontimeout: ThreadUpdater.cb.load,
+ timeout: $.MINUTE
}, {
whenModified: true
});
@@ -10317,7 +10294,7 @@
}
fetchCount = ThreadWatcher.fetchCount;
fetchCount.fetching++;
- return $.ajax("//a.4cdn.org/" + boardID + "/res/" + threadID + ".json", {
+ return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", {
onloadend: function() {
var status;
fetchCount.fetched++;
@@ -10385,7 +10362,7 @@
});
}
link = $.el('a', {
- href: href || ("/" + boardID + "/res/" + threadID),
+ href: href || ("/" + boardID + "/thread/" + threadID),
textContent: data.excerpt,
title: data.excerpt
});
@@ -11541,7 +11518,7 @@
var status;
ExpandThread.statuses[thread] = status = {};
a.textContent = ExpandThread.text.apply(ExpandThread, ['...'].concat(__slice.call(a.textContent.match(/\d+/g))));
- return status.req = $.cache("//a.4cdn.org/" + thread.board + "/res/" + thread + ".json", function() {
+ return status.req = $.cache("//a.4cdn.org/" + thread.board + "/thread/" + thread + ".json", function() {
delete status.req;
return ExpandThread.parse(this, thread, a);
});
@@ -12192,7 +12169,7 @@
if (g.VIEW !== 'index') {
return;
}
- url = "/" + thread.board + "/res/" + thread;
+ url = "/" + thread.board + "/thread/" + thread;
if (tab) {
return $.open(url);
} else {
@@ -13491,7 +13468,7 @@
data = {
isReply: true,
file: {
- URL: '//i.4cdn.org/g/src/1334437723720.jpg',
+ URL: '//i.4cdn.org/g/1334437723720.jpg',
name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg',
size: '276 KB',
sizeInBytes: 276 * 1024,
diff --git a/builds/crx/manifest.json b/builds/crx/manifest.json
index edcd12791..dc981f65a 100755
--- a/builds/crx/manifest.json
+++ b/builds/crx/manifest.json
@@ -16,7 +16,7 @@
}],
"homepage_url": "https://github.com/ccd0/4chan-x",
"update_url": "https://ccd0.github.io/4chan-x/builds/updates.xml",
- "minimum_chrome_version": "32",
+ "minimum_chrome_version": "33",
"permissions": [
"storage",
"http://*/",
diff --git a/builds/crx/script.js b/builds/crx/script.js
index 1f0cdd71c..09d1f8a1b 100644
--- a/builds/crx/script.js
+++ b/builds/crx/script.js
@@ -1231,7 +1231,7 @@
Post.prototype.parseQuote = function(quotelink) {
var fullID, match;
- if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/res\/\d+#p(\d+)$/))) {
+ if (!(match = quotelink.href.match(/boards\.4chan\.org\/([^\/]+)\/thread\/\d+#p(\d+)$/))) {
return;
}
this.nodes.quotelinks.push(quotelink);
@@ -1265,7 +1265,7 @@
size *= 1024;
}
this.file.sizeInBytes = size;
- this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//t.4cdn.org/" + this.board + "/thumb/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg";
+ this.file.thumbURL = that.isArchived ? thumb.src : "" + location.protocol + "//t.4cdn.org/" + this.board + "/" + (this.file.URL.match(/(\d+)\./)[1]) + "s.jpg";
this.file.name = (nameNode = $('span', fileText)) ? nameNode.title || nameNode.textContent : fileText.title;
this.file.name = this.file.name.replace(/%22/g, '"');
this.file.isImage = /(jpg|png|gif)$/i.test(this.file.name);
@@ -1998,26 +1998,7 @@
Polyfill = {
init: function() {
- this.notificationPermission();
- this.toBlob();
- return this.visibility();
- },
- notificationPermission: function() {
- if (!window.Notification || 'permission' in Notification || !window.webkitNotifications) {
- return;
- }
- return Object.defineProperty(Notification, 'permission', {
- get: function() {
- switch (webkitNotifications.checkPermission()) {
- case 0:
- return 'granted';
- case 1:
- return 'default';
- case 2:
- return 'denied';
- }
- }
- });
+ return this.toBlob();
},
toBlob: function() {
var _base;
@@ -2033,26 +2014,6 @@
type: 'image/png'
}));
});
- },
- visibility: function() {
- if ('visibilityState' in d) {
- return;
- }
- Object.defineProperties(HTMLDocument.prototype, {
- visibilityState: {
- get: function() {
- return this.webkitVisibilityState;
- }
- },
- hidden: {
- get: function() {
- return this.webkitHidden;
- }
- }
- });
- return $.on(d, 'webkitvisibilitychange', function() {
- return $.event('visibilitychange');
- });
}
};
@@ -3755,12 +3716,12 @@
o.file = {
name: data.filename + data.ext,
timestamp: "" + data.tim + data.ext,
- url: boardID === 'f' ? "//i.4cdn.org/" + boardID + "/src/" + data.filename + data.ext : "//i.4cdn.org/" + boardID + "/src/" + data.tim + data.ext,
+ url: boardID === 'f' ? "//i.4cdn.org/" + boardID + "/" + data.filename + data.ext : "//i.4cdn.org/" + boardID + "/" + data.tim + data.ext,
height: data.h,
width: data.w,
MD5: data.md5,
size: data.fsize,
- turl: "//" + (Build.thumbRotate()) + ".t.4cdn.org/" + boardID + "/thumb/" + data.tim + "s.jpg",
+ turl: "//" + (Build.thumbRotate()) + ".t.4cdn.org/" + boardID + "/" + data.tim + "s.jpg",
theight: data.tn_h,
twidth: data.tn_w,
isSpoiler: !!data.spoiler,
@@ -3847,14 +3808,14 @@
if (isOP && g.VIEW === 'index') {
pageNum = Math.floor(Index.liveThreadData.keys.indexOf("" + postID) / Index.threadsNumPerPage);
pageIcon = " Page " + pageNum + "";
- replyLink = " [Reply]";
+ replyLink = " [Reply]";
} else {
pageIcon = replyLink = '';
}
container = $.el('div', {
id: "pc" + postID,
className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
- innerHTML: "" + (isOP ? '' : ">>
") + "" + (isOP ? fileHTML : '') + "
" + ' ' + "
" + (subject || '') + "" + ' ' + "
" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag) + "" + " " + "
" + date + "" + ' ' + "
No." + postID + "" + (pageIcon + sticky + closed + replyLink) + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + ' ' + "
"
+ innerHTML: (isOP ? '' : ">>
") + ("") + (isOP ? fileHTML : '') + "
" + ("
") + ("
" + (subject || '') + " ") + ("
") + emailStart + ("" + (name || '') + "") + tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag + ' ' + ("
" + date + " ") + "
" + ("No.") + ("" + postID + "") + pageIcon + sticky + closed + replyLink + '' + '
' + (isOP ? '' : fileHTML) + ("
" + (comment || '') + "
") + '
'
});
_ref = $$('.quotelink', container);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -3863,7 +3824,7 @@
if (href[0] === '/') {
continue;
}
- quote.href = "/" + boardID + "/res/" + href;
+ quote.href = "/" + boardID + "/thread/" + href;
}
return container;
},
@@ -3878,7 +3839,7 @@
return $.el('a', {
className: 'summary',
textContent: text.join(' '),
- href: "/" + boardID + "/res/" + threadID
+ href: "/" + boardID + "/thread/" + threadID
});
},
thread: function(board, data, full) {
@@ -3919,7 +3880,7 @@
comment = thread.OP.nodes.comment.innerHTML.replace(/(
\s*){2,}/g, '
');
root = $.el('div', {
className: 'catalog-thread',
- innerHTML: "" + postCount + " / " + fileCount + " / " + pageCount + "
" + subject + ""
+ innerHTML: "" + postCount + " / " + fileCount + " / " + pageCount + "
" + subject + ""
});
root.dataset.fullID = thread.fullID;
if (thread.isPinned) {
@@ -4077,7 +4038,7 @@
}
root.textContent = "Loading post No." + postID + "...";
if (threadID) {
- return $.cache("//a.4cdn.org/" + boardID + "/res/" + threadID + ".json", function() {
+ 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', {
@@ -4211,7 +4172,7 @@
width: data.media.media_w,
MD5: data.media.media_hash,
size: data.media.media_size,
- turl: data.media.thumb_link || ("//t.4cdn.org/" + boardID + "/thumb/" + data.media.preview_orig),
+ turl: data.media.thumb_link || ("//t.4cdn.org/" + boardID + "/" + data.media.preview_orig),
theight: data.media.preview_h,
twidth: data.media.preview_w,
isSpoiler: data.media.spoiler === '1'
@@ -4987,27 +4948,10 @@
className: 'show-post-button fa fa-plus-square-o',
href: 'javascript:;'
});
- Post.callbacks.push({
+ return Post.callbacks.push({
name: 'Post Hiding',
cb: this.node
});
- return $.get('hiddenThreads', null, function(_arg) {
- var board, boardID, hiddenThreads, threadID, val, _base, _base1, _ref;
- hiddenThreads = _arg.hiddenThreads;
- if (!hiddenThreads) {
- return;
- }
- _ref = hiddenThreads.boards;
- for (boardID in _ref) {
- board = _ref[boardID];
- for (threadID in board) {
- val = board[threadID];
- ((_base = ((_base1 = PostHiding.db.data.boards)[boardID] || (_base1[boardID] = {})))[threadID] || (_base[threadID] = {}))[threadID] = val;
- }
- }
- PostHiding.db.save();
- return $["delete"]('hiddenThreads');
- });
},
node: function() {
var a, data, label;
@@ -5408,7 +5352,7 @@
var a, frag, hash, text;
frag = QuoteBacklink.frag.cloneNode(true);
a = frag.lastElementChild;
- a.href = "/" + quoter.board + "/res/" + quoter.thread + "#p" + quoter;
+ a.href = "/" + quoter.board + "/thread/" + quoter.thread + "#p" + quoter;
a.textContent = text = QuoteBacklink.funk(quoter.ID);
if (quoter.isDead) {
$.addClass(a, 'deadlink');
@@ -5980,7 +5924,7 @@
quoteID = "" + boardID + "." + postID;
if (post = g.posts[quoteID]) {
a = $.el('a', {
- href: "/" + boardID + "/res/" + post.thread + "#p" + postID,
+ href: "/" + boardID + "/thread/" + post.thread + "#p" + postID,
className: post.isDead ? 'quotelink deadlink' : 'quotelink',
textContent: quote
});
@@ -6318,7 +6262,9 @@
_ref = $$('br', frag);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
- $.replace(node, $.tn('\n>'));
+ if (node !== frag.lastElementChild) {
+ $.replace(node, $.tn('\n>'));
+ }
}
_ref1 = $$('s', frag);
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
@@ -6911,7 +6857,7 @@
isReply: isReply,
threadID: threadID
});
- URL = threadID === postID ? "/" + g.BOARD + "/res/" + threadID : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? "/" + g.BOARD + "/res/" + threadID + "#p" + postID : void 0;
+ 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;
if (URL) {
if (Conf['Open Post in New Tab']) {
$.open(URL);
@@ -8131,8 +8077,12 @@
delete post.file.videoControls;
}
$.rmClass(post.nodes.root, 'expanded-image');
- $.rmClass(thumb, 'expanding');
- return post.file.isExpanded = false;
+ $.rmClass(post.file.thumb, 'expanding');
+ delete post.file.isExpanding;
+ delete post.file.isExpanded;
+ if (post.file.isVideo && post.file.fullImage) {
+ return post.file.fullImage.pause();
+ }
},
expand: function(post, src, disableAutoplay) {
var el, isVideo, thumb, _ref;
@@ -8248,19 +8198,40 @@
return $.add(file.text, file.videoControls);
},
error: function() {
- var URL, post, src, timeoutID;
+ var URL, error, post, src, timeoutID;
post = Get.postFromNode(this);
$.rm(this);
+ delete post.file.isReady;
delete post.file.fullImage;
if (!($.hasClass(post.file.thumb, 'expanding') || $.hasClass(post.nodes.root, 'expanded-image'))) {
return;
}
ImageExpand.contract(post);
+ if (this.error && this.error.code !== this.error.MEDIA_ERR_NETWORK) {
+ error = (function() {
+ switch (this.error.code) {
+ case 1:
+ return 'MEDIA_ERR_ABORTED';
+ case 3:
+ return 'MEDIA_ERR_DECODE';
+ case 4:
+ return 'MEDIA_ERR_SRC_NOT_SUPPORTED';
+ case 5:
+ return 'MEDIA_ERR_ENCRYPTED';
+ }
+ }).call(this);
+ post.file.error = $.el('div', {
+ textContent: "Playback error: " + error,
+ className: 'warning'
+ });
+ $.after(post.file.thumb, post.file.error);
+ return;
+ }
src = this.src.split('/');
if (src[2] === 'i.4cdn.org') {
URL = Redirect.to('file', {
boardID: src[3],
- filename: src[5]
+ filename: src[4].replace(/\?.+$/, '')
});
if (URL) {
setTimeout(ImageExpand.expand, 10000, post, URL);
@@ -8395,10 +8366,11 @@
el: el,
latestEvent: e,
endEvents: 'mouseout click',
- asapTest: function() {
- return el.videoHeight || el.naturalHeight;
+ asapTest: post.file.isImage ? function() {
+ return el.naturalHeight;
+ } : function() {
+ return el.readyState >= el.HAVE_CURRENT_DATA;
},
- noRemove: true,
cb: function() {
if (isVideo) {
el.pause();
@@ -8419,7 +8391,7 @@
if (src[2] === 'i.4cdn.org') {
URL = Redirect.to('file', {
boardID: src[3],
- filename: src[5].replace(/\?.+$/, '')
+ filename: src[4].replace(/\?.+$/, '')
});
if (URL) {
this.src = URL;
@@ -10024,9 +9996,12 @@
if ((_ref = ThreadUpdater.req) != null) {
_ref.abort();
}
- url = "//a.4cdn.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json";
+ url = "//a.4cdn.org/" + ThreadUpdater.thread.board + "/thread/" + ThreadUpdater.thread + ".json";
return ThreadUpdater.req = $.ajax(url, {
- onloadend: ThreadUpdater.cb.load
+ onabort: ThreadUpdater.cb.load,
+ onloadend: ThreadUpdater.cb.load,
+ ontimeout: ThreadUpdater.cb.load,
+ timeout: $.MINUTE
}, {
whenModified: true
});
@@ -10334,7 +10309,7 @@
}
fetchCount = ThreadWatcher.fetchCount;
fetchCount.fetching++;
- return $.ajax("//a.4cdn.org/" + boardID + "/res/" + threadID + ".json", {
+ return $.ajax("//a.4cdn.org/" + boardID + "/thread/" + threadID + ".json", {
onloadend: function() {
var status;
fetchCount.fetched++;
@@ -10402,7 +10377,7 @@
});
}
link = $.el('a', {
- href: href || ("/" + boardID + "/res/" + threadID),
+ href: href || ("/" + boardID + "/thread/" + threadID),
textContent: data.excerpt,
title: data.excerpt
});
@@ -11557,7 +11532,7 @@
var status;
ExpandThread.statuses[thread] = status = {};
a.textContent = ExpandThread.text.apply(ExpandThread, ['...'].concat(__slice.call(a.textContent.match(/\d+/g))));
- return status.req = $.cache("//a.4cdn.org/" + thread.board + "/res/" + thread + ".json", function() {
+ return status.req = $.cache("//a.4cdn.org/" + thread.board + "/thread/" + thread + ".json", function() {
delete status.req;
return ExpandThread.parse(this, thread, a);
});
@@ -12208,7 +12183,7 @@
if (g.VIEW !== 'index') {
return;
}
- url = "/" + thread.board + "/res/" + thread;
+ url = "/" + thread.board + "/thread/" + thread;
if (tab) {
return $.open(url);
} else {
@@ -13510,7 +13485,7 @@
data = {
isReply: true,
file: {
- URL: '//i.4cdn.org/g/src/1334437723720.jpg',
+ URL: '//i.4cdn.org/g/1334437723720.jpg',
name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg',
size: '276 KB',
sizeInBytes: 276 * 1024,
diff --git a/css/style.css b/css/style.css
new file mode 100644
index 000000000..323abf62f
--- /dev/null
+++ b/css/style.css
@@ -0,0 +1,1071 @@
+/* General */
+.dialog {
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .15);
+ border: 1px solid;
+ display: block;
+ padding: 0;
+}
+.field {
+ background-color: #FFF;
+ border: 1px solid #CCC;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ color: #333;
+ font-family: inherit;
+ font-size: 13px;
+ margin: 0;
+ padding: 2px 4px 3px;
+ outline: none;
+ transition: color .25s, border-color .25s, flex .25s;
+}
+.field::-moz-placeholder,
+.field:hover::-moz-placeholder {
+ color: #AAA !important;
+}
+.field:hover {
+ border-color: #999;
+}
+.field:hover, .field:focus {
+ color: #000;
+}
+.field[disabled] {
+ background-color: #F2F2F2;
+ color: #888;
+}
+.field::-webkit-search-decoration {
+ display: none;
+}
+.move {
+ cursor: move;
+}
+label, .watcher-toggler {
+ cursor: pointer;
+}
+a[href="javascript:;"] {
+ text-decoration: none;
+}
+.warning {
+ color: red;
+}
+
+/* 4chan style fixes */
+.opContainer, .op {
+ display: block !important;
+}
+.post {
+ overflow: visible !important;
+}
+.reply > .file > .fileText {
+ margin: 0 20px;
+}
+[hidden] {
+ display: none !important;
+}
+
+/* fixed, z-index */
+#overlay,
+#qp, #ihover,
+#updater, #thread-stats,
+#navlinks, #header,
+#qr {
+ position: fixed;
+}
+#overlay {
+ z-index: 999;
+}
+#notifications {
+ z-index: 70;
+}
+#qp, #ihover {
+ z-index: 60;
+}
+#menu {
+ z-index: 50;
+}
+#navlinks, #updater, #thread-stats {
+ z-index: 40;
+}
+#qr {
+ z-index: 30;
+}
+#thread-watcher:hover {
+ z-index: 20;
+}
+#header {
+ z-index: 10;
+}
+#thread-watcher {
+ z-index: 5;
+}
+
+/* Header */
+:root.top-header body {
+ margin-top: 2em;
+}
+:root.bottom-header body {
+ margin-bottom: 2em;
+}
+:root.fourchan-x #navtopright,
+:root.fourchan-x #navbotright,
+:root.fourchan-x:not(.show-original-top-board-list) #boardNavDesktop,
+:root.fourchan-x:not(.show-original-bot-board-list) #boardNavDesktopFoot {
+ display: none !important;
+}
+#header {
+ right: 0;
+ left: 0;
+ pointer-events: none;
+}
+#header.top {
+ top: 0;
+}
+#header.bottom {
+ bottom: 0;
+}
+#header-bar {
+ border-width: 0;
+ display: flex;
+ padding: 3px;
+ position: relative;
+ transition: all .1s .05s ease-in-out;
+ pointer-events: initial;
+}
+#header.top #header-bar {
+ border-bottom-width: 1px;
+}
+#header.bottom #header-bar {
+ box-shadow: 0 -1px 2px rgba(0, 0, 0, .15);
+ border-top-width: 1px;
+}
+#board-list {
+ flex: 1;
+ align-self: center;
+ text-align: center;
+}
+#header-bar.autohide:not(:hover) {
+ box-shadow: none;
+ transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);
+}
+#header-bar.scroll:not(:hover) {
+ transition: -webkit-transform .2s !important;
+ transition: transform .2s !important;
+}
+#header.top #header-bar.autohide:not(:hover) {
+ margin-bottom: -1em;
+ -webkit-transform: translateY(-100%);
+ transform: translateY(-100%);
+}
+#header.bottom #header-bar.autohide:not(:hover) {
+ -webkit-transform: translateY(100%);
+ transform: translateY(100%);
+}
+#header-bar-hitzone {
+ left: 0;
+ right: 0;
+ height: 10px;
+ position: absolute;
+}
+#header-bar:not(.autohide) #header-bar-hitzone {
+ display: none;
+}
+#header.top #header-bar-hitzone {
+ bottom: -10px;
+}
+#header.bottom #header-bar-hitzone {
+ top: -10px;
+}
+#header-bar a:not(.entry) {
+ text-decoration: none;
+ padding: 1px;
+}
+.shortcut:not(:last-child)::after {
+ content: " / ";
+}
+.brackets-wrap::before {
+ content: " [ ";
+}
+.brackets-wrap::after {
+ content: " ] ";
+}
+
+/* Notifications */
+#notifications {
+ height: 0;
+ text-align: center;
+ pointer-events: initial;
+}
+#header.bottom #notifications {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+}
+.notification {
+ color: #FFF;
+ font-weight: 700;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .15);
+ border-radius: 2px;
+ margin: 1px auto;
+ width: 500px;
+ max-width: 100%;
+ position: relative;
+ transition: all .25s ease-in-out;
+}
+.notification.error {
+ background-color: hsla(0, 100%, 38%, .9);
+}
+.notification.warning {
+ background-color: hsla(36, 100%, 38%, .9);
+}
+.notification.info {
+ background-color: hsla(200, 100%, 38%, .9);
+}
+.notification.success {
+ background-color: hsla(104, 100%, 38%, .9);
+}
+.notification a {
+ color: white;
+}
+.notification > .close {
+ padding: 7px;
+ top: 0;
+ right: 0;
+ position: absolute;
+}
+.message {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 6px 20px;
+ max-height: 200px;
+ width: 100%;
+ overflow: auto;
+}
+
+/* Settings */
+:root.fourchan-x body {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+#overlay {
+ background-color: rgba(0, 0, 0, .5);
+ display: flex;
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+}
+#fourchanx-settings {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ box-shadow: 0 0 15px rgba(0, 0, 0, .15);
+ height: 600px;
+ max-height: 100%;
+ width: 900px;
+ max-width: 100%;
+ margin: auto;
+ padding: 3px;
+ display: flex;
+ flex-direction: column;
+}
+#fourchanx-settings > nav {
+ display: flex;
+ padding: 2px 2px .5em;
+}
+#fourchanx-settings > nav a {
+ text-decoration: underline;
+}
+#fourchanx-settings > nav a.close {
+ text-decoration: none;
+ padding: 0 2px;
+}
+.sections-list {
+ flex: 1;
+}
+.tab-selected {
+ font-weight: 700;
+}
+#fourchanx-settings > section {
+ flex: 1;
+ overflow: auto;
+}
+.section-sauce ul,
+.section-rice ul {
+ list-style: none;
+ margin: 0;
+ padding: 8px;
+}
+.section-sauce li,
+.section-rice li {
+ padding-left: 4px;
+}
+.section-main label {
+ text-decoration: underline;
+}
+.section-filter ul,
+.section-qr ul {
+ padding: 0;
+}
+.section-filter li,
+.section-qr li {
+ margin: 10px 40px;
+}
+.section-filter textarea {
+ height: 500px;
+}
+.section-qr textarea {
+ height: 200px;
+}
+.section-sauce textarea {
+ height: 350px;
+}
+.section-rice .field[name="boardnav"] {
+ width: 100%;
+}
+.section-rice textarea {
+ height: 150px;
+}
+.section-archives table {
+ width: 100%;
+}
+.section-archives th:not(:first-child) {
+ width: 30%;
+}
+.section-archives td {
+ text-align: center;
+}
+.section-archives select {
+ width: 90%;
+}
+.section-keybinds .field {
+ font-family: monospace;
+}
+#fourchanx-settings fieldset {
+ border: 1px solid;
+ border-radius: 3px;
+}
+#fourchanx-settings legend {
+ font-weight: 700;
+}
+#fourchanx-settings textarea {
+ font-family: monospace;
+ min-width: 100%;
+ max-width: 100%;
+}
+#fourchanx-settings code {
+ color: #000;
+ background-color: #FFF;
+ padding: 0 2px;
+}
+.unscroll {
+ overflow: hidden;
+}
+
+/* Index */
+:root.index-loading .navLinks,
+:root.index-loading .board,
+:root.index-loading .pagelist,
+:root:not(.catalog-mode) #index-size {
+ display: none;
+}
+#nav-links {
+ display: flex;
+ align-items: baseline;
+}
+#index-search {
+ padding-right: 1.5em;
+ width: 100px;
+ transition: color .25s, border-color .25s, width .25s;
+}
+#index-search:focus,
+#index-search[data-searching] {
+ width: 200px;
+}
+#index-search-clear {
+ color: gray;
+ position: relative;
+ left: -1.25em;
+ width: 0;
+}
+<% if (type === 'crx') { %>
+/* ``::-webkit-*'' selectors break selector lists on Firefox. */
+#index-search::-webkit-search-cancel-button,
+<% } %>
+#index-search:not([data-searching]) + #index-search-clear {
+ display: none;
+}
+.summary {
+ text-decoration: none;
+}
+.catalog-mode .board {
+ text-align: center;
+}
+.catalog-thread {
+ display: inline-flex;
+ text-align: left;
+ flex-direction: column;
+ align-items: center;
+ margin: 0 2px 5px;
+ word-break: break-word;
+ vertical-align: top;
+}
+.catalog-small .catalog-thread {
+ width: 165px;
+ max-height: 320px;
+}
+.catalog-large .catalog-thread {
+ width: 270px;
+ max-height: 410px;
+}
+.thumb {
+ flex-shrink: 0;
+ position: relative;
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+ background-position: center;
+ border-radius: 2px;
+ box-shadow: 0 0 5px rgba(0, 0, 0, .25);
+}
+.thumb:not(.deleted-file):not(.no-file) {
+ min-width: 30px;
+ min-height: 30px;
+}
+.thumb.spoiler-file {
+ background-size: 100px;
+ width: 100px;
+ height: 100px;
+}
+.thumb.deleted-file {
+ background-size: 127px 13px;
+ width: 127px;
+ height: 13px;
+ padding: 20px 11px;
+}
+.thumb.no-file {
+ background-size: 77px 13px;
+ width: 77px;
+ height: 13px;
+ padding: 20px 36px;
+}
+.thread-icons > img {
+ width: 1em;
+ height: 1em;
+ margin: 0;
+ vertical-align: text-top;
+}
+.thumb:not(:hover):not(:focus) > .menu-button:not(.open):not(:focus) > i {
+ display: none;
+}
+.thumb > .menu-button {
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+.thumb > .menu-button > i {
+ width: 1em;
+ height: 1em;
+ padding: 1px;
+ border-radius: 0 2px 0 2px;
+ font-size: 14px;
+ text-align: center;
+<% if (type === 'userscript') { %>
+ line-height: normal;
+<% } %>
+}
+.thread-stats {
+ flex-shrink: 0;
+ cursor: help;
+ font-size: 10px;
+ font-weight: 700;
+ margin-top: 2px;
+}
+.catalog-thread > .subject {
+ flex-shrink: 0;
+ font-weight: 700;
+ line-height: 1;
+ text-align: center;
+}
+.catalog-thread > .comment {
+ flex-shrink: 1;
+ align-self: stretch;
+ overflow: hidden;
+ text-align: center;
+}
+.thread-info {
+ position: fixed;
+ background-color: inherit;
+ padding: 2px;
+ border-radius: 2px;
+ box-shadow: 0 0 5px rgba(0, 0, 0, .25);
+}
+.thread-info .post {
+ margin: 0;
+}
+
+/* Announcement Hiding */
+:root.hide-announcement #globalMessage,
+:root.hide-announcement-enabled #toggleMsgBtn {
+ display: none;
+}
+a.hide-announcement {
+ float: left;
+ font-size: 14px;
+}
+
+/* Unread */
+#unread-line {
+ margin: 0;
+}
+
+/* Thread Updater */
+#updater:not(:hover) {
+ background: none;
+ border: none;
+ box-shadow: none;
+}
+#updater > .move {
+ padding: 0 3px;
+}
+#updater > div:last-child {
+ text-align: center;
+}
+#updater input[type="number"] {
+ width: 4em;
+}
+#updater:not(:hover) > div:not(.move) {
+ display: none;
+}
+#updater input[type="button"] {
+ width: 100%;
+}
+.new {
+ color: limegreen;
+}
+
+/* Thread Watcher */
+#thread-watcher {
+ max-width: 200px;
+ min-width: 150px;
+ padding: 3px;
+ position: absolute;
+}
+#thread-watcher > div:first-child {
+ display: flex;
+ align-items: center;
+}
+#thread-watcher .move {
+ flex: 1;
+}
+#watcher-status:not(:empty)::before {
+ content: "(";
+}
+#watcher-status:not(:empty)::after {
+ content: ")";
+}
+#watched-threads:not(:hover) {
+ max-height: 150px;
+ overflow: hidden;
+}
+#watched-threads div {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+#watched-threads .current {
+ font-weight: 700;
+}
+#watched-threads a {
+ text-decoration: none;
+}
+#watched-threads .dead-thread a[title] {
+ text-decoration: line-through;
+}
+
+/* Thread Stats */
+#thread-stats {
+ background: none;
+ border: none;
+ box-shadow: none;
+}
+
+/* Quote */
+.deadlink {
+ text-decoration: none !important;
+}
+.backlink.deadlink:not(.forwardlink),
+.quotelink.deadlink:not(.forwardlink) {
+ text-decoration: underline !important;
+}
+.inlined {
+ opacity: .5;
+}
+#qp input, .forwarded {
+ display: none;
+}
+.quotelink.forwardlink,
+.backlink.forwardlink {
+ text-decoration: none;
+ border-bottom: 1px dashed;
+}
+@supports (text-decoration-style: dashed) or (-moz-text-decoration-style: dashed) {
+ .quotelink.forwardlink,
+ .backlink.forwardlink {
+ text-decoration: underline;
+ -moz-text-decoration-style: dashed;
+ text-decoration-style: dashed;
+ border-bottom: none;
+ }
+}
+.filtered {
+ text-decoration: underline line-through;
+}
+.inline {
+ border: 1px solid;
+ display: table;
+ margin: 2px 0;
+}
+.inline .post {
+ border: 0 !important;
+ background-color: transparent !important;
+ display: table !important;
+ margin: 0 !important;
+ padding: 1px 2px !important;
+}
+#qp > .opContainer::after {
+ content: '';
+ clear: both;
+ display: table;
+}
+#qp .post {
+ border: none;
+ margin: 0;
+ padding: 2px 2px 5px;
+}
+#qp img {
+ max-height: 80vh;
+ max-width: 50vw;
+}
+.qphl {
+ outline: 2px solid rgba(216, 94, 49, .7);
+}
+
+/* File */
+.file-info:hover .fntrunc,
+.file-info:not(:hover) .fnfull,
+.expanded-image > .post > .file > .fileThumb > img[data-md5],
+:not(.expanded-image) > .post > .file > .fileThumb > .full-image {
+ display: none;
+}
+.expanding {
+ opacity: .5;
+}
+.expanded-image {
+ clear: both;
+}
+.expanded-image > .op > .file::after {
+ content: '';
+ clear: both;
+ display: table;
+}
+:root.fit-height .full-image {
+ max-height: 100vh;
+}
+:root.fit-width .full-image {
+ max-width: 100%;
+}
+:root.gecko.fit-width .full-image {
+ width: 100%;
+}
+.fileThumb > .warning {
+ clear: both;
+}
+#ihover {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ max-height: 100%;
+ max-width: 75%;
+ padding-bottom: 16px;
+}
+
+/* Index/Reply Navigation */
+#navlinks {
+ font-size: 16px;
+ top: 25px;
+ right: 10px;
+}
+
+/* Filter */
+.opContainer.filter-highlight {
+ box-shadow: inset 5px 0 rgba(255, 0, 0, .5);
+}
+.filter-highlight > .reply {
+ box-shadow: -5px 0 rgba(255, 0, 0, .5);
+}
+.pinned .thumb,
+.filter-highlight .thumb {
+ border: 2px solid rgba(255, 0, 0, .5);
+}
+
+/* Post Hiding */
+.hide-post-button,
+.show-post-button {
+ font-size: 14px;
+ line-height: 12px; /* Prevent the floating effect from affecting the thumbnail too */
+}
+.opContainer > .show-post-button,
+.hide-post-button {
+ float: left;
+ margin-right: 3px;
+}
+.stub input {
+ display: inline-block;
+}
+
+/* QR */
+:root.hide-original-post-form #postForm,
+:root.hide-original-post-form .postingMode,
+:root.hide-original-post-form #togglePostForm,
+#qr.autohide:not(.has-focus):not(:hover) > form {
+ display: none;
+}
+#qr select, #dump-button, .remove, .captcha-img {
+ cursor: pointer;
+}
+#qr > div {
+ min-width: 300px;
+ display: flex;
+ align-items: center;
+}
+#qr .move {
+ align-self: stretch;
+ flex: 1;
+}
+#qr select[data-name=thread] {
+ margin: 0;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ border: none;
+ background: none;
+ font: inherit;
+}
+#qr option {
+ color: #000;
+ background-color: #F7F7F7;
+}
+#qr .close {
+ padding: 0 3px;
+}
+#qr > form {
+ display: flex;
+ flex-direction: column;
+}
+.persona {
+ display: flex;
+}
+.persona .field {
+ flex: 1;
+ width: 0;
+}
+.persona .field:hover,
+.persona .field:focus {
+ flex: 3;
+}
+#dump-button {
+ background: linear-gradient(#EEE, #CCC);
+ border: 1px solid #CCC;
+ margin: 0;
+ padding: 2px 4px 3px;
+ outline: none;
+ width: 30px;
+}
+#dump-button:hover,
+#dump-button:focus {
+ background: linear-gradient(#FFF, #DDD);
+}
+#dump-button:active,
+.dump #dump-button:not(:hover):not(:focus) {
+ background: linear-gradient(#CCC, #DDD);
+}
+:root.gecko #dump-button {
+ padding: 0;
+}
+#qr:not(.dump) #dump-list,
+#qr:not(.dump) #add-post {
+ display: none;
+}
+#dump-list {
+ counter-reset: qrpreviews;
+ width: 0;
+ min-width: 100%;
+ overflow: hidden;
+ white-space: nowrap;
+ position: relative;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+}
+#dump-list:hover {
+ padding-bottom: 12px;
+ margin-bottom: -12px;
+ overflow-x: auto;
+ z-index: 1;
+}
+#dump-list::-webkit-scrollbar {
+ height: 12px;
+}
+#dump-list::-webkit-scrollbar-thumb {
+ border: 1px solid;
+}
+.qr-preview {
+ background-position: 50% 20%;
+ background-size: cover;
+ border: 1px solid #808080;
+ color: #FFF !important;
+ font-size: 12px;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ cursor: move;
+ display: inline-block;
+ height: 92px;
+ width: 92px;
+ margin: 4px;
+ padding: 2px;
+ opacity: .6;
+ outline: none;
+ overflow: hidden;
+ position: relative;
+ text-shadow: 0 0 2px #000;
+ transition: opacity .25s ease-in-out;
+ vertical-align: top;
+ white-space: pre;
+}
+.qr-preview:hover,
+.qr-preview:focus {
+ opacity: .9;
+ color: #FFF !important;
+}
+.qr-preview#selected {
+ opacity: 1;
+}
+.qr-preview::before {
+ counter-increment: qrpreviews;
+ content: counter(qrpreviews);
+ font-weight: 700;
+ text-shadow: 0 0 3px #000, 0 0 5px #000;
+ position: absolute;
+ top: 3px;
+ right: 3px;
+}
+.qr-preview.drag {
+ border-color: red;
+ border-style: dashed;
+ opacity: 1;
+}
+.qr-preview.over {
+ border-color: #FFF;
+ border-style: dashed;
+ opacity: 1;
+}
+a.remove {
+ color: #E00 !important;
+ padding: 1px;
+}
+.qr-preview > label {
+ background: rgba(0, 0, 0, .5);
+ right: 0;
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ text-align: center;
+}
+.qr-preview > label > input {
+ margin: 1px 0;
+ vertical-align: bottom;
+}
+#add-post {
+ align-self: flex-end;
+ font-size: 20px;
+ width: 1em;
+ margin-top: -1em;
+ text-align: center;
+ z-index: 1;
+}
+#qr textarea {
+ min-height: 160px;
+ min-width: 100%;
+ display: block;
+}
+#qr.has-captcha textarea {
+ min-height: 120px;
+}
+.textarea {
+ position: relative;
+}
+#char-count {
+ color: #000;
+ background: hsla(0, 0%, 100%, .5);
+ font-size: 8pt;
+ position: absolute;
+ bottom: 1px;
+ right: 1px;
+ pointer-events: none;
+}
+#char-count.warning {
+ color: red;
+}
+.captcha-img {
+ background: #FFF;
+ outline: 1px solid #CCC;
+ outline-offset: -1px;
+}
+.captcha-img > img {
+ display: block;
+ height: 57px;
+ width: 300px;
+}
+#file-n-submit {
+ display: flex;
+ align-items: center;
+}
+#file-n-submit input {
+ margin: 0;
+}
+#file-n-submit input[type='submit'] {
+ order: 1;
+}
+#file-n-submit.has-file #qr-no-file,
+#file-n-submit:not(.has-file) #qr-filename,
+#file-n-submit:not(.has-file) #qr-filesize,
+#file-n-submit:not(.has-file) #qr-file-spoiler,
+#file-n-submit:not(.has-file) #qr-filerm,
+#qr-filename:focus ~ #qr-filesize {
+ display: none;
+}
+#qr-no-file,
+#qr-filename,
+#qr-filesize,
+#qr-filerm,
+#qr-file-spoiler {
+ margin: 0 1px !important;
+}
+#qr-no-file {
+ cursor: default;
+ flex: 1;
+}
+#qr-filename {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background: none;
+ border: none;
+ color: inherit;
+ font: inherit;
+ flex: 1;
+ width: 0;
+ padding: 0;
+ text-overflow: ellipsis;
+}
+#qr-filesize {
+ font-size: .8em;
+}
+#qr-filesize::before {
+ content: "(";
+}
+#qr-filesize::after {
+ content: ")";
+}
+
+/* Menu */
+.menu-button {
+ position: relative;
+}
+@media screen and (resolution: 1dppx) {
+ .fa-bars {
+ font-size: 14px;
+ }
+ #shortcuts .fa-bars {
+ vertical-align: -1px;
+ }
+}
+#menu {
+ border-bottom: 0;
+ display: flex;
+ margin: 2px 0;
+ flex-direction: column;
+ position: absolute;
+ outline: none;
+}
+#menu.top {
+ top: 100%;
+}
+#menu.bottom {
+ bottom: 100%;
+}
+#menu.left {
+ left: 0;
+}
+#menu.right {
+ right: 0;
+}
+.entry {
+ cursor: pointer;
+ outline: none;
+ padding: 3px 7px;
+ position: relative;
+ text-decoration: none;
+ white-space: nowrap;
+}
+.entry.disabled {
+ color: graytext !important;
+}
+.entry.has-submenu {
+ padding-right: 20px;
+}
+.has-submenu::after {
+ content: '';
+ border-left: 6px solid;
+ border-top: 4px solid transparent;
+ border-bottom: 4px solid transparent;
+ display: inline-block;
+ margin: 4px;
+ position: absolute;
+ right: 3px;
+}
+.has-submenu:not(.focused) > .submenu {
+ display: none;
+}
+.submenu {
+ border-bottom: 0;
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ margin: -1px 0;
+}
+.submenu.top {
+ top: 0;
+}
+.submenu.bottom {
+ bottom: 0;
+}
+.submenu.left {
+ left: 100%;
+}
+.submenu.right {
+ right: 100%;
+}
+.entry input {
+ margin: 0;
+}
+
+/* Other */
+.linkified {
+ word-break: break-all;
+}
+.posteruid.painted {
+ padding: 0 5px;
+ border-radius: 1em;
+ font-size: 0.8em;
+ cursor: pointer;
+}
diff --git a/package.json b/package.json
index 910303c02..dcbf920c3 100755
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
"userjs": "4chan-X.user.js"
},
"min": {
- "chrome": "32",
+ "chrome": "33",
"firefox": "26",
"greasemonkey": "1.14"
}
@@ -33,7 +33,7 @@
"grunt-concurrent": "~0.5.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-coffee": "~0.10.1",
- "grunt-contrib-compress": "~0.7.0",
+ "grunt-contrib-compress": "~0.8.0",
"grunt-contrib-concat": "~0.4.0",
"grunt-contrib-copy": "~0.5.0",
"grunt-contrib-watch": "~0.6.1",
diff --git a/src/Filtering/PostHiding.coffee b/src/Filtering/PostHiding.coffee
index d227a2f07..b1298cf07 100755
--- a/src/Filtering/PostHiding.coffee
+++ b/src/Filtering/PostHiding.coffee
@@ -12,15 +12,6 @@ PostHiding =
name: 'Post Hiding'
cb: @node
- # XXX tmp conversion
- $.get 'hiddenThreads', null, ({hiddenThreads}) ->
- return unless hiddenThreads
- for boardID, board of hiddenThreads.boards
- for threadID, val of board
- ((PostHiding.db.data.boards[boardID] or= {})[threadID] or= {})[threadID] = val
- PostHiding.db.save()
- $.delete 'hiddenThreads'
-
node: ->
return if !@isReply and g.VIEW isnt 'index' or @isClone
diff --git a/src/General/Build.coffee b/src/General/Build.coffee
index af75f9dfb..23acd8b7d 100755
--- a/src/General/Build.coffee
+++ b/src/General/Build.coffee
@@ -42,14 +42,14 @@ Build =
name: data.filename + data.ext
timestamp: "#{data.tim}#{data.ext}"
url: if boardID is 'f'
- "//i.4cdn.org/#{boardID}/src/#{data.filename}#{data.ext}"
+ "//i.4cdn.org/#{boardID}/#{data.filename}#{data.ext}"
else
- "//i.4cdn.org/#{boardID}/src/#{data.tim}#{data.ext}"
+ "//i.4cdn.org/#{boardID}/#{data.tim}#{data.ext}"
height: data.h
width: data.w
MD5: data.md5
size: data.fsize
- turl: "//#{Build.thumbRotate()}.t.4cdn.org/#{boardID}/thumb/#{data.tim}s.jpg"
+ turl: "//#{Build.thumbRotate()}.t.4cdn.org/#{boardID}/#{data.tim}s.jpg"
theight: data.tn_h
twidth: data.tn_w
isSpoiler: !!data.spoiler
@@ -184,19 +184,55 @@ Build =
if isOP and g.VIEW is 'index'
pageNum = Index.liveThreadData.keys.indexOf("#{postID}") // Index.threadsNumPerPage
pageIcon = " Page #{pageNum}"
- replyLink = " [Reply]"
+ replyLink = " [Reply]"
else
pageIcon = replyLink = ''
container = $.el 'div',
id: "pc#{postID}"
className: "postContainer #{if isOP then 'op' else 'reply'}Container"
- innerHTML: <%= grunt.file.read('src/General/html/Build/post.html').replace(/>\s+/g, '>').replace(/\s+
+ innerHTML: \
+ (if isOP then '' else ">>
") +
+ "" +
+
+ (if isOP then fileHTML else '') +
+
+ "
" +
+ "
" +
+ "
#{subject or ''} " +
+ "
" +
+ emailStart +
+ "#{name or ''}" + tripcode +
+ capcodeStart + emailEnd + capcodeIcon + userID + flag +
+ ' ' +
+ "
#{date} " +
+ "
" +
+ "No." +
+ "#{postID}" +
+ pageIcon + sticky + closed + replyLink +
+ '' +
+ '
' +
+
+ (if isOP then '' else fileHTML) +
+
+ "
#{comment or ''}
" +
+
+ '
'
for quote in $$ '.quotelink', container
href = quote.getAttribute 'href'
continue if href[0] is '/' # Cross-board quote, or board link
- quote.href = "/#{boardID}/res/#{href}" # Fix pathnames
+ quote.href = "/#{boardID}/thread/#{href}" # Fix pathnames
container
@@ -208,7 +244,7 @@ Build =
$.el 'a',
className: 'summary'
textContent: text.join ' '
- href: "/#{boardID}/res/#{threadID}"
+ href: "/#{boardID}/thread/#{threadID}"
thread: (board, data, full) ->
Build.spoilerRange[board] = data.custom_spoiler
diff --git a/src/General/Get.coffee b/src/General/Get.coffee
index 3e7cdda7f..4f98ff3fc 100755
--- a/src/General/Get.coffee
+++ b/src/General/Get.coffee
@@ -70,7 +70,7 @@ Get =
root.textContent = "Loading post No.#{postID}..."
if threadID
- $.cache "//a.4cdn.org/#{boardID}/res/#{threadID}.json", ->
+ $.cache "//a.4cdn.org/#{boardID}/thread/#{threadID}.json", ->
Get.fetchedPost @, boardID, threadID, postID, root, context
else if url = Redirect.to 'post', {boardID, postID}
$.cache url,
@@ -200,7 +200,7 @@ Get =
width: data.media.media_w
MD5: data.media.media_hash
size: data.media.media_size
- turl: data.media.thumb_link or "//t.4cdn.org/#{boardID}/thumb/#{data.media.preview_orig}"
+ turl: data.media.thumb_link or "//t.4cdn.org/#{boardID}/#{data.media.preview_orig}"
theight: data.media.preview_h
twidth: data.media.preview_w
isSpoiler: data.media.spoiler is '1'
diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee
index 323226128..34726f6e1 100755
--- a/src/General/Settings.coffee
+++ b/src/General/Settings.coffee
@@ -383,7 +383,7 @@ Settings =
data =
isReply: true
file:
- URL: '//i.4cdn.org/g/src/1334437723720.jpg'
+ URL: '//i.4cdn.org/g/1334437723720.jpg'
name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg'
size: '276 KB'
sizeInBytes: 276 * 1024
diff --git a/src/General/html/Build/post.html b/src/General/html/Build/post.html
deleted file mode 100755
index fc41a1e0a..000000000
--- a/src/General/html/Build/post.html
+++ /dev/null
@@ -1,36 +0,0 @@
-"""#{if isOP then '' else ">>
"}
-
-
- #{if isOP then fileHTML else ''}
-
-
-
- #{' '}
#{subject or ''}#{' '}
-
- #{emailStart}
- #{name or ''}
- #{tripcode + capcodeStart + emailEnd + capcodeIcon + userID + flag}
- #{" "}
-
#{date}#{' '}
-
- No.
- #{postID}
- #{pageIcon + sticky + closed + replyLink}
-
-
-
- #{if isOP then '' else fileHTML}
-
-
#{comment or ''}
#{' '}
-
-
"""
diff --git a/src/General/html/Features/Thread-catalog-view.html b/src/General/html/Features/Thread-catalog-view.html
index 798de0cb4..1df9afccb 100644
--- a/src/General/html/Features/Thread-catalog-view.html
+++ b/src/General/html/Features/Thread-catalog-view.html
@@ -1,4 +1,4 @@
-
+
#{postCount} / #{fileCount} / #{pageCount}
diff --git a/src/General/lib/polyfill.coffee b/src/General/lib/polyfill.coffee
index 08ce47683..e09a458b0 100755
--- a/src/General/lib/polyfill.coffee
+++ b/src/General/lib/polyfill.coffee
@@ -1,21 +1,8 @@
Polyfill =
init: ->
<% if (type === 'crx') { %>
- @notificationPermission()
@toBlob()
- @visibility()
<% } %>
- notificationPermission: ->
- return if !window.Notification or 'permission' of Notification or !window.webkitNotifications
- Object.defineProperty Notification, 'permission',
- get: ->
- switch webkitNotifications.checkPermission()
- when 0
- 'granted'
- when 1
- 'default'
- when 2
- 'denied'
toBlob: ->
HTMLCanvasElement::toBlob or= (cb) ->
data = atob @toDataURL()[22..]
@@ -25,12 +12,3 @@ Polyfill =
for i in [0...l] by 1
ui8a[i] = data.charCodeAt i
cb new Blob [ui8a], type: 'image/png'
- visibility: ->
- # page visibility API
- return if 'visibilityState' of d
- Object.defineProperties HTMLDocument.prototype,
- visibilityState:
- get: -> @webkitVisibilityState
- hidden:
- get: -> @webkitHidden
- $.on d, 'webkitvisibilitychange', -> $.event 'visibilitychange'
diff --git a/src/General/lib/post.class b/src/General/lib/post.class
index 485ed36ec..6bb9e0948 100755
--- a/src/General/lib/post.class
+++ b/src/General/lib/post.class
@@ -101,7 +101,7 @@ class Post
return unless match = quotelink.href.match ///
boards\.4chan\.org/
([^/]+) # boardID
- /res/\d+#p
+ /thread/\d+#p
(\d+) # postID
$
///
@@ -134,7 +134,7 @@ class Post
@file.thumbURL = if that.isArchived
thumb.src
else
- "#{location.protocol}//t.4cdn.org/#{@board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg"
+ "#{location.protocol}//t.4cdn.org/#{@board}/#{@file.URL.match(/(\d+)\./)[1]}s.jpg"
@file.name = if nameNode = $ 'span', fileText
nameNode.title or nameNode.textContent
else
diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee
index 5e32e8328..7026e5ed4 100755
--- a/src/Images/ImageExpand.coffee
+++ b/src/Images/ImageExpand.coffee
@@ -113,8 +113,10 @@ ImageExpand =
$.rm post.file.videoControls
delete post.file.videoControls
$.rmClass post.nodes.root, 'expanded-image'
- $.rmClass thumb, 'expanding'
- post.file.isExpanded = false
+ $.rmClass post.file.thumb, 'expanding'
+ delete post.file.isExpanding
+ delete post.file.isExpanded
+ post.file.fullImage.pause() if post.file.isVideo and post.file.fullImage
expand: (post, src, disableAutoplay) ->
# Do not expand images of hidden/filtered replies, or already expanded pictures.
@@ -195,6 +197,7 @@ ImageExpand =
error: ->
post = Get.postFromNode @
$.rm @
+ delete post.file.isReady
delete post.file.fullImage
# Images can error:
# - before the image started loading.
@@ -204,11 +207,23 @@ ImageExpand =
return
ImageExpand.contract post
+ if @error and @error.code isnt @error.MEDIA_ERR_NETWORK # video
+ error = switch @error.code
+ when 1 then 'MEDIA_ERR_ABORTED'
+ when 3 then 'MEDIA_ERR_DECODE'
+ when 4 then 'MEDIA_ERR_SRC_NOT_SUPPORTED'
+ when 5 then 'MEDIA_ERR_ENCRYPTED'
+ post.file.error = $.el 'div',
+ textContent: "Playback error: #{error}"
+ className: 'warning'
+ $.after post.file.thumb, post.file.error
+ return
+
src = @src.split '/'
if src[2] is 'i.4cdn.org'
URL = Redirect.to 'file',
boardID: src[3]
- filename: src[5]
+ filename: src[4].replace /\?.+$/, ''
if URL
setTimeout ImageExpand.expand, 10000, post, URL
return
@@ -226,7 +241,7 @@ ImageExpand =
type: 'head'
<% } else { %>
# XXX CORS for i.4cdn.org WHEN?
- $.ajax "//a.4cdn.org/#{post.board}/res/#{post.thread}.json", onload: ->
+ $.ajax "//a.4cdn.org/#{post.board}/thread/#{post.thread}.json", onload: ->
return if @status isnt 200
for postObj in @response.posts
break if postObj.no is post.ID
diff --git a/src/Images/ImageHover.coffee b/src/Images/ImageHover.coffee
index f216a7ab6..fc01dda88 100755
--- a/src/Images/ImageHover.coffee
+++ b/src/Images/ImageHover.coffee
@@ -46,8 +46,10 @@ ImageHover =
el: el
latestEvent: e
endEvents: 'mouseout click'
- asapTest: -> (el.videoHeight or el.naturalHeight)
- noRemove: true
+ asapTest: if post.file.isImage
+ -> el.naturalHeight
+ else
+ -> el.readyState >= el.HAVE_CURRENT_DATA
cb: ->
if isVideo
el.pause()
@@ -62,7 +64,7 @@ ImageHover =
if src[2] is 'i.4cdn.org'
URL = Redirect.to 'file',
boardID: src[3]
- filename: src[5].replace /\?.+$/, ''
+ filename: src[4].replace /\?.+$/, ''
if URL
@src = URL
return
@@ -80,7 +82,7 @@ ImageHover =
type: 'head'
<% } else { %>
# XXX CORS for i.4cdn.org WHEN?
- $.ajax "//a.4cdn.org/#{post.board}/res/#{post.thread}.json", onload: ->
+ $.ajax "//a.4cdn.org/#{post.board}/thread/#{post.thread}.json", onload: ->
return if @status isnt 200
for postObj in @response.posts
break if postObj.no is post.ID
diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee
index 4f4059fe2..1d81c8c8c 100755
--- a/src/Miscellaneous/ExpandThread.coffee
+++ b/src/Miscellaneous/ExpandThread.coffee
@@ -49,7 +49,7 @@ ExpandThread =
expand: (thread, a, threadRoot) ->
ExpandThread.statuses[thread] = status = {}
a.textContent = ExpandThread.text '...', a.textContent.match(/\d+/g)...
- status.req = $.cache "//a.4cdn.org/#{thread.board}/res/#{thread}.json", ->
+ status.req = $.cache "//a.4cdn.org/#{thread.board}/thread/#{thread}.json", ->
delete status.req
ExpandThread.parse @, thread, a
contract: (thread, a, threadRoot) ->
diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee
index 69a19b390..f19a3cc5a 100755
--- a/src/Miscellaneous/Keybinds.coffee
+++ b/src/Miscellaneous/Keybinds.coffee
@@ -232,7 +232,7 @@ Keybinds =
open: (thread, tab) ->
return if g.VIEW isnt 'index'
- url = "/#{thread.board}/res/#{thread}"
+ url = "/#{thread.board}/thread/#{thread}"
if tab
$.open url
else
diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee
index 59208ff4a..444dde3fa 100755
--- a/src/Monitoring/ThreadUpdater.coffee
+++ b/src/Monitoring/ThreadUpdater.coffee
@@ -244,8 +244,13 @@ ThreadUpdater =
else
ThreadUpdater.set 'timer', 'Update'
ThreadUpdater.req?.abort()
- url = "//a.4cdn.org/#{ThreadUpdater.thread.board}/res/#{ThreadUpdater.thread}.json"
- ThreadUpdater.req = $.ajax url, onloadend: ThreadUpdater.cb.load,
+ url = "//a.4cdn.org/#{ThreadUpdater.thread.board}/thread/#{ThreadUpdater.thread}.json"
+ ThreadUpdater.req = $.ajax url,
+ onabort: ThreadUpdater.cb.load
+ onloadend: ThreadUpdater.cb.load
+ ontimeout: ThreadUpdater.cb.load
+ timeout: $.MINUTE
+ ,
whenModified: true
updateThreadStatus: (type, status) ->
diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee
index 4e7859368..303edd3d6 100755
--- a/src/Monitoring/ThreadWatcher.coffee
+++ b/src/Monitoring/ThreadWatcher.coffee
@@ -122,7 +122,7 @@ ThreadWatcher =
return if data.isDead
{fetchCount} = ThreadWatcher
fetchCount.fetching++
- $.ajax "//a.4cdn.org/#{boardID}/res/#{threadID}.json",
+ $.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json",
onloadend: ->
fetchCount.fetched++
if fetchCount.fetched is fetchCount.fetching
@@ -160,7 +160,7 @@ ThreadWatcher =
if data.isDead
href = Redirect.to 'thread', {boardID, threadID}
link = $.el 'a',
- href: href or "/#{boardID}/res/#{threadID}"
+ href: href or "/#{boardID}/thread/#{threadID}"
textContent: data.excerpt
title: data.excerpt
diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee
index 81303f1c7..358e6e01d 100644
--- a/src/Posting/QR.coffee
+++ b/src/Posting/QR.coffee
@@ -223,7 +223,7 @@ QR =
$.prepend frag, $.tn '[code]'
$.add frag, $.tn '[/code]'
for node in $$ 'br', frag
- $.replace node, $.tn '\n>'
+ $.replace node, $.tn '\n>' unless node is frag.lastElementChild
for node in $$ 's', frag
$.replace node, [$.tn('[spoiler]'), node.childNodes..., $.tn '[/spoiler]']
for node in $$ '.prettyprint', frag
@@ -800,9 +800,9 @@ QR =
QR.cooldown.set {req, post, isReply, threadID}
URL = if threadID is postID # new thread
- "/#{g.BOARD}/res/#{threadID}"
+ "/#{g.BOARD}/thread/#{threadID}"
else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index
- "/#{g.BOARD}/res/#{threadID}#p#{postID}"
+ "/#{g.BOARD}/thread/#{threadID}#p#{postID}"
if URL
if Conf['Open Post in New Tab']
$.open URL
diff --git a/src/Quotelinks/QuoteBacklink.coffee b/src/Quotelinks/QuoteBacklink.coffee
index 51e97ee58..9e451f1a5 100755
--- a/src/Quotelinks/QuoteBacklink.coffee
+++ b/src/Quotelinks/QuoteBacklink.coffee
@@ -55,7 +55,7 @@ QuoteBacklink =
buildBacklink: (quoted, quoter) ->
frag = QuoteBacklink.frag.cloneNode true
a = frag.lastElementChild
- a.href = "/#{quoter.board}/res/#{quoter.thread}#p#{quoter}"
+ a.href = "/#{quoter.board}/thread/#{quoter.thread}#p#{quoter}"
a.textContent = text = QuoteBacklink.funk quoter.ID
if quoter.isDead
$.addClass a, 'deadlink'
diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee
index c3baccd97..691312044 100755
--- a/src/Quotelinks/Quotify.coffee
+++ b/src/Quotelinks/Quotify.coffee
@@ -44,7 +44,7 @@ Quotify =
# Don't add 'deadlink' when quotifying in an archived post,
# and we don't know if the post died yet.
a = $.el 'a',
- href: "/#{boardID}/res/#{post.thread}#p#{postID}"
+ href: "/#{boardID}/thread/#{post.thread}#p#{postID}"
className: if post.isDead then 'quotelink deadlink' else 'quotelink'
textContent: quote
$.extend a.dataset, {boardID, threadID: post.thread.ID, postID}