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 + "
" + comment + "
" + innerHTML: "
" + postCount + " / " + fileCount + " / " + pageCount + "
" + subject + "
" + comment + "
" }); 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 + "
" + comment + "
" + innerHTML: "
" + postCount + " / " + fileCount + " / " + pageCount + "
" + subject + "
" + comment + "
" }); 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}