diff --git a/4chan_x.js b/4chan_x.js index dd6fe4949..ba90d4612 100644 --- a/4chan_x.js +++ b/4chan_x.js @@ -2,7 +2,7 @@ // @name 4chan x // @namespace aeosynth // @description Adds various features. -// @version 2.2.2 +// @version 2.3.0 // @copyright 2009-2011 James Campos // @license MIT; http://en.wikipedia.org/wiki/Mit_license // @include http://boards.4chan.org/* @@ -72,6 +72,7 @@ '404 Redirect': [true, 'Redirect dead threads'], 'Anonymize': [false, 'Make everybody anonymous'], 'Auto Watch': [true, 'Automatically watch threads that you start'], + 'Auto Watch Reply': [false, 'Automatically watch threads that you reply to'], 'Comment Expansion': [true, 'Expand too long comments'], 'Cooldown': [false, 'Prevent \'flood detected\' errors (buggy)'], 'Image Auto-Gif': [false, 'Animate gif thumbnails'], @@ -95,7 +96,7 @@ 'Unread Count': [true, 'Show unread post count in tab title'] }, textarea: { - flavors: ['http://regex.info/exif.cgi?url=', 'http://iqdb.org/?url=', 'http://tineye.com/search?url='].join('\n') + flavors: ['http://regex.info/exif.cgi?url=', 'http://iqdb.org/?url=', 'http://tineye.com/search?url=', '#http://saucenao.com/search.php?db=999&url='].join('\n') } }, updater: { @@ -180,9 +181,9 @@ }, dragstart: function(e) { var d, el, rect; + e.preventDefault(); ui.el = el = e.target.parentNode; d = document; - d.body.className = 'noselect'; d.addEventListener('mousemove', ui.drag, true); d.addEventListener('mouseup', ui.dragend, true); rect = el.getBoundingClientRect(); @@ -193,6 +194,7 @@ }, drag: function(e) { var bottom, el, left, right, top; + e.preventDefault(); el = ui.el; left = e.clientX - ui.dx; if (left < 20) { @@ -220,7 +222,6 @@ localStorage["" + id + "Left"] = el.style.left; localStorage["" + id + "Top"] = el.style.top; d = document; - d.body.className = ''; d.removeEventListener('mousemove', ui.drag, true); return d.removeEventListener('mouseup', ui.dragend, true); } @@ -1019,9 +1020,20 @@ return _results; }, submit: function(e) { - var form, isQR; + var form, id, isQR, op; form = e.target; isQR = form.parentNode.id === 'qr'; + if ($.config('Auto Watch Reply') && $.config('Thread Watcher')) { + if (g.REPLY && $('img.favicon').src === Favicon.empty) { + watcher.watch(null, g.THREAD_ID); + } else { + id = $('input[name=resto]').value; + op = d.getElementById(id); + if ($('img.favicon', op).src === Favicon.empty) { + watcher.watch(op, id); + } + } + } if (isQR) { $('#error').textContent = ''; } @@ -1090,7 +1102,7 @@ }, cooldownStart: function(duration) { var submit, submits, _i, _len; - submits = $$('#qr input[type=submit], form[name=post] input[type=submit]'); + submits = $$('#com_submit'); for (_i = 0, _len = submits.length; _i < _len; _i++) { submit = submits[_i]; submit.value = duration; @@ -1102,7 +1114,7 @@ cooldownCB: function() { var submit, submits, _i, _len; qr.duration = qr.duration - 1; - submits = $$('#qr input[type=submit], form[name=post] input[type=submit]'); + submits = $$('#com_submit'); for (_i = 0, _len = submits.length; _i < _len; _i++) { submit = submits[_i]; if (qr.duration === 0) { @@ -1439,67 +1451,64 @@ }; watcher = { init: function() { - var dialog, favicon, html, id, input, inputs, src, watched, watchedBoard, _i, _len, _results; + var dialog, favicon, html, input, inputs, _i, _len; html = '
Thread Watcher
'; dialog = ui.dialog('watcher', { top: '50px', left: '0px' }, html); $.append(d.body, dialog); - watched = $.getValue('watched', {}); - watcher.refresh(watched); - watchedBoard = watched[g.BOARD] || {}; inputs = $$('form > input[value=delete], div.thread > input[value=delete]'); - _results = []; for (_i = 0, _len = inputs.length; _i < _len; _i++) { input = inputs[_i]; - id = input.name; - if (id in watchedBoard) { - src = Favicon["default"]; - } else { - src = Favicon.empty; - } favicon = $.el('img', { - src: src, className: 'favicon' }); $.bind(favicon, 'click', watcher.cb.toggle); - _results.push($.before(input, favicon)); + $.before(input, favicon); } - return _results; + watcher.refresh($.getValue('watched', {})); + return setInterval((function() { + if (watcher.lastUpdated < $.getValue('watcher.lastUpdated', 0)) { + return watcher.refresh($.getValue('watched', {})); + } + }), 1000); }, refresh: function(watched) { - var board, div, id, props, _i, _len, _ref, _results; - _ref = $$('#watcher > div:not(.move)'); + var board, dialog, div, favicon, id, link, props, watchedBoard, x, _i, _j, _len, _len2, _ref, _ref2, _ref3; + dialog = $('#watcher'); + _ref = $$('div:not(.move)', dialog); for (_i = 0, _len = _ref.length; _i < _len; _i++) { div = _ref[_i]; $.remove(div); } - _results = []; for (board in watched) { - _results.push((function() { - var _ref2, _results2; - _ref2 = watched[board]; - _results2 = []; - for (id in _ref2) { - props = _ref2[id]; - _results2.push(watcher.addLink(props, $('#watcher'))); - } - return _results2; - })()); + _ref2 = watched[board]; + for (id in _ref2) { + props = _ref2[id]; + div = $.el('div'); + x = $.el('a', { + textContent: 'X' + }); + $.bind(x, 'click', watcher.cb.x); + link = $.el('a', props); + $.append(div, x, $.tn(' '), link); + $.append(dialog, div); + } } - return _results; - }, - addLink: function(props, dialog) { - var div, link, x; - div = $.el('div'); - x = $.el('a', { - textContent: 'X' - }); - $.bind(x, 'click', watcher.cb.x); - link = $.el('a', props); - $.append(div, x, $.tn(' '), link); - return $.append(dialog, div); + watchedBoard = watched[g.BOARD] || {}; + _ref3 = $$('img.favicon'); + for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) { + favicon = _ref3[_j]; + id = favicon.nextSibling.name; + if (id in watchedBoard) { + favicon.src = Favicon["default"]; + } else { + favicon.src = Favicon.empty; + } + } + $.setValue('watcher.lastUpdated', Date.now()); + return watcher.lastUpdated = Date.now(); }, cb: { toggle: function(e) { @@ -1516,30 +1525,20 @@ favicon = $('img.favicon', thread); id = favicon.nextSibling.name; if (favicon.src === Favicon.empty) { - return watcher.watch(thread); + return watcher.watch(thread, id); } else { return watcher.unwatch(g.BOARD, id); } }, unwatch: function(board, id) { - var favicon, input, watched; - if (input = $("input[name=\"" + id + "\"]")) { - favicon = input.previousSibling; - favicon.src = Favicon.empty; - } + var watched; watched = $.getValue('watched', {}); delete watched[board][id]; $.setValue('watched', watched); return watcher.refresh(watched); }, - watch: function(thread) { - var favicon, id, props, tc, watched, _name; - favicon = $('img.favicon', thread); - if (favicon.src === Favicon["default"]) { - return; - } - favicon.src = Favicon["default"]; - id = favicon.nextSibling.name; + watch: function(thread, id) { + var props, tc, watched, _name; tc = $('span.filetitle', thread).textContent || $('blockquote', thread).textContent; props = { textContent: "/" + g.BOARD + "/ - " + tc.slice(0, 25), @@ -1580,8 +1579,19 @@ }, cb: { node: function(root) { - var i, link, names, prefix, prefixes, span, suffix, _i, _len, _ref, _results; - prefixes = $.config('flavors').split('\n'); + var i, link, names, prefix, prefixes, s, span, suffix, _i, _len, _ref, _results; + prefixes = (function() { + var _i, _len, _ref, _results; + _ref = $.config('flavors').split('\n'); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + s = _ref[_i]; + if (s[0] !== '#') { + _results.push(s); + } + } + return _results; + })(); names = (function() { var _i, _len, _results; _results = []; @@ -1659,7 +1669,7 @@ for (_i = 0, _len = arr.length; _i < _len; _i++) { el = arr[_i]; a = $.el('a', { - textContent: '[ ! ]' + innerHTML: '[ ! ]' }); $.bind(a, 'click', quickReport.cb.report); $.after(el, a); @@ -1767,7 +1777,6 @@ case '3': case 'adv': case 'an': - case 'c': case 'ck': case 'co': case 'fa': @@ -2173,8 +2182,8 @@ if ($.config('Unread Count')) { unread.init(); } - if ($.config('Auto Watch') && location.hash === '#watch') { - watcher.watch(); + if ($.config('Auto Watch') && $.config('Thread Watcher') && location.hash === '#watch' && $('img.favicon').src === Favicon.empty) { + watcher.watch(null, g.THREAD_ID); } } else { threading.init(); @@ -2349,14 +2358,6 @@ #watcher > div:last-child {\ padding-bottom: 5px;\ }\ -\ - body.noselect {\ - -webkit-user-select: none;\ - -khtml-user-select: none;\ - -moz-user-select: none;\ - -o-user-select: none;\ - user-select: none;\ - }\ ' }; main.init(); diff --git a/changelog b/changelog index 805539893..c0e20b2c9 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,11 @@ +2.3.0 +- mayhem: + refresh watcher list on un/watch + auto refresh watcher list + update no-ip.org archive + fix auto-watch +- flavor comments + 2.2.2 - hopefully fix upgrading issues diff --git a/header b/header index 3a4745582..74b4d032e 100644 --- a/header +++ b/header @@ -2,7 +2,7 @@ // @name 4chan x // @namespace aeosynth // @description Adds various features. -// @version 2.2.2 +// @version 2.3.0 // @copyright 2009-2011 James Campos // @license MIT; http://en.wikipedia.org/wiki/Mit_license // @include http://boards.4chan.org/* diff --git a/script.coffee b/script.coffee index 0a7036ad4..4f9194936 100644 --- a/script.coffee +++ b/script.coffee @@ -15,6 +15,7 @@ config = '404 Redirect': [true, 'Redirect dead threads'] 'Anonymize': [false, 'Make everybody anonymous'] 'Auto Watch': [true, 'Automatically watch threads that you start'] + 'Auto Watch Reply': [false, 'Automatically watch threads that you reply to'] 'Comment Expansion': [true, 'Expand too long comments'] 'Cooldown': [false, 'Prevent \'flood detected\' errors (buggy)'] 'Image Auto-Gif': [false, 'Animate gif thumbnails'] @@ -41,6 +42,7 @@ config = 'http://regex.info/exif.cgi?url=' 'http://iqdb.org/?url=' 'http://tineye.com/search?url=' + '#http://saucenao.com/search.php?db=999&url=' ].join '\n' updater: checkbox: @@ -98,9 +100,10 @@ ui = (-> el.parentNode.removeChild(el)), true el dragstart: (e) -> + #prevent text selection + e.preventDefault() ui.el = el = e.target.parentNode d = document - d.body.className = 'noselect' d.addEventListener 'mousemove', ui.drag, true d.addEventListener 'mouseup', ui.dragend, true #distance from pointer to el edge is constant; calculate it here. @@ -112,6 +115,7 @@ ui = ui.width = document.body.clientWidth - el.offsetWidth ui.height = document.body.clientHeight - el.offsetHeight drag: (e) -> + e.preventDefault() {el} = ui left = e.clientX - ui.dx if left < 20 then left = '0px' @@ -134,7 +138,6 @@ ui = localStorage["#{id}Left"] = el.style.left localStorage["#{id}Top"] = el.style.top d = document - d.body.className = '' d.removeEventListener 'mousemove', ui.drag, true d.removeEventListener 'mouseup', ui.dragend, true @@ -770,6 +773,15 @@ qr = form = e.target isQR = form.parentNode.id == 'qr' + if $.config('Auto Watch Reply') and $.config('Thread Watcher') + if g.REPLY and $('img.favicon').src is Favicon.empty + watcher.watch null, g.THREAD_ID + else + id = $('input[name=resto]').value + op = d.getElementById id + if $('img.favicon', op).src is Favicon.empty + watcher.watch op, id + if isQR $('#error').textContent = '' @@ -834,7 +846,7 @@ qr = return true cooldownStart: (duration) -> - submits = $$ '#qr input[type=submit], form[name=post] input[type=submit]' + submits = $$ '#com_submit' for submit in submits submit.value = duration submit.disabled = true @@ -844,7 +856,7 @@ qr = cooldownCB: -> qr.duration = qr.duration - 1 - submits = $$ '#qr input[type=submit], form[name=post] input[type=submit]' + submits = $$ '#com_submit' for submit in submits if qr.duration == 0 submit.disabled = false @@ -1161,41 +1173,46 @@ watcher = dialog = ui.dialog 'watcher', top: '50px', left: '0px', html $.append d.body, dialog - #populate watcher - watched = $.getValue 'watched', {} - watcher.refresh watched - #add watch buttons - watchedBoard = watched[g.BOARD] or {} inputs = $$ 'form > input[value=delete], div.thread > input[value=delete]' for input in inputs - id = input.name - if id of watchedBoard - src = Favicon.default - else - src = Favicon.empty favicon = $.el 'img', - src: src className: 'favicon' $.bind favicon, 'click', watcher.cb.toggle $.before input, favicon + #populate watcher, display watch buttons + watcher.refresh $.getValue 'watched', {} + + setInterval (-> + if watcher.lastUpdated < $.getValue 'watcher.lastUpdated', 0 + watcher.refresh $.getValue 'watched', {} + ), 1000 + refresh: (watched) -> - for div in $$ '#watcher > div:not(.move)' + dialog = $ '#watcher' + for div in $$ 'div:not(.move)', dialog $.remove div for board of watched for id, props of watched[board] - watcher.addLink props, $ '#watcher' + div = $.el 'div' + x = $.el 'a', + textContent: 'X' + $.bind x, 'click', watcher.cb.x + link = $.el 'a', props - addLink: (props, dialog) -> - div = $.el 'div' - x = $.el 'a', - textContent: 'X' - $.bind x, 'click', watcher.cb.x - link = $.el 'a', props + $.append div, x, $.tn(' '), link + $.append dialog, div - $.append div, x, $.tn(' '), link - $.append dialog, div + watchedBoard = watched[g.BOARD] or {} + for favicon in $$ 'img.favicon' + id = favicon.nextSibling.name + if id of watchedBoard + favicon.src = Favicon.default + else + favicon.src = Favicon.empty + $.setValue 'watcher.lastUpdated', Date.now() + watcher.lastUpdated = Date.now() cb: toggle: (e) -> @@ -1209,29 +1226,18 @@ watcher = favicon = $ 'img.favicon', thread id = favicon.nextSibling.name if favicon.src == Favicon.empty - watcher.watch thread + watcher.watch thread, id else # favicon.src == Favicon.default watcher.unwatch g.BOARD, id unwatch: (board, id) -> - if input = $ "input[name=\"#{id}\"]" - favicon = input.previousSibling - favicon.src = Favicon.empty - watched = $.getValue 'watched', {} delete watched[board][id] $.setValue 'watched', watched watcher.refresh watched - watch: (thread) -> - favicon = $ 'img.favicon', thread - - #this happens if we try to auto-watch an already watched thread. - return if favicon.src is Favicon.default - - favicon.src = Favicon.default - id = favicon.nextSibling.name + watch: (thread, id) -> tc = $('span.filetitle', thread).textContent or $('blockquote', thread).textContent props = textContent: "/#{g.BOARD}/ - #{tc[...25]}" @@ -1263,7 +1269,7 @@ sauce = g.callbacks.push sauce.cb.node cb: node: (root) -> - prefixes = $.config('flavors').split '\n' + prefixes = (s for s in ($.config('flavors').split '\n') when s[0] != '#') names = (prefix.match(/(\w+)\./)[1] for prefix in prefixes) for span in $$ 'span.filesize', root suffix = $('a', span).href @@ -1312,7 +1318,7 @@ quickReport = arr = $$ 'span[id^=no]', root for el in arr a = $.el 'a', - textContent: '[ ! ]' + innerHTML: '[ ! ]' $.bind a, 'click', quickReport.cb.report $.after el, a $.after el, $.tn(' ') @@ -1389,7 +1395,7 @@ redirect = -> url = "http://green-oval.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}" when 'jp', 'm', 'tg' url = "http://archive.easymodo.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}" - when '3', 'adv', 'an', 'c', 'ck', 'co', 'fa', 'fit', 'int', 'k', 'mu', 'n', 'o', 'p', 'po', 'soc', 'sp', 'toy', 'trv', 'v', 'vp', 'x' + when '3', 'adv', 'an', 'ck', 'co', 'fa', 'fit', 'int', 'k', 'mu', 'n', 'o', 'p', 'po', 'soc', 'sp', 'toy', 'trv', 'v', 'vp', 'x' url = "http://archive.no-ip.org/#{g.BOARD}/thread/#{g.THREAD_ID}" else url = "http://boards.4chan.org/#{g.BOARD}" @@ -1697,8 +1703,9 @@ main = if $.config 'Unread Count' unread.init() - if $.config('Auto Watch') and location.hash is '#watch' - watcher.watch() + if $.config('Auto Watch') and $.config('Thread Watcher') and + location.hash is '#watch' and $('img.favicon').src is Favicon.empty + watcher.watch null, g.THREAD_ID else #not reply threading.init() @@ -1869,14 +1876,6 @@ main = #watcher > div:last-child { padding-bottom: 5px; } - - body.noselect { - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -o-user-select: none; - user-select: none; - } ' main.init()