Add Thread Watcher.

This commit is contained in:
Nicolas Stepien 2013-02-17 16:52:15 +01:00
parent 6eac5ca3b2
commit abb8128afd
4 changed files with 216 additions and 9 deletions

View File

@ -43,7 +43,7 @@
*/ */
(function() { (function() {
var $, $$, Anonymize, ArchiveLink, AutoGIF, Board, Build, Clone, Conf, Config, DeleteLink, DownloadLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, Header, ImageExpand, ImageHover, Main, Menu, Notification, Polyfill, Post, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Recursive, Redirect, RelativeDates, ReplyHiding, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, Time, UI, Unread, d, doc, g, var $, $$, Anonymize, ArchiveLink, AutoGIF, Board, Build, Clone, Conf, Config, DeleteLink, DownloadLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, Header, ImageExpand, ImageHover, Main, Menu, Notification, Polyfill, Post, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Recursive, Redirect, RelativeDates, ReplyHiding, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, d, doc, g,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__hasProp = {}.hasOwnProperty, __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
@ -93,8 +93,8 @@
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'], 'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'],
'Thread Stats': [true, 'Display reply and image count.'], 'Thread Stats': [true, 'Display reply and image count.'],
'Thread Watcher': [true, 'Bookmark threads.'], 'Thread Watcher': [true, 'Bookmark threads.'],
'Auto Watch': [true, 'Automatically watch threads that you start.'], 'Auto Watch': [true, 'Automatically watch threads you start.'],
'Auto Watch Reply': [false, 'Automatically watch threads that you reply to.'] 'Auto Watch Reply': [false, 'Automatically watch threads you reply to.']
}, },
Posting: { Posting: {
'Quick Reply': [true, 'WMD.'], 'Quick Reply': [true, 'WMD.'],
@ -1112,7 +1112,7 @@
var link, settings; var link, settings;
link = $.el('a', { link = $.el('a', {
className: 'settings-link', className: 'settings-link',
textContent: '4chan X Settings', textContent: '4chan X Alpha Settings',
href: 'javascript:;' href: 'javascript:;'
}); });
$.on(link, 'click', Settings.open); $.on(link, 'click', Settings.open);
@ -4231,7 +4231,7 @@
this.postCountEl = $('#post-count', this.dialog); this.postCountEl = $('#post-count', this.dialog);
this.fileCountEl = $('#file-count', this.dialog); this.fileCountEl = $('#file-count', this.dialog);
this.fileLimit = (function() { this.fileLimit = (function() {
switch (g.BOARD) { switch (g.BOARD.ID) {
case 'a': case 'a':
case 'b': case 'b':
case 'v': case 'v':
@ -4563,6 +4563,118 @@
} }
}; };
ThreadWatcher = {
init: function() {
if (g.VIEW === 'catalog' || !Conf['Thread Watcher']) {
return;
}
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '<div class=move>Thread Watcher</div>');
$.on(d, 'QRPostSuccessful', this.cb.post);
$.on(d, '4chanXInitFinished', this.ready);
$.sync('WatchedThreads', this.refresh);
return Thread.prototype.callbacks.push({
name: 'Thread Watcher',
cb: this.node
});
},
node: function() {
var favicon, op;
op = this.posts[this];
favicon = $.el('img', {
className: 'favicon'
});
$.on(favicon, 'click', ThreadWatcher.cb.toggle);
$.before($('input', op.nodes.post), favicon);
if (g.VIEW === 'thread' && this.ID === $.get('AutoWatch', 0)) {
ThreadWatcher.watch(this);
return $["delete"]('AutoWatch');
}
},
ready: function() {
ThreadWatcher.refresh();
return $.add(d.body, ThreadWatcher.dialog);
},
refresh: function(watched) {
var ID, board, div, favicon, id, link, nodes, op, props, thread, x, _ref, _ref1;
watched || (watched = $.get('WatchedThreads', {}));
nodes = [$('.move', ThreadWatcher.dialog)];
for (board in watched) {
_ref = watched[board];
for (id in _ref) {
props = _ref[id];
x = $.el('a', {
textContent: '×',
href: 'javascript:;'
});
$.on(x, 'click', ThreadWatcher.cb.x);
link = $.el('a', props);
link.title = link.textContent;
div = $.el('div');
$.add(div, [x, $.tn(' '), link]);
nodes.push(div);
}
}
ThreadWatcher.dialog.innerHTML = '';
$.add(ThreadWatcher.dialog, nodes);
watched = watched[g.BOARD] || {};
_ref1 = g.BOARD.threads;
for (ID in _ref1) {
thread = _ref1[ID];
op = thread.posts[thread];
favicon = $('.favicon', op.nodes.post);
favicon.src = ID in watched ? Favicon["default"] : Favicon.empty;
}
},
cb: {
toggle: function() {
return ThreadWatcher.toggle(Get.postFromNode(this).thread);
},
x: function() {
var thread;
thread = this.nextElementSibling.pathname.split('/');
return ThreadWatcher.unwatch(thread[1], thread[3]);
},
post: function(e) {
var postID, threadID, _ref;
_ref = e.detail, postID = _ref.postID, threadID = _ref.threadID;
if (threadID === '0') {
if (Conf['Auto Watch']) {
return $.set('AutoWatch', +postID);
}
} else if (Conf['Auto Watch Reply']) {
return ThreadWatcher.watch(g.BOARD.threads[threadID]);
}
}
},
toggle: function(thread) {
var op;
op = thread.posts[thread];
if ($('.favicon', op.nodes.post).src === Favicon.empty) {
return ThreadWatcher.watch(thread);
} else {
return ThreadWatcher.unwatch(thread.board, thread.ID);
}
},
unwatch: function(board, threadID) {
var watched;
watched = $.get('WatchedThreads', {});
delete watched[board][threadID];
ThreadWatcher.refresh(watched);
return $.set('WatchedThreads', watched);
},
watch: function(thread) {
var watched, _name;
watched = $.get('WatchedThreads', {});
watched[_name = thread.board] || (watched[_name] = {});
watched[thread.board][thread] = {
href: "/" + thread.board + "/res/" + thread,
textContent: Get.threadExcerpt(thread)
};
ThreadWatcher.refresh(watched);
return $.set('WatchedThreads', watched);
}
};
QR = { QR = {
init: function() { init: function() {
var link; var link;
@ -5945,6 +6057,7 @@
initFeature('Unread', Unread); initFeature('Unread', Unread);
initFeature('Thread Stats', ThreadStats); initFeature('Thread Stats', ThreadStats);
initFeature('Thread Updater', ThreadUpdater); initFeature('Thread Updater', ThreadUpdater);
initFeature('Thread Watcher', ThreadWatcher);
console.timeEnd('All initializations'); console.timeEnd('All initializations');
$.on(d, '4chanMainInit', Main.initStyle); $.on(d, '4chanMainInit', Main.initStyle);
return $.ready(Main.initReady); return $.ready(Main.initReady);

View File

@ -39,8 +39,8 @@ Config =
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'] 'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.']
'Thread Stats': [true, 'Display reply and image count.'] 'Thread Stats': [true, 'Display reply and image count.']
'Thread Watcher': [true, 'Bookmark threads.'] 'Thread Watcher': [true, 'Bookmark threads.']
'Auto Watch': [true, 'Automatically watch threads that you start.'] 'Auto Watch': [true, 'Automatically watch threads you start.']
'Auto Watch Reply': [false, 'Automatically watch threads that you reply to.'] 'Auto Watch Reply': [false, 'Automatically watch threads you reply to.']
Posting: Posting:
'Quick Reply': [true, 'WMD.'] 'Quick Reply': [true, 'WMD.']
'Persistent QR': [false, 'The Quick reply won\'t disappear after posting.'] 'Persistent QR': [false, 'The Quick reply won\'t disappear after posting.']

View File

@ -119,7 +119,7 @@ Settings =
# 4chan X settings link # 4chan X settings link
link = $.el 'a', link = $.el 'a',
className: 'settings-link' className: 'settings-link'
textContent: '4chan X Settings' textContent: '<%= meta.name %> Settings'
href: 'javascript:;' href: 'javascript:;'
$.on link, 'click', Settings.open $.on link, 'click', Settings.open
$.event 'AddMenuEntry', $.event 'AddMenuEntry',
@ -2686,7 +2686,7 @@ ThreadStats =
@postCountEl = $ '#post-count', @dialog @postCountEl = $ '#post-count', @dialog
@fileCountEl = $ '#file-count', @dialog @fileCountEl = $ '#file-count', @dialog
@fileLimit = # XXX boards config, need up to date data on this, check browser @fileLimit = # XXX boards config, need up to date data on this, check browser
switch g.BOARD switch g.BOARD.ID
when 'a', 'b', 'v', 'co', 'mlp' when 'a', 'b', 'v', 'co', 'mlp'
251 251
when 'vg' when 'vg'
@ -2950,3 +2950,96 @@ ThreadUpdater =
newPosts: posts newPosts: posts
deletedPosts: deletedPosts deletedPosts: deletedPosts
deletedFiles: deletedFiles deletedFiles: deletedFiles
ThreadWatcher =
init: ->
return if g.VIEW is 'catalog' or !Conf['Thread Watcher']
@dialog = UI.dialog 'watcher', 'top: 50px; left: 0px;',
'<div class=move>Thread Watcher</div>'
$.on d, 'QRPostSuccessful', @cb.post
$.on d, '4chanXInitFinished', @ready
$.sync 'WatchedThreads', @refresh
Thread::callbacks.push
name: 'Thread Watcher'
cb: @node
node: ->
op = @posts[@]
favicon = $.el 'img',
className: 'favicon'
$.on favicon, 'click', ThreadWatcher.cb.toggle
$.before $('input', op.nodes.post), favicon
if g.VIEW is 'thread' and @ID is $.get 'AutoWatch', 0
ThreadWatcher.watch @
$.delete 'AutoWatch'
ready: ->
ThreadWatcher.refresh()
$.add d.body, ThreadWatcher.dialog
refresh: (watched) ->
watched or= $.get 'WatchedThreads', {}
nodes = [$('.move', ThreadWatcher.dialog)]
for board of watched
for id, props of watched[board]
x = $.el 'a',
textContent: '×'
href: 'javascript:;'
$.on x, 'click', ThreadWatcher.cb.x
link = $.el 'a', props
link.title = link.textContent
div = $.el 'div'
$.add div, [x, $.tn(' '), link]
nodes.push div
ThreadWatcher.dialog.innerHTML = ''
$.add ThreadWatcher.dialog, nodes
watched = watched[g.BOARD] or {}
for ID, thread of g.BOARD.threads
op = thread.posts[thread]
favicon = $ '.favicon', op.nodes.post
favicon.src = if ID of watched
Favicon.default
else
Favicon.empty
return
cb:
toggle: ->
ThreadWatcher.toggle Get.postFromNode(@).thread
x: ->
thread = @nextElementSibling.pathname.split '/'
ThreadWatcher.unwatch thread[1], thread[3]
post: (e) ->
{postID, threadID} = e.detail
if threadID is '0'
if Conf['Auto Watch']
$.set 'AutoWatch', +postID
else if Conf['Auto Watch Reply']
ThreadWatcher.watch g.BOARD.threads[threadID]
toggle: (thread) ->
op = thread.posts[thread]
if $('.favicon', op.nodes.post).src is Favicon.empty
ThreadWatcher.watch thread
else
ThreadWatcher.unwatch thread.board, thread.ID
unwatch: (board, threadID) ->
watched = $.get 'WatchedThreads', {}
delete watched[board][threadID]
ThreadWatcher.refresh watched
$.set 'WatchedThreads', watched
watch: (thread) ->
watched = $.get 'WatchedThreads', {}
watched[thread.board] or= {}
watched[thread.board][thread] =
href: "/#{thread.board}/res/#{thread}"
textContent: Get.threadExcerpt thread
ThreadWatcher.refresh watched
$.set 'WatchedThreads', watched

View File

@ -343,6 +343,7 @@ Main =
initFeature 'Unread', Unread initFeature 'Unread', Unread
initFeature 'Thread Stats', ThreadStats initFeature 'Thread Stats', ThreadStats
initFeature 'Thread Updater', ThreadUpdater initFeature 'Thread Updater', ThreadUpdater
initFeature 'Thread Watcher', ThreadWatcher
console.timeEnd 'All initializations' console.timeEnd 'All initializations'
$.on d, '4chanMainInit', Main.initStyle $.on d, '4chanMainInit', Main.initStyle