diff --git a/4chan_x.user.js b/4chan_x.user.js index ee6f439ae..3be948806 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -836,6 +836,26 @@ open: function(url) { return (GM_openInTab || window.open)(url, '_blank'); }, + debounce: function(wait, fn) { + var args, exec, that, timeout; + timeout = null; + that = null; + args = null; + exec = function() { + fn.apply(that, args); + return timeout = null; + }; + return function() { + args = arguments; + that = this; + if (timeout) { + clearTimeout(timeout); + } else { + exec(); + } + return timeout = setTimeout(exec, wait); + }; + }, queueTask: (function() { var execTask, taskChannel, taskQueue; taskQueue = []; @@ -4926,6 +4946,10 @@ }, node: function() { var ID, post, posts, _ref; + Unread.thread = this; + Unread.lastReadPost = $.get("lastReadPosts." + this.board, { + threads: {} + }).threads[this] || 0; Unread.yourPosts = []; Unread.posts = []; Unread.title = d.title; @@ -4938,26 +4962,26 @@ } } Unread.addPosts(posts); - Unread.update(); $.on(d, 'ThreadUpdate', Unread.onUpdate); $.on(d, 'QRPostSuccessful', Unread.post); return $.on(d, 'scroll visibilitychange', Unread.read); }, addPosts: function(newPosts) { - var post, _i, _len, _ref; + var ID, post, _i, _len; for (_i = 0, _len = newPosts.length; _i < _len; _i++) { post = newPosts[_i]; - if (!((_ref = post.ID, __indexOf.call(Unread.yourPosts, _ref) >= 0) || post.isHidden)) { + ID = post.ID; + if (!(ID <= Unread.lastReadPost || post.isHidden || __indexOf.call(Unread.yourPosts, ID) >= 0)) { Unread.posts.push(post); } } - return Unread.read(); + Unread.read(); + return Unread.update(); }, onUpdate: function(e) { if (!e.detail[404]) { - Unread.addPosts(e.detail.newPosts); + return Unread.addPosts(e.detail.newPosts); } - return Unread.update(); }, post: function(e) { return Unread.yourPosts.push(+e.detail.postID); @@ -4979,11 +5003,21 @@ if (!i) { return; } + Unread.lastReadPost = Unread.posts[i - 1].ID; + Unread.saveLastReadPost(); Unread.posts = Unread.posts.slice(i); if (e) { return Unread.update(); } }, + saveLastReadPost: $.debounce($.SECOND, function() { + var lastReadPosts; + lastReadPosts = $.get("lastReadPosts." + Unread.thread.board, { + threads: {} + }); + lastReadPosts.threads[Unread.thread] = Unread.lastReadPost; + return $.set("lastReadPosts." + Unread.thread.board, lastReadPosts); + }), update: function() { var count; count = Unread.posts.length; diff --git a/Gruntfile.js b/Gruntfile.js index ac5216778..5f42ec483 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -60,15 +60,20 @@ module.exports = function(grunt) { } }, watch: { - files: [ - 'package.json', - 'lib/**/*.coffee', - 'src/**/*.coffee', - 'src/**/*.js', - 'css/**/*.css', - 'img/*' - ], - tasks: 'default' + all: { + options: { + interrupt: true + }, + files: [ + 'package.json', + 'lib/**/*.coffee', + 'src/**/*.coffee', + 'src/**/*.js', + 'css/**/*.css', + 'img/*' + ], + tasks: 'default' + } }, clean: { tmp: 'tmp' diff --git a/changelog b/changelog index ceaaf8abe..3adb79ac4 100644 --- a/changelog +++ b/changelog @@ -1,6 +1,7 @@ -alpha +beta - Mayhem Major rewrite of 4chan X. + New feature, the Header: Access the list of boards directly from the Header. From the Header's menu, access to: @@ -8,21 +9,31 @@ alpha Quick Reply shortcut Image Expansion Can be auto-hidden. + QR changes: Creating threads outside of the index is now possible. Selection-to-quote also applies to selected text inside the post, not just inside the comment. Quoting the OP will not insert the >>opnumber anymore unless the QR was already opened. + Image Expansion changes: Expanding OP images won't squish replies anymore. There is now a setting to allow expanding spoilers. + + Thread Updater changes: + The Thread Updater will pause when offline, and resume when online. + Added an option to always auto-scroll to the bottom. + + Unread posts changes: + Read posts won't be marked as unread after reloading a thread. + Visible posts will not be taken into account towards the unread count. + + Thread/Post Hiding changes: + Added Thread & Post Hiding in the Menu, with individual settings. + Thread & Post Hiding Buttons can now be disabled in the settings. + Recursive Hiding will be automatically applied when manually hiding a post. + Added touch and multi-touch support for dragging windows. - The Thread Updater will pause when offline, and resume when online. - Added an option to always auto-scroll to the bottom. Thread Stats post and file count will adjust with deleted posts. - Added Thread & Post Hiding in the Menu, with individual settings. - Thread & Post Hiding Buttons can now be disabled in the settings. - Recursive Hiding will be automatically applied when manually hiding a post. - Visible posts will not be taken into account towards the unread count. Added [math] tags keybind. Fix Chrome's install warning saying that 4chan X would execute on all domains. Fix Quote Backlinks not affecting inlined quotes. diff --git a/lib/$.coffee b/lib/$.coffee index 4490a4f32..a6b3d7862 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -139,6 +139,24 @@ $.extend $, root.dispatchEvent new CustomEvent event, {bubbles: true, detail} open: (url) -> (GM_openInTab or window.open) url, '_blank' + debounce: (wait, fn) -> + timeout = null + that = null + args = null + exec = -> + fn.apply that, args + timeout = null + -> + args = arguments + that = this + if timeout + # stop current reset + clearTimeout timeout + else + exec() + + # after wait, let next invocation execute immediately + timeout = setTimeout exec, wait queueTask: do -> # inspired by https://www.w3.org/Bugs/Public/show_bug.cgi?id=15007 taskQueue = [] diff --git a/src/features.coffee b/src/features.coffee index a1f876c54..8bd2e0332 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -3293,28 +3293,30 @@ Unread = cb: @node node: -> - Unread.yourPosts = [] - Unread.posts = [] - Unread.title = d.title + Unread.thread = @ + Unread.lastReadPost = $.get("lastReadPosts.#{@board}", threads: {}).threads[@] or 0 + Unread.yourPosts = [] + Unread.posts = [] + Unread.title = d.title posts = [] for ID, post of @posts posts.push post if post.isReply Unread.addPosts posts - Unread.update() $.on d, 'ThreadUpdate', Unread.onUpdate $.on d, 'QRPostSuccessful', Unread.post $.on d, 'scroll visibilitychange', Unread.read addPosts: (newPosts) -> for post in newPosts - unless post.ID in Unread.yourPosts or post.isHidden + {ID} = post + unless ID <= Unread.lastReadPost or post.isHidden or ID in Unread.yourPosts Unread.posts.push post Unread.read() + Unread.update() onUpdate: (e) -> unless e.detail[404] Unread.addPosts e.detail.newPosts - Unread.update() post: (e) -> Unread.yourPosts.push +e.detail.postID @@ -3327,9 +3329,17 @@ Unread = break if bottom > height # post is not completely read return unless i + Unread.lastReadPost = Unread.posts[i - 1].ID + Unread.saveLastReadPost() Unread.posts = Unread.posts[i..] Unread.update() if e + saveLastReadPost: $.debounce($.SECOND, -> + lastReadPosts = $.get "lastReadPosts.#{Unread.thread.board}", threads: {} + lastReadPosts.threads[Unread.thread] = Unread.lastReadPost + $.set "lastReadPosts.#{Unread.thread.board}", lastReadPosts + ) + update: -> count = Unread.posts.length