4chan-x/src/Monitoring/ThreadWatcher.coffee
Zixaphir a3c1afa5cd Merge -- probably bugged until I sort out everything
that's actually changed. Honestly, all I wanted was the
4cdn changes, but any excuse to merge was good enough, I guess.

Merge branch 'v3' of git://github.com/MayhemYDG/4chan-x into v3

Conflicts:
	CHANGELOG.md
	Gruntfile.coffee
	changelog-old
	css/style.css
	html/General/Settings.html
	html/Monitoring/ThreadUpdater.html
	html/Monitoring/ThreadWatcher.html
	html/Posting/QR.html
	package.json
	src/Filtering/ThreadHiding.coffee
	src/General/Build.coffee
	src/General/Config.coffee
	src/General/Header.coffee
	src/General/Main.coffee
	src/General/Settings.coffee
	src/General/lib/post.class
	src/General/meta/manifest.json
	src/Images/ImageExpand.coffee
	src/Meta/banner.js
	src/Miscellaneous/ExpandComment.coffee
	src/Miscellaneous/ExpandThread.coffee
	src/Miscellaneous/Keybinds.coffee
	src/Miscellaneous/Nav.coffee
	src/Monitoring/Favicon.coffee
	src/Monitoring/ThreadStats.coffee
	src/Monitoring/ThreadUpdater.coffee
	src/Monitoring/ThreadWatcher.coffee
	src/Monitoring/Unread.coffee
	src/Posting/QuickReply.coffee
2013-11-22 17:25:06 -07:00

310 lines
9.9 KiB
CoffeeScript
Executable File

