diff --git a/js/auto-reload.js b/js/auto-reload.js index 725b4b51..9f5fe052 100644 --- a/js/auto-reload.js +++ b/js/auto-reload.js @@ -9,10 +9,13 @@ * * Usage: * $config['additional_javascript'][] = 'js/jquery.min.js'; + * //$config['additional_javascript'][] = 'js/titlebar-notifications.js'; * $config['additional_javascript'][] = 'js/auto-reload.js'; * */ +auto_reload_enabled = true; // for watch.js to interop + $(document).ready(function(){ if($('div.banner').length == 0) return; // not index @@ -24,12 +27,17 @@ $(document).ready(function(){ var end_of_page = false; - var orig_title = document.title; var new_posts = 0; var first_new_post = null; - var update_title = function() { - document.title = (new_posts ? "("+new_posts+") " : "") + orig_title; - }; + + if (typeof update_title == "undefined") { + var update_title = function() { }; + } + + if (typeof add_title_collector != "undefined") + add_title_collector(function(){ + return new_posts; + }); var window_active = true; $(window).focus(function() { @@ -66,6 +74,7 @@ $(document).ready(function(){ recheck_activated(); } }); + time_loaded = Date.now(); // interop with watch.js } }); diff --git a/js/titlebar-notifications.js b/js/titlebar-notifications.js new file mode 100644 index 00000000..329c8812 --- /dev/null +++ b/js/titlebar-notifications.js @@ -0,0 +1,18 @@ +var orig_title = document.title; + +$(function(){ + orig_title = document.title; +}); + +update_title = function() { + var updates = 0; + for(var i in title_collectors) { + updates += title_collectors[i](); + } + document.title = (updates ? "("+updates+") " : "") + orig_title; +}; + +var title_collectors = []; +add_title_collector = function(f) { + title_collectors.push(f); +}; diff --git a/js/watch.js b/js/watch.js new file mode 100644 index 00000000..bb69872a --- /dev/null +++ b/js/watch.js @@ -0,0 +1,334 @@ +$(function(){ + var status = {}; + + time_loaded = Date.now(); + + var updating_suspended = false; + + var storage = function() { + return JSON.parse(localStorage.watch !== undefined ? localStorage.watch : "{}"); + }; + + var storage_save = function(s) { + localStorage.watch = JSON.stringify(s); + }; + + var osize = function(o) { + var size = 0; + for (var key in o) { + if (o.hasOwnProperty(key)) size++; + } + return size; + }; + + var is_pinned = function(boardconfig) { + return boardconfig.pinned || boardconfig.watched || (boardconfig.threads ? osize(boardconfig.threads) : false); + }; + var is_boardwatched = function(boardconfig) { + return boardconfig.watched; + }; + var is_threadwatched = function(boardconfig, thread) { + return boardconfig && boardconfig.threads && boardconfig.threads[thread]; + }; + var toggle_pinned = function(board) { + var st = storage(); + var bc = st[board] || {}; + if (is_pinned(bc)) { + bc.pinned = false; + bc.watched = false; + bc.threads = {}; + } + else { + bc.pinned = true; + } + st[board] = bc; + storage_save(st); + return bc.pinned; + }; + var toggle_boardwatched = function(board) { + var st = storage(); + var bc = st[board] || {}; + bc.watched = !is_boardwatched(bc) && Date.now(); + st[board] = bc; + storage_save(st); + return bc.watched; + }; + var toggle_threadwatched = function(board, thread) { + var st = storage(); + var bc = st[board] || {}; + if (is_threadwatched(bc, thread)) { + delete bc.threads[thread]; + } + else { + bc.threads = bc.threads || {}; + bc.threads[thread] = Date.now(); + } + st[board] = bc; + storage_save(st); + return is_threadwatched(bc, thread); + }; + var update_pinned = function() { + if (typeof update_title != "undefined") update_title(); + + var bl = $('.boardlist').first(); + $('#watch-pinned').remove(); + var pinned = $('
').css('display', 'inline-block').css('vertical-align', 'middle').appendTo(bl); + + var st = storage(); + for (var i in st) { + if (is_pinned(st[i])) { + var link; + if (bl.find('[href*="/'+i+'/"]:not(.cb-menuitem)').length) link = bl.find('[href*="/'+i+'/"]').first(); + + // TODO: fix path + else link = $('/'+i+'/').appendTo(pinned); + + if (link[0].origtitle === undefined) { + link[0].origtitle = link.html(); + } + else { + link.html(link[0].origtitle); + } + + if (st[i].watched) { + link.css("font-weight", "bold"); + if (status && status[i] && status[i].new_threads) { + link.html(link.html() + " (" + status[i].new_threads + ")"); + } + } + else if (st[i].threads && osize(st[i].threads)) { + link.css("font-style", "italic"); + + link.attr("data-board", i); + + if (status && status[i] && status[i].threads) { + var new_posts = 0; + for (var tid in status[i].threads) { + if (status[i].threads[tid] > 0) { + new_posts += status[i].threads[tid]; + } + } + if (new_posts > 0) { + link.html(link.html() + " (" + new_posts + ")"); + } + } + + link.mouseenter(function() { + updating_suspended = true; + + var list = $("
") + .css("top", $(this).position().top + 13 + $(this).height()) + .css("left", $(this).position().left) + .css("right", "auto") + .appendTo($(this)); + var board = $(this).attr("data-board"); + + for (var tid in storage()[board].threads) { + // TODO: fix path + var newposts = "(0)"; + if (status && status[board] && status[board].threads && status[board].threads[tid]) { + if (status[board].threads[tid] == -404) { + newposts = ""; + } + else { + newposts = "("+status[board].threads[tid]+")"; + } + } + + var tag = $("#"+tid+""+newposts+""); + + tag + .attr('data-thread', tid) + .addClass("cb-menuitem") + .appendTo(list) + .find(".cb-uri").mouseenter(function() { + this.oldval = $(this).html(); + $(this).css("min-width", $(this).width()); + $(this).html(""); + }) + .mouseleave(function() { + $(this).html(this.oldval); + }) + .click(function() { + var b = $(this).parent().parent().parent().attr("data-board"); + var t = $(this).parent().attr("data-thread"); + toggle_threadwatched(b, t); + $(this).parent().parent().parent().mouseleave(); + return false; + }); + } + + }).mouseleave(function() { + updating_suspended = false; + $('.boardlist .cb-menu').remove(); + }); + } + } + } + }; + var fetch_jsons = function() { + if (updating_suspended) return; + + var st = storage(); + for (var i in st) { + if (st[i].watched) { + // TODO: fix path + var r = $.getJSON("/"+i+"/threads.json", {nocache: Math.random()}, function(j, x, r) { + handle_board_json(r.board, j); + }); + r.board = i; + } + else if (st[i].threads) { + for (var j in st[i].threads) { + var r = $.getJSON("/"+i+"/res/"+j+".json", {nocache: Math.random()}, function(k, x, r) { + handle_thread_json(r.board, r.thread, k); + }).error(function(r) { + if(r.status == 404) handle_thread_404(r.board, r.thread); + }); + + r.board = i; + r.thread = j; + } + } + } + }; + + var handle_board_json = function(board, json) { + var last_thread; + + var new_threads = 0; + + for (var i in json) { + for (var j in json[i].threads) { + var thread = json[i].threads[j]; + + if (thread.last_modified > storage()[board].watched / 1000) { + last_thread = thread.no; + + new_threads++; + } + } + } + + status = status || {}; + status[board] = status[board] || {}; + status[board].last_thread = last_thread; + status[board].new_threads = new_threads; + update_pinned(); + }; + var handle_thread_json = function(board, threadid, json) { + for (var i in json.posts) { + var post = json.posts[i]; + + var new_posts = 0; + if (post.time > storage()[board].threads[threadid] / 1000) { + new_posts++; + } + status = status || {}; + status[board] = status[board] || {}; + status[board].threads = status[board].threads || {}; + status[board].threads[threadid] = new_posts; + update_pinned(); + } + }; + var handle_thread_404 = function(board, threadid) { + status = status || {}; + status[board] = status[board] || {}; + status[board].threads = status[board].threads || {}; + status[board].threads[threadid] = -404; //notify 404 + update_pinned(); + }; + + if (active_page == "thread") { + var board = $('form[name="post"] input[name="board"]').val(); + var thread = $('form[name="post"] input[name="thread"]').val(); + + var boardconfig = storage()[board] || {}; + + $('hr:first').before('
-
'); + $('#watch-thread a').html(is_threadwatched(boardconfig, thread) ? _("Stop watching this thread") : _("Watch this thread")).click(function() { + $(this).html(toggle_threadwatched(board, thread) ? _("Stop watching this thread") : _("Watch this thread")); + update_pinned(); + }); + } + if (active_page == "index") { + var board = $('form[name="post"] input[name="board"]').val(); + + var boardconfig = storage()[board] || {}; + + $('hr:first').before('
-
'); + $('#watch-pin a').html(is_pinned(boardconfig) ? _("Unpin this board") : _("Pin this board")).click(function() { + $(this).html(toggle_pinned(board) ? _("Unpin this board") : _("Pin this board")); + update_pinned(); + }); + + $('hr:first').before('
-
'); + $('#watch-board a').html(is_boardwatched(boardconfig) ? _("Stop watching this board") : _("Watch this board")).click(function() { + $(this).html(toggle_boardwatched(board) ? _("Stop watching this board") : _("Watch this board")); + $('#watch-pin a').html(is_pinned(board) ? _("Unpin this board") : _("Pin this board")); + update_pinned(); + }); + + } + + var check_post = function(frame, post) { + return post.length && $(frame).scrollTop() + $(frame).height() >= + post.position().top + post.height(); + } + + $(window).scroll(function() { + if (!status) return; + var refresh = false; + for(var bid in status) { + if (status[bid].new_threads && (active_page == "ukko" || active_page == "index") + && check_post(this, $('[data-board="'+bid+'"]#thread_'+status[bid].last_thread))) { + var st = storage() + st[bid].watched = time_loaded; + storage_save(st); + refresh = true; + } + if (!status[bid].threads) continue; + + for (var tid in status[bid].threads) { + if(status[bid].threads[tid] && check_post(this, $('[data-board="'+bid+'"]#thread_'+tid))) { + var st = storage(); + st[bid].threads[tid] = time_loaded; + storage_save(st); + refresh = true; + } + } + } + if (refresh) { + fetch_jsons(); + refresh = false; + } + }); + + if (typeof add_title_collector != "undefined") + add_title_collector(function() { + if (!status) return 0; + var sum = 0; + for (var bid in status) { + if (status[bid].new_threads) { + sum += status[bid].new_threads; + if (!status[bid].threads) continue; + for (var tid in status[bid].threads) { + if (status[bid].threads[tid] > 0) { + if (auto_reload_enabled && active_page == "thread") { + var board = $('form[name="post"] input[name="board"]').val(); + var thread = $('form[name="post"] input[name="thread"]').val(); + + if (board == bid && thread == tid) continue; + } + sum += status[bid].threads[tid]; + } + } + } + } + return sum; + }); + + update_pinned(); + fetch_jsons(); + setInterval(fetch_jsons, 10000); +});