diff --git a/4chan_x.js b/4chan_x.js index ac8680af2..5694cc216 100644 --- a/4chan_x.js +++ b/4chan_x.js @@ -7,6 +7,7 @@ // @license MIT; http://en.wikipedia.org/wiki/Mit_license // @include http://boards.4chan.org/* // @include http://sys.4chan.org/* +// @include file://* // @updateURL http://userscripts.org/scripts/source/51412.meta.js // ==/UserScript== @@ -71,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'], @@ -92,8 +94,7 @@ 'Thread Navigation': [true, 'Navigate to previous / next thread'], 'Thread Updater': [true, 'Update threads'], 'Thread Watcher': [true, 'Bookmark threads'], - 'Unread Count': [true, 'Show unread post count in tab title'], - 'Watch on Reply': [false, 'Automatically watch threads you reply to'] + '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=', '#http://saucenao.com/search.php?db=999&url='].join('\n') @@ -171,7 +172,7 @@ } else { 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) { _ref3.addEventListener('click', (function() { return el.parentNode.removeChild(el); @@ -988,15 +989,11 @@ } }, message: function(e) { - var data, dialog, error; + var data, dialog; data = e.data; dialog = $('#qr'); if (data) { - error = $.el('span', { - className: 'error', - textContent: data - }); - $.append(dialog, error); + $('#error').textContent = data; qr.autohide.unset(); } else { if (dialog) { @@ -1024,59 +1021,36 @@ return _results; }, submit: function(e) { - var form, isQR, recaptcha, span, thread, threads, value, _i, _len; + var form, id, isQR, op; form = e.target; 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) { watcher.watch(null, g.THREAD_ID); } else { - value = $('input[name=resto]').value; - threads = $$('div.op'); - for (_i = 0, _len = threads.length; _i < _len; _i++) { - thread = threads[_i]; - if (thread.id === value && $('img.favicon', thread).src === Favicon.empty) { - watcher.watch(thread, value); - } + id = $('input[name=resto]').value; + op = d.getElementById(id); + if ($('img.favicon', op).src === Favicon.empty) { + watcher.watch(op, id); } } } if (isQR) { - if (span = this.nextSibling) { - $.remove(span); - } + $('#error').textContent = ''; } if ($.config('Cooldown')) { if (qr.cooldown()) { e.preventDefault(); alert('Stop posting so often!'); if (isQR) { - span = $.el('span', { - className: 'error', - textContent: 'Stop posting so often!' - }); - $.append(this.parentNode, span); + $('#error').textContent = 'Stop posting so often!'; } return; } } - recaptcha = $('input[name=recaptcha_response_field]', this); - if (recaptcha.value) { - qr.sage = $('input[name=email]', form).value === 'sage'; - 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); - } + qr.sage = $('input[name=email]', form).value === 'sage'; + if (isQR) { + return qr.autohide.set(); } }, quote: function(e) { @@ -1156,35 +1130,35 @@ } }, dialog: function(link) { - var clone, dialog, el, html, resto, script, xpath, _i, _len, _ref; - html = "
Quick Reply X
"; + var MAX_FILE_SIZE, THREAD_ID, clone, dialog, el, html, spoiler; + MAX_FILE_SIZE = $('input[name="MAX_FILE_SIZE"]').value; + THREAD_ID = g.THREAD_ID || link.pathname.split('/').pop(); + html = "
Quick Reply X
"; dialog = ui.dialog('qr', { top: '0px', left: '0px' }, 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); - clone = $('form[name=post]').cloneNode(true); - _ref = $$('script', clone); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - 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 + if ($('.postarea label')) { + spoiler = $.el('label', { + innerHTML: " [Spoiler Image?]" }); - $.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); - dialog.style.width = dialog.offsetWidth; return dialog; }, persist: function() { @@ -2347,18 +2321,62 @@ #qr > div.move {\ text-align: right;\ }\ - #qr > form > div, /* ad */\ - #qr #recaptcha_table td:nth-of-type(3), /* captcha logos */\ - #qr td.rules {\ + #qr > div.move > input[name=name] {\ + float: left;\ + }\ + #qr #recaptcha_table td:nth-of-type(3) {/* captcha logos */\ 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 {\ display: none;\ }\ - #qr span.error {\ - position: absolute;\ - bottom: 0;\ - left: 0;\ + /* http://stackoverflow.com/questions/2610497/change-an-inputs-html5-placeholder-color-with-css */\ + #qr input:-webkit-input-placeholder {\ + color: grey;\ + }\ + #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 {\ diff --git a/Cakefile b/Cakefile index f02647b2c..96e71dc1c 100644 --- a/Cakefile +++ b/Cakefile @@ -18,6 +18,6 @@ build = -> task 'dev', -> build() - fs.watchFile INFILE, (curr, prev) -> + fs.watchFile INFILE, interval: 250, (curr, prev) -> if curr.mtime > prev.mtime build() diff --git a/script.coffee b/script.coffee index bae7c8d90..c1aad2961 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'] @@ -37,7 +38,6 @@ config = 'Thread Updater': [true, 'Update threads'] 'Thread Watcher': [true, 'Bookmark threads'] 'Unread Count': [true, 'Show unread post count in tab title'] - 'Watch on Reply': [false, 'Automatically watch threads you reply to'] textarea: flavors: [ 'http://regex.info/exif.cgi?url=' @@ -96,7 +96,7 @@ ui = top = localStorage["#{id}Top"] ? top if left then el.style.left = left else el.style.right = '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.parentNode.removeChild(el)), true el @@ -751,10 +751,7 @@ qr = {data} = e dialog = $ '#qr' if data # error message - error = $.el 'span', - className: 'error' - textContent: data - $.append dialog, error + $('#error').textContent = data qr.autohide.unset() else # success if dialog @@ -777,19 +774,17 @@ qr = form = e.target 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 watcher.watch null, g.THREAD_ID else - value = $('input[name=resto]').value - threads = $$ 'div.op' - for thread in threads - if thread.id is value and $('img.favicon', thread).src is Favicon.empty - watcher.watch thread, value + id = $('input[name=resto]').value + op = d.getElementById id + if $('img.favicon', op).src is Favicon.empty + watcher.watch op, id if isQR - if span = @nextSibling - $.remove span + $('#error').textContent = '' if $.config 'Cooldown' # check if we've posted on this board in another tab @@ -798,28 +793,13 @@ qr = alert 'Stop posting so often!' if isQR - span = $.el 'span', - className: 'error' - textContent: 'Stop posting so often!' - $.append @parentNode, span + $('#error').textContent = 'Stop posting so often!' return - recaptcha = $('input[name=recaptcha_response_field]', this) - if recaptcha.value - qr.sage = $('input[name=email]', form).value == 'sage' - 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 + qr.sage = $('input[name=email]', form).value == 'sage' + if isQR + qr.autohide.set() quote: (e) -> e.preventDefault() @@ -889,32 +869,51 @@ qr = clearInterval qr.cooldownIntervalID dialog: (link) -> - html = "
Quick Reply X
" + #maybe should be global + MAX_FILE_SIZE = $('input[name="MAX_FILE_SIZE"]').value + THREAD_ID = g.THREAD_ID or link.pathname.split('/').pop() + html = " +
+ + Quick Reply + + X +
+
+ + +
+
+
+
+
+
+
+
+ " 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 - clone = $('form[name=post]').cloneNode(true) - for script in $$ 'script', clone - $.remove script - clone.target = 'iframe' - $.bind clone, 'submit', qr.cb.submit + if $ '.postarea label' + spoiler = $.el 'label', + innerHTML: " [Spoiler Image?]" + $.after $('input[name=email]', dialog), spoiler + + # 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 - 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 - dialog.style.width = dialog.offsetWidth # lock dialog @@ -1829,18 +1828,62 @@ main = #qr > div.move { text-align: right; } - #qr > form > div, /* ad */ - #qr #recaptcha_table td:nth-of-type(3), /* captcha logos */ - #qr td.rules { + #qr > div.move > input[name=name] { + float: left; + } + #qr #recaptcha_table td:nth-of-type(3) {/* captcha logos */ 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 { display: none; } - #qr span.error { - position: absolute; - bottom: 0; - left: 0; + /* http://stackoverflow.com/questions/2610497/change-an-inputs-html5-placeholder-color-with-css */ + #qr input:-webkit-input-placeholder { + color: grey; + } + #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 {