diff --git a/CHANGELOG.md b/CHANGELOG.md index 772fa3131..8c3537620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,15 @@ **MayhemYDG**: - Tiny fixes +- Add page count to thread stats +- Better performance for Fit Height by using vh **seaweedchan**: +- Added OpenSettings event on 4chan X settings/sections open for userscripts like OneeChan and 4chan Style Script +- Changed defaults that use the arrow keys to shift+arrow key to not conflict with scrolling +- Made Mayhem's page count in thread stats optional +- Small bug fixes +- Fix YouTube videos in Firefox taking z-index priority +- Fix Persistent QR not working for /f/ - New image expansion option: `Advance on contract`. Advances to next post unless Fappe Tyme is enabled (temporary) - Change `.qr-link` to `.qr-link-container` and `.qr-link>a` to `.qr-link` - Update /q/'s posting cooldown @@ -16,6 +24,10 @@ **Wohlfe**: - Add /pol/ archiving for FoolzaShit +**zixaphir**: +- New option: `Image Prefetching`. Adds a toggle to the header menu for per-thread prefetching. +- Make Advance on contract work with Fappe Tyme + ### v2.0.4 *2013-05-15* **MayhemYDG**: diff --git a/LICENSE b/LICENSE index 2a1ed0010..1e7622f68 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* appchan x - Version 2.0.4 - 2013-05-23 +* appchan x - Version 2.0.4 - 2013-05-27 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE diff --git a/builds/appchan-x.js b/builds/appchan-x.js index bdb77753d..93ac23922 100644 --- a/builds/appchan-x.js +++ b/builds/appchan-x.js @@ -20,7 +20,7 @@ // ==/UserScript== /* -* appchan x - Version 2.0.4 - 2013-05-23 +* appchan x - Version 2.0.4 - 2013-05-27 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -186,6 +186,7 @@ 'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'], 'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'], 'Thread Stats': [true, 'Display reply and image count.'], + 'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'], 'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'], 'Thread Watcher': [true, 'Bookmark threads.'], 'Toggleable Thread Watcher': [false, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'], @@ -350,7 +351,7 @@ }, time: '%m/%d/%y(%a)%H:%M:%S', backlink: '>>%id', - fileInfo: '%l (%p%s, %r)', + fileInfo: '%L (%p%s, %r)', favicon: 'ferongr', usercss: "/* Tripcode Italics: */\n/*\nspan.postertrip {\nfont-style: italic;\n}\n*/\n\n/* Add a rounded border to thumbnails (but not expanded images): */\n/*\n.fileThumb > img:first-child {\nborder: solid 2px rgba(0,0,100,0.5);\nborder-radius: 10px;\n}\n*/\n\n/* Make highlighted posts look inset on the page: */\n/*\ndiv.post:target,\ndiv.post.highlight {\nbox-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);\n}\n*/", hotkeys: { @@ -373,11 +374,11 @@ 'fappeTyme': ['f', 'Fappe Tyme.'], 'Front page': ['0', 'Jump to page 0.'], 'Open front page': ['Shift+0', 'Open page 0 in a new tab.'], - 'Next page': ['Right', 'Jump to the next page.'], - 'Previous page': ['Left', 'Jump to the previous page.'], + 'Next page': ['Shift+Right', 'Jump to the next page.'], + 'Previous page': ['Shift+Left', 'Jump to the previous page.'], 'Open catalog': ['Shift+c', 'Open the catalog of the current board'], - 'Next thread': ['Down', 'See next thread.'], - 'Previous thread': ['Up', 'See previous thread.'], + 'Next thread': ['Shift+Down', 'See next thread.'], + 'Previous thread': ['Shift+Up', 'See previous thread.'], 'Expand thread': ['Ctrl+e', 'Expand thread.'], 'Open thread': ['o', 'Open thread in current tab.'], 'Open thread tab': ['Shift+o', 'Open thread in new tab.'], @@ -4164,7 +4165,7 @@ } flag = flagCode ? ("  + flagCode + ") : ''; if (file != null ? file.isDeleted : void 0) { - fileHtml = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; + fileHtml = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; } else if (file) { ext = file.name.slice(-3); if (!file.twidth && !file.theight && ext === 'gif') { @@ -6671,7 +6672,7 @@ regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, el: function() { return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" }); }, title: { @@ -6688,7 +6689,7 @@ style: 'border: 0; width: 150px; height: 45px;', el: function() { return $.el('object', { - innerHTML: "" + innerHTML: "" }); } }, @@ -6696,7 +6697,7 @@ regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, el: function() { return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.name + src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" }); }, title: { @@ -6711,8 +6712,8 @@ LiveLeak: { regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, el: function() { - return $.el('iframe', { - src: "http://www.liveleak.com/e/" + this.name + "?autostart=true" + return $.el('object', { + innerHTML: "" }); } }, @@ -6885,7 +6886,11 @@ this.db = new DataBoard('yourPosts'); $.ready(this.initReady); if (Conf['Persistent QR']) { - $.on(d, '4chanXInitFinished', this.persist); + if (g.BOARD.ID !== 'f') { + $.on(d, '4chanXInitFinished', this.persist); + } else { + $.ready(this.persist); + } } Post.prototype.callbacks.push({ name: 'Quick Reply', @@ -7150,6 +7155,9 @@ list = $("#list-" + type, QR.nodes.el); for (_i = 0, _len = arr.length; _i < _len; _i++) { val = arr[_i]; + if (!val) { + continue; + } $.add(list, $.el('option', { textContent: val })); @@ -8456,14 +8464,11 @@ } }, setFitness: function() { - var checked; - - checked = this.checked; - (checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); + (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); if (this.name !== 'Fit height') { return; } - if (checked) { + if (this.checked) { $.on(window, 'resize', ImageExpand.resize); if (!ImageExpand.style) { ImageExpand.style = $.addStyle(null); @@ -8474,6 +8479,9 @@ } } }, + resize: function() { + return ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:" + doc.clientHeight + "px}"; + }, toggle: function(post) { var headRect, node, rect, root, thumb, top; @@ -8669,9 +8677,6 @@ }; } }, - resize: function() { - return ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:" + doc.clientHeight + "px}"; - }, menuToggle: function(e) { return ImageExpand.opmenu.toggle(e, this, g); } @@ -8792,7 +8797,7 @@ return $.event('AddMenuEntry', { type: 'header', el: prefetch, - order: 120 + order: 104 }); }, node: function() { @@ -9278,20 +9283,23 @@ } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: "0 / 0", - id: 'thread-stats' + innerHTML: "0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : ""), + id: 'thread-stats', + title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "") }); $.ready(function() { return Header.addShortcut(sc); }); } else { - this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "
0 / 0
"); + this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "
0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : "") + "
"); $.ready(function() { return $.add(d.body, sc); }); } this.postCountEl = $('#post-count', sc); this.fileCountEl = $('#file-count', sc); + this.pageCountEl = $('#page-count', sc); + this.lastModified = '0'; return Thread.prototype.callbacks.push({ name: 'Thread Stats', cb: this.node @@ -9311,6 +9319,7 @@ } } ThreadStats.thread = this; + ThreadStats.fetchPage(); ThreadStats.update(postCount, fileCount); return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate); }, @@ -9331,6 +9340,43 @@ fileCountEl.textContent = fileCount; (thread.postLimit && !thread.isSticky ? $.addClass : $.rmClass)(postCountEl, 'warning'); return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning'); + }, + fetchPage: function() { + if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) { + return; + } + setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); + return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", { + onload: ThreadStats.onThreadsLoad + }, { + headers: { + 'If-Modified-Since': ThreadStats.lastModified + } + }); + }, + onThreadsLoad: function() { + var page, pages, thread, _i, _j, _len, _len1, _ref; + + if (!Conf["Page Count in Stats"]) { + return; + } + ThreadStats.lastModified = this.getResponseHeader('Last-Modified'); + if (this.status !== 200) { + return; + } + pages = JSON.parse(this.response); + for (_i = 0, _len = pages.length; _i < _len; _i++) { + page = pages[_i]; + _ref = page.threads; + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + thread = _ref[_j]; + if (thread.no === ThreadStats.thread.ID) { + ThreadStats.pageCountEl.textContent = page.page; + (page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); + return; + } + } + } } }; @@ -12766,11 +12812,17 @@ $.open("/" + g.BOARD + "/#delform"); break; case Conf['Next page']: + if (g.VIEW === 'thread') { + return; + } if (form = $('.next form')) { window.location = form.action; } break; case Conf['Previous page']: + if (g.VIEW === 'thread') { + return; + } if (form = $('.prev form')) { window.location = form.action; } @@ -13485,7 +13537,8 @@ (sectionToOpen ? sectionToOpen : links[0]).click(); $.on($('.close', dialog), 'click', Settings.close); $.on(overlay, 'click', Settings.close); - return $.add(d.body, [overlay, dialog]); + $.add(d.body, [overlay, dialog]); + return $.event('OpenSettings', null, dialog); }, close: function() { if (!Settings.dialog) { @@ -13521,7 +13574,8 @@ $.rmAll(section); section.className = "section-" + this.hyphenatedTitle; this.open(section, mode); - return section.scrollTop = 0; + section.scrollTop = 0; + return $.event('OpenSettings', null, section); }, main: function(section) { var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref; diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index d93ff8d7c..7d9c615c4 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -20,7 +20,7 @@ // ==/UserScript== /* -* appchan x - Version 2.0.4 - 2013-05-23 +* appchan x - Version 2.0.4 - 2013-05-27 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -186,6 +186,7 @@ 'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'], 'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'], 'Thread Stats': [true, 'Display reply and image count.'], + 'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'], 'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'], 'Thread Watcher': [true, 'Bookmark threads.'], 'Toggleable Thread Watcher': [false, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'], @@ -351,7 +352,7 @@ }, time: '%m/%d/%y(%a)%H:%M:%S', backlink: '>>%id', - fileInfo: '%l (%p%s, %r)', + fileInfo: '%L (%p%s, %r)', favicon: 'ferongr', usercss: "/* Tripcode Italics: */\n/*\nspan.postertrip {\nfont-style: italic;\n}\n*/\n\n/* Add a rounded border to thumbnails (but not expanded images): */\n/*\n.fileThumb > img:first-child {\nborder: solid 2px rgba(0,0,100,0.5);\nborder-radius: 10px;\n}\n*/\n\n/* Make highlighted posts look inset on the page: */\n/*\ndiv.post:target,\ndiv.post.highlight {\nbox-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);\n}\n*/", hotkeys: { @@ -374,11 +375,11 @@ 'fappeTyme': ['f', 'Fappe Tyme.'], 'Front page': ['0', 'Jump to page 0.'], 'Open front page': ['Shift+0', 'Open page 0 in a new tab.'], - 'Next page': ['Right', 'Jump to the next page.'], - 'Previous page': ['Left', 'Jump to the previous page.'], + 'Next page': ['Shift+Right', 'Jump to the next page.'], + 'Previous page': ['Shift+Left', 'Jump to the previous page.'], 'Open catalog': ['Shift+c', 'Open the catalog of the current board'], - 'Next thread': ['Down', 'See next thread.'], - 'Previous thread': ['Up', 'See previous thread.'], + 'Next thread': ['Shift+Down', 'See next thread.'], + 'Previous thread': ['Shift+Up', 'See previous thread.'], 'Expand thread': ['Ctrl+e', 'Expand thread.'], 'Open thread': ['o', 'Open thread in current tab.'], 'Open thread tab': ['Shift+o', 'Open thread in new tab.'], @@ -4160,7 +4161,7 @@ } flag = flagCode ? ("  + flagCode + ") : ''; if (file != null ? file.isDeleted : void 0) { - fileHtml = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; + fileHtml = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; } else if (file) { ext = file.name.slice(-3); if (!file.twidth && !file.theight && ext === 'gif') { @@ -6655,7 +6656,7 @@ regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, el: function() { return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" }); }, title: { @@ -6672,7 +6673,7 @@ style: 'border: 0; width: 150px; height: 45px;', el: function() { return $.el('object', { - innerHTML: "" + innerHTML: "" }); } }, @@ -6680,7 +6681,7 @@ regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, el: function() { return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.name + src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" }); }, title: { @@ -6695,8 +6696,8 @@ LiveLeak: { regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, el: function() { - return $.el('iframe', { - src: "http://www.liveleak.com/e/" + this.name + "?autostart=true" + return $.el('object', { + innerHTML: "" }); } }, @@ -6869,7 +6870,11 @@ this.db = new DataBoard('yourPosts'); $.ready(this.initReady); if (Conf['Persistent QR']) { - $.on(d, '4chanXInitFinished', this.persist); + if (g.BOARD.ID !== 'f') { + $.on(d, '4chanXInitFinished', this.persist); + } else { + $.ready(this.persist); + } } Post.prototype.callbacks.push({ name: 'Quick Reply', @@ -7134,6 +7139,9 @@ list = $("#list-" + type, QR.nodes.el); for (_i = 0, _len = arr.length; _i < _len; _i++) { val = arr[_i]; + if (!val) { + continue; + } $.add(list, $.el('option', { textContent: val })); @@ -8465,22 +8473,7 @@ } }, setFitness: function() { - var checked; - - checked = this.checked; - (checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); - if (this.name !== 'Fit height') { - return; - } - if (checked) { - $.on(window, 'resize', ImageExpand.resize); - if (!ImageExpand.style) { - ImageExpand.style = $.addStyle(null); - } - return ImageExpand.resize(); - } else { - return $.off(window, 'resize', ImageExpand.resize); - } + return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); } }, toggle: function(post) { @@ -8678,9 +8671,6 @@ }; } }, - resize: function() { - return ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:" + doc.clientHeight + "px}"; - }, menuToggle: function(e) { return ImageExpand.opmenu.toggle(e, this, g); } @@ -8801,7 +8791,7 @@ return $.event('AddMenuEntry', { type: 'header', el: prefetch, - order: 120 + order: 104 }); }, node: function() { @@ -9287,20 +9277,23 @@ } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: "0 / 0", - id: 'thread-stats' + innerHTML: "0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : ""), + id: 'thread-stats', + title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "") }); $.ready(function() { return Header.addShortcut(sc); }); } else { - this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "
0 / 0
"); + this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "
0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : "") + "
"); $.ready(function() { return $.add(d.body, sc); }); } this.postCountEl = $('#post-count', sc); this.fileCountEl = $('#file-count', sc); + this.pageCountEl = $('#page-count', sc); + this.lastModified = '0'; return Thread.prototype.callbacks.push({ name: 'Thread Stats', cb: this.node @@ -9320,6 +9313,7 @@ } } ThreadStats.thread = this; + ThreadStats.fetchPage(); ThreadStats.update(postCount, fileCount); return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate); }, @@ -9340,6 +9334,43 @@ fileCountEl.textContent = fileCount; (thread.postLimit && !thread.isSticky ? $.addClass : $.rmClass)(postCountEl, 'warning'); return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning'); + }, + fetchPage: function() { + if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) { + return; + } + setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); + return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", { + onload: ThreadStats.onThreadsLoad + }, { + headers: { + 'If-Modified-Since': ThreadStats.lastModified + } + }); + }, + onThreadsLoad: function() { + var page, pages, thread, _i, _j, _len, _len1, _ref; + + if (!Conf["Page Count in Stats"]) { + return; + } + ThreadStats.lastModified = this.getResponseHeader('Last-Modified'); + if (this.status !== 200) { + return; + } + pages = JSON.parse(this.response); + for (_i = 0, _len = pages.length; _i < _len; _i++) { + page = pages[_i]; + _ref = page.threads; + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + thread = _ref[_j]; + if (thread.no === ThreadStats.thread.ID) { + ThreadStats.pageCountEl.textContent = page.page; + (page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); + return; + } + } + } } }; @@ -12775,11 +12806,17 @@ $.open("/" + g.BOARD + "/#delform"); break; case Conf['Next page']: + if (g.VIEW === 'thread') { + return; + } if (form = $('.next form')) { window.location = form.action; } break; case Conf['Previous page']: + if (g.VIEW === 'thread') { + return; + } if (form = $('.prev form')) { window.location = form.action; } @@ -13494,7 +13531,8 @@ (sectionToOpen ? sectionToOpen : links[0]).click(); $.on($('.close', dialog), 'click', Settings.close); $.on(overlay, 'click', Settings.close); - return $.add(d.body, [overlay, dialog]); + $.add(d.body, [overlay, dialog]); + return $.event('OpenSettings', null, dialog); }, close: function() { if (!Settings.dialog) { @@ -13530,7 +13568,8 @@ $.rmAll(section); section.className = "section-" + this.hyphenatedTitle; this.open(section, mode); - return section.scrollTop = 0; + section.scrollTop = 0; + return $.event('OpenSettings', null, section); }, main: function(section) { var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref; diff --git a/builds/crx/script.js b/builds/crx/script.js index 75776790f..9dc79b956 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* appchan x - Version 2.0.4 - 2013-05-23 +* appchan x - Version 2.0.4 - 2013-05-27 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -167,6 +167,7 @@ 'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'], 'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'], 'Thread Stats': [true, 'Display reply and image count.'], + 'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'], 'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'], 'Thread Watcher': [true, 'Bookmark threads.'], 'Toggleable Thread Watcher': [false, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'], @@ -331,7 +332,7 @@ }, time: '%m/%d/%y(%a)%H:%M:%S', backlink: '>>%id', - fileInfo: '%l (%p%s, %r)', + fileInfo: '%L (%p%s, %r)', favicon: 'ferongr', usercss: "/* Tripcode Italics: */\n/*\nspan.postertrip {\nfont-style: italic;\n}\n*/\n\n/* Add a rounded border to thumbnails (but not expanded images): */\n/*\n.fileThumb > img:first-child {\nborder: solid 2px rgba(0,0,100,0.5);\nborder-radius: 10px;\n}\n*/\n\n/* Make highlighted posts look inset on the page: */\n/*\ndiv.post:target,\ndiv.post.highlight {\nbox-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);\n}\n*/", hotkeys: { @@ -354,11 +355,11 @@ 'fappeTyme': ['f', 'Fappe Tyme.'], 'Front page': ['0', 'Jump to page 0.'], 'Open front page': ['Shift+0', 'Open page 0 in a new tab.'], - 'Next page': ['Right', 'Jump to the next page.'], - 'Previous page': ['Left', 'Jump to the previous page.'], + 'Next page': ['Shift+Right', 'Jump to the next page.'], + 'Previous page': ['Shift+Left', 'Jump to the previous page.'], 'Open catalog': ['Shift+c', 'Open the catalog of the current board'], - 'Next thread': ['Down', 'See next thread.'], - 'Previous thread': ['Up', 'See previous thread.'], + 'Next thread': ['Shift+Down', 'See next thread.'], + 'Previous thread': ['Shift+Up', 'See previous thread.'], 'Expand thread': ['Ctrl+e', 'Expand thread.'], 'Open thread': ['o', 'Open thread in current tab.'], 'Open thread tab': ['Shift+o', 'Open thread in new tab.'], @@ -4161,7 +4162,7 @@ } flag = flagCode ? ("  + flagCode + ") : ''; if (file != null ? file.isDeleted : void 0) { - fileHtml = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; + fileHtml = isOP ? ("
") + ("File deleted.") + "
" : ("
") + ("File deleted.") + "
"; } else if (file) { ext = file.name.slice(-3); if (!file.twidth && !file.theight && ext === 'gif') { @@ -6656,7 +6657,7 @@ regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/, el: function() { return $.el('iframe', { - src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque" }); }, title: { @@ -6673,7 +6674,7 @@ style: 'border: 0; width: 150px; height: 45px;', el: function() { return $.el('object', { - innerHTML: "" + innerHTML: "" }); } }, @@ -6681,7 +6682,7 @@ regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/, el: function() { return $.el('iframe', { - src: "//player.vimeo.com/video/" + this.name + src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque" }); }, title: { @@ -6696,8 +6697,8 @@ LiveLeak: { regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/, el: function() { - return $.el('iframe', { - src: "http://www.liveleak.com/e/" + this.name + "?autostart=true" + return $.el('object', { + innerHTML: "" }); } }, @@ -6870,7 +6871,11 @@ this.db = new DataBoard('yourPosts'); $.ready(this.initReady); if (Conf['Persistent QR']) { - $.on(d, '4chanXInitFinished', this.persist); + if (g.BOARD.ID !== 'f') { + $.on(d, '4chanXInitFinished', this.persist); + } else { + $.ready(this.persist); + } } Post.prototype.callbacks.push({ name: 'Quick Reply', @@ -7136,6 +7141,9 @@ list = $("#list-" + type, QR.nodes.el); for (_i = 0, _len = arr.length; _i < _len; _i++) { val = arr[_i]; + if (!val) { + continue; + } $.add(list, $.el('option', { textContent: val })); @@ -8442,22 +8450,7 @@ } }, setFitness: function() { - var checked; - - checked = this.checked; - (checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); - if (this.name !== 'Fit height') { - return; - } - if (checked) { - $.on(window, 'resize', ImageExpand.resize); - if (!ImageExpand.style) { - ImageExpand.style = $.addStyle(null); - } - return ImageExpand.resize(); - } else { - return $.off(window, 'resize', ImageExpand.resize); - } + return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); } }, toggle: function(post) { @@ -8655,9 +8648,6 @@ }; } }, - resize: function() { - return ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:" + doc.clientHeight + "px}"; - }, menuToggle: function(e) { return ImageExpand.opmenu.toggle(e, this, g); } @@ -8778,7 +8768,7 @@ return $.event('AddMenuEntry', { type: 'header', el: prefetch, - order: 120 + order: 104 }); }, node: function() { @@ -9264,20 +9254,23 @@ } if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { - innerHTML: "0 / 0", - id: 'thread-stats' + innerHTML: "0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : ""), + id: 'thread-stats', + title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "") }); $.ready(function() { return Header.addShortcut(sc); }); } else { - this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "
0 / 0
"); + this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "
0 / 0" + (Conf["Page Count in Stats"] ? " / 0" : "") + "
"); $.ready(function() { return $.add(d.body, sc); }); } this.postCountEl = $('#post-count', sc); this.fileCountEl = $('#file-count', sc); + this.pageCountEl = $('#page-count', sc); + this.lastModified = '0'; return Thread.prototype.callbacks.push({ name: 'Thread Stats', cb: this.node @@ -9297,6 +9290,7 @@ } } ThreadStats.thread = this; + ThreadStats.fetchPage(); ThreadStats.update(postCount, fileCount); return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate); }, @@ -9317,6 +9311,43 @@ fileCountEl.textContent = fileCount; (thread.postLimit && !thread.isSticky ? $.addClass : $.rmClass)(postCountEl, 'warning'); return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning'); + }, + fetchPage: function() { + if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) { + return; + } + setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE); + return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", { + onload: ThreadStats.onThreadsLoad + }, { + headers: { + 'If-Modified-Since': ThreadStats.lastModified + } + }); + }, + onThreadsLoad: function() { + var page, pages, thread, _i, _j, _len, _len1, _ref; + + if (!Conf["Page Count in Stats"]) { + return; + } + ThreadStats.lastModified = this.getResponseHeader('Last-Modified'); + if (this.status !== 200) { + return; + } + pages = JSON.parse(this.response); + for (_i = 0, _len = pages.length; _i < _len; _i++) { + page = pages[_i]; + _ref = page.threads; + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + thread = _ref[_j]; + if (thread.no === ThreadStats.thread.ID) { + ThreadStats.pageCountEl.textContent = page.page; + (page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning'); + return; + } + } + } } }; @@ -12757,11 +12788,17 @@ $.open("/" + g.BOARD + "/#delform"); break; case Conf['Next page']: + if (g.VIEW === 'thread') { + return; + } if (form = $('.next form')) { window.location = form.action; } break; case Conf['Previous page']: + if (g.VIEW === 'thread') { + return; + } if (form = $('.prev form')) { window.location = form.action; } @@ -13476,7 +13513,8 @@ (sectionToOpen ? sectionToOpen : links[0]).click(); $.on($('.close', dialog), 'click', Settings.close); $.on(overlay, 'click', Settings.close); - return $.add(d.body, [overlay, dialog]); + $.add(d.body, [overlay, dialog]); + return $.event('OpenSettings', null, dialog); }, close: function() { if (!Settings.dialog) { @@ -13512,7 +13550,8 @@ $.rmAll(section); section.className = "section-" + this.hyphenatedTitle; this.open(section, mode); - return section.scrollTop = 0; + section.scrollTop = 0; + return $.event('OpenSettings', null, section); }, main: function(section) { var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref; diff --git a/src/General/Build.coffee b/src/General/Build.coffee index 1f14e7107..67f6793e5 100644 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -117,11 +117,11 @@ Build = if file?.isDeleted fileHtml = if isOP - "
" + + "
" + "File deleted." + "
" else - "
" + + "
" + "File deleted." + "
" else if file diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 028099a70..2ba5b277a 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -221,6 +221,10 @@ Config = true 'Display reply and image count.' ] + 'Page Count in Stats': [ + false + 'Display the page count in the thread stats as well.' + ] 'Updater and Stats in Header': [ true, 'Places the thread updater and thread stats in the header instead of floating them.' @@ -813,7 +817,7 @@ http://iqdb.org/?url=%TURL backlink: '>>%id' - fileInfo: '%l (%p%s, %r)' + fileInfo: '%L (%p%s, %r)' favicon: 'ferongr' @@ -924,11 +928,11 @@ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2); 'Open page 0 in a new tab.' ] 'Next page': [ - 'Right' + 'Shift+Right' 'Jump to the next page.' ] 'Previous page': [ - 'Left' + 'Shift+Left' 'Jump to the previous page.' ] 'Open catalog': [ @@ -937,11 +941,11 @@ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2); ] # Thread Navigation 'Next thread': [ - 'Down' + 'Shift+Down' 'See next thread.' ] 'Previous thread': [ - 'Up' + 'Shift+Up' 'See previous thread.' ] 'Expand thread': [ diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 032f65953..7ea9d7031 100644 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -93,6 +93,8 @@ Settings = $.add d.body, [overlay, dialog] + $.event 'OpenSettings', null, dialog + close: -> return unless Settings.dialog $.rm Settings.overlay @@ -117,6 +119,7 @@ Settings = section.className = "section-#{@hyphenatedTitle}" @open section, mode section.scrollTop = 0 + $.event 'OpenSettings', null, section main: (section) -> items = {} diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index e6f33bbd8..b74eadd85 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -55,16 +55,20 @@ ImageExpand = $.queueTask func, post return setFitness: -> - {checked} = @ - (if checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-' + (if @checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-' +<% if (type === 'userjs') { %> +# XXX Opera doesn't support CSS vh. return unless @name is 'Fit height' - if checked + if @checked $.on window, 'resize', ImageExpand.resize unless ImageExpand.style ImageExpand.style = $.addStyle null ImageExpand.resize() else $.off window, 'resize', ImageExpand.resize + resize: -> + ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:#{doc.clientHeight}px}" +<% } %> toggle: (post) -> {thumb} = post.file @@ -204,8 +208,5 @@ ImageExpand = $.on input, 'change', $.cb.checked el: label - resize: -> - ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:#{doc.clientHeight}px}" - menuToggle: (e) -> - ImageExpand.opmenu.toggle e, @, g \ No newline at end of file + ImageExpand.opmenu.toggle e, @, g diff --git a/src/Images/ImageLoader.coffee b/src/Images/ImageLoader.coffee index 4146116b5..b4df767d2 100644 --- a/src/Images/ImageLoader.coffee +++ b/src/Images/ImageLoader.coffee @@ -18,7 +18,7 @@ ImageLoader = $.event 'AddMenuEntry', type: 'header' el: prefetch - order: 120 + order: 104 node: -> return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index 8862fd11d..776cb059e 100644 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -151,7 +151,7 @@ Linkify = regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/ el: -> $.el 'iframe', - src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}" + src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}?wmode=opaque" title: api: -> "https://gdata.youtube.com/feeds/api/videos/#{@name}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode" text: -> JSON.parse(@responseText).entry.title.$t @@ -161,13 +161,13 @@ Linkify = style: 'border: 0; width: 150px; height: 45px;' el: -> $.el 'object', - innerHTML: "" + innerHTML: "" Vimeo: regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/ el: -> $.el 'iframe', - src: "//player.vimeo.com/video/#{@name}" + src: "//player.vimeo.com/video/#{@name}?wmode=opaque" title: api: -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{@name}" text: -> JSON.parse(@responseText).title @@ -175,8 +175,8 @@ Linkify = LiveLeak: regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/ el: -> - $.el 'iframe', - src: "http://www.liveleak.com/e/#{@name}?autostart=true" + $.el 'object', + innerHTML: "" audio: regExp: /(.*\.(mp3|ogg|wav))$/ diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index b2b023f4a..f7045e8cf 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -79,9 +79,11 @@ Keybinds = when Conf['Open front page'] $.open "/#{g.BOARD}/#delform" when Conf['Next page'] + return if g.VIEW is 'thread' if form = $ '.next form' window.location = form.action when Conf['Previous page'] + return if g.VIEW is 'thread' if form = $ '.prev form' window.location = form.action when Conf['Open catalog'] diff --git a/src/Monitoring/ThreadStats.coffee b/src/Monitoring/ThreadStats.coffee index 7cb857ca5..8cfae31c5 100644 --- a/src/Monitoring/ThreadStats.coffee +++ b/src/Monitoring/ThreadStats.coffee @@ -4,18 +4,21 @@ ThreadStats = if Conf['Updater and Stats in Header'] @dialog = sc = $.el 'span', - innerHTML: "0 / 0" + innerHTML: "0 / 0#{if Conf["Page Count in Stats"] then " / 0" else ""}" id: 'thread-stats' + title: 'Post Count / File Count' + (if Conf["Page Count in Stats"] then " / Page Count" else "") $.ready -> Header.addShortcut sc else @dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;', - "
0 / 0
" + "
0 / 0#{if Conf["Page Count in Stats"] then " / 0" else ""}
" $.ready => $.add d.body, sc @postCountEl = $ '#post-count', sc @fileCountEl = $ '#file-count', sc + @pageCountEl = $ '#page-count', sc + @lastModified = '0' Thread::callbacks.push name: 'Thread Stats' @@ -28,6 +31,7 @@ ThreadStats = postCount++ fileCount++ if post.file ThreadStats.thread = @ + ThreadStats.fetchPage() ThreadStats.update postCount, fileCount $.on d, 'ThreadUpdate', ThreadStats.onUpdate @@ -41,4 +45,22 @@ ThreadStats = postCountEl.textContent = postCount fileCountEl.textContent = fileCount (if thread.postLimit and !thread.isSticky then $.addClass else $.rmClass) postCountEl, 'warning' - (if thread.fileLimit and !thread.isSticky then $.addClass else $.rmClass) fileCountEl, 'warning' \ No newline at end of file + (if thread.fileLimit and !thread.isSticky then $.addClass else $.rmClass) fileCountEl, 'warning' + + fetchPage: -> + return if ThreadStats.thread.isDead or !Conf["Page Count in Stats"] + setTimeout ThreadStats.fetchPage, 2 * $.MINUTE + $.ajax "//api.4chan.org/#{ThreadStats.thread.board}/threads.json", onload: ThreadStats.onThreadsLoad, + headers: 'If-Modified-Since': ThreadStats.lastModified + + onThreadsLoad: -> + return if !Conf["Page Count in Stats"] + ThreadStats.lastModified = @getResponseHeader 'Last-Modified' + return if @status isnt 200 + pages = JSON.parse @response + for page in pages + for thread in page.threads + if thread.no is ThreadStats.thread.ID + ThreadStats.pageCountEl.textContent = page.page + (if page.page is pages.length - 1 then $.addClass else $.rmClass) ThreadStats.pageCountEl, 'warning' + return diff --git a/src/Posting/QuickReply.coffee b/src/Posting/QuickReply.coffee index 79909909e..011242f38 100644 --- a/src/Posting/QuickReply.coffee +++ b/src/Posting/QuickReply.coffee @@ -3,7 +3,12 @@ QR = @db = new DataBoard 'yourPosts' $.ready @initReady - $.on d, '4chanXInitFinished', @persist if Conf['Persistent QR'] + + if Conf['Persistent QR'] + unless g.BOARD.ID is 'f' + $.on d, '4chanXInitFinished', @persist + else + $.ready @persist Post::callbacks.push name: 'Quick Reply' @@ -209,6 +214,8 @@ QR = loadPersonas: (type, arr) -> list = $ "#list-#{type}", QR.nodes.el for val in arr + # XXX Firefox displays empty