ThreadWatcher =
init: ->
return if !Conf['Thread Watcher']
@shortcut = sc = $.el 'a',
id: 'watcher-link'
textContent: 'Watcher'
href: 'javascript:;'
className: 'disabled fa fa-eye-open'
@db = new DataBoard 'watchedThreads', @refresh, true
@dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', <%= importHTML('Monitoring/ThreadWatcher') %>
@status = $ '#watcher-status', @dialog
@list = @dialog.lastElementChild
$.on d, 'QRPostSuccessful', @cb.post
$.on d, 'ThreadUpdate', @cb.threadUpdate if g.VIEW is 'thread'
$.on sc, 'click', @toggleWatcher
$.on $('.move>.close', ThreadWatcher.dialog), 'click', @toggleWatcher
$.on d, '4chanXInitFinished', @ready
switch g.VIEW
when 'index'
$.on d, 'IndexRefresh', @cb.onIndexRefresh
when 'thread'
$.on d, 'ThreadUpdate', @cb.onThreadRefresh
if Conf['Toggleable Thread Watcher']
Header.addShortcut sc
$.addClass doc, 'fixed-watcher'
now = Date.now()
if (@db.data.lastChecked or 0) < now - 2 * $.HOUR
@db.data.lastChecked = now
ThreadWatcher.fetchAllStatus()
@db.save()
Thread.callbacks.push
name: 'Thread Watcher'
cb: @node
node: ->
toggler = $.el 'img',
className: 'watch-thread-link'
$.on toggler, 'click', ThreadWatcher.cb.toggle
$.before $('input', @OP.nodes.post), toggler
ready: ->
$.off d, '4chanXInitFinished', ThreadWatcher.ready
return unless Main.isThisPageLegit()
ThreadWatcher.refresh()
$.add d.body, ThreadWatcher.dialog
if Conf['Toggleable Thread Watcher']
ThreadWatcher.dialog.hidden = true
return unless Conf['Auto Watch']
$.get 'AutoWatch', 0, ({AutoWatch}) ->
return unless thread = g.BOARD.threads[AutoWatch]
ThreadWatcher.add thread
$.delete 'AutoWatch'
toggleWatcher: ->
$.toggleClass ThreadWatcher.shortcut, 'disabled'
ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden
cb:
openAll: ->
return if $.hasClass @, 'disabled'
for a in $$ 'a[title]', ThreadWatcher.list
$.open a.href
$.event 'CloseMenu'
checkThreads: ->
return if $.hasClass @, 'disabled'
ThreadWatcher.fetchAllStatus()
pruneDeads: ->
return if $.hasClass @, 'disabled'
for {boardID, threadID, data} in ThreadWatcher.getAll() when data.isDead
delete ThreadWatcher.db.data.boards[boardID][threadID]
ThreadWatcher.db.deleteIfEmpty {boardID}
ThreadWatcher.db.save()
ThreadWatcher.refresh()
$.event 'CloseMenu'
toggle: ->
ThreadWatcher.toggle Get.postFromNode(@).thread
rm: ->
[boardID, threadID] = @parentNode.dataset.fullID.split '.'
ThreadWatcher.rm boardID, +threadID
post: (e) ->
{board, postID, threadID} = e.detail
if postID is threadID
if Conf['Auto Watch']
$.set 'AutoWatch', threadID
else if Conf['Auto Watch Reply']
ThreadWatcher.add board.threads[threadID]
onIndexRefresh: ->
{db} = ThreadWatcher
boardID = g.BOARD.ID
for threadID, data of db.data.boards[boardID] when not data.isDead and threadID not of g.BOARD.threads
if Conf['Auto Prune']
ThreadWatcher.db.delete {boardID, threadID}
else
data.isDead = true
ThreadWatcher.db.set {boardID, threadID, val: data}
ThreadWatcher.refresh()
onThreadRefresh: (e) ->
{thread} = e.detail
return unless e.detail[404] and ThreadWatcher.db.get {boardID: thread.board.ID, threadID: thread.ID}
# Update 404 status.
ThreadWatcher.add thread
fetchCount:
fetched: 0
fetching: 0
fetchAllStatus: ->
return unless (threads = ThreadWatcher.getAll()).length
ThreadWatcher.status.textContent = '...'
for thread in threads
ThreadWatcher.fetchStatus thread
return
fetchStatus: ({boardID, threadID, data}) ->
return if data.isDead
{fetchCount} = ThreadWatcher
fetchCount.fetching++
$.ajax "//a.4cdn.org/#{boardID}/res/#{threadID}.json",
onloadend: ->
fetchCount.fetched++
if fetchCount.fetched is fetchCount.fetching
fetchCount.fetched = 0
fetchCount.fetching = 0
status = ''
else
status = "#{Math.round fetchCount.fetched / fetchCount.fetching * 100}%"
ThreadWatcher.status.textContent = status
return if @status isnt 404
if Conf['Auto Prune']
ThreadWatcher.db.delete {boardID, threadID}
else
data.isDead = true
ThreadWatcher.db.set {boardID, threadID, val: data}
ThreadWatcher.refresh()
,
type: 'head'
getAll: ->
all = []
for boardID, threads of ThreadWatcher.db.data.boards
if Conf['Current Board'] and boardID isnt g.BOARD.ID
continue
for threadID, data of threads
all.push {boardID, threadID, data}
all
makeLine: (boardID, threadID, data) ->
x = $.el 'a',
className: 'fa fa-times'
href: 'javascript:;'
$.on x, 'click', ThreadWatcher.cb.rm
if data.isDead
href = Redirect.to 'thread', {boardID, threadID}
link = $.el 'a',
href: href or "/#{boardID}/res/#{threadID}"
textContent: data.excerpt
title: data.excerpt
div = $.el 'div'
fullID = "#{boardID}.#{threadID}"
div.dataset.fullID = fullID
$.addClass div, 'current' if g.VIEW is 'thread' and fullID is "#{g.BOARD}.#{g.THREADID}"
$.addClass div, 'dead-thread' if data.isDead
$.add div, [x, $.tn(' '), link]
div
refresh: ->
nodes = []
for {boardID, threadID, data} in ThreadWatcher.getAll()
nodes.push ThreadWatcher.makeLine boardID, threadID, data
{list} = ThreadWatcher
$.rmAll list
$.add list, nodes
for threadID, thread of g.BOARD.threads
toggler = $ '.watch-thread-link', thread.OP.nodes.post
watched = ThreadWatcher.db.get {boardID: thread.board.ID, threadID}
helper = if watched then ['addClass', 'Unwatch'] else ['rmClass', 'Watch']
$[helper[0]] toggler, 'watched'
toggler.title = "#{helper[1]} Thread"
for refresher in ThreadWatcher.menu.refreshers
refresher()
return
toggle: (thread) ->
boardID = thread.board.ID
threadID = thread.ID
if ThreadWatcher.db.get {boardID, threadID}
ThreadWatcher.rm boardID, threadID
else
ThreadWatcher.add thread
add: (thread) ->
data = {}
boardID = thread.board.ID
threadID = thread.ID
if thread.isDead
if Conf['Auto Prune'] and ThreadWatcher.db.get {boardID, threadID}
ThreadWatcher.rm boardID, threadID
return
data.isDead = true
data.excerpt = Get.threadExcerpt thread
ThreadWatcher.db.set {boardID, threadID, val: data}
ThreadWatcher.refresh()
rm: (boardID, threadID) ->
ThreadWatcher.db.delete {boardID, threadID}
ThreadWatcher.refresh()
convert: (oldFormat) ->
newFormat = {}
for boardID, threads of oldFormat
for threadID, data of threads
(newFormat[boardID] or= {})[threadID] = excerpt: data.textContent
newFormat
menu:
refreshers: []
init: ->
return if !Conf['Thread Watcher']
menu = new UI.Menu 'thread watcher'
$.on $('.menu-button', ThreadWatcher.dialog), 'click', (e) ->
menu.toggle e, @, ThreadWatcher
@addHeaderMenuEntry()
@addMenuEntries()
addHeaderMenuEntry: ->
return if g.VIEW isnt 'thread'
entryEl = $.el 'a',
href: 'javascript:;'
$.event 'AddMenuEntry',
type: 'header'
el: entryEl
order: 60
$.on entryEl, 'click', -> ThreadWatcher.toggle g.threads["#{g.BOARD}.#{g.THREADID}"]
@refreshers.push ->
[addClass, rmClass, text] = if $ '.current', ThreadWatcher.list
['unwatch-thread', 'watch-thread', 'Unwatch thread']
else
['watch-thread', 'unwatch-thread', 'Watch thread']
$.addClass entryEl, addClass
$.rmClass entryEl, rmClass
entryEl.textContent = text
addMenuEntries: ->
entries = []
# `Open all` entry
entries.push
cb: ThreadWatcher.cb.openAll
entry:
type: 'thread watcher'
el: $.el 'a',
textContent: 'Open all threads'
refresh: -> (if ThreadWatcher.list.firstElementChild then $.rmClass else $.addClass) @el, 'disabled'
# `Check 404'd threads` entry
entries.push
cb: ThreadWatcher.cb.checkThreads
entry:
type: 'thread watcher'
el: $.el 'a',
textContent: 'Check 404\'d threads'
refresh: -> (if $('div:not(.dead-thread)', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled'
# `Prune 404'd threads` entry
entries.push
cb: ThreadWatcher.cb.pruneDeads
entry:
type: 'thread watcher'
el: $.el 'a',
textContent: 'Prune 404\'d threads'
refresh: -> (if $('.dead-thread', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled'
# `Settings` entries:
subEntries = []
for name, conf of Config.threadWatcher
subEntries.push @createSubEntry name, conf[1]
entries.push
entry:
type: 'thread watcher'
el: $.el 'span',
textContent: 'Settings'
subEntries: subEntries
for {entry, cb, refresh} in entries
entry.el.href = 'javascript:;' if entry.el.nodeName is 'A'
$.on entry.el, 'click', cb if cb
@refreshers.push refresh.bind entry if refresh
$.event 'AddMenuEntry', entry
return
createSubEntry: (name, desc) ->
entry =
type: 'thread watcher'
el: $.el 'label',
innerHTML: "<input type=checkbox name='#{name}'> #{name}"
title: desc
input = entry.el.firstElementChild
input.checked = Conf[name]
$.on input, 'change', $.cb.checked
$.on input, 'change', ThreadWatcher.refresh if name is 'Current Board'
entry