diff --git a/4chan_x.js b/4chan_x.js
index ef3148119..3f9a982bf 100644
--- a/4chan_x.js
+++ b/4chan_x.js
@@ -56,7 +56,7 @@
*/
(function() {
- var $, $$, DAY, a, arr, as, autoWatch, autohide, b, board, callback, changeCheckbox, changeValue, clearHidden, closeQR, config, cooldown, cutoff, d, delform, down, editSauce, el, expand, expandComment, expandThread, formSubmit, g, getConfig, getThread, getTime, hide, hideReply, hideThread, href, html, i, id, iframe, iframeLoad, imageClick, imageExpand, imageExpandClick, imageHover, imageResize, imageThumb, imageToggle, imageType, imageTypeChange, img, inAfter, inBefore, input, inputs, keyModeInsert, keyModeNormal, keydown, keypress, l1, lastChecked, log, m, mv, n, navbotr, navtopr, nodeInserted, now, omitted, onloadComment, onloadThread, option, options, parseResponse, pathname, qrListener, qrText, quickReply, recaptcha, recaptchaListener, recaptchaReload, redirect, replace, replyNav, report, request, rm, scroll, scrollThread, show, showReply, showThread, slice, span, src, start, stopPropagation, temp, text, textContent, thread, threadF, threads, tn, tzOffset, ui, up, updateAuto, updateCallback, updateFavicon, updateInterval, updateNow, updateTime, updateTitle, updateVerbose, updaterMake, watch, watchX, watcher, watcherUpdate, x, zeroPad, _, _base, _i, _j, _k, _l, _len, _len2, _len3, _len4, _len5, _len6, _len7, _m, _n, _ref, _ref2, _ref3, _ref4, _ref5;
+ var $, $$, DAY, a, arr, as, autoWatch, callback, changeCheckbox, changeValue, clearHidden, closeQR, config, cooldown, cutoff, d, delform, down, editSauce, el, expand, expandComment, expandThread, g, getConfig, getThread, getTime, hide, hideReply, hideThread, href, html, i, id, imageClick, imageExpand, imageExpandClick, imageHover, imageResize, imageThumb, imageToggle, imageType, imageTypeChange, img, inAfter, inBefore, input, inputs, keyModeInsert, keyModeNormal, keydown, keypress, l1, lastChecked, log, m, mv, n, navbotr, navtopr, nodeInserted, now, omitted, onloadComment, onloadThread, option, options, parseResponse, pathname, qr, recaptcha, recaptchaListener, recaptchaReload, redirect, replace, replyNav, report, request, rm, scroll, scrollThread, show, showReply, showThread, slice, span, src, start, stopPropagation, temp, text, textContent, threadF, threads, tn, tzOffset, ui, up, updateAuto, updateCallback, updateFavicon, updateInterval, updateNow, updateTime, updateTitle, updateVerbose, updaterMake, watch, watchX, watcher, watcherUpdate, x, zeroPad, _i, _j, _k, _l, _len, _len2, _len3, _len4, _len5, _len6, _len7, _m, _n, _ref, _ref2, _ref3, _ref4;
var __slice = Array.prototype.slice;
if (typeof console != "undefined" && console !== null) {
log = console.log;
@@ -241,6 +241,29 @@
return object;
};
$.extend($, {
+ addClass: function(el, className) {
+ return el.className += ' ' + className;
+ },
+ removeClass: function(el, className) {
+ return el.className = el.className.replace(' ' + className, '');
+ },
+ rm: function(el) {
+ return el.parentNode.removeChild(el);
+ },
+ append: function(parent, child) {
+ return parent.appendChild(child);
+ },
+ before: function(root, el) {
+ return root.parentNode.insertBefore(el, root);
+ },
+ el: function(tag, properties) {
+ var el;
+ el = d.createElement(tag);
+ if (properties) {
+ $.extend(el, properties);
+ }
+ return el;
+ },
bind: function(el, eventType, handler) {
return el.addEventListener(eventType, handler, true);
},
@@ -387,17 +410,6 @@
return n;
}
};
- autohide = function() {
- var klass, qr;
- qr = $('#qr');
- klass = qr.className;
- if (this.checked) {
- klass += ' auto';
- } else {
- klass = klass.replace(' auto', '');
- }
- return qr.className = klass;
- };
autoWatch = function() {
var autoText;
autoText = $('textarea', this).value.slice(0, 25);
@@ -500,28 +512,6 @@
}
}
};
- formSubmit = function(e) {
- var recaptcha, span, _ref;
- if (span = this.nextSibling) {
- rm(span);
- }
- recaptcha = $('input[name=recaptcha_response_field]', this);
- if (recaptcha.value) {
- if ((_ref = $('#qr input[title=autohide]:not(:checked)')) != null) {
- _ref.click();
- }
- return g.sage = $('#qr input[name=email]').value === 'sage' ? true : false;
- } else {
- e.preventDefault();
- span = n('span', {
- className: 'error',
- textContent: 'You forgot to type in the verification.'
- });
- mv(span, this.parentNode);
- alert('You forgot to type in the verification.');
- return recaptcha.focus();
- }
- };
hideReply = function(reply) {
var a, div, name, p, table, trip, _ref;
if (p = this.parentNode) {
@@ -576,42 +566,6 @@
return inBefore(div, a);
}
};
- iframeLoad = function() {
- var auto, error, f, qr, span, submit, _ref, _ref2;
- if (g.iframe = !g.iframe) {
- return;
- }
- $('iframe').src = 'about:blank';
- qr = $('#qr');
- if (error = GM_getValue('error')) {
- span = n('span', {
- textContent: error,
- className: 'error'
- });
- mv(span, qr);
- if ((_ref = $('input[title=autohide]:checked', qr)) != null) {
- _ref.click();
- }
- } else if (g.REPLY && getConfig('Persistent QR')) {
- $('textarea', qr).value = '';
- $('input[name=recaptcha_response_field]', qr).value = '';
- f = $('input[type=file]', qr).parentNode;
- f.innerHTML = f.innerHTML;
- submit = $('input[type=submit]', qr);
- submit.value = g.sage ? 60 : 30;
- submit.disabled = true;
- window.setTimeout(cooldown, 1000);
- auto = submit.previousSibling.lastChild;
- if (auto.checked) {
- if ((_ref2 = $('input[title=autohide]:checked', qr)) != null) {
- _ref2.click();
- }
- }
- } else {
- rm(qr);
- }
- return recaptchaReload();
- };
imageHover = {
init: function() {
var img;
@@ -830,9 +784,14 @@
}
}
if (e.shiftKey) {
- return quickReply(qrLink);
+ $.append(d.body, qr.dialog(qrLink));
+ return $('#qr textarea').focus();
} else {
- return quickReply(qrLink, qrText(qrLink));
+ e = {
+ preventDefault: function() {},
+ target: qrLink
+ };
+ return qr.cb.quote(e);
}
break;
case "J":
@@ -926,7 +885,7 @@
}
};
nodeInserted = function(e) {
- var callback, qr, target, _i, _len, _ref, _results;
+ var callback, dialog, target, _i, _len, _ref, _results;
target = e.target;
if (target.nodeName === 'TABLE') {
_ref = g.callbacks;
@@ -936,9 +895,9 @@
_results.push(callback(target));
}
return _results;
- } else if (target.id === 'recaptcha_challenge_field' && (qr = $('#qr'))) {
- $('#recaptcha_image img', qr).src = "http://www.google.com/recaptcha/api/image?c=" + target.value;
- return $('#recaptcha_challenge_field', qr).value = target.value;
+ } else if (target.id === 'recaptcha_challenge_field' && (dialog = $('#qr'))) {
+ $('#recaptcha_image img', dialog).src = "http://www.google.com/recaptcha/api/image?c=" + target.value;
+ return $('#recaptcha_challenge_field', dialog).value = target.value;
}
};
onloadComment = function(responseText, a, href) {
@@ -1029,73 +988,200 @@
opbq = $('blockquote', body);
return [replies, opbq];
};
- qrListener = function(e) {
- var link, text;
- e.preventDefault();
- link = e.target;
- text = qrText(link);
- return quickReply(link, text);
- };
- qrText = function(link) {
- var id, s, selection, text, _ref;
- text = '>>' + link.parentNode.id.match(/\d+$/)[0] + '\n';
- selection = window.getSelection();
- id = (_ref = x('preceding::span[@id][1]', selection.anchorNode)) != null ? _ref.id : void 0;
- if ((s = selection.toString()) && (id === link.parentNode.id)) {
- text += ">" + s;
- }
- return text;
- };
- quickReply = function(link, text) {
- var auto, autoBox, clone, form, html, input, qr, script, submit, textarea, xpath, _i, _len, _ref, _ref2;
- if (!(qr = $('#qr'))) {
- html = "
";
- qr = ui.dialog('qr', 'topleft', html);
- $('input[title=autohide]', qr).addEventListener('click', autohide, true);
- form = $('form[name=post]');
- clone = form.cloneNode(true);
+ qr = {
+ init: function() {
+ var iframe;
+ g.callbacks.push(qr.cb.node);
+ iframe = $.el('iframe', {
+ name: 'iframe'
+ });
+ $.append(d.body, iframe);
+ $.bind(iframe, 'load', qr.cb.load);
+ $.bind(window, 'message', qr.cb.messageTop);
+ return $('#recaptcha_response_field').id = '';
+ },
+ autohide: {
+ set: function() {
+ var _ref;
+ return (_ref = $('#qr input[title=autohide]:not(:checked)')) != null ? _ref.click() : void 0;
+ },
+ unset: function() {
+ var _ref;
+ return (_ref = $('#qr input[title=autohide]:checked')) != null ? _ref.click() : void 0;
+ }
+ },
+ cb: {
+ autohide: function(e) {
+ var dialog;
+ dialog = $('#qr');
+ if (this.checked) {
+ return $.addClass(dialog, 'auto');
+ } else {
+ return $.removeClass(dialog, 'auto');
+ }
+ },
+ load: function(e) {
+ return e.target.contentWindow.postMessage('', '*');
+ },
+ messageIframe: function(e) {
+ var message;
+ message = $('table b').firstChild.textContent;
+ e.source.postMessage(message, '*');
+ return window.location = 'about:blank';
+ },
+ messageTop: function(e) {
+ var data, dialog, error;
+ data = e.data;
+ dialog = $('#qr');
+ if (data === 'Post successful!') {
+ if (dialog) {
+ if (getConfig('Persistent QR')) {
+ qr.refresh(dialog);
+ } else {
+ $.rm(dialog);
+ }
+ }
+ } else {
+ error = $.el('span', {
+ className: 'error',
+ textContent: data
+ });
+ $.append(dialog, error);
+ qr.autohide.unset();
+ }
+ return recaptchaReload();
+ },
+ node: function(root) {
+ var quote, quotes, _i, _len, _results;
+ quotes = $$('a.quotejs:not(:first-child)', root);
+ _results = [];
+ for (_i = 0, _len = quotes.length; _i < _len; _i++) {
+ quote = quotes[_i];
+ _results.push($.bind(quote, 'click', qr.cb.quote));
+ }
+ return _results;
+ },
+ submit: function(e) {
+ var recaptcha, span;
+ if (span = this.nextSibling) {
+ $.rm(span);
+ }
+ recaptcha = $('input[name=recaptcha_response_field]', this);
+ if (recaptcha.value) {
+ qr.autohide.set();
+ return g.sage = $('#qr input[name=email]').value === 'sage';
+ } else {
+ e.preventDefault();
+ span = $.el('span', {
+ className: 'error',
+ textContent: 'You forgot to type in the verification.'
+ });
+ $.append(this.parentNode, span);
+ alert('You forgot to type in the verification.');
+ return recaptcha.focus();
+ }
+ },
+ quote: function(e) {
+ var dialog, id, s, selection, selectionID, ta, target, text, _ref;
+ e.preventDefault();
+ target = e.target;
+ if (!(dialog = $('#qr'))) {
+ dialog = qr.dialog(target);
+ }
+ id = target.textContent;
+ text = ">>" + id + "\n";
+ selection = window.getSelection();
+ if (s = selection.toString()) {
+ selectionID = (_ref = x('preceding::input[@type="checkbox"][1]', selection.anchorNode)) != null ? _ref.name : void 0;
+ if (selectionID === id) {
+ text += ">" + s + "\n";
+ }
+ }
+ ta = $('textarea', dialog);
+ ta.focus();
+ return ta.value += text;
+ },
+ refresh: function(dialog) {
+ var auto, f, submit, _ref;
+ $('textarea', dialog).value = '';
+ $('input[name=recaptcha_response_field]', dialog).value = '';
+ f = $('input[type=file]', dialog).parentNode;
+ f.innerHTML = f.innerHTML;
+ submit = $('input[type=submit]', qr);
+ submit.value = g.sage ? 60 : 30;
+ submit.disabled = true;
+ window.setTimeout(cooldown, 1000);
+ auto = submit.previousSibling.lastChild;
+ if (auto.checked) {
+ return (_ref = $('input[title=autohide]:checked', qr)) != null ? _ref.click() : void 0;
+ }
+ }
+ },
+ dialog: function(link) {
+ var auto, autobox, clone, dialog, el, html, input, script, submit, xpath, _i, _len, _ref;
+ html = "";
+ dialog = ui.dialog('qr', {
+ top: '0px',
+ left: '0px'
+ }, html);
+ el = $('input[title=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];
- rm(script);
+ $.rm(script);
}
- m($('input[name=recaptcha_response_field]', clone), {
- listener: ['keydown', recaptchaListener]
- });
- m(clone, {
- listener: ['submit', formSubmit],
- target: 'iframe'
- });
+ clone.target = 'iframe';
+ $.bind(clone, 'submit', qr.cb.submit);
+ $.bind($('input[name=recaptcha_response_field]', clone), 'keydown', recaptchaListener);
if (!g.REPLY) {
xpath = 'preceding::span[@class="postername"][1]/preceding::input[1]';
- input = n('input', {
+ input = $.el('input', {
type: 'hidden',
name: 'resto',
value: x(xpath, link).name
});
- mv(input, clone);
+ $.append(clone, input);
} else if (getConfig('Persistent QR')) {
submit = $('input[type=submit]', clone);
- auto = n('label', {
+ auto = $.el('label', {
textContent: 'Auto'
});
- autoBox = n('input', {
+ autobox = $.el('input', {
type: 'checkbox'
});
- mv(autoBox, auto);
- inBefore(submit, auto);
+ $.append(auto, autobox);
+ $.before(submit, auto);
+ }
+ $.append(dialog, clone);
+ $.append(d.body, dialog);
+ dialog.style.width = dialog.offsetWidth;
+ return dialog;
+ },
+ persist: function() {
+ $.append(d.body, qr.dialog());
+ return qr.autohide.set();
+ },
+ sys: function() {
+ var board, html, id, recaptcha, thread, _, _base, _ref, _ref2;
+ $.bind(window, 'message', qr.cb.messageIframe);
+ if (recaptcha = $('#recaptcha_response_field')) {
+ $.bind(recaptcha, 'keydown', recaptchaListener);
+ }
+ if (getConfig('Auto Watch')) {
+ html = $('b').innerHTML;
+ _ref = html.match(//), _ = _ref[0], thread = _ref[1], id = _ref[2];
+ if (thread === '0') {
+ _ref2 = $('meta', d).content.match(/4chan.org\/(\w+)\//), _ = _ref2[0], board = _ref2[1];
+ (_base = g.watched)[board] || (_base[board] = []);
+ g.watched[board].push({
+ id: id,
+ text: GM_getValue('autoText')
+ });
+ return GM_setValue('watched', JSON.stringify(g.watched));
+ }
}
- mv(clone, qr);
- mv(qr, d.body);
- qr.style.width = qr.offsetWidth;
- }
- if ((_ref2 = $('input[title=autohide]:checked', qr)) != null) {
- _ref2.click();
- }
- textarea = $('textarea', qr);
- textarea.focus();
- if (text) {
- return textarea.value += text;
}
};
recaptchaListener = function(e) {
@@ -1484,7 +1570,6 @@
favDefault: ((_ref = $('link[rel="shortcut icon"]', d)) != null ? _ref.href : void 0) || '',
favEmpty: 'http://static.4chan.org/image/favicon-dis.ico',
flavors: ['http://regex.info/exif.cgi?url=', 'http://iqdb.org/?url=', 'http://saucenao.com/search.php?db=999&url=', 'http://tineye.com/search?url='].join('\n'),
- iframe: false,
watched: JSON.parse(GM_getValue('watched', '{}')),
xhrs: []
};
@@ -1504,31 +1589,6 @@
if ($.isDST()) {
g.chanOffset -= 1;
}
- if (location.hostname.split('.')[0] === 'sys') {
- if (recaptcha = $('#recaptcha_response_field')) {
- m(recaptcha, {
- listener: ['keydown', recaptchaListener]
- });
- } else if (b = $('table font b')) {
- GM_setValue('error', b.firstChild.textContent);
- } else {
- GM_setValue('error', '');
- if (getConfig('Auto Watch')) {
- html = $('b').innerHTML;
- _ref2 = html.match(//), _ = _ref2[0], thread = _ref2[1], id = _ref2[2];
- if (thread === '0') {
- board = $('meta', d).content.match(/4chan.org\/(\w+)\//)[1];
- (_base = g.watched)[board] || (_base[board] = []);
- g.watched[board].push({
- id: id,
- text: GM_getValue('autoText')
- });
- GM_setValue('watched', JSON.stringify(g.watched));
- }
- }
- }
- return;
- }
lastChecked = GM_getValue('lastChecked', 0);
now = getTime();
DAY = 24 * 60 * 60;
@@ -1628,6 +1688,10 @@
background: lime;\
}\
');
+ if (location.hostname === 'sys.4chan.org') {
+ qr.sys();
+ return;
+ }
if (navtopr = $('#navtopr a')) {
a = n('a', {
textContent: '4chan X',
@@ -1647,9 +1711,9 @@
} else {
return;
}
- _ref3 = $$('#recaptcha_table a');
- for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
- el = _ref3[_i];
+ _ref2 = $$('#recaptcha_table a');
+ for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
+ el = _ref2[_i];
el.tabIndex = 1;
}
recaptcha = $('#recaptcha_response_field');
@@ -1677,9 +1741,9 @@
innerHTML: " "
});
imageType = GM_getValue('imageType', 'full');
- _ref4 = $$("option", expand);
- for (_j = 0, _len2 = _ref4.length; _j < _len2; _j++) {
- option = _ref4[_j];
+ _ref3 = $$("option", expand);
+ for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
+ option = _ref3[_j];
if (option.textContent === imageType) {
option.selected = true;
break;
@@ -1719,7 +1783,7 @@
}
if (getConfig('Localize Time')) {
g.callbacks.push(function(root) {
- var date, day, dotw, hour, min_sec, month, s, span, spans, year, _i, _len, _ref, _results;
+ var date, day, dotw, hour, min_sec, month, s, span, spans, year, _, _i, _len, _ref, _results;
spans = $$('span[id^=no]', root);
_results = [];
for (_i = 0, _len = spans.length; _i < _len; _i++) {
@@ -1779,7 +1843,7 @@
}
if (getConfig('Reply Hiding')) {
g.callbacks.push(function(root) {
- var next, obj, td, tds, _i, _len, _results;
+ var id, next, obj, td, tds, _i, _len, _results;
tds = $$('td.doubledash', root);
_results = [];
for (_i = 0, _len = tds.length; _i < _len; _i++) {
@@ -1807,23 +1871,7 @@
});
}
if (getConfig('Quick Reply')) {
- iframe = n('iframe', {
- name: 'iframe',
- listener: ['load', iframeLoad]
- });
- hide(iframe);
- mv(iframe, d.body);
- g.callbacks.push(function(root) {
- var quote, quotes, _i, _len, _results;
- quotes = $$('a.quotejs:not(:first-child)', root);
- _results = [];
- for (_i = 0, _len = quotes.length; _i < _len; _i++) {
- quote = quotes[_i];
- _results.push(quote.addEventListener('click', qrListener, true));
- }
- return _results;
- });
- recaptcha.id = '';
+ qr.init();
}
if (getConfig('Quick Report')) {
g.callbacks.push(function(root) {
@@ -1939,8 +1987,7 @@
updaterMake();
}
if (getConfig('Quick Reply') && getConfig('Persistent QR')) {
- quickReply();
- $('#qr input[title=autohide]').click();
+ qr.persist();
}
if (getConfig('Post in Title')) {
if (!(text = $('span.filetitle').textContent)) {
@@ -2036,9 +2083,9 @@
}
}
}
- _ref5 = g.callbacks;
- for (_n = 0, _len7 = _ref5.length; _n < _len7; _n++) {
- callback = _ref5[_n];
+ _ref4 = g.callbacks;
+ for (_n = 0, _len7 = _ref4.length; _n < _len7; _n++) {
+ callback = _ref4[_n];
callback();
}
d.body.addEventListener('DOMNodeInserted', nodeInserted, true);
diff --git a/script.coffee b/script.coffee
index 08dc9d31e..06cca25e0 100644
--- a/script.coffee
+++ b/script.coffee
@@ -143,12 +143,26 @@ $.extend = (object, properties) ->
object
$.extend $,
+ addClass: (el, className) ->
+ el.className += ' ' + className
+ removeClass: (el, className) ->
+ el.className = el.className.replace ' ' + className, ''
+ rm: (el) ->
+ el.parentNode.removeChild el
+ append: (parent, child) ->
+ parent.appendChild child
+ before: (root, el) ->
+ root.parentNode.insertBefore el, root
+ el: (tag, properties) ->
+ el = d.createElement tag
+ $.extend el, properties if properties
+ el
bind: (el, eventType, handler) ->
el.addEventListener eventType, handler, true
unbind: (el, eventType, handler) ->
el.removeEventListener eventType, handler, true
isDST: ->
- # XXX this should be isDSTinNY
+ # XXX this should check for DST in NY
###
http://en.wikipedia.org/wiki/Daylight_saving_time_in_the_United_States
Since 2007, daylight saving time starts on the second Sunday of March
@@ -249,15 +263,6 @@ zeroPad = (n) ->
if n < 10 then '0' + n else n
#funks
-autohide = ->
- qr = $ '#qr'
- klass = qr.className
- if @checked
- klass += ' auto'
- else
- klass = klass.replace(' auto', '')
- qr.className = klass
-
autoWatch = ->
#TODO look for subject
autoText = $('textarea', this).value.slice(0, 25)
@@ -345,22 +350,6 @@ getThread = ->
if bottom > 0 #we have not scrolled past
return [thread, i]
-formSubmit = (e) ->
- if span = @nextSibling
- rm span
- recaptcha = $('input[name=recaptcha_response_field]', this)
- if recaptcha.value
- $('#qr input[title=autohide]:not(:checked)')?.click()
- g.sage = if $('#qr input[name=email]').value is 'sage' then true else false
- else
- e.preventDefault()
- span = n 'span',
- className: 'error'
- textContent: 'You forgot to type in the verification.'
- mv span, @parentNode
- alert 'You forgot to type in the verification.'
- recaptcha.focus()
-
hideReply = (reply) ->
if p = @parentNode
reply = p.nextSibling
@@ -406,35 +395,6 @@ hideThread = (div) ->
listener: ['click', showThread]
inBefore div, a
-iframeLoad = ->
- if g.iframe = !g.iframe
- return
- $('iframe').src = 'about:blank'
- qr = $ '#qr'
- if error = GM_getValue 'error'
- span = n 'span',
- textContent: error
- className: 'error'
- mv span, qr
- $('input[title=autohide]:checked', qr)?.click()
- else if g.REPLY and getConfig 'Persistent QR'
- $('textarea', qr).value = ''
- $('input[name=recaptcha_response_field]', qr).value = ''
- # XXX file.value = '' doesn't work in opera
- f = $('input[type=file]', qr).parentNode
- f.innerHTML = f.innerHTML
- submit = $ 'input[type=submit]', qr
- submit.value = if g.sage then 60 else 30
- submit.disabled = true
- window.setTimeout cooldown, 1000
- auto = submit.previousSibling.lastChild
- if auto.checked
- #unhide the qr so you know it's ready for the next item
- $('input[title=autohide]:checked', qr)?.click()
- else
- rm qr
- recaptchaReload()
-
imageHover =
init: ->
img = n 'img', id: 'iHover'
@@ -615,9 +575,14 @@ keyModeNormal = (e) ->
unless qrLink = $ 'td.replyhl span[id] a:not(:first-child)', thread
qrLink = $ "span#nothread#{thread.id} a:not(:first-child)", thread
if e.shiftKey
- quickReply qrLink
+ $.append d.body, qr.dialog qrLink
+ $('#qr textarea').focus()
else
- quickReply qrLink, qrText qrLink
+ # qrLink.click() doesn't work, so use this hack
+ e =
+ preventDefault: ->
+ target: qrLink
+ qr.cb.quote e
when "J"
if e.shiftKey
if not g.REPLY then [root] = getThread()
@@ -687,9 +652,9 @@ nodeInserted = (e) ->
if target.nodeName is 'TABLE'
for callback in g.callbacks
callback target
- else if target.id is 'recaptcha_challenge_field' and qr = $ '#qr'
- $('#recaptcha_image img', qr).src = "http://www.google.com/recaptcha/api/image?c=" + target.value
- $('#recaptcha_challenge_field', qr).value = target.value
+ else if target.id is 'recaptcha_challenge_field' and dialog = $ '#qr'
+ $('#recaptcha_image img', dialog).src = "http://www.google.com/recaptcha/api/image?c=" + target.value
+ $('#recaptcha_challenge_field', dialog).value = target.value
onloadComment = (responseText, a, href) ->
[_, op, id] = href.match /(\d+)#(\d+)/
@@ -760,63 +725,169 @@ parseResponse = (responseText) ->
opbq = $ 'blockquote', body
return [replies, opbq]
-qrListener = (e) ->
- e.preventDefault()
- link = e.target
- text = qrText link
- quickReply link, text
+qr =
+ init: ->
+ g.callbacks.push qr.cb.node
+ iframe = $.el 'iframe',
+ name: 'iframe'
+ $.append d.body, iframe
+ $.bind iframe, 'load', qr.cb.load
+ $.bind window, 'message', qr.cb.messageTop
-qrText = (link) ->
- #we can't just use textContent b/c of the xxxs. goddamit moot.
- text = '>>' + link.parentNode.id.match(/\d+$/)[0] + '\n'
+ #hack - nuke id so it doesn't grab focus when reloading
+ $('#recaptcha_response_field').id = ''
- selection = window.getSelection()
- id = x('preceding::span[@id][1]', selection.anchorNode)?.id
- if (s = selection.toString()) and (id is link.parentNode.id)
- text += ">#{s}"
+ autohide:
+ set: ->
+ $('#qr input[title=autohide]:not(:checked)')?.click()
+ unset: ->
+ $('#qr input[title=autohide]:checked')?.click()
- text
+ cb:
+ autohide: (e) ->
+ dialog = $ '#qr'
+ if @checked
+ $.addClass dialog, 'auto'
+ else
+ $.removeClass dialog, 'auto'
-quickReply = (link, text) ->
- unless qr = $ '#qr'
- html = ""
- qr = ui.dialog 'qr', 'topleft', html
- $('input[title=autohide]', qr).addEventListener 'click', autohide, true
+ load: (e) ->
+ e.target.contentWindow.postMessage '', '*'
- form = $ 'form[name=post]'
- clone = form.cloneNode true
- #remove recaptcha scripts
+ messageIframe: (e) ->
+ message = $('table b').firstChild.textContent
+ e.source.postMessage message, '*'
+ window.location = 'about:blank'
+
+ messageTop: (e) ->
+ {data} = e
+ dialog = $ '#qr'
+ if data is 'Post successful!'
+ if dialog
+ if getConfig 'Persistent QR'
+ qr.refresh dialog
+ else
+ $.rm dialog
+ else
+ error = $.el 'span',
+ className: 'error'
+ textContent: data
+ $.append dialog, error
+ qr.autohide.unset()
+
+ recaptchaReload()
+
+ node: (root) ->
+ quotes = $$ 'a.quotejs:not(:first-child)', root
+ for quote in quotes
+ $.bind quote, 'click', qr.cb.quote
+
+ submit: (e) ->
+ if span = @nextSibling
+ $.rm span
+ recaptcha = $('input[name=recaptcha_response_field]', this)
+ if recaptcha.value
+ qr.autohide.set()
+ g.sage = $('#qr input[name=email]').value == 'sage'
+ else
+ e.preventDefault()
+ span = $.el 'span',
+ className: 'error'
+ textContent: 'You forgot to type in the verification.'
+ $.append @parentNode, span
+ alert 'You forgot to type in the verification.'
+ recaptcha.focus()
+
+ quote: (e) ->
+ e.preventDefault()
+ {target} = e
+ unless dialog = $ '#qr'
+ dialog = qr.dialog target
+
+ id = target.textContent
+ text = ">>#{id}\n"
+
+ selection = window.getSelection()
+ if s = selection.toString()
+ selectionID = x('preceding::input[@type="checkbox"][1]', selection.anchorNode)?.name
+ if selectionID == id
+ text += ">#{s}\n"
+
+ ta = $ 'textarea', dialog
+ ta.focus()
+ ta.value += text
+
+ refresh: (dialog) ->
+ $('textarea', dialog).value = ''
+ $('input[name=recaptcha_response_field]', dialog).value = ''
+ # XXX file.value = '' doesn't work in opera
+ f = $('input[type=file]', dialog).parentNode
+ f.innerHTML = f.innerHTML
+ submit = $ 'input[type=submit]', qr
+ submit.value = if g.sage then 60 else 30
+ submit.disabled = true
+ window.setTimeout cooldown, 1000
+ auto = submit.previousSibling.lastChild
+ if auto.checked
+ #unhide the qr so you know it's ready for the next item
+ $('input[title=autohide]:checked', qr)?.click()
+
+ dialog: (link) ->
+ html = ""
+ dialog = ui.dialog 'qr', top: '0px', left: '0px', html
+ el = $ 'input[title=autohide]', dialog
+ $.bind el, 'click', qr.cb.autohide
+
+ clone = $('form[name=post]').cloneNode(true)
for script in $$ 'script', clone
- rm script
- m $('input[name=recaptcha_response_field]', clone),
- listener: ['keydown', recaptchaListener]
- m clone,
- listener: ['submit', formSubmit]
- target: 'iframe'
+ $.rm script
+ clone.target = 'iframe'
+ $.bind clone, 'submit', qr.cb.submit
+ $.bind $('input[name=recaptcha_response_field]', clone), 'keydown', recaptchaListener
+
if not g.REPLY
#figure out which thread we're replying to
xpath = 'preceding::span[@class="postername"][1]/preceding::input[1]'
- input = n 'input',
+ input = $.el 'input',
type: 'hidden'
name: 'resto'
value: x(xpath, link).name
- mv input, clone
+ $.append clone, input
else if getConfig 'Persistent QR'
submit = $ 'input[type=submit]', clone
- auto = n 'label',
+ auto = $.el 'label',
textContent: 'Auto'
- autoBox = n 'input',
+ autobox = $.el 'input',
type: 'checkbox'
- mv autoBox, auto
- inBefore submit, auto
- mv clone, qr
- mv qr, d.body
- qr.style.width = qr.offsetWidth #lock
+ $.append auto, autobox
+ $.before submit, auto
- $('input[title=autohide]:checked', qr)?.click()
- textarea = $('textarea', qr)
- textarea.focus()
- if text then textarea.value += text
+ $.append dialog, clone
+ $.append d.body, dialog
+ dialog.style.width = dialog.offsetWidth # lock
+
+ dialog
+
+ persist: ->
+ $.append d.body, qr.dialog()
+ qr.autohide.set()
+
+ sys: ->
+ $.bind window, 'message', qr.cb.messageIframe
+ if recaptcha = $ '#recaptcha_response_field'
+ # post reporting
+ $.bind recaptcha, 'keydown', recaptchaListener
+ if getConfig 'Auto Watch'
+ html = $('b').innerHTML
+ [_, thread, id] = html.match(//)
+ if thread is '0'
+ [_, board] = $('meta', d).content.match(/4chan.org\/(\w+)\//)
+ g.watched[board] or= []
+ g.watched[board].push {
+ id: id,
+ text: GM_getValue 'autoText'
+ }
+ GM_setValue 'watched', JSON.stringify g.watched
recaptchaListener = (e) ->
if e.keyCode is 8 and @value is ''
@@ -1127,7 +1198,6 @@ g =
'http://saucenao.com/search.php?db=999&url='
'http://tineye.com/search?url='
].join '\n'
- iframe: false
watched: JSON.parse(GM_getValue('watched', '{}'))
xhrs: []
g.favHalo = if /ws/.test g.favDefault then 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAZklEQVR4XrWRQQoAIQwD+6L97j7Ih9WTQQxhDqJQCk4Mranuvqod6LgwawSqSuUmWSPw/UNlJlnDAmA2ARjABLYj8ZyCzJHHqOg+GdAKZmKPIQUzuYrxicHqEgHzP9g7M0+hj45sAnRWxtPj3zSPAAAAAElFTkSuQmCC' else 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEUAAABmzDP///8AAABet0i+AAAAAXRSTlMAQObYZgAAAExJREFUeF4tyrENgDAMAMFXKuQswQLBG3mOlBnFS1gwDfIYLpEivvjq2MlqjmYvYg5jWEzCwtDSQlwcXKCVLrpFbvLvvSf9uZJ2HusDtJAY7Tkn1oYAAAAASUVORK5CYII='
@@ -1145,26 +1215,6 @@ tzOffset = (new Date()).getTimezoneOffset() / 60
g.chanOffset = 5 - tzOffset# 4chan = EST = GMT -5
if $.isDST() then g.chanOffset -= 1
-if location.hostname.split('.')[0] is 'sys'
- if recaptcha = $ '#recaptcha_response_field'
- m recaptcha, listener: ['keydown', recaptchaListener]
- else if b = $ 'table font b'
- GM_setValue 'error', b.firstChild.textContent
- else
- GM_setValue 'error', ''
- if getConfig 'Auto Watch'
- html = $('b').innerHTML
- [_, thread, id] = html.match(//)
- if thread is '0'
- board = $('meta', d).content.match(/4chan.org\/(\w+)\//)[1]
- g.watched[board] or= []
- g.watched[board].push {
- id: id,
- text: GM_getValue 'autoText'
- }
- GM_setValue 'watched', JSON.stringify g.watched
- return
-
lastChecked = GM_getValue('lastChecked', 0)
now = getTime()
DAY = 24 * 60 * 60
@@ -1263,6 +1313,9 @@ GM_addStyle '
}
'
+if location.hostname is 'sys.4chan.org'
+ qr.sys()
+ return
if navtopr = $ '#navtopr a'
a = n 'a',
textContent: '4chan X'
@@ -1388,20 +1441,7 @@ if getConfig 'Reply Hiding'
hideReply(next)
if getConfig 'Quick Reply'
- iframe = n 'iframe',
- name: 'iframe'
- listener: ['load', iframeLoad]
- hide(iframe)
- mv iframe, d.body
-
- g.callbacks.push (root) ->
- quotes = $$('a.quotejs:not(:first-child)', root)
- for quote in quotes
- quote.addEventListener('click', qrListener, true)
-
- #hack - nuke id so it doesn't grab focus when reloading
- recaptcha.id = ''
-
+ qr.init()
if getConfig 'Quick Report'
g.callbacks.push (root) ->
@@ -1481,8 +1521,7 @@ if g.REPLY
if getConfig 'Thread Updater'
updaterMake()
if getConfig('Quick Reply') and getConfig 'Persistent QR'
- quickReply()
- $('#qr input[title=autohide]').click()
+ qr.persist()
if getConfig 'Post in Title'
unless text = $('span.filetitle').textContent
text = $('blockquote').textContent