merge master

This commit is contained in:
James Campos 2011-05-07 15:16:43 -07:00
commit df0450f208
4 changed files with 134 additions and 126 deletions

View File

@ -2,7 +2,7 @@
// @name 4chan x // @name 4chan x
// @namespace aeosynth // @namespace aeosynth
// @description Adds various features. // @description Adds various features.
// @version 2.2.2 // @version 2.3.0
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com> // @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
// @license MIT; http://en.wikipedia.org/wiki/Mit_license // @license MIT; http://en.wikipedia.org/wiki/Mit_license
// @include http://boards.4chan.org/* // @include http://boards.4chan.org/*
@ -72,6 +72,7 @@
'404 Redirect': [true, 'Redirect dead threads'], '404 Redirect': [true, 'Redirect dead threads'],
'Anonymize': [false, 'Make everybody anonymous'], 'Anonymize': [false, 'Make everybody anonymous'],
'Auto Watch': [true, 'Automatically watch threads that you start'], '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'], 'Comment Expansion': [true, 'Expand too long comments'],
'Cooldown': [false, 'Prevent \'flood detected\' errors (buggy)'], 'Cooldown': [false, 'Prevent \'flood detected\' errors (buggy)'],
'Image Auto-Gif': [false, 'Animate gif thumbnails'], 'Image Auto-Gif': [false, 'Animate gif thumbnails'],
@ -95,7 +96,7 @@
'Unread Count': [true, 'Show unread post count in tab title'] 'Unread Count': [true, 'Show unread post count in tab title']
}, },
textarea: { 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: { updater: {
@ -180,9 +181,9 @@
}, },
dragstart: function(e) { dragstart: function(e) {
var d, el, rect; var d, el, rect;
e.preventDefault();
ui.el = el = e.target.parentNode; ui.el = el = e.target.parentNode;
d = document; d = document;
d.body.className = 'noselect';
d.addEventListener('mousemove', ui.drag, true); d.addEventListener('mousemove', ui.drag, true);
d.addEventListener('mouseup', ui.dragend, true); d.addEventListener('mouseup', ui.dragend, true);
rect = el.getBoundingClientRect(); rect = el.getBoundingClientRect();
@ -193,6 +194,7 @@
}, },
drag: function(e) { drag: function(e) {
var bottom, el, left, right, top; var bottom, el, left, right, top;
e.preventDefault();
el = ui.el; el = ui.el;
left = e.clientX - ui.dx; left = e.clientX - ui.dx;
if (left < 20) { if (left < 20) {
@ -220,7 +222,6 @@
localStorage["" + id + "Left"] = el.style.left; localStorage["" + id + "Left"] = el.style.left;
localStorage["" + id + "Top"] = el.style.top; localStorage["" + id + "Top"] = el.style.top;
d = document; d = document;
d.body.className = '';
d.removeEventListener('mousemove', ui.drag, true); d.removeEventListener('mousemove', ui.drag, true);
return d.removeEventListener('mouseup', ui.dragend, true); return d.removeEventListener('mouseup', ui.dragend, true);
} }
@ -1019,9 +1020,20 @@
return _results; return _results;
}, },
submit: function(e) { submit: function(e) {
var form, isQR; var form, id, isQR, op;
form = e.target; form = e.target;
isQR = form.parentNode.id === 'qr'; 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) { if (isQR) {
$('#error').textContent = ''; $('#error').textContent = '';
} }
@ -1090,7 +1102,7 @@
}, },
cooldownStart: function(duration) { cooldownStart: function(duration) {
var submit, submits, _i, _len; 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++) { for (_i = 0, _len = submits.length; _i < _len; _i++) {
submit = submits[_i]; submit = submits[_i];
submit.value = duration; submit.value = duration;
@ -1102,7 +1114,7 @@
cooldownCB: function() { cooldownCB: function() {
var submit, submits, _i, _len; var submit, submits, _i, _len;
qr.duration = qr.duration - 1; 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++) { for (_i = 0, _len = submits.length; _i < _len; _i++) {
submit = submits[_i]; submit = submits[_i];
if (qr.duration === 0) { if (qr.duration === 0) {
@ -1439,67 +1451,64 @@
}; };
watcher = { watcher = {
init: function() { init: function() {
var dialog, favicon, html, id, input, inputs, src, watched, watchedBoard, _i, _len, _results; var dialog, favicon, html, input, inputs, _i, _len;
html = '<div class=move>Thread Watcher</div>'; html = '<div class=move>Thread Watcher</div>';
dialog = ui.dialog('watcher', { dialog = ui.dialog('watcher', {
top: '50px', top: '50px',
left: '0px' left: '0px'
}, html); }, html);
$.append(d.body, dialog); $.append(d.body, dialog);
watched = $.getValue('watched', {});
watcher.refresh(watched);
watchedBoard = watched[g.BOARD] || {};
inputs = $$('form > input[value=delete], div.thread > input[value=delete]'); inputs = $$('form > input[value=delete], div.thread > input[value=delete]');
_results = [];
for (_i = 0, _len = inputs.length; _i < _len; _i++) { for (_i = 0, _len = inputs.length; _i < _len; _i++) {
input = inputs[_i]; input = inputs[_i];
id = input.name;
if (id in watchedBoard) {
src = Favicon["default"];
} else {
src = Favicon.empty;
}
favicon = $.el('img', { favicon = $.el('img', {
src: src,
className: 'favicon' className: 'favicon'
}); });
$.bind(favicon, 'click', watcher.cb.toggle); $.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) { refresh: function(watched) {
var board, div, id, props, _i, _len, _ref, _results; var board, dialog, div, favicon, id, link, props, watchedBoard, x, _i, _j, _len, _len2, _ref, _ref2, _ref3;
_ref = $$('#watcher > div:not(.move)'); dialog = $('#watcher');
_ref = $$('div:not(.move)', dialog);
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
div = _ref[_i]; div = _ref[_i];
$.remove(div); $.remove(div);
} }
_results = [];
for (board in watched) { for (board in watched) {
_results.push((function() { _ref2 = watched[board];
var _ref2, _results2; for (id in _ref2) {
_ref2 = watched[board]; props = _ref2[id];
_results2 = []; div = $.el('div');
for (id in _ref2) { x = $.el('a', {
props = _ref2[id]; textContent: 'X'
_results2.push(watcher.addLink(props, $('#watcher'))); });
} $.bind(x, 'click', watcher.cb.x);
return _results2; link = $.el('a', props);
})()); $.append(div, x, $.tn(' '), link);
$.append(dialog, div);
}
} }
return _results; watchedBoard = watched[g.BOARD] || {};
}, _ref3 = $$('img.favicon');
addLink: function(props, dialog) { for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
var div, link, x; favicon = _ref3[_j];
div = $.el('div'); id = favicon.nextSibling.name;
x = $.el('a', { if (id in watchedBoard) {
textContent: 'X' favicon.src = Favicon["default"];
}); } else {
$.bind(x, 'click', watcher.cb.x); favicon.src = Favicon.empty;
link = $.el('a', props); }
$.append(div, x, $.tn(' '), link); }
return $.append(dialog, div); $.setValue('watcher.lastUpdated', Date.now());
return watcher.lastUpdated = Date.now();
}, },
cb: { cb: {
toggle: function(e) { toggle: function(e) {
@ -1516,30 +1525,20 @@
favicon = $('img.favicon', thread); favicon = $('img.favicon', thread);
id = favicon.nextSibling.name; id = favicon.nextSibling.name;
if (favicon.src === Favicon.empty) { if (favicon.src === Favicon.empty) {
return watcher.watch(thread); return watcher.watch(thread, id);
} else { } else {
return watcher.unwatch(g.BOARD, id); return watcher.unwatch(g.BOARD, id);
} }
}, },
unwatch: function(board, id) { unwatch: function(board, id) {
var favicon, input, watched; var watched;
if (input = $("input[name=\"" + id + "\"]")) {
favicon = input.previousSibling;
favicon.src = Favicon.empty;
}
watched = $.getValue('watched', {}); watched = $.getValue('watched', {});
delete watched[board][id]; delete watched[board][id];
$.setValue('watched', watched); $.setValue('watched', watched);
return watcher.refresh(watched); return watcher.refresh(watched);
}, },
watch: function(thread) { watch: function(thread, id) {
var favicon, id, props, tc, watched, _name; var props, tc, watched, _name;
favicon = $('img.favicon', thread);
if (favicon.src === Favicon["default"]) {
return;
}
favicon.src = Favicon["default"];
id = favicon.nextSibling.name;
tc = $('span.filetitle', thread).textContent || $('blockquote', thread).textContent; tc = $('span.filetitle', thread).textContent || $('blockquote', thread).textContent;
props = { props = {
textContent: "/" + g.BOARD + "/ - " + tc.slice(0, 25), textContent: "/" + g.BOARD + "/ - " + tc.slice(0, 25),
@ -1580,8 +1579,19 @@
}, },
cb: { cb: {
node: function(root) { node: function(root) {
var i, link, names, prefix, prefixes, span, suffix, _i, _len, _ref, _results; var i, link, names, prefix, prefixes, s, span, suffix, _i, _len, _ref, _results;
prefixes = $.config('flavors').split('\n'); 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() { names = (function() {
var _i, _len, _results; var _i, _len, _results;
_results = []; _results = [];
@ -1659,7 +1669,7 @@
for (_i = 0, _len = arr.length; _i < _len; _i++) { for (_i = 0, _len = arr.length; _i < _len; _i++) {
el = arr[_i]; el = arr[_i];
a = $.el('a', { a = $.el('a', {
textContent: '[ ! ]' innerHTML: '[&nbsp;!&nbsp;]'
}); });
$.bind(a, 'click', quickReport.cb.report); $.bind(a, 'click', quickReport.cb.report);
$.after(el, a); $.after(el, a);
@ -1767,7 +1777,6 @@
case '3': case '3':
case 'adv': case 'adv':
case 'an': case 'an':
case 'c':
case 'ck': case 'ck':
case 'co': case 'co':
case 'fa': case 'fa':
@ -2173,8 +2182,8 @@
if ($.config('Unread Count')) { if ($.config('Unread Count')) {
unread.init(); unread.init();
} }
if ($.config('Auto Watch') && location.hash === '#watch') { if ($.config('Auto Watch') && $.config('Thread Watcher') && location.hash === '#watch' && $('img.favicon').src === Favicon.empty) {
watcher.watch(); watcher.watch(null, g.THREAD_ID);
} }
} else { } else {
threading.init(); threading.init();
@ -2349,14 +2358,6 @@
#watcher > div:last-child {\ #watcher > div:last-child {\
padding-bottom: 5px;\ 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(); main.init();

View File

@ -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 2.2.2
- hopefully fix upgrading issues - hopefully fix upgrading issues

2
header
View File

@ -2,7 +2,7 @@
// @name 4chan x // @name 4chan x
// @namespace aeosynth // @namespace aeosynth
// @description Adds various features. // @description Adds various features.
// @version 2.2.2 // @version 2.3.0
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com> // @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
// @license MIT; http://en.wikipedia.org/wiki/Mit_license // @license MIT; http://en.wikipedia.org/wiki/Mit_license
// @include http://boards.4chan.org/* // @include http://boards.4chan.org/*

View File

@ -15,6 +15,7 @@ config =
'404 Redirect': [true, 'Redirect dead threads'] '404 Redirect': [true, 'Redirect dead threads']
'Anonymize': [false, 'Make everybody anonymous'] 'Anonymize': [false, 'Make everybody anonymous']
'Auto Watch': [true, 'Automatically watch threads that you start'] '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'] 'Comment Expansion': [true, 'Expand too long comments']
'Cooldown': [false, 'Prevent \'flood detected\' errors (buggy)'] 'Cooldown': [false, 'Prevent \'flood detected\' errors (buggy)']
'Image Auto-Gif': [false, 'Animate gif thumbnails'] 'Image Auto-Gif': [false, 'Animate gif thumbnails']
@ -41,6 +42,7 @@ config =
'http://regex.info/exif.cgi?url=' 'http://regex.info/exif.cgi?url='
'http://iqdb.org/?url=' 'http://iqdb.org/?url='
'http://tineye.com/search?url=' 'http://tineye.com/search?url='
'#http://saucenao.com/search.php?db=999&url='
].join '\n' ].join '\n'
updater: updater:
checkbox: checkbox:
@ -98,9 +100,10 @@ ui =
(-> el.parentNode.removeChild(el)), true (-> el.parentNode.removeChild(el)), true
el el
dragstart: (e) -> dragstart: (e) ->
#prevent text selection
e.preventDefault()
ui.el = el = e.target.parentNode ui.el = el = e.target.parentNode
d = document d = document
d.body.className = 'noselect'
d.addEventListener 'mousemove', ui.drag, true d.addEventListener 'mousemove', ui.drag, true
d.addEventListener 'mouseup', ui.dragend, true d.addEventListener 'mouseup', ui.dragend, true
#distance from pointer to el edge is constant; calculate it here. #distance from pointer to el edge is constant; calculate it here.
@ -112,6 +115,7 @@ ui =
ui.width = document.body.clientWidth - el.offsetWidth ui.width = document.body.clientWidth - el.offsetWidth
ui.height = document.body.clientHeight - el.offsetHeight ui.height = document.body.clientHeight - el.offsetHeight
drag: (e) -> drag: (e) ->
e.preventDefault()
{el} = ui {el} = ui
left = e.clientX - ui.dx left = e.clientX - ui.dx
if left < 20 then left = '0px' if left < 20 then left = '0px'
@ -134,7 +138,6 @@ ui =
localStorage["#{id}Left"] = el.style.left localStorage["#{id}Left"] = el.style.left
localStorage["#{id}Top"] = el.style.top localStorage["#{id}Top"] = el.style.top
d = document d = document
d.body.className = ''
d.removeEventListener 'mousemove', ui.drag, true d.removeEventListener 'mousemove', ui.drag, true
d.removeEventListener 'mouseup', ui.dragend, true d.removeEventListener 'mouseup', ui.dragend, true
@ -770,6 +773,15 @@ qr =
form = e.target form = e.target
isQR = form.parentNode.id == 'qr' 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 if isQR
$('#error').textContent = '' $('#error').textContent = ''
@ -834,7 +846,7 @@ qr =
return true return true
cooldownStart: (duration) -> cooldownStart: (duration) ->
submits = $$ '#qr input[type=submit], form[name=post] input[type=submit]' submits = $$ '#com_submit'
for submit in submits for submit in submits
submit.value = duration submit.value = duration
submit.disabled = true submit.disabled = true
@ -844,7 +856,7 @@ qr =
cooldownCB: -> cooldownCB: ->
qr.duration = qr.duration - 1 qr.duration = qr.duration - 1
submits = $$ '#qr input[type=submit], form[name=post] input[type=submit]' submits = $$ '#com_submit'
for submit in submits for submit in submits
if qr.duration == 0 if qr.duration == 0
submit.disabled = false submit.disabled = false
@ -1161,41 +1173,46 @@ watcher =
dialog = ui.dialog 'watcher', top: '50px', left: '0px', html dialog = ui.dialog 'watcher', top: '50px', left: '0px', html
$.append d.body, dialog $.append d.body, dialog
#populate watcher
watched = $.getValue 'watched', {}
watcher.refresh watched
#add watch buttons #add watch buttons
watchedBoard = watched[g.BOARD] or {}
inputs = $$ 'form > input[value=delete], div.thread > input[value=delete]' inputs = $$ 'form > input[value=delete], div.thread > input[value=delete]'
for input in inputs for input in inputs
id = input.name
if id of watchedBoard
src = Favicon.default
else
src = Favicon.empty
favicon = $.el 'img', favicon = $.el 'img',
src: src
className: 'favicon' className: 'favicon'
$.bind favicon, 'click', watcher.cb.toggle $.bind favicon, 'click', watcher.cb.toggle
$.before input, favicon $.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) -> refresh: (watched) ->
for div in $$ '#watcher > div:not(.move)' dialog = $ '#watcher'
for div in $$ 'div:not(.move)', dialog
$.remove div $.remove div
for board of watched for board of watched
for id, props of watched[board] 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) -> $.append div, x, $.tn(' '), link
div = $.el 'div' $.append dialog, div
x = $.el 'a',
textContent: 'X'
$.bind x, 'click', watcher.cb.x
link = $.el 'a', props
$.append div, x, $.tn(' '), link watchedBoard = watched[g.BOARD] or {}
$.append dialog, div 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: cb:
toggle: (e) -> toggle: (e) ->
@ -1209,29 +1226,18 @@ watcher =
favicon = $ 'img.favicon', thread favicon = $ 'img.favicon', thread
id = favicon.nextSibling.name id = favicon.nextSibling.name
if favicon.src == Favicon.empty if favicon.src == Favicon.empty
watcher.watch thread watcher.watch thread, id
else # favicon.src == Favicon.default else # favicon.src == Favicon.default
watcher.unwatch g.BOARD, id watcher.unwatch g.BOARD, id
unwatch: (board, id) -> unwatch: (board, id) ->
if input = $ "input[name=\"#{id}\"]"
favicon = input.previousSibling
favicon.src = Favicon.empty
watched = $.getValue 'watched', {} watched = $.getValue 'watched', {}
delete watched[board][id] delete watched[board][id]
$.setValue 'watched', watched $.setValue 'watched', watched
watcher.refresh watched watcher.refresh watched
watch: (thread) -> watch: (thread, id) ->
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
tc = $('span.filetitle', thread).textContent or $('blockquote', thread).textContent tc = $('span.filetitle', thread).textContent or $('blockquote', thread).textContent
props = props =
textContent: "/#{g.BOARD}/ - #{tc[...25]}" textContent: "/#{g.BOARD}/ - #{tc[...25]}"
@ -1263,7 +1269,7 @@ sauce =
g.callbacks.push sauce.cb.node g.callbacks.push sauce.cb.node
cb: cb:
node: (root) -> 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) names = (prefix.match(/(\w+)\./)[1] for prefix in prefixes)
for span in $$ 'span.filesize', root for span in $$ 'span.filesize', root
suffix = $('a', span).href suffix = $('a', span).href
@ -1312,7 +1318,7 @@ quickReport =
arr = $$ 'span[id^=no]', root arr = $$ 'span[id^=no]', root
for el in arr for el in arr
a = $.el 'a', a = $.el 'a',
textContent: '[ ! ]' innerHTML: '[&nbsp;!&nbsp;]'
$.bind a, 'click', quickReport.cb.report $.bind a, 'click', quickReport.cb.report
$.after el, a $.after el, a
$.after el, $.tn(' ') $.after el, $.tn(' ')
@ -1389,7 +1395,7 @@ redirect = ->
url = "http://green-oval.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}" url = "http://green-oval.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}"
when 'jp', 'm', 'tg' when 'jp', 'm', 'tg'
url = "http://archive.easymodo.net/cgi-board.pl/#{g.BOARD}/thread/#{g.THREAD_ID}" 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}" url = "http://archive.no-ip.org/#{g.BOARD}/thread/#{g.THREAD_ID}"
else else
url = "http://boards.4chan.org/#{g.BOARD}" url = "http://boards.4chan.org/#{g.BOARD}"
@ -1697,8 +1703,9 @@ main =
if $.config 'Unread Count' if $.config 'Unread Count'
unread.init() unread.init()
if $.config('Auto Watch') and location.hash is '#watch' if $.config('Auto Watch') and $.config('Thread Watcher') and
watcher.watch() location.hash is '#watch' and $('img.favicon').src is Favicon.empty
watcher.watch null, g.THREAD_ID
else #not reply else #not reply
threading.init() threading.init()
@ -1869,14 +1876,6 @@ main =
#watcher > div:last-child { #watcher > div:last-child {
padding-bottom: 5px; 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() main.init()