diff --git a/4chan_x.user.js b/4chan_x.user.js index 2ea4bdef7..afced9c46 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -64,7 +64,7 @@ */ (function() { - var $, $$, DAY, Favicon, HOUR, MINUTE, Main, NAMESPACE, Recaptcha, SECOND, Time, VERSION, anonymize, conf, config, cooldown, d, engine, expandComment, expandThread, filter, flatten, g, getTitle, imgExpand, imgGif, imgHover, key, keybinds, log, nav, options, qr, quoteBacklink, quoteDR, quoteInline, quoteOP, quotePreview, redirect, replyHiding, reportButton, revealSpoilers, sauce, strikethroughQuotes, threadHiding, threadStats, threading, titlePost, ui, unread, updater, val, watcher, _base; + var $, $$, DAY, Favicon, HOUR, MINUTE, Main, NAMESPACE, SECOND, Time, VERSION, anonymize, conf, config, d, engine, expandComment, expandThread, filter, flatten, g, getTitle, imgExpand, imgGif, imgHover, key, keybinds, log, nav, options, quoteBacklink, quoteDR, quoteInline, quoteOP, quotePreview, redirect, replyHiding, reportButton, revealSpoilers, sauce, strikethroughQuotes, threadHiding, threadStats, threading, titlePost, ui, unread, updater, val, watcher, _base; var __slice = Array.prototype.slice; config = { @@ -105,7 +105,7 @@ 'Auto Watch Reply': [false, 'Automatically watch threads that you reply to'] }, Posting: { - 'Auto Noko': [true, 'Always redirect to your post'], + 'Auto Noko': [true, 'Redirect to your post'], 'Cooldown': [true, 'Prevent `flood detected` errors'], 'Quick Reply': [true, 'Reply without leaving the page'], 'Persistent QR': [false, 'Quick reply won\'t disappear after posting. Only in replies.'], @@ -893,11 +893,7 @@ thread = nav.getThread(); switch (key) { case conf.close: - if (o = $('#overlay')) { - $.rm(o); - } else if (qr.el) { - qr.close(); - } + if (o = $('#overlay')) $.rm(o); break; case conf.spoiler: ta = e.target; @@ -916,7 +912,6 @@ window.location = "/" + g.BOARD + "/0#0"; break; case conf.openEmptyQR: - keybinds.qr(thread); break; case conf.nextReply: keybinds.hl.next(thread); @@ -934,7 +929,6 @@ expandThread.toggle(thread); break; case conf.openQR: - keybinds.qr(thread, true); break; case conf.expandImages: keybinds.img(thread); @@ -964,11 +958,6 @@ if ((_ref3 = $('input[value=Previous]')) != null) _ref3.click(); break; case conf.submit: - if (qr.el) { - qr.submit.call($('form', qr.el)); - } else { - $('.postarea form').submit(); - } break; case conf.unreadCountTo0: unread.replies.length = 0; @@ -1059,14 +1048,6 @@ return imgExpand.toggle(thumb.parentNode); } }, - qr: function(thread, quote) { - if (quote) { - return qr.quote.call($('.quotejs + a', $('.replyhl', thread) || thread)); - } else { - if (!qr.el) qr.dialog('', thread != null ? thread.firstChild.id : void 0); - return $('textarea', qr.el).focus(); - } - }, open: function(thread, tab) { var id, url; id = thread.firstChild.id; @@ -1438,370 +1419,6 @@ } }; - cooldown = { - init: function() { - var match, time, _; - if (match = location.search.match(/cooldown=(\d+)/)) { - _ = match[0], time = match[1]; - if ($.get(g.BOARD + '/cooldown', 0) < time) { - $.set(g.BOARD + '/cooldown', time); - } - } - if (Date.now() < $.get(g.BOARD + '/cooldown', 0)) cooldown.start(); - $.on(window, 'storage', function(e) { - if (e.key === ("" + NAMESPACE + g.BOARD + "/cooldown")) { - return cooldown.start(); - } - }); - if (g.REPLY) return $('.postarea form').action += '?cooldown'; - }, - start: function() { - var submit, _i, _len, _ref; - cooldown.duration = Math.ceil(($.get(g.BOARD + '/cooldown', 0) - Date.now()) / 1000); - if (!(cooldown.duration > 0)) return; - _ref = $$('#com_submit'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - submit = _ref[_i]; - submit.value = cooldown.duration; - submit.disabled = true; - } - return setTimeout(cooldown.cb, 1000); - }, - cb: function() { - var submit, submits, _i, _j, _len, _len2, _results; - submits = $$('#com_submit'); - if (--cooldown.duration) { - setTimeout(cooldown.cb, 1000); - _results = []; - for (_i = 0, _len = submits.length; _i < _len; _i++) { - submit = submits[_i]; - _results.push(submit.value = cooldown.duration); - } - return _results; - } else { - for (_j = 0, _len2 = submits.length; _j < _len2; _j++) { - submit = submits[_j]; - submit.disabled = false; - submit.value = 'Submit'; - } - return qr.autoPost(); - } - } - }; - - qr = { - init: function() { - var iframe; - g.callbacks.push(qr.node); - $.on($('#recaptcha_challenge_field_holder'), 'DOMNodeInserted', qr.captchaNode); - qr.captchaTime = Date.now(); - qr.spoiler = $('.postarea label') ? '' : ''; - qr.acceptFiles = $('.rules').textContent.match(/: (.+) /)[1].replace(/\w+/g, function(type) { - switch (type) { - case 'JPG': - return 'image/JPEG'; - case 'PDF': - return 'application/' + type; - default: - return 'image/' + type; - } - }); - iframe = $.el('iframe', { - name: 'iframe', - hidden: true - }); - $.add(d.body, iframe); - return $('#recaptcha_response_field').id = ''; - }, - attach: function() { - var fileDiv; - fileDiv = $.el('div', { - innerHTML: "X" - }); - $.on(fileDiv.firstChild, 'change', qr.validateFileSize); - $.on(fileDiv.lastChild, 'click', (function() { - return $.rm(this.parentNode); - })); - return $.add($('#files', qr.el), fileDiv); - }, - attachNext: function() { - var file, fileDiv, oldFile; - fileDiv = $.rm($('#files div', qr.el)); - file = fileDiv.firstChild; - oldFile = $('#qr_form input[type=file]', qr.el); - return $.replace(oldFile, file); - }, - autoPost: function() { - if (qr.el && $('#auto', qr.el).checked) { - return qr.submit.call($('form', qr.el)); - } - }, - captchaNode: function(e) { - if (!qr.el) return; - val = e.target.value; - $('img', qr.el).src = "http://www.google.com/recaptcha/api/image?c=" + val; - qr.challenge = val; - return qr.captchaTime = Date.now(); - }, - captchaKeydown: function(e) { - var captchas; - if (!(e.keyCode === 13 && this.value)) return; - if (cooldown.duration) $('#auto', qr.el).checked = true; - captchas = $.get('captchas', []); - captchas.push({ - challenge: qr.challenge, - response: this.value, - time: qr.captchaTime - }); - $.set('captchas', captchas); - $('#captchas', qr.el).textContent = captchas.length + ' captchas'; - Recaptcha.reload(); - this.value = ''; - if (!$('textarea', qr.el).value && !$('input[type=file]', qr.el).files.length) { - return e.preventDefault(); - } - }, - close: function() { - $.rm(qr.el); - return qr.el = null; - }, - dialog: function(link) { - var THREAD_ID, c, html, m, submitDisabled, submitValue; - submitValue = $('#com_submit').value; - submitDisabled = $('#com_submit').disabled ? 'disabled' : ''; - THREAD_ID = g.THREAD_ID || $.x('ancestor::div[@class="thread"]/div', link).id; - qr.challenge = $('#recaptcha_challenge_field').value; - html = " X
Quick Reply
" + qr.spoiler + "
" + ($.get('captchas', []).length) + " captchas
attach another file
"; - qr.el = ui.dialog('qr', 'top: 0; right: 0;', html); - c = d.cookie; - $('input[name=name]', qr.el).value = (m = c.match(/4chan_name=([^;]+)/)) ? decodeURIComponent(m[1]) : ''; - $('input[name=email]', qr.el).value = (m = c.match(/4chan_email=([^;]+)/)) ? decodeURIComponent(m[1]) : ''; - $('input[name=pwd]', qr.el).value = (m = c.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('input[name=pwd]').value; - $.on($('input[name=name]', qr.el), 'mousedown', function(e) { - return e.stopPropagation(); - }); - $.on($('input[name=upfile]', qr.el), 'change', qr.validateFileSize); - $.on($('#close', qr.el), 'click', qr.close); - $.on($('form', qr.el), 'submit', qr.submit); - $.on($('#attach', qr.el), 'click', qr.attach); - $.on($('img', qr.el), 'click', Recaptcha.reload); - $.on($('#dummy', qr.el), 'keydown', Recaptcha.listener); - $.on($('#dummy', qr.el), 'keydown', qr.captchaKeydown); - return $.add(d.body, qr.el); - }, - message: function(data) { - var duration, fileCount, tc; - $('iframe[name=iframe]').src = 'about:blank'; - fileCount = $('#files', qr.el).childElementCount; - tc = data.textContent; - if (!/successful!|uploaded!$/.test(tc)) { - if (tc === void 0) { - data.textContent = "Connection error with sys.4chan.org."; - } - $.extend($('#error', qr.el), data); - $('#recaptcha_response_field', qr.el).value = ''; - $('#autohide', qr.el).checked = false; - if (tc === 'You seem to have mistyped the verification.') { - setTimeout(qr.autoPost, 1000); - } else if (tc === 'Error: Duplicate file entry detected.' && fileCount) { - $('textarea', qr.el).value += '\n' + tc + ' ' + data.href; - qr.attachNext(); - setTimeout(qr.autoPost, 1000); - } - return; - } - if (qr.el) { - if (g.REPLY && (conf['Persistent QR'] || fileCount)) { - qr.refresh(); - if (fileCount) qr.attachNext(); - } else { - qr.close(); - } - } - if (conf['Cooldown']) { - duration = qr.sage ? 60 : 30; - $.set(g.BOARD + '/cooldown', Date.now() + duration * 1000); - return cooldown.start(); - } - }, - node: function(root) { - var quote; - quote = $('a.quotejs:not(:first-child)', root); - return $.on(quote, 'click', qr.quote); - }, - postInvalid: function() { - var captcha, captchas, content, cutoff, dummy, response; - content = $('textarea', qr.el).value || $('input[type=file]', qr.el).files.length; - if (!content) return 'Error: No text entered.'; - /* - captchas expire after 30 minutes, see window.RecaptchaState.timeout. - cutoff 5 minutes before then, b/c posting takes time. - */ - cutoff = Date.now() - 25 * MINUTE; - captchas = $.get('captchas', []); - while (captcha = captchas.shift()) { - if (captcha.time > cutoff) break; - } - $.set('captchas', captchas); - $('#captchas', qr.el).textContent = captchas.length + ' captchas'; - if (!captcha) { - dummy = $('#dummy', qr.el); - if (!(response = dummy.value)) { - return 'You forgot to type in the verification'; - } - captcha = { - challenge: qr.challenge, - response: response - }; - dummy.value = ''; - Recaptcha.reload(); - } - $('#recaptcha_challenge_field', qr.el).value = captcha.challenge; - $('#recaptcha_response_field', qr.el).value = captcha.response; - return false; - }, - quote: function(e) { - var caretPos, id, s, selection, selectionID, ta, text, _ref; - if (e) e.preventDefault(); - if (qr.el) { - $('#autohide', qr.el).checked = false; - } else { - qr.dialog(this); - } - id = this.textContent; - text = ">>" + id + "\n"; - selection = window.getSelection(); - if (s = selection.toString()) { - selectionID = (_ref = $.x('ancestor-or-self::blockquote/preceding-sibling::input', selection.anchorNode)) != null ? _ref.name : void 0; - if (selectionID === id) { - s = s.replace(/\n/g, '\n>'); - text += ">" + s + "\n"; - } - } - ta = $('textarea', qr.el); - caretPos = ta.selectionStart; - ta.value = ta.value.slice(0, caretPos) + text + ta.value.slice(ta.selectionEnd, ta.value.length); - ta.focus(); - return ta.selectionEnd = ta.selectionStart = caretPos + text.length + 1 * (engine === 'presto'); - }, - refresh: function() { - var m, newFile, oldFile, _ref; - $('[name=sub]', qr.el).value = ''; - $('[name=email]', qr.el).value = (m = d.cookie.match(/4chan_email=([^;]+)/)) ? decodeURIComponent(m[1]) : ''; - $('[name=com]', qr.el).value = ''; - $('[name=recaptcha_response_field]', qr.el).value = ''; - if (!conf['Remember Spoiler']) { - if ((_ref = $('[name=spoiler]', qr.el)) != null) _ref.checked = false; - } - oldFile = $('[type=file]', qr.el); - newFile = $.el('input', { - type: 'file', - name: 'upfile', - accept: qr.acceptFiles - }); - return $.replace(oldFile, newFile); - }, - submit: function(e) { - var id, msg, op; - if (msg = qr.postInvalid()) { - if (typeof e.preventDefault === "function") e.preventDefault(); - alert(msg); - if (msg === 'You forgot to type in the verification.') { - $('#dummy', qr.el).focus(); - } - return; - } - if (conf['Auto Watch Reply'] && conf['Thread Watcher']) { - if (g.REPLY && $('img.favicon').src === Favicon.empty) { - watcher.watch(null, g.THREAD_ID); - } else { - id = $('input[name=resto]', qr.el).value; - op = $.id(id); - if ($('img.favicon', op).src === Favicon.empty) watcher.watch(op, id); - } - } - if (!e) this.submit(); - $('#error', qr.el).textContent = ''; - if (conf['Auto Hide QR']) $('#autohide', qr.el).checked = true; - return qr.sage = /sage/i.test($('input[name=email]', this).value); - }, - sys: function() { - var c, duration, id, noko, recaptcha, sage, search, thread, url, watch, _, _ref, _ref2; - if (recaptcha = $('#recaptcha_response_field')) { - $.on(recaptcha, 'keydown', Recaptcha.listener); - return; - } - /* - http://code.google.com/p/chromium/issues/detail?id=20773 - Let content scripts see other frames (instead of them being undefined) - - To access the parent, we have to break out of the sandbox and evaluate - in the global context. - */ - $.globalEval(function() { - var data, node, _ref; - data = {}; - if (node = (_ref = document.querySelector('td b')) != null ? _ref.firstChild : void 0) { - data.textContent = node.textContent; - if (node.href) data.href = node.href; - } - return parent.postMessage(data, '*'); - }); - c = (_ref = $('b')) != null ? _ref.lastChild : void 0; - if (!(c && c.nodeType === 8)) return; - _ref2 = c.textContent.match(/thread:(\d+),no:(\d+)/), _ = _ref2[0], thread = _ref2[1], id = _ref2[2]; - search = location.search; - cooldown = /cooldown/.test(search); - noko = /noko/.test(search); - sage = /sage/.test(search); - watch = /watch/.test(search); - url = "http://boards.4chan.org/" + g.BOARD; - if (watch && thread === '0') { - url += "/res/" + id + "?watch"; - } else if (noko) { - url += '/res/'; - url += thread === '0' ? id : thread; - } - if (cooldown) { - duration = Date.now() + (sage ? 60 : 30) * 1000; - url += '?cooldown=' + duration; - } - if (noko) url += '#' + id; - return window.location = url; - }, - validateFileSize: function(e) { - var file; - if (!(this.files[0].size > $('input[name=MAX_FILE_SIZE]').value)) return; - file = $.el('input', { - type: 'file', - name: 'upfile', - accept: qr.acceptFiles - }); - $.on(file, 'change', qr.validateFileSize); - $.replace(this, file); - $('#error', qr.el).textContent = 'Error: File too large.'; - return alert('Error: File too large.'); - } - }; - - Recaptcha = { - init: function() { - var el, _i, _len, _ref; - _ref = $$('#recaptcha_table a'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - el = _ref[_i]; - el.tabIndex = 1; - } - return $.on($('#recaptcha_response_field'), 'keydown', Recaptcha.listener); - }, - listener: function(e) { - if (e.keyCode === 8 && this.value === '') return Recaptcha.reload(); - }, - reload: function() { - return window.location = 'javascript:Recaptcha.reload()'; - } - }; - threading = { init: function() { return threading.thread($('body > form').firstChild); @@ -3112,7 +2729,7 @@ } }, onLoad: function() { - var callback, canPost, form, node, nodes, _i, _j, _len, _len2, _ref; + var callback, node, nodes, _i, _j, _len, _len2, _ref; $.off(d, 'DOMContentLoaded', Main.onLoad); if (conf['404 Redirect'] && d.title === '4chan - 404' && /^\d+$/.test(g.THREAD_ID)) { redirect(); @@ -3123,21 +2740,8 @@ $.addStyle(Main.css); threading.init(); Favicon.init(); - if ((form = $('form[name=post]')) && (canPost = !!$('#recaptcha_response_field'))) { - Recaptcha.init(); - if (g.REPLY && conf['Auto Watch Reply'] && conf['Thread Watcher']) { - $.on(form, 'submit', function() { - if ($('img.favicon').src === Favicon.empty) { - return watcher.watch(null, g.THREAD_ID); - } - }); - } - } - if (conf['Auto Noko'] && canPost) form.action += '?noko'; - if (conf['Cooldown'] && canPost) cooldown.init(); if (conf['Image Expansion']) imgExpand.init(); if (conf['Reveal Spoilers'] && $('.postarea label')) revealSpoilers.init(); - if (conf['Quick Reply']) qr.init(); if (conf['Thread Watcher']) watcher.init(); if (conf['Keybinds']) keybinds.init(); if (conf['Reply Navigation'] || conf['Index Navigation']) nav.init(); @@ -3146,10 +2750,6 @@ if (conf['Thread Stats']) threadStats.init(); if (conf['Post in Title']) titlePost.init(); if (conf['Unread Count']) unread.init(); - if (conf['Quick Reply'] && conf['Persistent QR'] && canPost) { - qr.dialog(); - if (conf['Auto Hide QR']) $('#autohide', qr.el).checked = true; - } } else { if (conf['Thread Hiding']) threadHiding.init(); if (conf['Thread Expansion']) expandThread.init(); diff --git a/script.coffee b/script.coffee index 0996d7256..c87846d4f 100644 --- a/script.coffee +++ b/script.coffee @@ -32,7 +32,7 @@ config = 'Auto Watch': [true, 'Automatically watch threads that you start'] 'Auto Watch Reply': [false, 'Automatically watch threads that you reply to'] Posting: - 'Auto Noko': [true, 'Always redirect to your post'] + 'Auto Noko': [true, 'Redirect to your post'] 'Cooldown': [true, 'Prevent `flood detected` errors'] 'Quick Reply': [true, 'Reply without leaving the page'] 'Persistent QR': [false, 'Quick reply won\'t disappear after posting. Only in replies.'] @@ -651,8 +651,7 @@ keybinds = when conf.close if o = $ '#overlay' $.rm o - else if qr.el - qr.close() + # else close QR when conf.spoiler ta = e.target return unless ta.nodeName is 'TEXTAREA' @@ -671,7 +670,7 @@ keybinds = when conf.zero window.location = "/#{g.BOARD}/0#0" when conf.openEmptyQR - keybinds.qr thread + ;# QR when conf.nextReply keybinds.hl.next thread when conf.previousReply @@ -683,7 +682,7 @@ keybinds = when conf.expandThread expandThread.toggle thread when conf.openQR - keybinds.qr thread, true + ;# QR when conf.expandImages keybinds.img thread when conf.nextThread @@ -703,10 +702,7 @@ keybinds = when conf.previousPage $('input[value=Previous]')?.click() when conf.submit - if qr.el - qr.submit.call $ 'form', qr.el - else - $('.postarea form').submit() + ;# QR when conf.unreadCountTo0 unread.replies.length = 0 unread.updateTitle() @@ -747,14 +743,6 @@ keybinds = thumb = $ 'img[md5]', root imgExpand.toggle thumb.parentNode - qr: (thread, quote) -> - if quote - qr.quote.call $ '.quotejs + a', $('.replyhl', thread) or thread - else - unless qr.el - qr.dialog '', thread?.firstChild.id - $('textarea', qr.el).focus() - open: (thread, tab) -> id = thread.firstChild.id url = "http://boards.4chan.org/#{g.BOARD}/res/#{id}" @@ -1080,358 +1068,6 @@ options = Favicon.update() if g.REPLY and conf['Unread Count'] @nextElementSibling.innerHTML = " " -cooldown = - #TODO merge into qr - init: -> - if match = location.search.match /cooldown=(\d+)/ - [_, time] = match - $.set g.BOARD+'/cooldown', time if $.get(g.BOARD+'/cooldown', 0) < time - cooldown.start() if Date.now() < $.get g.BOARD+'/cooldown', 0 - $.on window, 'storage', (e) -> cooldown.start() if e.key is "#{NAMESPACE}#{g.BOARD}/cooldown" - $('.postarea form').action += '?cooldown' if g.REPLY - - start: -> - cooldown.duration = Math.ceil ($.get(g.BOARD+'/cooldown', 0) - Date.now()) / 1000 - return unless cooldown.duration > 0 - for submit in $$ '#com_submit' - submit.value = cooldown.duration - submit.disabled = true - setTimeout cooldown.cb, 1000 - - cb: -> - submits = $$ '#com_submit' - if --cooldown.duration - setTimeout cooldown.cb, 1000 - for submit in submits - submit.value = cooldown.duration - else - for submit in submits - submit.disabled = false - submit.value = 'Submit' - qr.autoPost() - -qr = - # TODO - # error handling / logging - # persistent captcha - # rm Recaptcha - # email reverts - init: -> - g.callbacks.push qr.node - $.on $('#recaptcha_challenge_field_holder'), 'DOMNodeInserted', qr.captchaNode - qr.captchaTime = Date.now() - - qr.spoiler = if $('.postarea label') then '' else '' - qr.acceptFiles = $('.rules').textContent.match(/: (.+) /)[1].replace /\w+/g, (type) -> - switch type - when 'JPG' - 'image/JPEG' - when 'PDF' - 'application/' + type - else - 'image/' + type - - iframe = $.el 'iframe', - name: 'iframe' - hidden: true - $.add d.body, iframe - - #hack - nuke id so it doesn't grab focus when reloading - $('#recaptcha_response_field').id = '' - - attach: -> - fileDiv = $.el 'div', innerHTML: "X" - $.on fileDiv.firstChild, 'change', qr.validateFileSize - $.on fileDiv.lastChild, 'click', (-> $.rm @parentNode) - $.add $('#files', qr.el), fileDiv - - attachNext: -> - fileDiv = $.rm $('#files div', qr.el) - file = fileDiv.firstChild - oldFile = $ '#qr_form input[type=file]', qr.el - $.replace oldFile, file - - autoPost: -> - if qr.el and $('#auto', qr.el).checked - qr.submit.call $ 'form', qr.el - - captchaNode: (e) -> - return unless qr.el - val = e.target.value - $('img', qr.el).src = "http://www.google.com/recaptcha/api/image?c=" + val - qr.challenge = val - qr.captchaTime = Date.now() - - captchaKeydown: (e) -> - return unless e.keyCode is 13 and @value #enter, captcha filled - - $('#auto', qr.el).checked = true if cooldown.duration #enable autoposting - - captchas = $.get 'captchas', [] - captchas.push - challenge: qr.challenge - response: @value - time: qr.captchaTime - $.set 'captchas', captchas - $('#captchas', qr.el).textContent = captchas.length + ' captchas' - Recaptcha.reload() - @value = '' - - if !$('textarea', qr.el).value and !$('input[type=file]', qr.el).files.length - e.preventDefault() - - close: -> - $.rm qr.el - qr.el = null - - dialog: (link) -> - submitValue = $('#com_submit').value - submitDisabled = if $('#com_submit').disabled then 'disabled' else '' - #FIXME inlined cross-thread quotes - THREAD_ID = g.THREAD_ID or $.x('ancestor::div[@class="thread"]/div', link).id - qr.challenge = $('#recaptcha_challenge_field').value - - html = " - X - -
- - Quick Reply -
-
-
- - - - -
#{qr.spoiler}
-
-
-
-
#{$.get('captchas', []).length} captchas
-
-
-
-
attach another file
-
- - " - qr.el = ui.dialog 'qr', 'top: 0; right: 0;', html - - c = d.cookie - $('input[name=name]', qr.el).value = - if m = c.match(/4chan_name=([^;]+)/) then decodeURIComponent m[1] else '' - $('input[name=email]', qr.el).value = - if m = c.match(/4chan_email=([^;]+)/) then decodeURIComponent m[1] else '' - $('input[name=pwd]', qr.el).value = - if m = c.match(/4chan_pass=([^;]+)/) then decodeURIComponent m[1] else $('input[name=pwd]').value - - $.on $('input[name=name]', qr.el), 'mousedown', (e) -> e.stopPropagation() - $.on $('input[name=upfile]', qr.el), 'change', qr.validateFileSize - $.on $('#close', qr.el), 'click', qr.close - $.on $('form', qr.el), 'submit', qr.submit - $.on $('#attach', qr.el), 'click', qr.attach - $.on $('img', qr.el), 'click', Recaptcha.reload - $.on $('#dummy', qr.el), 'keydown', Recaptcha.listener - $.on $('#dummy', qr.el), 'keydown', qr.captchaKeydown - - $.add d.body, qr.el - - message: (data) -> - $('iframe[name=iframe]').src = 'about:blank' - fileCount = $('#files', qr.el).childElementCount - - tc = data.textContent - unless /successful!|uploaded!$/.test tc # error message, not a successful post - if tc is undefined - data.textContent = "Connection error with sys.4chan.org." - $.extend $('#error', qr.el), data - $('#recaptcha_response_field', qr.el).value = '' - $('#autohide', qr.el).checked = false - if tc is 'You seem to have mistyped the verification.' - setTimeout qr.autoPost, 1000 - else if tc is 'Error: Duplicate file entry detected.' and fileCount - $('textarea', qr.el).value += '\n' + tc + ' ' + data.href - qr.attachNext() - setTimeout qr.autoPost, 1000 - return - - if qr.el - if g.REPLY and (conf['Persistent QR'] or fileCount) - qr.refresh() - if fileCount - qr.attachNext() - else - qr.close() - if conf['Cooldown'] - duration = if qr.sage then 60 else 30 - $.set g.BOARD+'/cooldown', Date.now() + duration * 1000 - cooldown.start() - - node: (root) -> - quote = $ 'a.quotejs:not(:first-child)', root - $.on quote, 'click', qr.quote - - postInvalid: -> - content = $('textarea', qr.el).value or $('input[type=file]', qr.el).files.length - return 'Error: No text entered.' unless content - - ### - captchas expire after 30 minutes, see window.RecaptchaState.timeout. - cutoff 5 minutes before then, b/c posting takes time. - ### - - cutoff = Date.now() - 25*MINUTE - captchas = $.get 'captchas', [] - while captcha = captchas.shift() - if captcha.time > cutoff - break - $.set 'captchas', captchas - - $('#captchas', qr.el).textContent = captchas.length + ' captchas' - - unless captcha - dummy = $ '#dummy', qr.el - return 'You forgot to type in the verification' unless response = dummy.value - captcha = - challenge: qr.challenge - response: response - dummy.value = '' - Recaptcha.reload() - - $('#recaptcha_challenge_field', qr.el).value = captcha.challenge - $('#recaptcha_response_field', qr.el).value = captcha.response - - false - - quote: (e) -> - e.preventDefault() if e - - if qr.el - $('#autohide', qr.el).checked = false - else - qr.dialog @ - - id = @textContent - text = ">>#{id}\n" - - selection = window.getSelection() - if s = selection.toString() - selectionID = $.x('ancestor-or-self::blockquote/preceding-sibling::input', selection.anchorNode)?.name - if selectionID is id - s = s.replace /\n/g, '\n>' - text += ">#{s}\n" - - ta = $ 'textarea', qr.el - caretPos = ta.selectionStart - #replace selection for text - ta.value = ta.value.slice(0, caretPos) + text + ta.value.slice(ta.selectionEnd, ta.value.length) - ta.focus() - #move the caret to the end of the new quote - ta.selectionEnd = ta.selectionStart = caretPos + text.length + 1*(engine is 'presto') - - refresh: -> - $('[name=sub]', qr.el).value = '' - $('[name=email]', qr.el).value = if m = d.cookie.match(/4chan_email=([^;]+)/) then decodeURIComponent m[1] else '' - $('[name=com]', qr.el).value = '' - $('[name=recaptcha_response_field]', qr.el).value = '' - $('[name=spoiler]', qr.el)?.checked = false unless conf['Remember Spoiler'] - # XXX opera doesn't allow resetting file inputs w/ file.value = '' - oldFile = $ '[type=file]', qr.el - newFile = $.el 'input', type: 'file', name: 'upfile', accept: qr.acceptFiles - $.replace oldFile, newFile - - submit: (e) -> - #XXX `e` won't exist if we're here from `qr.submit.call form`. - if msg = qr.postInvalid() - e.preventDefault?() - alert msg - if msg is 'You forgot to type in the verification.' - $('#dummy', qr.el).focus() - return - - if conf['Auto Watch Reply'] and conf['Thread Watcher'] - if g.REPLY and $('img.favicon').src is Favicon.empty - watcher.watch null, g.THREAD_ID - else - id = $('input[name=resto]', qr.el).value - op = $.id id - if $('img.favicon', op).src is Favicon.empty - watcher.watch op, id - - if !e then @submit() - $('#error', qr.el).textContent = '' - $('#autohide', qr.el).checked = true if conf['Auto Hide QR'] - qr.sage = /sage/i.test $('input[name=email]', @).value - - sys: -> - if recaptcha = $ '#recaptcha_response_field' #post reporting - $.on recaptcha, 'keydown', Recaptcha.listener - return - - ### - http://code.google.com/p/chromium/issues/detail?id=20773 - Let content scripts see other frames (instead of them being undefined) - - To access the parent, we have to break out of the sandbox and evaluate - in the global context. - ### - $.globalEval -> - data = {} - if node = document.querySelector('td b')?.firstChild - data.textContent = node.textContent - data.href = node.href if node.href - parent.postMessage data, '*' - - c = $('b')?.lastChild - - return unless c and c.nodeType is 8 #comment node - - [_, thread, id] = c.textContent.match(/thread:(\d+),no:(\d+)/) - - {search} = location - cooldown = /cooldown/.test search - noko = /noko/ .test search - sage = /sage/ .test search - watch = /watch/ .test search - - url = "http://boards.4chan.org/#{g.BOARD}" - - if watch and thread is '0' - url += "/res/#{id}?watch" - else if noko - url += '/res/' - url += if thread is '0' then id else thread - if cooldown - duration = Date.now() + (if sage then 60 else 30) * 1000 - url += '?cooldown=' + duration - if noko - url += '#' + id - - window.location = url - - validateFileSize: (e) -> - return unless @files[0].size > $('input[name=MAX_FILE_SIZE]').value - - file = $.el 'input', type: 'file', name: 'upfile', accept: qr.acceptFiles - $.on file, 'change', qr.validateFileSize - $.replace @, file - - $('#error', qr.el).textContent = 'Error: File too large.' - alert 'Error: File too large.' - -Recaptcha = - init: -> - #hack to tab from comment straight to recaptcha - for el in $$ '#recaptcha_table a' - el.tabIndex = 1 - $.on $('#recaptcha_response_field'), 'keydown', Recaptcha.listener - listener: (e) -> - if e.keyCode is 8 and @value is '' # backspace to reload - Recaptcha.reload() - reload: -> - window.location = 'javascript:Recaptcha.reload()' - threading = init: -> threading.thread $('body > form').firstChild @@ -2464,29 +2100,13 @@ Main = threading.init() Favicon.init() - #recaptcha may be blocked, eg by noscript - if (form = $ 'form[name=post]') and (canPost = !!$ '#recaptcha_response_field') - Recaptcha.init() - if g.REPLY and conf['Auto Watch Reply'] and conf['Thread Watcher'] - $.on form, 'submit', -> if $('img.favicon').src is Favicon.empty - watcher.watch null, g.THREAD_ID - #major features - if conf['Auto Noko'] and canPost - form.action += '?noko' - - if conf['Cooldown'] and canPost - cooldown.init() - if conf['Image Expansion'] imgExpand.init() if conf['Reveal Spoilers'] and $('.postarea label') revealSpoilers.init() - if conf['Quick Reply'] - qr.init() - if conf['Thread Watcher'] watcher.init() @@ -2509,11 +2129,6 @@ Main = if conf['Unread Count'] unread.init() - if conf['Quick Reply'] and conf['Persistent QR'] and canPost - qr.dialog() - if conf['Auto Hide QR'] - $('#autohide', qr.el).checked = true - else #not reply if conf['Thread Hiding'] threadHiding.init()