Merge branch 'master' of git://github.com/aeosynth/4chan-x

This commit is contained in:
Nicolas Stepien 2011-05-08 02:35:25 +02:00
commit fc8863e3c7
3 changed files with 193 additions and 132 deletions

View File

@ -7,6 +7,7 @@
// @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/*
// @include http://sys.4chan.org/* // @include http://sys.4chan.org/*
// @include file://*
// @updateURL http://userscripts.org/scripts/source/51412.meta.js // @updateURL http://userscripts.org/scripts/source/51412.meta.js
// ==/UserScript== // ==/UserScript==
@ -71,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'],
@ -92,8 +94,7 @@
'Thread Navigation': [true, 'Navigate to previous / next thread'], 'Thread Navigation': [true, 'Navigate to previous / next thread'],
'Thread Updater': [true, 'Update threads'], 'Thread Updater': [true, 'Update threads'],
'Thread Watcher': [true, 'Bookmark threads'], 'Thread Watcher': [true, 'Bookmark threads'],
'Unread Count': [true, 'Show unread post count in tab title'], 'Unread Count': [true, 'Show unread post count in tab title']
'Watch on Reply': [false, 'Automatically watch threads you reply to']
}, },
textarea: { textarea: {
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') 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')
@ -171,7 +172,7 @@
} else { } else {
el.style.bottom = '0px'; el.style.bottom = '0px';
} }
el.querySelector('div.move').addEventListener('mousedown', ui.dragstart, true); el.querySelector('div.move').addEventListener('mousedown', ui.dragstart, false);
if ((_ref3 = el.querySelector('div.move a[name=close]')) != null) { if ((_ref3 = el.querySelector('div.move a[name=close]')) != null) {
_ref3.addEventListener('click', (function() { _ref3.addEventListener('click', (function() {
return el.parentNode.removeChild(el); return el.parentNode.removeChild(el);
@ -988,15 +989,11 @@
} }
}, },
message: function(e) { message: function(e) {
var data, dialog, error; var data, dialog;
data = e.data; data = e.data;
dialog = $('#qr'); dialog = $('#qr');
if (data) { if (data) {
error = $.el('span', { $('#error').textContent = data;
className: 'error',
textContent: data
});
$.append(dialog, error);
qr.autohide.unset(); qr.autohide.unset();
} else { } else {
if (dialog) { if (dialog) {
@ -1024,59 +1021,36 @@
return _results; return _results;
}, },
submit: function(e) { submit: function(e) {
var form, isQR, recaptcha, span, thread, threads, value, _i, _len; var form, id, isQR, op;
form = e.target; form = e.target;
isQR = form.parentNode.id === 'qr'; isQR = form.parentNode.id === 'qr';
if ($.config('Watch on Reply') && $.config('Thread Watcher')) { if ($.config('Auto Watch Reply') && $.config('Thread Watcher')) {
if (g.REPLY && $('img.favicon').src === Favicon.empty) { if (g.REPLY && $('img.favicon').src === Favicon.empty) {
watcher.watch(null, g.THREAD_ID); watcher.watch(null, g.THREAD_ID);
} else { } else {
value = $('input[name=resto]').value; id = $('input[name=resto]').value;
threads = $$('div.op'); op = d.getElementById(id);
for (_i = 0, _len = threads.length; _i < _len; _i++) { if ($('img.favicon', op).src === Favicon.empty) {
thread = threads[_i]; watcher.watch(op, id);
if (thread.id === value && $('img.favicon', thread).src === Favicon.empty) {
watcher.watch(thread, value);
}
} }
} }
} }
if (isQR) { if (isQR) {
if (span = this.nextSibling) { $('#error').textContent = '';
$.remove(span);
}
} }
if ($.config('Cooldown')) { if ($.config('Cooldown')) {
if (qr.cooldown()) { if (qr.cooldown()) {
e.preventDefault(); e.preventDefault();
alert('Stop posting so often!'); alert('Stop posting so often!');
if (isQR) { if (isQR) {
span = $.el('span', { $('#error').textContent = 'Stop posting so often!';
className: 'error',
textContent: 'Stop posting so often!'
});
$.append(this.parentNode, span);
} }
return; return;
} }
} }
recaptcha = $('input[name=recaptcha_response_field]', this); qr.sage = $('input[name=email]', form).value === 'sage';
if (recaptcha.value) { if (isQR) {
qr.sage = $('input[name=email]', form).value === 'sage'; return qr.autohide.set();
if (isQR) {
return qr.autohide.set();
}
} else {
e.preventDefault();
alert('You forgot to type in the verification.');
recaptcha.focus();
if (isQR) {
span = $.el('span', {
className: 'error',
textContent: 'You forgot to type in the verification.'
});
return $.append(this.parentNode, span);
}
} }
}, },
quote: function(e) { quote: function(e) {
@ -1156,35 +1130,35 @@
} }
}, },
dialog: function(link) { dialog: function(link) {
var clone, dialog, el, html, resto, script, xpath, _i, _len, _ref; var MAX_FILE_SIZE, THREAD_ID, clone, dialog, el, html, spoiler;
html = "<div class=move>Quick Reply <input type=checkbox title=autohide> <a name=close title=close>X</a></div>"; MAX_FILE_SIZE = $('input[name="MAX_FILE_SIZE"]').value;
THREAD_ID = g.THREAD_ID || link.pathname.split('/').pop();
html = " <div class=move> <input class=inputtext type=text name=name placeholder=Name form=qr_form> Quick Reply <input type=checkbox id=autohide title=autohide> <a name=close title=close>X</a> </div> <form name=post action=http://sys.4chan.org/" + g.BOARD + "/post method=POST enctype=multipart/form-data target=iframe id=qr_form> <input type=hidden name=MAX_FILE_SIZE value=" + MAX_FILE_SIZE + "> <input type=hidden name=resto value=" + THREAD_ID + "> <div><input class=inputtext type=text name=email placeholder=E-mail></div> <div><input class=inputtext type=text name=sub placeholder=Subject><input type=submit value=Submit id=com_submit></div> <div><textarea class=inputtext name=com placeholder=Comment></textarea></div> <div id=qr_captcha></div> <div><input type=file name=upfile></div> <div><input class=inputtext type=password name=pwd maxlength=8 placeholder=Password><input type=hidden name=mode value=regist></div> </form> <div id=error class=error></div> ";
dialog = ui.dialog('qr', { dialog = ui.dialog('qr', {
top: '0px', top: '0px',
left: '0px' left: '0px'
}, html); }, html);
el = $('input[title=autohide]', dialog); $.bind($('input[name=name]', dialog), 'mousedown', function(e) {
return e.stopPropagation();
});
el = $('#autohide', dialog);
$.bind(el, 'click', qr.cb.autohide); $.bind(el, 'click', qr.cb.autohide);
clone = $('form[name=post]').cloneNode(true); if ($('.postarea label')) {
_ref = $$('script', clone); spoiler = $.el('label', {
for (_i = 0, _len = _ref.length; _i < _len; _i++) { innerHTML: " [<input type=checkbox name=spoiler>Spoiler Image?]"
script = _ref[_i];
$.remove(script);
}
clone.target = 'iframe';
$.bind(clone, 'submit', qr.cb.submit);
$.bind($('input[name=recaptcha_response_field]', clone), 'keydown', Recaptcha.listener);
if (!g.REPLY) {
xpath = 'preceding::span[@class="postername"][1]/preceding::input[1]';
resto = $.el('input', {
type: 'hidden',
name: 'resto',
value: $.x(xpath, link).name
}); });
$.before(clone.lastChild, resto); $.after($('input[name=email]', dialog), spoiler);
} }
$.append(dialog, clone); clone = $('#recaptcha_widget_div').cloneNode(true);
$.append($('#qr_captcha', dialog), clone);
$.extend($('input[name=recaptcha_response_field]', clone), {
placeholder: 'Verification',
className: 'inputtext',
required: true
});
$.bind($('form', dialog), 'submit', qr.cb.submit);
$.bind($('input[name=recaptcha_response_field]', clone), 'keydown', Recaptcha.listener);
$.append(d.body, dialog); $.append(d.body, dialog);
dialog.style.width = dialog.offsetWidth;
return dialog; return dialog;
}, },
persist: function() { persist: function() {
@ -2347,18 +2321,62 @@
#qr > div.move {\ #qr > div.move {\
text-align: right;\ text-align: right;\
}\ }\
#qr > form > div, /* ad */\ #qr > div.move > input[name=name] {\
#qr #recaptcha_table td:nth-of-type(3), /* captcha logos */\ float: left;\
#qr td.rules {\ }\
#qr #recaptcha_table td:nth-of-type(3) {/* captcha logos */\
display: none;\ display: none;\
}\ }\
#qr {\
width: 302px;\
}\
#qr form, #qr #com_submit, #qr input[type="file"] {\
margin: 0;\
}\
#qr textarea {\
width: 302px;\
height: 80px;\
}\
#qr *:not(input):not(textarea) {\
padding: 0 !important;\
}\
#qr.auto:not(:hover) form {\ #qr.auto:not(:hover) form {\
display: none;\ display: none;\
}\ }\
#qr span.error {\ /* http://stackoverflow.com/questions/2610497/change-an-inputs-html5-placeholder-color-with-css */\
position: absolute;\ #qr input:-webkit-input-placeholder {\
bottom: 0;\ color: grey;\
left: 0;\ }\
#qr input:-moz-placeholder {\
color: grey;\
}\
/* qr reCAPTCHA */\
#qr_captcha input {\
border: 1px solid #AAA !important;\
margin-top: 2px;\
padding: 2px 4px 3px;\
}\
#qr tr {\
height: auto;\
}\
#qr .recaptchatable #recaptcha_image {\
border: 1px solid #AAA !important;\
}\
#qr #recaptcha_reload, #qr #recaptcha_switch_audio, #qr #recaptcha_whatsthis {\
height: 0;\
width: 0;\
padding: 10px 6px !important;\
margin-left: -16px;\
position: relative;\
}\
#recaptcha_reload {\
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAQAAAD8fJRsAAAAcUlEQVQY02P4z4AKGYKhNJQKYzgIZjxn+I8kwdCGrAkuwRAOZrUwhKBL7GP4ziCPYg8jROI/wzQ0B1yBSXiiCKeBjAMbhab+P0gExFCHu3o3QxzIwSC/MCC5+hPDezDdjOzB/ww/wYw9DCGoPt+CHjQAYxCCmpNUoxoAAAAASUVORK5CYII=) no-repeat center;\
}\
#recaptcha_switch_audio {\
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAQAAAD8fJRsAAAAVUlEQVQYV42NMQ6AMAwDPbTQjQEE//8OPCqkhgZXMJBTJMc3BCjBJrlA6uNL1Np6MTordq+N+cLAotHKlxhk/4lMjMu43M9z4CKRmSoJEarqxDOTHidPWTEdrdlTpwAAAABJRU5ErkJggg==) no-repeat center;\
}\
#recaptcha_whatsthis {\
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAQAAAD8fJRsAAAAk0lEQVQYV3WMsQ3CMBBFf0ECmYDJqIkFk0TpkcgEUCeegWzADoi0yQbm3cUFBeifrX/vWZZ2f+K4UlDURCKtcua4VfpK64oJDg/a66zFe1hFpN7AHWvnIprY8nPSk9zpVxcTLYukmXZynEWp3peXLpxV9CrF1L6OtDGL2kTB1QBmPTj2pIEUJkwdNehNBpphxOZ3PgIeQ0jaC7S6AAAAAElFTkSuQmCC) no-repeat center;\
}\ }\
\ \
#updater {\ #updater {\

