diff --git a/CHANGELOG.md b/CHANGELOG.md index d958a8900..0fdfcc85a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ - **ccd0** +- Some keybind bugfixes. +- Begin work toward compatibility with new URLs. - `Loop in New Tab` (enabled by default) causes videos opened in a separate tab to loop, and applies your settings for inline expanded videos to them. - Add WebM support to gallery (currently no controls). - Add PDF support to gallery, disabled by default. Enable with `PDF in Gallery`. @@ -13,6 +14,11 @@ **fgts** - Update archive list. +**MayhemYDG** +- Update 4chan namespaces support. +- Better handling of webm playback errors. + + **Nebukazar** - `Quote Threading` disabled by default - Added missing titles to Header icons diff --git a/LICENSE b/LICENSE index 851b8fae1..3fc34ff4e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* appchan x - Version 2.9.15 - 2014-04-11 +* appchan x - Version 2.9.15 - 2014-04-19 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE diff --git a/builds/4chan-X.meta.js b/builds/4chan-X.meta.js index 70021551c..fc3598040 100755 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.7.7 +// @version 1.7.8 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index 59c767c54..d67d0de53 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -25,7 +25,7 @@ // ==/UserScript== /* -* appchan x - Version 2.9.15 - 2014-04-11 +* appchan x - Version 2.9.15 - 2014-04-19 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -428,8 +428,8 @@ 'Open Gallery': ['g', 'Opens the gallery.'], 'fappeTyme': ['f', 'Fappe Tyme.'], 'werkTyme': ['Shift+w', 'Werk Tyme'], - 'Front page': ['0', 'Jump to page 0.'], - 'Open front page': ['Shift+0', 'Open page 0 in a new tab.'], + 'Front page': ['0', 'Jump to front page.'], + 'Open front page': ['Shift+0', 'Open front page in a new tab.'], 'Next page': ['Shift+Right', 'Jump to the next page.'], 'Previous page': ['Shift+Left', 'Jump to the previous page.'], 'Search form': ['Ctrl+Alt+s', 'Focus the search field on the board index.'], @@ -3364,7 +3364,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); @@ -3398,7 +3398,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); @@ -4130,23 +4130,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) { @@ -4161,26 +4144,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'); - }); } }; @@ -5234,6 +5197,7 @@ return +window.location.pathname.split('/')[2]; }, userPageNav: function(pageNum) { + Navigate.pushState(pageNum === 0 ? './' : pageNum); if (Conf['Refreshed Navigation'] && Conf['Index Mode'] !== 'all pages') { return Index.update(pageNum); } else { @@ -5244,7 +5208,7 @@ if (Index.currentPage === pageNum && !Index.root.parentElement) { return; } - history.pushState(null, '', pageNum === 0 ? './' : pageNum); + Navigate.pushState(pageNum === 0 ? './' : pageNum); return Index.pageLoad(pageNum); }, pageLoad: function(pageNum) { @@ -5829,12 +5793,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, @@ -5921,14 +5885,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++) { @@ -5937,7 +5901,7 @@ if (href[0] === '/') { continue; } - quote.href = "/" + boardID + "/res/" + href; + quote.href = "/" + boardID + "/thread/" + href; } return container; }, @@ -5952,7 +5916,7 @@ return $.el('a', { className: 'summary', textContent: text.join(' '), - href: "/" + boardID + "/res/" + threadID + href: "/" + boardID + "/thread/" + threadID }); }, thread: function(board, data, full) { @@ -5993,7 +5957,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) { @@ -6151,7 +6115,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', { @@ -6285,7 +6249,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' @@ -7060,27 +7024,10 @@ href: 'javascript:;', textContent: '\uf196' }); - 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; @@ -7481,7 +7428,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'); @@ -8053,7 +8000,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 }); @@ -8912,7 +8859,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++) { @@ -10801,8 +10750,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; @@ -10918,19 +10871,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); @@ -10941,7 +10915,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) { @@ -11046,7 +11020,6 @@ TrashQueue.remove(el); } else { el = $.el((isVideo ? 'video' : 'img'), { - className: 'full-image', src: post.file.URL }); post.file.fullImage = el; @@ -11076,10 +11049,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(); @@ -11100,7 +11074,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; @@ -11115,7 +11089,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) { @@ -12167,9 +12141,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 }); @@ -12467,7 +12444,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++; @@ -12536,7 +12513,7 @@ }); } link = $.el('a', { - href: href || ("/" + boardID + "/res/" + threadID), + href: href || ("/" + boardID + "/thread/" + threadID), textContent: data.excerpt, title: data.excerpt }); @@ -15233,7 +15210,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); }); @@ -15733,7 +15710,7 @@ return Conf[hotkey] = key; }, keydown: function(e) { - var key, notification, notifications, op, target, thread, threadRoot, _i, _len, _ref; + var form, key, notification, notifications, op, target, thread, threadRoot, _i, _len, _ref; if (!(key = Keybinds.keyCode(e))) { return; } @@ -15827,7 +15804,9 @@ ThreadUpdater.update(); break; case 'index': - Index.update(); + if (Conf['JSON Navigation']) { + Index.update(); + } } break; case Conf['Watch']: @@ -15853,7 +15832,7 @@ }); break; case Conf['Front page']: - if (g.VIEW === 'index') { + if (Conf['JSON Navigation'] && g.VIEW === 'index') { Index.userPageNav(0); } else { window.location = "/" + g.BOARD + "/"; @@ -15863,19 +15842,39 @@ $.open("/" + g.BOARD + "/"); break; case Conf['Next page']: - if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'all pages')) { + if (g.VIEW !== 'index') { return; } - $('.next button, .next a', Index.pagelist).click(); + if (Conf['JSON Navigation']) { + if (Conf['Index Mode'] !== 'all pages') { + $('.next button', Index.pagelist).click(); + } + } else { + if (form = $('.next form')) { + window.location = form.action; + } + } break; case Conf['Previous page']: - if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'all pages')) { + if (g.VIEW !== 'index') { return; } - $('.prev button, .prev a', Index.pagelist).click(); + if (Conf['JSON Navigation']) { + if (Conf['Index Mode'] !== 'all pages') { + $('.prev button', Index.pagelist).click(); + } + } else { + if (form = $('.prev form')) { + window.location = form.action; + } + } break; case Conf['Search form']: - Index.searchInput.focus(); + if (Conf['JSON Navigation']) { + Index.searchInput.focus(); + } else { + $.id('search-btn').click(); + } break; case Conf['Paged mode']: if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'paged')) { @@ -15901,6 +15900,19 @@ } Index.cycleSortType(); break; + case Conf['Open catalog']: + if (Conf['External Catalog']) { + window.location = CatalogLinks.external(g.BOARD.ID); + } else { + if (!Conf['JSON Navigation']) { + return window.location = "/" + g.BOARD + "/catalog"; + } + if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'catalog')) { + return; + } + Index.setIndexMode('catalog'); + } + break; case Conf['Next thread']: if (g.VIEW !== 'index') { return; @@ -16033,7 +16045,7 @@ if (g.VIEW !== 'index') { return; } - url = "/" + thread.board + "/res/" + thread; + url = "/" + thread.board + "/thread/" + thread; if (tab) { return $.open(url); } else { @@ -16651,7 +16663,7 @@ if (threadID) { view = 'thread'; } else { - pageNum = view; + pageNum = +view; view = 'index'; } path = this.pathname; @@ -16660,9 +16672,8 @@ } Navigate.makeBreadCrumb(this.href, view, boardID, threadID); if (this.id !== 'popState') { - history.pushState('internal', '', path); + Navigate.pushState(path); } - Navigate.path = this.pathname; Navigate.setMode(this); if (!(view === 'index' && 'index' === g.VIEW && boardID === g.BOARD.ID)) { Navigate.disconnect(); @@ -16786,6 +16797,10 @@ return Main.handleErrors(errors); } }, + pushState: function(path) { + history.pushState(null, '', path); + return Navigate.path = window.location.pathname; + }, popstate: function() { var a; a = $.el('a', { @@ -17251,7 +17266,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, @@ -17874,6 +17889,7 @@ g.VIEW = (function() { switch (pathname[2]) { case 'res': + case 'thread': return 'thread'; case 'catalog': return 'catalog'; diff --git a/builds/crx/manifest.json b/builds/crx/manifest.json index 61f6c20e7..626f09848 100644 --- a/builds/crx/manifest.json +++ b/builds/crx/manifest.json @@ -15,7 +15,7 @@ "run_at": "document_start" }], "homepage_url": "http://zixaphir.github.com/appchan-x/", - "minimum_chrome_version": "32", + "minimum_chrome_version": "33", "permissions": [ "storage", "http://*/", diff --git a/builds/crx/script.js b/builds/crx/script.js index 4143357d2..e335655e8 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* appchan x - Version 2.9.15 - 2014-04-11 +* appchan x - Version 2.9.15 - 2014-04-19 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -403,8 +403,8 @@ 'Open Gallery': ['g', 'Opens the gallery.'], 'fappeTyme': ['f', 'Fappe Tyme.'], 'werkTyme': ['Shift+w', 'Werk Tyme'], - 'Front page': ['0', 'Jump to page 0.'], - 'Open front page': ['Shift+0', 'Open page 0 in a new tab.'], + 'Front page': ['0', 'Jump to front page.'], + 'Open front page': ['Shift+0', 'Open front page in a new tab.'], 'Next page': ['Shift+Right', 'Jump to the next page.'], 'Previous page': ['Shift+Left', 'Jump to the previous page.'], 'Search form': ['Ctrl+Alt+s', 'Focus the search field on the board index.'], @@ -3419,7 +3419,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); @@ -3453,7 +3453,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); @@ -4186,26 +4186,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; @@ -4221,26 +4202,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'); - }); } }; @@ -5294,6 +5255,7 @@ return +window.location.pathname.split('/')[2]; }, userPageNav: function(pageNum) { + Navigate.pushState(pageNum === 0 ? './' : pageNum); if (Conf['Refreshed Navigation'] && Conf['Index Mode'] !== 'all pages') { return Index.update(pageNum); } else { @@ -5304,7 +5266,7 @@ if (Index.currentPage === pageNum && !Index.root.parentElement) { return; } - history.pushState(null, '', pageNum === 0 ? './' : pageNum); + Navigate.pushState(pageNum === 0 ? './' : pageNum); return Index.pageLoad(pageNum); }, pageLoad: function(pageNum) { @@ -5889,12 +5851,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, @@ -5981,14 +5943,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++) { @@ -5997,7 +5959,7 @@ if (href[0] === '/') { continue; } - quote.href = "/" + boardID + "/res/" + href; + quote.href = "/" + boardID + "/thread/" + href; } return container; }, @@ -6012,7 +5974,7 @@ return $.el('a', { className: 'summary', textContent: text.join(' '), - href: "/" + boardID + "/res/" + threadID + href: "/" + boardID + "/thread/" + threadID }); }, thread: function(board, data, full) { @@ -6053,7 +6015,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) { @@ -6211,7 +6173,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', { @@ -6345,7 +6307,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' @@ -7113,27 +7075,10 @@ href: 'javascript:;', textContent: '\uf196' }); - 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; @@ -7534,7 +7479,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'); @@ -8106,7 +8051,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 }); @@ -8975,7 +8920,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++) { @@ -10839,8 +10786,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; @@ -10956,19 +10907,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); @@ -11073,7 +11045,6 @@ TrashQueue.remove(el); } else { el = $.el((isVideo ? 'video' : 'img'), { - className: 'full-image', src: post.file.URL }); post.file.fullImage = el; @@ -11103,10 +11074,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(); @@ -11127,7 +11099,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; @@ -12183,9 +12155,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 }); @@ -12483,7 +12458,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++; @@ -12552,7 +12527,7 @@ }); } link = $.el('a', { - href: href || ("/" + boardID + "/res/" + threadID), + href: href || ("/" + boardID + "/thread/" + threadID), textContent: data.excerpt, title: data.excerpt }); @@ -15254,7 +15229,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); }); @@ -15754,7 +15729,7 @@ return Conf[hotkey] = key; }, keydown: function(e) { - var key, notification, notifications, op, target, thread, threadRoot, _i, _len, _ref; + var form, key, notification, notifications, op, target, thread, threadRoot, _i, _len, _ref; if (!(key = Keybinds.keyCode(e))) { return; } @@ -15848,7 +15823,9 @@ ThreadUpdater.update(); break; case 'index': - Index.update(); + if (Conf['JSON Navigation']) { + Index.update(); + } } break; case Conf['Watch']: @@ -15874,7 +15851,7 @@ }); break; case Conf['Front page']: - if (g.VIEW === 'index') { + if (Conf['JSON Navigation'] && g.VIEW === 'index') { Index.userPageNav(0); } else { window.location = "/" + g.BOARD + "/"; @@ -15884,19 +15861,39 @@ $.open("/" + g.BOARD + "/"); break; case Conf['Next page']: - if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'all pages')) { + if (g.VIEW !== 'index') { return; } - $('.next button, .next a', Index.pagelist).click(); + if (Conf['JSON Navigation']) { + if (Conf['Index Mode'] !== 'all pages') { + $('.next button', Index.pagelist).click(); + } + } else { + if (form = $('.next form')) { + window.location = form.action; + } + } break; case Conf['Previous page']: - if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'all pages')) { + if (g.VIEW !== 'index') { return; } - $('.prev button, .prev a', Index.pagelist).click(); + if (Conf['JSON Navigation']) { + if (Conf['Index Mode'] !== 'all pages') { + $('.prev button', Index.pagelist).click(); + } + } else { + if (form = $('.prev form')) { + window.location = form.action; + } + } break; case Conf['Search form']: - Index.searchInput.focus(); + if (Conf['JSON Navigation']) { + Index.searchInput.focus(); + } else { + $.id('search-btn').click(); + } break; case Conf['Paged mode']: if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'paged')) { @@ -15922,6 +15919,19 @@ } Index.cycleSortType(); break; + case Conf['Open catalog']: + if (Conf['External Catalog']) { + window.location = CatalogLinks.external(g.BOARD.ID); + } else { + if (!Conf['JSON Navigation']) { + return window.location = "/" + g.BOARD + "/catalog"; + } + if (!(g.VIEW === 'index' && Conf['Index Mode'] !== 'catalog')) { + return; + } + Index.setIndexMode('catalog'); + } + break; case Conf['Next thread']: if (g.VIEW !== 'index') { return; @@ -16054,7 +16064,7 @@ if (g.VIEW !== 'index') { return; } - url = "/" + thread.board + "/res/" + thread; + url = "/" + thread.board + "/thread/" + thread; if (tab) { return $.open(url); } else { @@ -16677,7 +16687,7 @@ if (threadID) { view = 'thread'; } else { - pageNum = view; + pageNum = +view; view = 'index'; } path = this.pathname; @@ -16686,9 +16696,8 @@ } Navigate.makeBreadCrumb(this.href, view, boardID, threadID); if (this.id !== 'popState') { - history.pushState('internal', '', path); + Navigate.pushState(path); } - Navigate.path = this.pathname; Navigate.setMode(this); if (!(view === 'index' && 'index' === g.VIEW && boardID === g.BOARD.ID)) { Navigate.disconnect(); @@ -16812,6 +16821,10 @@ return Main.handleErrors(errors); } }, + pushState: function(path) { + history.pushState(null, '', path); + return Navigate.path = window.location.pathname; + }, popstate: function() { var a; a = $.el('a', { @@ -17275,7 +17288,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, @@ -17892,6 +17905,7 @@ g.VIEW = (function() { switch (pathname[2]) { case 'res': + case 'thread': return 'thread'; case 'catalog': return 'catalog'; diff --git a/builds/updates.xml b/builds/updates.xml index 5c043194a..12f1e8175 100644 --- a/builds/updates.xml +++ b/builds/updates.xml @@ -1,7 +1,7 @@ - + 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 9aed0aa76..67310d13d 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "userjs": "appchan-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 979694e63..1ac78466f 100644 --- a/src/Filtering/PostHiding.coffee +++ b/src/Filtering/PostHiding.coffee @@ -14,15 +14,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/Config.coffee b/src/General/Config.coffee index 8aa4d1eaf..d9db9b44a 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -1029,11 +1029,11 @@ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2); # Board Navigation 'Front page': [ '0' - 'Jump to page 0.' + 'Jump to front page.' ] 'Open front page': [ 'Shift+0' - 'Open page 0 in a new tab.' + 'Open front page in a new tab.' ] 'Next page': [ 'Shift+Right' 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/Index.coffee b/src/General/Index.coffee index a402c8f0c..986f9c1fa 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -424,6 +424,7 @@ Index = +window.location.pathname.split('/')[2] userPageNav: (pageNum) -> + Navigate.pushState if pageNum is 0 then './' else pageNum if Conf['Refreshed Navigation'] and Conf['Index Mode'] isnt 'all pages' Index.update pageNum else @@ -431,7 +432,7 @@ Index = pageNav: (pageNum) -> return if Index.currentPage is pageNum and not Index.root.parentElement - history.pushState null, '', if pageNum is 0 then './' else pageNum + Navigate.pushState if pageNum is 0 then './' else pageNum Index.pageLoad pageNum pageLoad: (pageNum) -> diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 983981c07..f550ad5a3 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -8,7 +8,7 @@ Main = return if g.BOARD.ID in ['z', 'fk'] g.VIEW = switch pathname[2] - when 'res' + when 'res', 'thread' 'thread' when 'catalog' 'catalog' diff --git a/src/General/Navigate.coffee b/src/General/Navigate.coffee index 36b701ede..0ed52dfd9 100644 --- a/src/General/Navigate.coffee +++ b/src/General/Navigate.coffee @@ -226,7 +226,7 @@ Navigate = if threadID view = 'thread' else - pageNum = view + pageNum = +view view = 'index' # path is "/boardID/". See the problem? path = @pathname @@ -234,8 +234,7 @@ Navigate = Navigate.makeBreadCrumb @href, view, boardID, threadID - history.pushState 'internal', '', path unless @id is 'popState' - Navigate.path = @pathname + Navigate.pushState path unless @id is 'popState' Navigate.setMode @ @@ -347,6 +346,10 @@ Navigate = Main.handleErrors errors if errors + pushState: (path) -> + history.pushState null, '', path + Navigate.path = window.location.pathname + popstate: -> a = $.el 'a', href: window.location diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 1a6e450fe..fe9fe80a5 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -356,7 +356,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 ab3b89750..a54818122 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 f99d6cfd2..3117402c8 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -116,8 +116,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. @@ -198,6 +200,7 @@ ImageExpand = error: -> post = Get.postFromNode @ $.rm @ + delete post.file.isReady delete post.file.fullImage # Images can error: # - before the image started loading. @@ -207,11 +210,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 @@ -229,7 +244,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..746b982b4 100755 --- a/src/Images/ImageHover.coffee +++ b/src/Images/ImageHover.coffee @@ -25,7 +25,6 @@ ImageHover = TrashQueue.remove el else el = $.el (if isVideo then 'video' else 'img'), - className: 'full-image' src: post.file.URL post.file.fullImage = el {thumb} = post.file @@ -46,8 +45,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 +63,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 +81,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 fd875db0a..38de66ec2 100755 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -74,7 +74,7 @@ Keybinds = when 'thread' ThreadUpdater.update() when 'index' - Index.update() + if Conf['JSON Navigation'] then Index.update() when Conf['Watch'] ThreadWatcher.toggle thread # Images @@ -90,20 +90,33 @@ Keybinds = FappeTyme.cb.toggle.call {name: 'werk'} # Board Navigation when Conf['Front page'] - if g.VIEW is 'index' + if Conf['JSON Navigation'] and g.VIEW is 'index' Index.userPageNav 0 else window.location = "/#{g.BOARD}/" when Conf['Open front page'] $.open "/#{g.BOARD}/" when Conf['Next page'] - return unless g.VIEW is 'index' and Conf['Index Mode'] isnt 'all pages' - $('.next button, .next a', Index.pagelist).click() + return unless g.VIEW is 'index' + if Conf['JSON Navigation'] + if Conf['Index Mode'] isnt 'all pages' + $('.next button', Index.pagelist).click() + else + if form = $ '.next form' + window.location = form.action when Conf['Previous page'] - return unless g.VIEW is 'index' and Conf['Index Mode'] isnt 'all pages' - $('.prev button, .prev a', Index.pagelist).click() + return unless g.VIEW is 'index' + if Conf['JSON Navigation'] + if Conf['Index Mode'] isnt 'all pages' + $('.prev button', Index.pagelist).click() + else + if form = $ '.prev form' + window.location = form.action when Conf['Search form'] - Index.searchInput.focus() + if Conf['JSON Navigation'] + Index.searchInput.focus() + else + $.id('search-btn').click() when Conf['Paged mode'] return unless g.VIEW is 'index' and Conf['Index Mode'] isnt 'paged' Index.setIndexMode 'paged' @@ -116,6 +129,13 @@ Keybinds = when Conf['Cycle sort type'] return unless g.VIEW is 'index' Index.cycleSortType() + when Conf['Open catalog'] + if Conf['External Catalog'] + window.location = CatalogLinks.external(g.BOARD.ID) + else + return window.location = "/#{g.BOARD}/catalog" unless Conf['JSON Navigation'] + return unless g.VIEW is 'index' and Conf['Index Mode'] isnt 'catalog' + Index.setIndexMode 'catalog' # Thread Navigation when Conf['Next thread'] return if g.VIEW isnt 'index' @@ -219,7 +239,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 53ba9e438..f2996409b 100755 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -245,8 +245,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 6821fa18b..e876fe65e 100755 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -114,7 +114,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 @@ -153,7 +153,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 26f58027c..a8bf2c32a 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -219,7 +219,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 diff --git a/src/Quotelinks/QuoteBacklink.coffee b/src/Quotelinks/QuoteBacklink.coffee index a475aef42..ff4d3c5e8 100755 --- a/src/Quotelinks/QuoteBacklink.coffee +++ b/src/Quotelinks/QuoteBacklink.coffee @@ -56,7 +56,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}