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.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.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()