View File

@ -18,6 +18,6 @@ build = ->
task 'dev', -> task 'dev', ->
build() build()
fs.watchFile INFILE, (curr, prev) -> fs.watchFile INFILE, interval: 250, (curr, prev) ->
if curr.mtime > prev.mtime if curr.mtime > prev.mtime
build() build()

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']
@ -37,7 +38,6 @@ config =
'Thread Updater': [true, 'Update threads'] 'Thread Updater': [true, 'Update threads']
'Thread Watcher': [true, 'Bookmark threads'] 'Thread Watcher': [true, 'Bookmark threads']
'Unread Count': [true, 'Show unread post count in tab title'] 'Unread Count': [true, 'Show unread post count in tab title']
'Watch on Reply': [false, 'Automatically watch threads you reply to']
textarea: textarea:
flavors: [ flavors: [
'http://regex.info/exif.cgi?url=' 'http://regex.info/exif.cgi?url='
@ -96,7 +96,7 @@ ui =
top = localStorage["#{id}Top"] ? top top = localStorage["#{id}Top"] ? top
if left then el.style.left = left else el.style.right = '0px' if left then el.style.left = left else el.style.right = '0px'
if top then el.style.top = top else el.style.bottom = '0px' if top then el.style.top = top else el.style.bottom = '0px'
el.querySelector('div.move').addEventListener 'mousedown', ui.dragstart, true el.querySelector('div.move').addEventListener 'mousedown', ui.dragstart, false
el.querySelector('div.move a[name=close]')?.addEventListener 'click', el.querySelector('div.move a[name=close]')?.addEventListener 'click',
(-> el.parentNode.removeChild(el)), true (-> el.parentNode.removeChild(el)), true
el el
@ -751,10 +751,7 @@ qr =
{data} = e {data} = e
dialog = $ '#qr' dialog = $ '#qr'
if data # error message if data # error message
error = $.el 'span', $('#error').textContent = data
className: 'error'
textContent: data
$.append dialog, error
qr.autohide.unset() qr.autohide.unset()
else # success else # success
if dialog if dialog
@ -777,19 +774,17 @@ qr =
form = e.target form = e.target
isQR = form.parentNode.id == 'qr' isQR = form.parentNode.id == 'qr'
if $.config('Watch on Reply') and $.config('Thread Watcher') if $.config('Auto Watch Reply') and $.config('Thread Watcher')
if g.REPLY and $('img.favicon').src is Favicon.empty if g.REPLY and $('img.favicon').src is Favicon.empty
watcher.watch null, g.THREAD_ID watcher.watch null, g.THREAD_ID
else else
value = $('input[name=resto]').value id = $('input[name=resto]').value
threads = $$ 'div.op' op = d.getElementById id
for thread in threads if $('img.favicon', op).src is Favicon.empty
if thread.id is value and $('img.favicon', thread).src is Favicon.empty watcher.watch op, id
watcher.watch thread, value
if isQR if isQR
if span = @nextSibling $('#error').textContent = ''
$.remove span
if $.config 'Cooldown' if $.config 'Cooldown'
# check if we've posted on this board in another tab # check if we've posted on this board in another tab
@ -798,28 +793,13 @@ qr =
alert 'Stop posting so often!' alert 'Stop posting so often!'
if isQR if isQR
span = $.el 'span', $('#error').textContent = 'Stop posting so often!'
className: 'error'
textContent: 'Stop posting so often!'
$.append @parentNode, span
return return
recaptcha = $('input[name=recaptcha_response_field]', this) qr.sage = $('input[name=email]', form).value == 'sage'
if recaptcha.value if isQR
qr.sage = $('input[name=email]', form).value == 'sage' qr.autohide.set()
if isQR
qr.autohide.set()
else
e.preventDefault()
alert 'You forgot to type in the verification.'
recaptcha.focus()
if isQR
span = $.el 'span',
className: 'error'
textContent: 'You forgot to type in the verification.'
$.append @parentNode, span
quote: (e) -> quote: (e) ->
e.preventDefault() e.preventDefault()
@ -889,32 +869,51 @@ qr =
clearInterval qr.cooldownIntervalID clearInterval qr.cooldownIntervalID
dialog: (link) -> dialog: (link) ->
html = "<div class=move>Quick Reply <input type=checkbox title=autohide> <a name=close title=close>X</a></div>" #maybe should be global
MAX_FILE_SIZE = $('input[name="MAX_FILE_SIZE"]').value
THREAD_ID = g.THREAD_ID or link.pathname.split('/').pop()
html = "
<div class=move>
<input class=inputtext type=text name=name placeholder=Name form=qr_form>
Quick Reply
<input type=checkbox id=autohide title=autohide>
<a name=close title=close>X</a>
</div>
<form name=post action=http://sys.4chan.org/#{g.BOARD}/post method=POST enctype=multipart/form-data target=iframe id=qr_form>
<input type=hidden name=MAX_FILE_SIZE value=#{MAX_FILE_SIZE}>
<input type=hidden name=resto value=#{THREAD_ID}>
<div><input class=inputtext type=text name=email placeholder=E-mail></div>
<div><input class=inputtext type=text name=sub placeholder=Subject><input type=submit value=Submit id=com_submit></div>
<div><textarea class=inputtext name=com placeholder=Comment></textarea></div>
<div id=qr_captcha></div>
<div><input type=file name=upfile></div>
<div><input class=inputtext type=password name=pwd maxlength=8 placeholder=Password><input type=hidden name=mode value=regist></div>
</form>
<div id=error class=error></div>
"
dialog = ui.dialog 'qr', top: '0px', left: '0px', html dialog = ui.dialog 'qr', top: '0px', left: '0px', html
el = $ 'input[title=autohide]', dialog
$.bind $('input[name=name]', dialog), 'mousedown', (e) -> e.stopPropagation()
el = $ '#autohide', dialog
$.bind el, 'click', qr.cb.autohide $.bind el, 'click', qr.cb.autohide
clone = $('form[name=post]').cloneNode(true) if $ '.postarea label'
for script in $$ 'script', clone spoiler = $.el 'label',
$.remove script innerHTML: " [<input type=checkbox name=spoiler>Spoiler Image?]"
clone.target = 'iframe' $.after $('input[name=email]', dialog), spoiler
$.bind clone, 'submit', qr.cb.submit
# TODO try w/o cloning
clone = $('#recaptcha_widget_div').cloneNode(true)
$.append $('#qr_captcha', dialog), clone
$.extend $('input[name=recaptcha_response_field]', clone),
placeholder: 'Verification'
className: 'inputtext'
required: true
$.bind $('form', dialog), 'submit', qr.cb.submit
$.bind $('input[name=recaptcha_response_field]', clone), 'keydown', Recaptcha.listener $.bind $('input[name=recaptcha_response_field]', clone), 'keydown', Recaptcha.listener
if not g.REPLY
#figure out which thread we're replying to
xpath = 'preceding::span[@class="postername"][1]/preceding::input[1]'
resto = $.el 'input',
type: 'hidden'
name: 'resto'
value: $.x(xpath, link).name
# place resto before table to let userstyles know we're responding to a thread
$.before clone.lastChild, resto
$.append dialog, clone
$.append d.body, dialog $.append d.body, dialog
dialog.style.width = dialog.offsetWidth # lock
dialog dialog
@ -1829,18 +1828,62 @@ main =
#qr > div.move { #qr > div.move {
text-align: right; text-align: right;
} }
#qr > form > div, /* ad */ #qr > div.move > input[name=name] {
#qr #recaptcha_table td:nth-of-type(3), /* captcha logos */ float: left;
#qr td.rules { }
#qr #recaptcha_table td:nth-of-type(3) {/* captcha logos */
display: none; display: none;
} }
#qr {
width: 302px;
}
#qr form, #qr #com_submit, #qr input[type="file"] {
margin: 0;
}
#qr textarea {
width: 302px;
height: 80px;
}
#qr *:not(input):not(textarea) {
padding: 0 !important;
}
#qr.auto:not(:hover) form { #qr.auto:not(:hover) form {
display: none; display: none;
} }
#qr span.error { /* http://stackoverflow.com/questions/2610497/change-an-inputs-html5-placeholder-color-with-css */
position: absolute; #qr input:-webkit-input-placeholder {
bottom: 0; color: grey;
left: 0; }
#qr input:-moz-placeholder {
color: grey;
}
/* qr reCAPTCHA */
#qr_captcha input {
border: 1px solid #AAA !important;
margin-top: 2px;
padding: 2px 4px 3px;
}
#qr tr {
height: auto;
}
#qr .recaptchatable #recaptcha_image {
border: 1px solid #AAA !important;
}
#qr #recaptcha_reload, #qr #recaptcha_switch_audio, #qr #recaptcha_whatsthis {
height: 0;
width: 0;
padding: 10px 6px !important;
margin-left: -16px;
position: relative;
}
#recaptcha_reload {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAQAAAD8fJRsAAAAcUlEQVQY02P4z4AKGYKhNJQKYzgIZjxn+I8kwdCGrAkuwRAOZrUwhKBL7GP4ziCPYg8jROI/wzQ0B1yBSXiiCKeBjAMbhab+P0gExFCHu3o3QxzIwSC/MCC5+hPDezDdjOzB/ww/wYw9DCGoPt+CHjQAYxCCmpNUoxoAAAAASUVORK5CYII=) no-repeat center;
}
#recaptcha_switch_audio {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAQAAAD8fJRsAAAAVUlEQVQYV42NMQ6AMAwDPbTQjQEE//8OPCqkhgZXMJBTJMc3BCjBJrlA6uNL1Np6MTordq+N+cLAotHKlxhk/4lMjMu43M9z4CKRmSoJEarqxDOTHidPWTEdrdlTpwAAAABJRU5ErkJggg==) no-repeat center;
}
#recaptcha_whatsthis {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAQAAAD8fJRsAAAAk0lEQVQYV3WMsQ3CMBBFf0ECmYDJqIkFk0TpkcgEUCeegWzADoi0yQbm3cUFBeifrX/vWZZ2f+K4UlDURCKtcua4VfpK64oJDg/a66zFe1hFpN7AHWvnIprY8nPSk9zpVxcTLYukmXZynEWp3peXLpxV9CrF1L6OtDGL2kTB1QBmPTj2pIEUJkwdNehNBpphxOZ3PgIeQ0jaC7S6AAAAAElFTkSuQmCC) no-repeat center;
} }
#updater { #updater {