merge master
This commit is contained in:
commit
0a6149f0ac
445
4chan_x.user.js
445
4chan_x.user.js
@ -2,7 +2,7 @@
|
|||||||
// @name 4chan x
|
// @name 4chan x
|
||||||
// @namespace aeosynth
|
// @namespace aeosynth
|
||||||
// @description Adds various features.
|
// @description Adds various features.
|
||||||
// @version 11.8.4.0
|
// @version 11.8.6.0
|
||||||
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
|
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
|
||||||
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
|
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
|
||||||
// @include http://boards.4chan.org/*
|
// @include http://boards.4chan.org/*
|
||||||
@ -100,7 +100,8 @@
|
|||||||
'Auto Noko': [true, 'Always redirect to your post'],
|
'Auto Noko': [true, 'Always redirect to your post'],
|
||||||
'Cooldown': [true, 'Prevent \'flood detected\' errors'],
|
'Cooldown': [true, 'Prevent \'flood detected\' errors'],
|
||||||
'Quick Reply': [true, 'Reply without leaving the page'],
|
'Quick Reply': [true, 'Reply without leaving the page'],
|
||||||
'Persistent QR': [false, 'Quick reply won\'t disappear after posting. Only in replies.']
|
'Persistent QR': [false, 'Quick reply won\'t disappear after posting. Only in replies.'],
|
||||||
|
'Auto Hide QR': [true, 'Automatically auto-hide the quick reply when posting']
|
||||||
},
|
},
|
||||||
Quoting: {
|
Quoting: {
|
||||||
'Quote Backlinks': [true, 'Add quote backlinks'],
|
'Quote Backlinks': [true, 'Add quote backlinks'],
|
||||||
@ -111,7 +112,7 @@
|
|||||||
'Indicate OP quote': [true, 'Add \'(OP)\' to OP quotes']
|
'Indicate OP quote': [true, 'Add \'(OP)\' to OP quotes']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
flavors: ['http://regex.info/exif.cgi?url=', 'http://iqdb.org/?url=', 'http://google.com/searchbyimage?image_url=', '#http://tineye.com/search?url=', '#http://saucenao.com/search.php?db=999&url=', '#http://imgur.com/upload?url='].join('\n'),
|
flavors: ['http://regex.info/exif.cgi?url=', 'http://iqdb.org/?url=', 'http://google.com/searchbyimage?image_url=', '#http://tineye.com/search?url=', '#http://saucenao.com/search.php?db=999&url=', '#http://imgur.com/upload?url=', '#http://anonym.to/?'].join('\n'),
|
||||||
time: '%m/%d/%y(%a)%H:%M',
|
time: '%m/%d/%y(%a)%H:%M',
|
||||||
hotkeys: {
|
hotkeys: {
|
||||||
close: 'Esc',
|
close: 'Esc',
|
||||||
@ -292,6 +293,9 @@
|
|||||||
return object;
|
return object;
|
||||||
};
|
};
|
||||||
$.extend($, {
|
$.extend($, {
|
||||||
|
id: function(id) {
|
||||||
|
return d.getElementById(id);
|
||||||
|
},
|
||||||
globalEval: function(code) {
|
globalEval: function(code) {
|
||||||
var script;
|
var script;
|
||||||
script = $.el('script', {
|
script = $.el('script', {
|
||||||
@ -614,13 +618,13 @@
|
|||||||
_results = [];
|
_results = [];
|
||||||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
||||||
backlink = _ref2[_i];
|
backlink = _ref2[_i];
|
||||||
_results.push(!d.getElementById(backlink.hash.slice(1)) ? $.rm(backlink) : void 0);
|
_results.push(!$.id(backlink.hash.slice(1)) ? $.rm(backlink) : void 0);
|
||||||
}
|
}
|
||||||
return _results;
|
return _results;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parse: function(req, pathname, thread, a) {
|
parse: function(req, pathname, thread, a) {
|
||||||
var body, br, next, quote, table, tables, _i, _j, _len, _len2, _ref, _results;
|
var body, br, link, next, quote, reply, table, tables, _i, _j, _k, _len, _len2, _len3, _ref, _ref2, _results;
|
||||||
if (req.status !== 200) {
|
if (req.status !== 200) {
|
||||||
a.textContent = "" + req.status + " " + req.statusText;
|
a.textContent = "" + req.status + " " + req.statusText;
|
||||||
$.unbind(a, 'click', expandThread.cb.toggle);
|
$.unbind(a, 'click', expandThread.cb.toggle);
|
||||||
@ -634,18 +638,25 @@
|
|||||||
body = $.el('body', {
|
body = $.el('body', {
|
||||||
innerHTML: req.responseText
|
innerHTML: req.responseText
|
||||||
});
|
});
|
||||||
_ref = $$('a.quotelink', body);
|
_ref = $$('td[id]', body);
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
quote = _ref[_i];
|
reply = _ref[_i];
|
||||||
|
_ref2 = $$('a.quotelink', reply);
|
||||||
|
for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
|
||||||
|
quote = _ref2[_j];
|
||||||
if (quote.getAttribute('href') === quote.hash) {
|
if (quote.getAttribute('href') === quote.hash) {
|
||||||
quote.pathname = pathname;
|
quote.pathname = pathname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
link = $('a.quotejs', reply);
|
||||||
|
link.href = "res/" + thread.firstChild.id + "#" + reply.id;
|
||||||
|
link.nextSibling.href = "res/" + thread.firstChild.id + "#q" + reply.id;
|
||||||
|
}
|
||||||
tables = $$('form[name=delform] table', body);
|
tables = $$('form[name=delform] table', body);
|
||||||
tables.pop();
|
tables.pop();
|
||||||
_results = [];
|
_results = [];
|
||||||
for (_j = 0, _len2 = tables.length; _j < _len2; _j++) {
|
for (_k = 0, _len3 = tables.length; _k < _len3; _k++) {
|
||||||
table = tables[_j];
|
table = tables[_k];
|
||||||
_results.push($.before(br, table));
|
_results.push($.before(br, table));
|
||||||
}
|
}
|
||||||
return _results;
|
return _results;
|
||||||
@ -722,15 +733,15 @@
|
|||||||
node = _ref[_i];
|
node = _ref[_i];
|
||||||
node.removeAttribute('accesskey');
|
node.removeAttribute('accesskey');
|
||||||
}
|
}
|
||||||
return $.bind(d, 'keydown', keybinds.cb.keydown);
|
return $.bind(d, 'keydown', keybinds.keydown);
|
||||||
},
|
},
|
||||||
cb: {
|
|
||||||
keydown: function(e) {
|
keydown: function(e) {
|
||||||
var o, range, selEnd, selStart, ta, thread, valEnd, valMid, valStart, value, _ref, _ref2, _ref3;
|
var o, range, selEnd, selStart, ta, thread, valEnd, valMid, valStart, value, _ref, _ref2, _ref3;
|
||||||
|
updater.focus = true;
|
||||||
if (((_ref = e.target.nodeName) === 'TEXTAREA' || _ref === 'INPUT') && !e.altKey && !e.ctrlKey && !(e.keyCode === 27)) {
|
if (((_ref = e.target.nodeName) === 'TEXTAREA' || _ref === 'INPUT') && !e.altKey && !e.ctrlKey && !(e.keyCode === 27)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(key = keybinds.cb.keyCode(e))) {
|
if (!(key = keybinds.keyCode(e))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
thread = nav.getThread();
|
thread = nav.getThread();
|
||||||
@ -855,7 +866,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
img: function(thread, all) {
|
img: function(thread, all) {
|
||||||
var root, thumb;
|
var root, thumb;
|
||||||
@ -873,7 +883,7 @@
|
|||||||
qrLink = $("span[id^=nothread] a:not(:first-child)", thread);
|
qrLink = $("span[id^=nothread] a:not(:first-child)", thread);
|
||||||
}
|
}
|
||||||
if (quote) {
|
if (quote) {
|
||||||
return qr.quote(qrLink);
|
return qr.quote.call(qrLink);
|
||||||
} else {
|
} else {
|
||||||
if (!qr.el) {
|
if (!qr.el) {
|
||||||
qr.dialog(qrLink);
|
qr.dialog(qrLink);
|
||||||
@ -1043,7 +1053,7 @@
|
|||||||
var arr, checked, description, dialog, hiddenNum, hiddenThreads, hidingul, html, input, key, li, link, main, obj, overlay, ul, _i, _j, _k, _len, _len2, _len3, _ref, _ref2, _ref3, _ref4;
|
var arr, checked, description, dialog, hiddenNum, hiddenThreads, hidingul, html, input, key, li, link, main, obj, overlay, ul, _i, _j, _k, _len, _len2, _len3, _ref, _ref2, _ref3, _ref4;
|
||||||
hiddenThreads = $.getValue("hiddenThreads/" + g.BOARD + "/", {});
|
hiddenThreads = $.getValue("hiddenThreads/" + g.BOARD + "/", {});
|
||||||
hiddenNum = Object.keys(g.hiddenReplies).length + Object.keys(hiddenThreads).length;
|
hiddenNum = Object.keys(g.hiddenReplies).length + Object.keys(hiddenThreads).length;
|
||||||
html = " <div class='reply dialog'> <div id=optionsbar> <div id=floaty> <a name=main>main</a> | <a name=flavors>sauce</a> | <a name=time>time</a> | <a name=keybinds>keybinds</a> </div> <div id=credits> <a href=http://chat.now.im/x/aeos>support throd</a> | <a href=https://github.com/aeosynth/4chan-x/issues>github</a> | <a href=https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2DBVZBUAM4DHC&lc=US&item_name=Aeosynth¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted>donate</a> </div> </div> <hr> <div id=content> <div id=main> </div> <textarea name=flavors id=flavors hidden>" + conf['flavors'] + "</textarea> <div id=time hidden> <div><input type=text name=time value='" + conf['time'] + "'> <span id=timePreview></span></div> <table> <caption>Format specifiers <a href=http://en.wikipedia.org/wiki/Date_%28Unix%29#Formatting>(source)</a></caption> <tbody> <tr><th>Specifier</th><th>Description</th><th>Values/Example</th></tr> <tr><td>%a</td><td>weekday, abbreviated</td><td>Sat</td></tr> <tr><td>%A</td><td>weekday, full</td><td>Saturday</td></tr> <tr><td>%b</td><td>month, abbreviated</td><td>Jun</td></tr> <tr><td>%B</td><td>month, full length</td><td>June</td></tr> <tr><td>%d</td><td>day of the month, zero padded</td><td>03</td></tr> <tr><td>%H</td><td>hour (24 hour clock) zero padded</td><td>13</td></tr> <tr><td>%I (uppercase i)</td><td>hour (12 hour clock) zero padded</td><td>02</td></tr> <tr><td>%m</td><td>month, zero padded</td><td>06</td></tr> <tr><td>%M</td><td>minutes, zero padded</td><td>54</td></tr> <tr><td>%p</td><td>upper case AM or PM</td><td>PM</td></tr> <tr><td>%P</td><td>lower case am or pm</td><td>pm</td></tr> <tr><td>%y</td><td>two digit year</td><td>00-99</td></tr> </tbody> </table> </div> <div id=keybinds hidden> <table> <tbody> <tr><th>Actions</th><th>Keybinds</th></tr> <tr><td>Close Options or QR</td><td><input type=text name=close></td></tr> <tr><td>Quick spoiler</td><td><input type=text name=spoiler></td></tr> <tr><td>Open QR with post number inserted</td><td><input type=text name=openQR></td></tr> <tr><td>Open QR without post number inserted</td><td><input type=text name=openEmptyQR></td></tr> <tr><td>Submit post</td><td><input type=text name=submit></td></tr> <tr><td>Select next reply</td><td><input type=text name=nextReply ></td></tr> <tr><td>Select previous reply</td><td><input type=text name=previousReply></td></tr> <tr><td>See next thread</td><td><input type=text name=nextThread></td></tr> <tr><td>See previous thread</td><td><input type=text name=previousThread></td></tr> <tr><td>Jump to the next page</td><td><input type=text name=nextPage></td></tr> <tr><td>Jump to the previous page</td><td><input type=text name=previousPage></td></tr> <tr><td>Jump to page 0</td><td><input type=text name=zero></td></tr> <tr><td>Open thread in current tab</td><td><input type=text name=openThread></td></tr> <tr><td>Open thread in new tab</td><td><input type=text name=openThreadTab></td></tr> <tr><td>Expand thread</td><td><input type=text name=expandThread></td></tr> <tr><td>Watch thread</td><td><input type=text name=watch></td></tr> <tr><td>Hide thread</td><td><input type=text name=hide></td></tr> <tr><td>Expand selected image</td><td><input type=text name=expandImages></td></tr> <tr><td>Expand all images</td><td><input type=text name=expandAllImages></td></tr> <tr><td>Update now</td><td><input type=text name=update></td></tr> <tr><td>Reset the unread count to 0</td><td><input type=text name=unreadCountTo0></td></tr> </tbody> </table> </div> </div> </div> ";
|
html = " <div class='reply dialog'> <div id=optionsbar> <div id=floaty> <a name=main>main</a> | <a name=flavors>sauce</a> | <a name=time>time</a> | <a name=keybinds>keybinds</a> </div> <div id=credits> <a href=http://chat.now.im/x/aeos>support throd</a> | <a href=https://github.com/aeosynth/4chan-x/issues>github</a> | <a href=https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2DBVZBUAM4DHC&lc=US&item_name=Aeosynth¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted>donate</a> </div> </div> <hr> <div id=content> <div id=main> </div> <textarea name=flavors id=flavors hidden>" + conf['flavors'] + "</textarea> <div id=time hidden> <div><input type=text name=time value='" + conf['time'] + "'> <span id=timePreview></span></div> <table> <caption>Format specifiers <a href=http://en.wikipedia.org/wiki/Date_%28Unix%29#Formatting>(source)</a></caption> <tbody> <tr><th>Specifier</th><th>Description</th><th>Values/Example</th></tr> <tr><th colspan=3>Year</th></tr> <tr><td>%y</td><td>two digit year</td><td>00-99</td></tr> <tr><th colspan=3>Month</th></tr> <tr><td>%b</td><td>month, abbreviated</td><td>Jun</td></tr> <tr><td>%B</td><td>month, full length</td><td>June</td></tr> <tr><td>%m</td><td>month, zero padded</td><td>06</td></tr> <tr><th colspan=3>Day</th></tr> <tr><td>%a</td><td>weekday, abbreviated</td><td>Sat</td></tr> <tr><td>%A</td><td>weekday, full</td><td>Saturday</td></tr> <tr><td>%d</td><td>day of the month, zero padded</td><td>03</td></tr> <tr><td>%e</td><td>day of the month</td><td>3</td></tr> <tr><th colspan=3>Time</th></tr> <tr><td>%H</td><td>hour (24 hour clock) zero padded</td><td>13</td></tr> <tr><td>%l (lowercase L)</td><td>hour (12 hour clock)</td><td>1</td></tr> <tr><td>%I (uppercase i)</td><td>hour (12 hour clock) zero padded</td><td>01</td></tr> <tr><td>%k</td><td>hour (24 hour clock)</td><td>13</td></tr> <tr><td>%M</td><td>minutes, zero padded</td><td>54</td></tr> <tr><td>%p</td><td>upper case AM or PM</td><td>PM</td></tr> <tr><td>%P</td><td>lower case am or pm</td><td>pm</td></tr> </tbody> </table> </div> <div id=keybinds hidden> <table> <tbody> <tr><th>Actions</th><th>Keybinds</th></tr> <tr><td>Close Options or QR</td><td><input type=text name=close></td></tr> <tr><td>Quick spoiler</td><td><input type=text name=spoiler></td></tr> <tr><td>Open QR with post number inserted</td><td><input type=text name=openQR></td></tr> <tr><td>Open QR without post number inserted</td><td><input type=text name=openEmptyQR></td></tr> <tr><td>Submit post</td><td><input type=text name=submit></td></tr> <tr><td>Select next reply</td><td><input type=text name=nextReply ></td></tr> <tr><td>Select previous reply</td><td><input type=text name=previousReply></td></tr> <tr><td>See next thread</td><td><input type=text name=nextThread></td></tr> <tr><td>See previous thread</td><td><input type=text name=previousThread></td></tr> <tr><td>Jump to the next page</td><td><input type=text name=nextPage></td></tr> <tr><td>Jump to the previous page</td><td><input type=text name=previousPage></td></tr> <tr><td>Jump to page 0</td><td><input type=text name=zero></td></tr> <tr><td>Open thread in current tab</td><td><input type=text name=openThread></td></tr> <tr><td>Open thread in new tab</td><td><input type=text name=openThreadTab></td></tr> <tr><td>Expand thread</td><td><input type=text name=expandThread></td></tr> <tr><td>Watch thread</td><td><input type=text name=watch></td></tr> <tr><td>Hide thread</td><td><input type=text name=hide></td></tr> <tr><td>Expand selected image</td><td><input type=text name=expandImages></td></tr> <tr><td>Expand all images</td><td><input type=text name=expandAllImages></td></tr> <tr><td>Update now</td><td><input type=text name=update></td></tr> <tr><td>Reset the unread count to 0</td><td><input type=text name=unreadCountTo0></td></tr> </tbody> </table> </div> </div> </div> ";
|
||||||
dialog = $.el('div', {
|
dialog = $.el('div', {
|
||||||
id: 'options',
|
id: 'options',
|
||||||
innerHTML: html
|
innerHTML: html
|
||||||
@ -1130,7 +1140,7 @@
|
|||||||
keybind: function(e) {
|
keybind: function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if ((key = keybinds.cb.keyCode(e)) == null) {
|
if ((key = keybinds.keyCode(e)) == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.value = key;
|
this.value = key;
|
||||||
@ -1197,7 +1207,7 @@
|
|||||||
cb: function() {
|
cb: function() {
|
||||||
var submit, submits, _i, _j, _len, _len2, _results;
|
var submit, submits, _i, _j, _len, _len2, _results;
|
||||||
submits = $$('#com_submit');
|
submits = $$('#com_submit');
|
||||||
if (--cooldown.duration) {
|
if (--cooldown.duration > 0) {
|
||||||
_results = [];
|
_results = [];
|
||||||
for (_i = 0, _len = submits.length; _i < _len; _i++) {
|
for (_i = 0, _len = submits.length; _i < _len; _i++) {
|
||||||
submit = submits[_i];
|
submit = submits[_i];
|
||||||
@ -1212,7 +1222,7 @@
|
|||||||
submit.value = 'Submit';
|
submit.value = 'Submit';
|
||||||
}
|
}
|
||||||
if (qr.el && $('#auto', qr.el).checked) {
|
if (qr.el && $('#auto', qr.el).checked) {
|
||||||
return qr.submit.call($('form', qr.el));
|
return qr.autoPost();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1220,32 +1230,102 @@
|
|||||||
qr = {
|
qr = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var iframe;
|
var iframe;
|
||||||
g.callbacks.push(qr.cb.node);
|
g.callbacks.push(qr.node);
|
||||||
|
$.bind(window, 'message', qr.message);
|
||||||
|
$.bind($('#recaptcha_challenge_field_holder'), 'DOMNodeInserted', qr.captchaNode);
|
||||||
|
qr.captcha = [];
|
||||||
iframe = $.el('iframe', {
|
iframe = $.el('iframe', {
|
||||||
name: 'iframe',
|
name: 'iframe',
|
||||||
hidden: true
|
hidden: true
|
||||||
});
|
});
|
||||||
$.append(d.body, iframe);
|
$.append(d.body, iframe);
|
||||||
$.bind(window, 'message', qr.cb.message);
|
|
||||||
return $('#recaptcha_response_field').id = '';
|
return $('#recaptcha_response_field').id = '';
|
||||||
},
|
},
|
||||||
autohide: {
|
attach: function() {
|
||||||
set: function() {
|
var fileDiv;
|
||||||
var _ref;
|
$('#auto', qr.el).checked = true;
|
||||||
return (_ref = $('#autohide:not(:checked)', qr.el)) != null ? _ref.click() : void 0;
|
fileDiv = $.el('div', {
|
||||||
|
innerHTML: '<input type=file name=upfile><a>X</a>'
|
||||||
|
});
|
||||||
|
$.bind(fileDiv.lastChild, 'click', (function() {
|
||||||
|
return $.rm(this.parentNode);
|
||||||
|
}));
|
||||||
|
return $.prepend(qr.files, fileDiv);
|
||||||
},
|
},
|
||||||
unset: function() {
|
attachNext: function() {
|
||||||
var _ref;
|
var file, fileDiv, oldFile;
|
||||||
return (_ref = $('#autohide:checked', qr.el)) != null ? _ref.click() : void 0;
|
fileDiv = $.rm(qr.files.lastChild);
|
||||||
|
file = fileDiv.firstChild;
|
||||||
|
oldFile = $('#qr_form input[type=file]', qr.el);
|
||||||
|
return $.replace(oldFile, file);
|
||||||
|
},
|
||||||
|
autoPost: function() {
|
||||||
|
var captcha, responseField;
|
||||||
|
responseField = $('#recaptcha_response_field', qr.el);
|
||||||
|
if (!responseField.value && (captcha = qr.captcha.shift())) {
|
||||||
|
$('#recaptcha_challenge_field', qr.el).value = captcha.challenge;
|
||||||
|
responseField.value = captcha.response;
|
||||||
|
responseField.nextSibling.textContent = qr.captcha.length + ' captcha cached';
|
||||||
|
}
|
||||||
|
return qr.submit.call($('form', qr.el));
|
||||||
|
},
|
||||||
|
captchaNode: function(e) {
|
||||||
|
var target;
|
||||||
|
if (!qr.el) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target = e.target;
|
||||||
|
$('img', qr.el).src = "http://www.google.com/recaptcha/api/image?c=" + target.value;
|
||||||
|
return $('#recaptcha_challenge_field', qr.el).value = target.value;
|
||||||
|
},
|
||||||
|
captchaKeydown: function(e) {
|
||||||
|
if (e.keyCode === 13 && cooldown.duration) {
|
||||||
|
$('#auto', qr.el).checked = true;
|
||||||
|
if (conf['Auto Hide QR']) {
|
||||||
|
$('#autohide', qr.el).checked = true;
|
||||||
|
}
|
||||||
|
return qr.captchaPush.call(this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cb: {
|
captchaPush: function() {
|
||||||
autohide: function(e) {
|
var l;
|
||||||
if (this.checked) {
|
l = qr.captcha.push({
|
||||||
return $.addClass(qr.el, 'auto');
|
challenge: $('#recaptcha_challenge_field', qr.el).value,
|
||||||
} else {
|
response: this.value
|
||||||
return $.removeClass(qr.el, 'auto');
|
});
|
||||||
}
|
this.nextSibling.textContent = l + ' captcha cached';
|
||||||
|
Recaptcha.reload();
|
||||||
|
return this.value = '';
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
$.rm(qr.el);
|
||||||
|
return qr.el = null;
|
||||||
|
},
|
||||||
|
dialog: function(link) {
|
||||||
|
var THREAD_ID, challenge, html, spoiler, submitDisabled, submitValue;
|
||||||
|
submitValue = $('#com_submit').value;
|
||||||
|
submitDisabled = $('#com_submit').disabled ? 'disabled' : '';
|
||||||
|
THREAD_ID = g.THREAD_ID || $.x('ancestor::div[@class="thread"]/div', link).id;
|
||||||
|
spoiler = $('.postarea label') ? '<label> [<input type=checkbox name=spoiler>Spoiler Image?]</label>' : '';
|
||||||
|
challenge = $('#recaptcha_challenge_field').value;
|
||||||
|
html = " <a id=close title=close>X</a> <input type=checkbox id=autohide title=autohide> <div class=move> <input class=inputtext type=text name=name placeholder=Name form=qr_form> Quick Reply </div> <form name=post action=http://sys.4chan.org/" + g.BOARD + "/post method=POST enctype=multipart/form-data target=iframe id=qr_form> <input type=hidden name=resto value=" + THREAD_ID + "> <input type=hidden name=recaptcha_challenge_field id=recaptcha_challenge_field value=" + challenge + "> <div><input class=inputtext type=text name=email placeholder=E-mail>" + spoiler + "</div> <div><input class=inputtext type=text name=sub placeholder=Subject><input type=submit value=" + submitValue + " id=com_submit " + submitDisabled + "><label><input type=checkbox id=auto>auto</label></div> <div><textarea class=inputtext name=com placeholder=Comment></textarea></div> <div><img src=http://www.google.com/recaptcha/api/image?c=" + challenge + "></div> <div><input class=inputtext type=text name=recaptcha_response_field placeholder=Verification required autocomplete=off id=recaptcha_response_field>0 captcha cached</div> <div><input type=file name=upfile></div> <div><input class=inputtext type=password name=pwd maxlength=8 placeholder=Password><input type=hidden name=mode value=regist><a name=attach>attach another file</a></div> </form> <div id=files></div> <a id=error class=error></a> ";
|
||||||
|
qr.el = ui.dialog('qr', {
|
||||||
|
top: '0px',
|
||||||
|
left: '0px'
|
||||||
|
}, html);
|
||||||
|
qr.files = $('#files', qr.el);
|
||||||
|
qr.refresh();
|
||||||
|
$('textarea', qr.el).value = $('textarea').value;
|
||||||
|
$.bind($('input[name=name]', qr.el), 'mousedown', function(e) {
|
||||||
|
return e.stopPropagation();
|
||||||
|
});
|
||||||
|
$.bind($('#close', qr.el), 'click', qr.close);
|
||||||
|
$.bind($('form', qr.el), 'submit', qr.submit);
|
||||||
|
$.bind($('a[name=attach]', qr.el), 'click', qr.attach);
|
||||||
|
$.bind($('img', qr.el), 'click', Recaptcha.reload);
|
||||||
|
$.bind($('#recaptcha_response_field', qr.el), 'keydown', Recaptcha.listener);
|
||||||
|
$.bind($('#recaptcha_response_field', qr.el), 'keydown', qr.captchaKeydown);
|
||||||
|
return $.append(d.body, qr.el);
|
||||||
},
|
},
|
||||||
message: function(e) {
|
message: function(e) {
|
||||||
var data, duration;
|
var data, duration;
|
||||||
@ -1253,14 +1333,29 @@
|
|||||||
$('iframe[name=iframe]').src = 'about:blank';
|
$('iframe[name=iframe]').src = 'about:blank';
|
||||||
data = e.data;
|
data = e.data;
|
||||||
if (data) {
|
if (data) {
|
||||||
$('input[name=recaptcha_response_field]', qr.el).value = '';
|
data = JSON.parse(data);
|
||||||
$.extend($('#error', qr.el), JSON.parse(data));
|
$.extend($('#error', qr.el), data);
|
||||||
qr.autohide.unset();
|
$('#recaptcha_response_field', qr.el).value = '';
|
||||||
|
$('#autohide', qr.el).checked = false;
|
||||||
|
if (data.textContent === 'You seem to have mistyped the verification.') {
|
||||||
|
if (qr.captcha.length) {
|
||||||
|
qr.autoPost();
|
||||||
|
}
|
||||||
|
} else if (data.textContent === 'Error: Duplicate file entry detected.' && qr.files.childElementCount) {
|
||||||
|
$('textarea', qr.el).value += '\n' + data.textContent + ' ' + data.href;
|
||||||
|
qr.attachNext();
|
||||||
|
if (qr.captcha.length) {
|
||||||
|
qr.autoPost();
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (qr.el) {
|
if (qr.el) {
|
||||||
if (g.REPLY && conf['Persistent QR']) {
|
if (g.REPLY && (conf['Persistent QR'] || qr.files.childElementCount)) {
|
||||||
qr.refresh();
|
qr.refresh();
|
||||||
|
if (qr.files.childElementCount) {
|
||||||
|
qr.attachNext();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qr.close();
|
qr.close();
|
||||||
}
|
}
|
||||||
@ -1274,12 +1369,41 @@
|
|||||||
node: function(root) {
|
node: function(root) {
|
||||||
var quote;
|
var quote;
|
||||||
quote = $('a.quotejs:not(:first-child)', root);
|
quote = $('a.quotejs:not(:first-child)', root);
|
||||||
return $.bind(quote, 'click', qr.cb.quote);
|
return $.bind(quote, 'click', qr.quote);
|
||||||
},
|
},
|
||||||
quote: function(e) {
|
quote: function(e) {
|
||||||
|
var id, s, selection, selectionID, ta, text, _ref;
|
||||||
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return qr.quote(this);
|
|
||||||
}
|
}
|
||||||
|
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('preceding::input[@type="checkbox"][1]', selection.anchorNode)) != null ? _ref.name : void 0;
|
||||||
|
if (selectionID === id) {
|
||||||
|
s = s.replace(/\n/g, '\n>');
|
||||||
|
text += ">" + s + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ta = $('textarea', qr.el);
|
||||||
|
ta.focus();
|
||||||
|
return ta.value += text;
|
||||||
|
},
|
||||||
|
refresh: function() {
|
||||||
|
var auto, c, m;
|
||||||
|
auto = $('#auto', qr.el).checked;
|
||||||
|
$('form', qr.el).reset();
|
||||||
|
$('#auto', qr.el).checked = auto;
|
||||||
|
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]) : '';
|
||||||
|
return $('input[name=pwd]', qr.el).value = (m = c.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('input[name=pwd]').value;
|
||||||
},
|
},
|
||||||
submit: function(e) {
|
submit: function(e) {
|
||||||
var id, inputfile, isQR, op;
|
var id, inputfile, isQR, op;
|
||||||
@ -1288,7 +1412,7 @@
|
|||||||
watcher.watch(null, g.THREAD_ID);
|
watcher.watch(null, g.THREAD_ID);
|
||||||
} else {
|
} else {
|
||||||
id = $('input[name=resto]', qr.el).value;
|
id = $('input[name=resto]', qr.el).value;
|
||||||
op = d.getElementById(id);
|
op = $.id(id);
|
||||||
if ($('img.favicon', op).src === Favicon.empty) {
|
if ($('img.favicon', op).src === Favicon.empty) {
|
||||||
watcher.watch(op, id);
|
watcher.watch(op, id);
|
||||||
}
|
}
|
||||||
@ -1310,72 +1434,14 @@
|
|||||||
this.submit();
|
this.submit();
|
||||||
}
|
}
|
||||||
$('#error', qr.el).textContent = '';
|
$('#error', qr.el).textContent = '';
|
||||||
qr.autohide.set();
|
if (conf['Auto Hide QR']) {
|
||||||
|
$('#autohide', qr.el).checked = true;
|
||||||
|
}
|
||||||
return qr.sage = /sage/i.test($('input[name=email]', this).value);
|
return qr.sage = /sage/i.test($('input[name=email]', this).value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quote: function(link) {
|
|
||||||
var id, s, selection, selectionID, ta, text, _ref;
|
|
||||||
if (qr.el) {
|
|
||||||
qr.autohide.unset();
|
|
||||||
} else {
|
|
||||||
qr.dialog(link);
|
|
||||||
}
|
|
||||||
id = link.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) {
|
|
||||||
s = s.replace(/\n/g, '\n>');
|
|
||||||
text += ">" + s + "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ta = $('textarea', qr.el);
|
|
||||||
ta.focus();
|
|
||||||
return ta.value += text;
|
|
||||||
},
|
|
||||||
refresh: function() {
|
|
||||||
var c, m;
|
|
||||||
$('form', qr.el).reset();
|
|
||||||
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]) : '';
|
|
||||||
return $('input[name=pwd]', qr.el).value = (m = c.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $('input[name=pwd]').value;
|
|
||||||
},
|
|
||||||
dialog: function(link) {
|
|
||||||
var THREAD_ID, challenge, html, spoiler, submitDisabled, submitValue;
|
|
||||||
submitValue = $('#com_submit').value;
|
|
||||||
submitDisabled = $('#com_submit').disabled ? 'disabled' : '';
|
|
||||||
THREAD_ID = g.THREAD_ID || $.x('ancestor::div[@class="thread"]/div', link).id;
|
|
||||||
spoiler = $('.postarea label') ? '<label> [<input type=checkbox name=spoiler>Spoiler Image?]</label>' : '';
|
|
||||||
challenge = $('input[name=recaptcha_challenge_field]').value;
|
|
||||||
html = " <div class=move> <input class=inputtext type=text name=name placeholder=Name form=qr_form> Quick Reply <input type=checkbox id=autohide title=autohide> <a name=close title=close>X</a> </div> <form name=post action=http://sys.4chan.org/" + g.BOARD + "/post method=POST enctype=multipart/form-data target=iframe id=qr_form> <input type=hidden name=resto value=" + THREAD_ID + "> <input type=hidden name=recaptcha_challenge_field value=" + challenge + "> <div><input class=inputtext type=text name=email placeholder=E-mail>" + spoiler + "</div> <div><input class=inputtext type=text name=sub placeholder=Subject><input type=submit value=" + submitValue + " id=com_submit " + submitDisabled + "><label><input type=checkbox id=auto>auto</label></div> <div><textarea class=inputtext name=com placeholder=Comment></textarea></div> <div><img src=http://www.google.com/recaptcha/api/image?c=" + challenge + "></div> <div><input class=inputtext type=text name=recaptcha_response_field placeholder=Verification required autocomplete=off></div> <div><input type=file name=upfile></div> <div><input class=inputtext type=password name=pwd maxlength=8 placeholder=Password><input type=hidden name=mode value=regist></div> </form> <a id=error class=error></a> ";
|
|
||||||
qr.el = ui.dialog('qr', {
|
|
||||||
top: '0px',
|
|
||||||
left: '0px'
|
|
||||||
}, html);
|
|
||||||
qr.refresh();
|
|
||||||
$.bind($('input[name=name]', qr.el), 'mousedown', function(e) {
|
|
||||||
return e.stopPropagation();
|
|
||||||
});
|
|
||||||
$.bind($('#autohide', qr.el), 'click', qr.cb.autohide);
|
|
||||||
$.bind($('a[name=close]', qr.el), 'click', qr.close);
|
|
||||||
$.bind($('form', qr.el), 'submit', qr.submit);
|
|
||||||
$.bind($('img', qr.el), 'click', Recaptcha.reload);
|
|
||||||
$.bind($('input[name=recaptcha_response_field]', qr.el), 'keydown', Recaptcha.listener);
|
|
||||||
return $.append(d.body, qr.el);
|
|
||||||
},
|
|
||||||
persist: function() {
|
|
||||||
qr.dialog();
|
|
||||||
return qr.autohide.set();
|
|
||||||
},
|
|
||||||
close: function() {
|
|
||||||
$.rm(qr.el);
|
|
||||||
return qr.el = null;
|
|
||||||
},
|
|
||||||
sys: function() {
|
sys: function() {
|
||||||
var c, duration, id, noko, recaptcha, thread, _, _ref;
|
var c, duration, id, noko, recaptcha, sage, search, thread, url, watch, _, _ref;
|
||||||
if (recaptcha = $('#recaptcha_response_field')) {
|
if (recaptcha = $('#recaptcha_response_field')) {
|
||||||
$.bind(recaptcha, 'keydown', Recaptcha.listener);
|
$.bind(recaptcha, 'keydown', Recaptcha.listener);
|
||||||
return;
|
return;
|
||||||
@ -1403,28 +1469,26 @@
|
|||||||
c = $('b').lastChild;
|
c = $('b').lastChild;
|
||||||
if (c.nodeType === 8) {
|
if (c.nodeType === 8) {
|
||||||
_ref = c.textContent.match(/thread:(\d+),no:(\d+)/), _ = _ref[0], thread = _ref[1], id = _ref[2];
|
_ref = c.textContent.match(/thread:(\d+),no:(\d+)/), _ = _ref[0], thread = _ref[1], id = _ref[2];
|
||||||
noko = /auto_noko/.test(location.search);
|
search = location.search;
|
||||||
if (thread === '0') {
|
cooldown = /cooldown/.test(search);
|
||||||
if (/auto_watch/.test(location.search)) {
|
noko = /noko/.test(search);
|
||||||
return window.location = "http://boards.4chan.org/" + g.BOARD + "/res/" + id + "#watch";
|
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) {
|
} else if (noko) {
|
||||||
return window.location = "http://boards.4chan.org/" + g.BOARD + "/res/" + id;
|
url += '/res/';
|
||||||
|
url += thread === '0' ? id : thread;
|
||||||
}
|
}
|
||||||
} else if (/cooldown/.test(location.search)) {
|
if (cooldown) {
|
||||||
duration = Date.now() + 30000;
|
duration = Date.now() + (sage ? 60 : 30) * 1000;
|
||||||
if (/sage/.test(location.search)) {
|
url += '?cooldown=' + duration;
|
||||||
duration += 30000;
|
|
||||||
}
|
}
|
||||||
if (noko) {
|
if (noko) {
|
||||||
return window.location = "http://boards.4chan.org/" + g.BOARD + "/res/" + thread + "?cooldown=" + duration + "#" + id;
|
url += '#' + id;
|
||||||
} else {
|
|
||||||
return window.location = "http://boards.4chan.org/" + g.BOARD + "?cooldown=" + duration;
|
|
||||||
}
|
|
||||||
} else if (noko) {
|
|
||||||
return window.location = "http://boards.4chan.org/" + g.BOARD + "/res/" + thread + "#" + id;
|
|
||||||
} else {
|
|
||||||
return window.location = "http://boards.4chan.org/" + g.BOARD;
|
|
||||||
}
|
}
|
||||||
|
return window.location = url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1555,6 +1619,14 @@
|
|||||||
updater = {
|
updater = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var checkbox, checked, dialog, html, input, name, title, _i, _len, _ref;
|
var checkbox, checked, dialog, html, input, name, title, _i, _len, _ref;
|
||||||
|
if (conf['Scrolling']) {
|
||||||
|
$.bind(window, 'focus', (function() {
|
||||||
|
return updater.focus = true;
|
||||||
|
}));
|
||||||
|
$.bind(window, 'blur', (function() {
|
||||||
|
return updater.focus = false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
html = "<div class=move><span id=count></span> <span id=timer>-" + conf['Interval'] + "</span></div>";
|
html = "<div class=move><span id=count></span> <span id=timer>-" + conf['Interval'] + "</span></div>";
|
||||||
checkbox = config.updater.checkbox;
|
checkbox = config.updater.checkbox;
|
||||||
for (name in checkbox) {
|
for (name in checkbox) {
|
||||||
@ -1644,7 +1716,7 @@
|
|||||||
while ((reply = replies.pop()) && (reply.id > id)) {
|
while ((reply = replies.pop()) && (reply.id > id)) {
|
||||||
arr.push(reply.parentNode.parentNode.parentNode);
|
arr.push(reply.parentNode.parentNode.parentNode);
|
||||||
}
|
}
|
||||||
scroll = conf['Scrolling'] && arr.length && (d.body.scrollHeight - d.body.clientHeight - window.scrollY < 20);
|
scroll = conf['Scrolling'] && updater.focus && arr.length && (d.body.scrollHeight - d.body.clientHeight - window.scrollY < 20);
|
||||||
updater.timer.textContent = '-' + conf['Interval'];
|
updater.timer.textContent = '-' + conf['Interval'];
|
||||||
if (conf['Verbose']) {
|
if (conf['Verbose']) {
|
||||||
updater.count.textContent = '+' + arr.length;
|
updater.count.textContent = '+' + arr.length;
|
||||||
@ -1889,22 +1961,9 @@
|
|||||||
foo: function() {
|
foo: function() {
|
||||||
var code;
|
var code;
|
||||||
code = conf['time'].replace(/%([A-Za-z])/g, function(s, c) {
|
code = conf['time'].replace(/%([A-Za-z])/g, function(s, c) {
|
||||||
switch (c) {
|
if (c in Time.formatters) {
|
||||||
case 'a':
|
return "' + Time.formatters." + c + "() + '";
|
||||||
case 'A':
|
} else {
|
||||||
case 'b':
|
|
||||||
case 'B':
|
|
||||||
case 'd':
|
|
||||||
case 'H':
|
|
||||||
case 'I':
|
|
||||||
case 'm':
|
|
||||||
case 'M':
|
|
||||||
case 'p':
|
|
||||||
case 'P':
|
|
||||||
case 'y':
|
|
||||||
return "' + Time." + c + "() + '";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1919,49 +1978,60 @@
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
formatters: {
|
||||||
a: function() {
|
a: function() {
|
||||||
return this.day[this.date.getDay()].slice(0, 3);
|
return Time.day[Time.date.getDay()].slice(0, 3);
|
||||||
},
|
},
|
||||||
A: function() {
|
A: function() {
|
||||||
return this.day[this.date.getDay()];
|
return Time.day[Time.date.getDay()];
|
||||||
},
|
},
|
||||||
b: function() {
|
b: function() {
|
||||||
return this.month[this.date.getMonth()].slice(0, 3);
|
return Time.month[Time.date.getMonth()].slice(0, 3);
|
||||||
},
|
},
|
||||||
B: function() {
|
B: function() {
|
||||||
return this.month[this.date.getMonth()];
|
return Time.month[Time.date.getMonth()];
|
||||||
},
|
},
|
||||||
d: function() {
|
d: function() {
|
||||||
return this.zeroPad(this.date.getDate());
|
return Time.zeroPad(Time.date.getDate());
|
||||||
|
},
|
||||||
|
e: function() {
|
||||||
|
return Time.date.getDate();
|
||||||
},
|
},
|
||||||
H: function() {
|
H: function() {
|
||||||
return this.zeroPad(this.date.getHours());
|
return Time.zeroPad(Time.date.getHours());
|
||||||
},
|
},
|
||||||
I: function() {
|
I: function() {
|
||||||
return this.zeroPad(this.date.getHours() % 12 || 12);
|
return Time.zeroPad(Time.date.getHours() % 12 || 12);
|
||||||
|
},
|
||||||
|
k: function() {
|
||||||
|
return Time.date.getHours();
|
||||||
|
},
|
||||||
|
l: function() {
|
||||||
|
return Time.date.getHours() % 12 || 12;
|
||||||
},
|
},
|
||||||
m: function() {
|
m: function() {
|
||||||
return this.zeroPad(this.date.getMonth() + 1);
|
return Time.zeroPad(Time.date.getMonth() + 1);
|
||||||
},
|
},
|
||||||
M: function() {
|
M: function() {
|
||||||
return this.zeroPad(this.date.getMinutes());
|
return Time.zeroPad(Time.date.getMinutes());
|
||||||
},
|
},
|
||||||
p: function() {
|
p: function() {
|
||||||
if (this.date.getHours() < 12) {
|
if (Time.date.getHours() < 12) {
|
||||||
return 'AM';
|
return 'AM';
|
||||||
} else {
|
} else {
|
||||||
return 'PM';
|
return 'PM';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
P: function() {
|
P: function() {
|
||||||
if (this.date.getHours() < 12) {
|
if (Time.date.getHours() < 12) {
|
||||||
return 'am';
|
return 'am';
|
||||||
} else {
|
} else {
|
||||||
return 'pm';
|
return 'pm';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
y: function() {
|
y: function() {
|
||||||
return this.date.getFullYear() - 2000;
|
return Time.date.getFullYear() - 2000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
titlePost = {
|
titlePost = {
|
||||||
@ -1991,7 +2061,7 @@
|
|||||||
}
|
}
|
||||||
_results = [];
|
_results = [];
|
||||||
for (qid in quotes) {
|
for (qid in quotes) {
|
||||||
if (!(el = d.getElementById(qid))) {
|
if (!(el = $.id(qid))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!conf['OP Backlinks'] && el.className === 'op') {
|
if (!conf['OP Backlinks'] && el.className === 'op') {
|
||||||
@ -2042,20 +2112,10 @@
|
|||||||
},
|
},
|
||||||
toggle: function(e) {
|
toggle: function(e) {
|
||||||
var el, hidden, id, inline, inlined, pathname, root, table, threadID, _i, _len, _ref;
|
var el, hidden, id, inline, inlined, pathname, root, table, threadID, _i, _len, _ref;
|
||||||
|
if (e.shiftKey || e.altKey || e.ctrlKey || e.button !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
/*
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=674955
|
|
||||||
`mouseout` does not fire when element removed
|
|
||||||
RESOLVED INVALID
|
|
||||||
|
|
||||||
inline a post, then hover over an inlined quote / image, then remove
|
|
||||||
the inlined post by clicking `enter` on the still-focused link - the
|
|
||||||
mouseout event doesn't fire, and the quote preview / image hover remains.
|
|
||||||
|
|
||||||
we can prevent this sequence by `blur`-ing the clicked links. chrome
|
|
||||||
doesn't focus clicked links anyway.
|
|
||||||
*/
|
|
||||||
this.blur();
|
|
||||||
id = this.hash.slice(1);
|
id = this.hash.slice(1);
|
||||||
if (table = $("#i" + id, $.x('ancestor::td[1]', this))) {
|
if (table = $("#i" + id, $.x('ancestor::td[1]', this))) {
|
||||||
$.rm(table);
|
$.rm(table);
|
||||||
@ -2063,14 +2123,14 @@
|
|||||||
_ref = $$('input', table);
|
_ref = $$('input', table);
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
inlined = _ref[_i];
|
inlined = _ref[_i];
|
||||||
if (hidden = d.getElementById(inlined.name)) {
|
if (hidden = $.id(inlined.name)) {
|
||||||
$.show($.x('ancestor::table[1]', hidden));
|
$.show($.x('ancestor::table[1]', hidden));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
root = this.parentNode.nodeName === 'FONT' ? this.parentNode : this.nextSibling ? this.nextSibling : this;
|
root = this.parentNode.nodeName === 'FONT' ? this.parentNode : this.nextSibling ? this.nextSibling : this;
|
||||||
if (el = d.getElementById(id)) {
|
if (el = $.id(id)) {
|
||||||
inline = quoteInline.table(id, el.innerHTML);
|
inline = quoteInline.table(id, el.innerHTML);
|
||||||
if (this.className === 'backlink') {
|
if (this.className === 'backlink') {
|
||||||
if ($("a.backlink[href='#" + id + "']", el)) {
|
if ($("a.backlink[href='#" + id + "']", el)) {
|
||||||
@ -2166,7 +2226,7 @@
|
|||||||
});
|
});
|
||||||
$.append(d.body, qp);
|
$.append(d.body, qp);
|
||||||
id = this.hash.slice(1);
|
id = this.hash.slice(1);
|
||||||
if (el = d.getElementById(id)) {
|
if (el = $.id(id)) {
|
||||||
qp.innerHTML = el.innerHTML;
|
qp.innerHTML = el.innerHTML;
|
||||||
if (conf['Quote Highlighting']) {
|
if (conf['Quote Highlighting']) {
|
||||||
$.addClass(el, 'qphl');
|
$.addClass(el, 'qphl');
|
||||||
@ -2191,7 +2251,7 @@
|
|||||||
},
|
},
|
||||||
mouseout: function() {
|
mouseout: function() {
|
||||||
var el;
|
var el;
|
||||||
if (el = d.getElementById(this.hash.slice(1))) {
|
if (el = $.id(this.hash.slice(1))) {
|
||||||
$.removeClass(el, 'qphl');
|
$.removeClass(el, 'qphl');
|
||||||
}
|
}
|
||||||
return ui.hoverend();
|
return ui.hoverend();
|
||||||
@ -2318,6 +2378,7 @@
|
|||||||
},
|
},
|
||||||
scroll: function(e) {
|
scroll: function(e) {
|
||||||
var bottom, height, i, reply, _len, _ref;
|
var bottom, height, i, reply, _len, _ref;
|
||||||
|
updater.focus = true;
|
||||||
height = d.body.clientHeight;
|
height = d.body.clientHeight;
|
||||||
_ref = unread.replies;
|
_ref = unread.replies;
|
||||||
for (i = 0, _len = _ref.length; i < _len; i++) {
|
for (i = 0, _len = _ref.length; i < _len; i++) {
|
||||||
@ -2420,29 +2481,15 @@
|
|||||||
el = _ref2[_i];
|
el = _ref2[_i];
|
||||||
el.tabIndex = 1;
|
el.tabIndex = 1;
|
||||||
}
|
}
|
||||||
$.bind($('#recaptcha_challenge_field_holder'), 'DOMNodeInserted', Recaptcha.reloaded);
|
|
||||||
return $.bind($('#recaptcha_response_field'), 'keydown', Recaptcha.listener);
|
return $.bind($('#recaptcha_response_field'), 'keydown', Recaptcha.listener);
|
||||||
},
|
},
|
||||||
listener: function(e) {
|
listener: function(e) {
|
||||||
if (e.keyCode === 8 && this.value === '') {
|
if (e.keyCode === 8 && this.value === '') {
|
||||||
Recaptcha.reload();
|
return Recaptcha.reload();
|
||||||
}
|
|
||||||
if (e.keyCode === 13 && cooldown.duration) {
|
|
||||||
$('#auto', qr.el).checked = true;
|
|
||||||
return qr.autohide.set();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reload: function() {
|
reload: function() {
|
||||||
return window.location = 'javascript:Recaptcha.reload()';
|
return window.location = 'javascript:Recaptcha.reload()';
|
||||||
},
|
|
||||||
reloaded: function(e) {
|
|
||||||
var target;
|
|
||||||
if (!qr.el) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
target = e.target;
|
|
||||||
$('img', qr.el).src = "http://www.google.com/recaptcha/api/image?c=" + target.value;
|
|
||||||
return $('input[name=recaptcha_challenge_field]', qr.el).value = target.value;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
nodeInserted = function(e) {
|
nodeInserted = function(e) {
|
||||||
@ -2704,10 +2751,13 @@
|
|||||||
$.bind(form, 'submit', qr.submit);
|
$.bind(form, 'submit', qr.submit);
|
||||||
}
|
}
|
||||||
threading.init();
|
threading.init();
|
||||||
if (conf['Auto Noko']) {
|
if (g.REPLY && (id = location.hash.slice(1)) && /\d/.test(id[0]) && !$.id(id)) {
|
||||||
$('.postarea form').action += '?auto_noko';
|
scrollTo(0, d.body.scrollHeight);
|
||||||
}
|
}
|
||||||
if (conf['Cooldown']) {
|
if (conf['Auto Noko'] && canPost) {
|
||||||
|
form.action += '?noko';
|
||||||
|
}
|
||||||
|
if (conf['Cooldown'] && canPost) {
|
||||||
cooldown.init();
|
cooldown.init();
|
||||||
}
|
}
|
||||||
if (conf['Image Expansion']) {
|
if (conf['Image Expansion']) {
|
||||||
@ -2734,7 +2784,7 @@
|
|||||||
if (conf['Reply Hiding']) {
|
if (conf['Reply Hiding']) {
|
||||||
replyHiding.init();
|
replyHiding.init();
|
||||||
}
|
}
|
||||||
if (canPost && conf['Quick Reply']) {
|
if (conf['Quick Reply'] && canPost) {
|
||||||
qr.init();
|
qr.init();
|
||||||
}
|
}
|
||||||
if (conf['Report Button']) {
|
if (conf['Report Button']) {
|
||||||
@ -2765,8 +2815,11 @@
|
|||||||
if (conf['Image Preloading']) {
|
if (conf['Image Preloading']) {
|
||||||
imgPreloading.init();
|
imgPreloading.init();
|
||||||
}
|
}
|
||||||
if (conf['Quick Reply'] && conf['Persistent QR']) {
|
if (conf['Quick Reply'] && conf['Persistent QR'] && canPost) {
|
||||||
qr.persist();
|
qr.dialog();
|
||||||
|
if (conf['Auto Hide QR']) {
|
||||||
|
$('#autohide', qr.el).checked = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (conf['Post in Title']) {
|
if (conf['Post in Title']) {
|
||||||
titlePost.init();
|
titlePost.init();
|
||||||
@ -2780,7 +2833,7 @@
|
|||||||
if (conf['Reply Navigation']) {
|
if (conf['Reply Navigation']) {
|
||||||
nav.init();
|
nav.init();
|
||||||
}
|
}
|
||||||
if (conf['Auto Watch'] && conf['Thread Watcher'] && location.hash === '#watch' && $('img.favicon').src === Favicon.empty) {
|
if (conf['Auto Watch'] && conf['Thread Watcher'] && /watch/.test(location.search) && $('img.favicon').src === Favicon.empty) {
|
||||||
watcher.watch(null, g.THREAD_ID);
|
watcher.watch(null, g.THREAD_ID);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2797,7 +2850,7 @@
|
|||||||
expandComment.init();
|
expandComment.init();
|
||||||
}
|
}
|
||||||
if (conf['Auto Watch']) {
|
if (conf['Auto Watch']) {
|
||||||
$('.postarea form').action += '?auto_watch';
|
$('.postarea form').action += '?watch';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ref3 = $$('div.op');
|
_ref3 = $$('div.op');
|
||||||
@ -2922,6 +2975,9 @@
|
|||||||
\
|
\
|
||||||
#qr {\
|
#qr {\
|
||||||
position: fixed;\
|
position: fixed;\
|
||||||
|
max-height: 100%;\
|
||||||
|
overflow-x: hidden;\
|
||||||
|
overflow-y: auto;\
|
||||||
}\
|
}\
|
||||||
#qr > div.move {\
|
#qr > div.move {\
|
||||||
text-align: right;\
|
text-align: right;\
|
||||||
@ -2939,7 +2995,10 @@
|
|||||||
width: 100%;\
|
width: 100%;\
|
||||||
height: 120px;\
|
height: 120px;\
|
||||||
}\
|
}\
|
||||||
#qr.auto:not(:hover) > form {\
|
#qr #close, #qr #autohide {\
|
||||||
|
float: right;\
|
||||||
|
}\
|
||||||
|
#qr:not(:hover) > #autohide:checked ~ form {\
|
||||||
height: 0;\
|
height: 0;\
|
||||||
overflow: hidden;\
|
overflow: hidden;\
|
||||||
}\
|
}\
|
||||||
@ -3008,6 +3067,10 @@
|
|||||||
[hidden] {\
|
[hidden] {\
|
||||||
display: none;\
|
display: none;\
|
||||||
}\
|
}\
|
||||||
|
\
|
||||||
|
#files > input {\
|
||||||
|
display: block;\
|
||||||
|
}\
|
||||||
'
|
'
|
||||||
};
|
};
|
||||||
main.init();
|
main.init();
|
||||||
|
|||||||
10
README.md
10
README.md
@ -1,7 +1,13 @@
|
|||||||
|
# Installing
|
||||||
|
|
||||||
|
[master](https://github.com/aeosynth/4chan-x/raw/master/4chan_x.user.js) - bleeding edge. exciting new features, exciting new bugs.
|
||||||
|
|
||||||
|
[stable](https://github.com/aeosynth/4chan-x/raw/stable/4chan_x.user.js) - tries to be bug free.
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
[install nodejs and npm](https://github.com/joyent/node/wiki/Installation),
|
[install nodejs and npm](https://github.com/joyent/node/wiki/Installation),
|
||||||
install [coffee-script](https://github.com/jashkenas/coffee-script/) with
|
install [coffee-script](https://github.com/jashkenas/coffee-script/) with
|
||||||
`npm install -g coffee-script`, clone 4chan x, cd into it and run
|
`npm install -g coffee-script`, clone 4chan x, cd into it and run
|
||||||
`npm link coffee-script`. actually build it with `cake dev &`.
|
`npm link coffee-script`. actually build it with `cake build`. for development
|
||||||
kill the process with `killall node`.
|
(continuous builds), run `cake dev &`. kill the process with `killall node`.
|
||||||
|
|||||||
15
changelog
15
changelog
@ -1,4 +1,19 @@
|
|||||||
github
|
github
|
||||||
|
- mayhem:
|
||||||
|
- fix post links in expanded threads
|
||||||
|
- fix 4chan X in closed threads
|
||||||
|
- aeosynth:
|
||||||
|
- only auto scroll focused tabs
|
||||||
|
- quote inlining: only work on unmodified left-click
|
||||||
|
- select multiple files (one at a time)
|
||||||
|
- captcha caching
|
||||||
|
- qr: optional auto hiding
|
||||||
|
- copy old textarea value
|
||||||
|
- scroll to bottom of page if post isn't found (thumbnail generation takes
|
||||||
|
time)
|
||||||
|
- only scroll focused tabs
|
||||||
|
- time: %e, %k, %l
|
||||||
|
- reverted hovering fix
|
||||||
|
|
||||||
2.17.1
|
2.17.1
|
||||||
- mayhem:
|
- mayhem:
|
||||||
|
|||||||
2
header
2
header
@ -2,7 +2,7 @@
|
|||||||
// @name 4chan x
|
// @name 4chan x
|
||||||
// @namespace aeosynth
|
// @namespace aeosynth
|
||||||
// @description Adds various features.
|
// @description Adds various features.
|
||||||
// @version 11.8.4.0
|
// @version 11.8.6.0
|
||||||
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
|
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
|
||||||
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
|
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
|
||||||
// @include http://boards.4chan.org/*
|
// @include http://boards.4chan.org/*
|
||||||
|
|||||||
434
script.coffee
434
script.coffee
@ -34,6 +34,7 @@ config =
|
|||||||
'Cooldown': [true, 'Prevent \'flood detected\' errors']
|
'Cooldown': [true, 'Prevent \'flood detected\' errors']
|
||||||
'Quick Reply': [true, 'Reply without leaving the page']
|
'Quick Reply': [true, 'Reply without leaving the page']
|
||||||
'Persistent QR': [false, 'Quick reply won\'t disappear after posting. Only in replies.']
|
'Persistent QR': [false, 'Quick reply won\'t disappear after posting. Only in replies.']
|
||||||
|
'Auto Hide QR': [true, 'Automatically auto-hide the quick reply when posting']
|
||||||
Quoting:
|
Quoting:
|
||||||
'Quote Backlinks': [true, 'Add quote backlinks']
|
'Quote Backlinks': [true, 'Add quote backlinks']
|
||||||
'OP Backlinks': [false, 'Add backlinks to the OP']
|
'OP Backlinks': [false, 'Add backlinks to the OP']
|
||||||
@ -48,6 +49,7 @@ config =
|
|||||||
'#http://tineye.com/search?url='
|
'#http://tineye.com/search?url='
|
||||||
'#http://saucenao.com/search.php?db=999&url='
|
'#http://saucenao.com/search.php?db=999&url='
|
||||||
'#http://imgur.com/upload?url='
|
'#http://imgur.com/upload?url='
|
||||||
|
'#http://anonym.to/?'
|
||||||
].join '\n'
|
].join '\n'
|
||||||
time: '%m/%d/%y(%a)%H:%M'
|
time: '%m/%d/%y(%a)%H:%M'
|
||||||
hotkeys:
|
hotkeys:
|
||||||
@ -200,6 +202,8 @@ $.extend = (object, properties) ->
|
|||||||
object
|
object
|
||||||
|
|
||||||
$.extend $,
|
$.extend $,
|
||||||
|
id: (id) ->
|
||||||
|
d.getElementById id
|
||||||
globalEval: (code) ->
|
globalEval: (code) ->
|
||||||
script = $.el 'script',
|
script = $.el 'script',
|
||||||
textContent: "(#{code})()"
|
textContent: "(#{code})()"
|
||||||
@ -430,7 +434,7 @@ expandThread =
|
|||||||
while (prev = table.previousSibling) and (prev.nodeName is 'TABLE')
|
while (prev = table.previousSibling) and (prev.nodeName is 'TABLE')
|
||||||
$.rm prev
|
$.rm prev
|
||||||
for backlink in $$ '.op a.backlink'
|
for backlink in $$ '.op a.backlink'
|
||||||
$.rm backlink if !d.getElementById backlink.hash[1..]
|
$.rm backlink if !$.id backlink.hash[1..]
|
||||||
|
|
||||||
|
|
||||||
parse: (req, pathname, thread, a) ->
|
parse: (req, pathname, thread, a) ->
|
||||||
@ -449,9 +453,13 @@ expandThread =
|
|||||||
body = $.el 'body',
|
body = $.el 'body',
|
||||||
innerHTML: req.responseText
|
innerHTML: req.responseText
|
||||||
|
|
||||||
for quote in $$ 'a.quotelink', body
|
for reply in $$ 'td[id]', body
|
||||||
|
for quote in $$ 'a.quotelink', reply
|
||||||
if quote.getAttribute('href') is quote.hash
|
if quote.getAttribute('href') is quote.hash
|
||||||
quote.pathname = pathname
|
quote.pathname = pathname
|
||||||
|
link = $ 'a.quotejs', reply
|
||||||
|
link.href = "res/#{thread.firstChild.id}##{reply.id}"
|
||||||
|
link.nextSibling.href = "res/#{thread.firstChild.id}#q#{reply.id}"
|
||||||
tables = $$ 'form[name=delform] table', body
|
tables = $$ 'form[name=delform] table', body
|
||||||
tables.pop()
|
tables.pop()
|
||||||
for table in tables
|
for table in tables
|
||||||
@ -515,12 +523,12 @@ keybinds =
|
|||||||
init: ->
|
init: ->
|
||||||
for node in $$ '[accesskey]'
|
for node in $$ '[accesskey]'
|
||||||
node.removeAttribute 'accesskey'
|
node.removeAttribute 'accesskey'
|
||||||
$.bind d, 'keydown', keybinds.cb.keydown
|
$.bind d, 'keydown', keybinds.keydown
|
||||||
|
|
||||||
cb:
|
|
||||||
keydown: (e) ->
|
keydown: (e) ->
|
||||||
|
updater.focus = true
|
||||||
return if e.target.nodeName in ['TEXTAREA', 'INPUT'] and not e.altKey and not e.ctrlKey and not (e.keyCode is 27)
|
return if e.target.nodeName in ['TEXTAREA', 'INPUT'] and not e.altKey and not e.ctrlKey and not (e.keyCode is 27)
|
||||||
return unless key = keybinds.cb.keyCode e
|
return unless key = keybinds.keyCode e
|
||||||
|
|
||||||
thread = nav.getThread()
|
thread = nav.getThread()
|
||||||
switch key
|
switch key
|
||||||
@ -623,7 +631,7 @@ keybinds =
|
|||||||
qrLink = $ "span[id^=nothread] a:not(:first-child)", thread
|
qrLink = $ "span[id^=nothread] a:not(:first-child)", thread
|
||||||
|
|
||||||
if quote
|
if quote
|
||||||
qr.quote qrLink
|
qr.quote.call qrLink
|
||||||
else
|
else
|
||||||
unless qr.el
|
unless qr.el
|
||||||
qr.dialog qrLink
|
qr.dialog qrLink
|
||||||
@ -786,18 +794,28 @@ options =
|
|||||||
<caption>Format specifiers <a href=http://en.wikipedia.org/wiki/Date_%28Unix%29#Formatting>(source)</a></caption>
|
<caption>Format specifiers <a href=http://en.wikipedia.org/wiki/Date_%28Unix%29#Formatting>(source)</a></caption>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><th>Specifier</th><th>Description</th><th>Values/Example</th></tr>
|
<tr><th>Specifier</th><th>Description</th><th>Values/Example</th></tr>
|
||||||
<tr><td>%a</td><td>weekday, abbreviated</td><td>Sat</td></tr>
|
<tr><th colspan=3>Year</th></tr>
|
||||||
<tr><td>%A</td><td>weekday, full</td><td>Saturday</td></tr>
|
<tr><td>%y</td><td>two digit year</td><td>00-99</td></tr>
|
||||||
|
|
||||||
|
<tr><th colspan=3>Month</th></tr>
|
||||||
<tr><td>%b</td><td>month, abbreviated</td><td>Jun</td></tr>
|
<tr><td>%b</td><td>month, abbreviated</td><td>Jun</td></tr>
|
||||||
<tr><td>%B</td><td>month, full length</td><td>June</td></tr>
|
<tr><td>%B</td><td>month, full length</td><td>June</td></tr>
|
||||||
<tr><td>%d</td><td>day of the month, zero padded</td><td>03</td></tr>
|
|
||||||
<tr><td>%H</td><td>hour (24 hour clock) zero padded</td><td>13</td></tr>
|
|
||||||
<tr><td>%I (uppercase i)</td><td>hour (12 hour clock) zero padded</td><td>02</td></tr>
|
|
||||||
<tr><td>%m</td><td>month, zero padded</td><td>06</td></tr>
|
<tr><td>%m</td><td>month, zero padded</td><td>06</td></tr>
|
||||||
|
|
||||||
|
<tr><th colspan=3>Day</th></tr>
|
||||||
|
<tr><td>%a</td><td>weekday, abbreviated</td><td>Sat</td></tr>
|
||||||
|
<tr><td>%A</td><td>weekday, full</td><td>Saturday</td></tr>
|
||||||
|
<tr><td>%d</td><td>day of the month, zero padded</td><td>03</td></tr>
|
||||||
|
<tr><td>%e</td><td>day of the month</td><td>3</td></tr>
|
||||||
|
|
||||||
|
<tr><th colspan=3>Time</th></tr>
|
||||||
|
<tr><td>%H</td><td>hour (24 hour clock) zero padded</td><td>13</td></tr>
|
||||||
|
<tr><td>%l (lowercase L)</td><td>hour (12 hour clock)</td><td>1</td></tr>
|
||||||
|
<tr><td>%I (uppercase i)</td><td>hour (12 hour clock) zero padded</td><td>01</td></tr>
|
||||||
|
<tr><td>%k</td><td>hour (24 hour clock)</td><td>13</td></tr>
|
||||||
<tr><td>%M</td><td>minutes, zero padded</td><td>54</td></tr>
|
<tr><td>%M</td><td>minutes, zero padded</td><td>54</td></tr>
|
||||||
<tr><td>%p</td><td>upper case AM or PM</td><td>PM</td></tr>
|
<tr><td>%p</td><td>upper case AM or PM</td><td>PM</td></tr>
|
||||||
<tr><td>%P</td><td>lower case am or pm</td><td>pm</td></tr>
|
<tr><td>%P</td><td>lower case am or pm</td><td>pm</td></tr>
|
||||||
<tr><td>%y</td><td>two digit year</td><td>00-99</td></tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -892,7 +910,7 @@ options =
|
|||||||
keybind: (e) ->
|
keybind: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
return unless (key = keybinds.cb.keyCode e)?
|
return unless (key = keybinds.keyCode e)?
|
||||||
@value = key
|
@value = key
|
||||||
$.setValue @name, key
|
$.setValue @name, key
|
||||||
conf[@name] = key
|
conf[@name] = key
|
||||||
@ -936,7 +954,7 @@ cooldown =
|
|||||||
|
|
||||||
cb: ->
|
cb: ->
|
||||||
submits = $$ '#com_submit'
|
submits = $$ '#com_submit'
|
||||||
if --cooldown.duration
|
if --cooldown.duration > 0
|
||||||
for submit in submits
|
for submit in submits
|
||||||
submit.value = cooldown.duration
|
submit.value = cooldown.duration
|
||||||
else
|
else
|
||||||
@ -945,32 +963,115 @@ cooldown =
|
|||||||
submit.disabled = false
|
submit.disabled = false
|
||||||
submit.value = 'Submit'
|
submit.value = 'Submit'
|
||||||
if qr.el and $('#auto', qr.el).checked
|
if qr.el and $('#auto', qr.el).checked
|
||||||
qr.submit.call $ 'form', qr.el
|
qr.autoPost()
|
||||||
|
|
||||||
qr =
|
qr =
|
||||||
|
# TODO
|
||||||
|
# error handling
|
||||||
|
# persistent captcha
|
||||||
|
# rm Recaptcha
|
||||||
|
# error too large error should happen on attach
|
||||||
init: ->
|
init: ->
|
||||||
g.callbacks.push qr.cb.node
|
g.callbacks.push qr.node
|
||||||
|
$.bind window, 'message', qr.message
|
||||||
|
$.bind $('#recaptcha_challenge_field_holder'), 'DOMNodeInserted', qr.captchaNode
|
||||||
|
qr.captcha = []
|
||||||
|
|
||||||
iframe = $.el 'iframe',
|
iframe = $.el 'iframe',
|
||||||
name: 'iframe'
|
name: 'iframe'
|
||||||
hidden: true
|
hidden: true
|
||||||
$.append d.body, iframe
|
$.append d.body, iframe
|
||||||
$.bind window, 'message', qr.cb.message
|
|
||||||
|
|
||||||
#hack - nuke id so it doesn't grab focus when reloading
|
#hack - nuke id so it doesn't grab focus when reloading
|
||||||
$('#recaptcha_response_field').id = ''
|
$('#recaptcha_response_field').id = ''
|
||||||
|
|
||||||
autohide:
|
attach: ->
|
||||||
set: ->
|
$('#auto', qr.el).checked = true
|
||||||
$('#autohide:not(:checked)', qr.el)?.click()
|
fileDiv = $.el 'div', innerHTML: '<input type=file name=upfile><a>X</a>'
|
||||||
unset: ->
|
$.bind fileDiv.lastChild, 'click', (-> $.rm @parentNode)
|
||||||
$('#autohide:checked', qr.el)?.click()
|
$.prepend qr.files, fileDiv
|
||||||
|
|
||||||
cb:
|
attachNext: ->
|
||||||
autohide: (e) ->
|
fileDiv = $.rm qr.files.lastChild
|
||||||
if @checked
|
file = fileDiv.firstChild
|
||||||
$.addClass qr.el, 'auto'
|
oldFile = $ '#qr_form input[type=file]', qr.el
|
||||||
else
|
$.replace oldFile, file
|
||||||
$.removeClass qr.el, 'auto'
|
|
||||||
|
autoPost: ->
|
||||||
|
responseField = $ '#recaptcha_response_field', qr.el
|
||||||
|
if !responseField.value and captcha = qr.captcha.shift()
|
||||||
|
$('#recaptcha_challenge_field', qr.el).value = captcha.challenge
|
||||||
|
responseField.value = captcha.response
|
||||||
|
responseField.nextSibling.textContent = qr.captcha.length + ' captcha cached'
|
||||||
|
qr.submit.call $ 'form', qr.el
|
||||||
|
|
||||||
|
captchaNode: (e) ->
|
||||||
|
return unless qr.el
|
||||||
|
{target} = e
|
||||||
|
$('img', qr.el).src = "http://www.google.com/recaptcha/api/image?c=" + target.value
|
||||||
|
$('#recaptcha_challenge_field', qr.el).value = target.value
|
||||||
|
|
||||||
|
captchaKeydown: (e) ->
|
||||||
|
if e.keyCode is 13 and cooldown.duration # press enter to enable auto-post if cooldown is still running
|
||||||
|
$('#auto', qr.el).checked = true
|
||||||
|
$('#autohide', qr.el).checked = true if conf['Auto Hide QR']
|
||||||
|
qr.captchaPush.call this
|
||||||
|
|
||||||
|
captchaPush: ->
|
||||||
|
l = qr.captcha.push
|
||||||
|
challenge: $('#recaptcha_challenge_field', qr.el).value
|
||||||
|
response: @value
|
||||||
|
@nextSibling.textContent = l + ' captcha cached'
|
||||||
|
Recaptcha.reload()
|
||||||
|
@value = ''
|
||||||
|
|
||||||
|
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
|
||||||
|
spoiler = if $('.postarea label') then '<label> [<input type=checkbox name=spoiler>Spoiler Image?]</label>' else ''
|
||||||
|
challenge = $('#recaptcha_challenge_field').value
|
||||||
|
html = "
|
||||||
|
<a id=close title=close>X</a>
|
||||||
|
<input type=checkbox id=autohide title=autohide>
|
||||||
|
<div class=move>
|
||||||
|
<input class=inputtext type=text name=name placeholder=Name form=qr_form>
|
||||||
|
Quick Reply
|
||||||
|
</div>
|
||||||
|
<form name=post action=http://sys.4chan.org/#{g.BOARD}/post method=POST enctype=multipart/form-data target=iframe id=qr_form>
|
||||||
|
<input type=hidden name=resto value=#{THREAD_ID}>
|
||||||
|
<input type=hidden name=recaptcha_challenge_field id=recaptcha_challenge_field value=#{challenge}>
|
||||||
|
<div><input class=inputtext type=text name=email placeholder=E-mail>#{spoiler}</div>
|
||||||
|
<div><input class=inputtext type=text name=sub placeholder=Subject><input type=submit value=#{submitValue} id=com_submit #{submitDisabled}><label><input type=checkbox id=auto>auto</label></div>
|
||||||
|
<div><textarea class=inputtext name=com placeholder=Comment></textarea></div>
|
||||||
|
<div><img src=http://www.google.com/recaptcha/api/image?c=#{challenge}></div>
|
||||||
|
<div><input class=inputtext type=text name=recaptcha_response_field placeholder=Verification required autocomplete=off id=recaptcha_response_field>0 captcha cached</div>
|
||||||
|
<div><input type=file name=upfile></div>
|
||||||
|
<div><input class=inputtext type=password name=pwd maxlength=8 placeholder=Password><input type=hidden name=mode value=regist><a name=attach>attach another file</a></div>
|
||||||
|
</form>
|
||||||
|
<div id=files></div>
|
||||||
|
<a id=error class=error></a>
|
||||||
|
"
|
||||||
|
qr.el = ui.dialog 'qr', top: '0px', left: '0px', html
|
||||||
|
qr.files = $ '#files', qr.el
|
||||||
|
|
||||||
|
qr.refresh()
|
||||||
|
$('textarea', qr.el).value = $('textarea').value
|
||||||
|
|
||||||
|
$.bind $('input[name=name]', qr.el), 'mousedown', (e) -> e.stopPropagation()
|
||||||
|
$.bind $('#close', qr.el), 'click', qr.close
|
||||||
|
$.bind $('form', qr.el), 'submit', qr.submit
|
||||||
|
$.bind $('a[name=attach]', qr.el), 'click', qr.attach
|
||||||
|
$.bind $('img', qr.el), 'click', Recaptcha.reload
|
||||||
|
$.bind $('#recaptcha_response_field', qr.el), 'keydown', Recaptcha.listener
|
||||||
|
$.bind $('#recaptcha_response_field', qr.el), 'keydown', qr.captchaKeydown
|
||||||
|
|
||||||
|
$.append d.body, qr.el
|
||||||
|
|
||||||
message: (e) ->
|
message: (e) ->
|
||||||
Recaptcha.reload()
|
Recaptcha.reload()
|
||||||
@ -978,14 +1079,25 @@ qr =
|
|||||||
|
|
||||||
{data} = e
|
{data} = e
|
||||||
if data # error message
|
if data # error message
|
||||||
$('input[name=recaptcha_response_field]', qr.el).value = ''
|
data = JSON.parse data
|
||||||
$.extend $('#error', qr.el), JSON.parse data
|
$.extend $('#error', qr.el), data
|
||||||
qr.autohide.unset()
|
$('#recaptcha_response_field', qr.el).value = ''
|
||||||
|
$('#autohide', qr.el).checked = false
|
||||||
|
if data.textContent is 'You seem to have mistyped the verification.'
|
||||||
|
if qr.captcha.length
|
||||||
|
qr.autoPost()
|
||||||
|
else if data.textContent is 'Error: Duplicate file entry detected.' and qr.files.childElementCount
|
||||||
|
$('textarea', qr.el).value += '\n' + data.textContent + ' ' + data.href
|
||||||
|
qr.attachNext()
|
||||||
|
if qr.captcha.length
|
||||||
|
qr.autoPost()
|
||||||
return
|
return
|
||||||
|
|
||||||
if qr.el
|
if qr.el
|
||||||
if g.REPLY and conf['Persistent QR']
|
if g.REPLY and (conf['Persistent QR'] or qr.files.childElementCount)
|
||||||
qr.refresh()
|
qr.refresh()
|
||||||
|
if qr.files.childElementCount
|
||||||
|
qr.attachNext()
|
||||||
else
|
else
|
||||||
qr.close()
|
qr.close()
|
||||||
if conf['Cooldown']
|
if conf['Cooldown']
|
||||||
@ -995,11 +1107,38 @@ qr =
|
|||||||
|
|
||||||
node: (root) ->
|
node: (root) ->
|
||||||
quote = $ 'a.quotejs:not(:first-child)', root
|
quote = $ 'a.quotejs:not(:first-child)', root
|
||||||
$.bind quote, 'click', qr.cb.quote
|
$.bind quote, 'click', qr.quote
|
||||||
|
|
||||||
quote: (e) ->
|
quote: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault() if e
|
||||||
qr.quote @
|
|
||||||
|
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('preceding::input[@type="checkbox"][1]', selection.anchorNode)?.name
|
||||||
|
if selectionID == id
|
||||||
|
s = s.replace /\n/g, '\n>'
|
||||||
|
text += ">#{s}\n"
|
||||||
|
|
||||||
|
ta = $ 'textarea', qr.el
|
||||||
|
ta.focus()
|
||||||
|
ta.value += text
|
||||||
|
|
||||||
|
refresh: ->
|
||||||
|
auto = $('#auto', qr.el).checked
|
||||||
|
$('form', qr.el).reset()
|
||||||
|
$('#auto', qr.el).checked = auto
|
||||||
|
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
|
||||||
|
|
||||||
submit: (e) ->
|
submit: (e) ->
|
||||||
if conf['Auto Watch Reply'] and conf['Thread Watcher']
|
if conf['Auto Watch Reply'] and conf['Thread Watcher']
|
||||||
@ -1007,7 +1146,7 @@ qr =
|
|||||||
watcher.watch null, g.THREAD_ID
|
watcher.watch null, g.THREAD_ID
|
||||||
else
|
else
|
||||||
id = $('input[name=resto]', qr.el).value
|
id = $('input[name=resto]', qr.el).value
|
||||||
op = d.getElementById id
|
op = $.id id
|
||||||
if $('img.favicon', op).src is Favicon.empty
|
if $('img.favicon', op).src is Favicon.empty
|
||||||
watcher.watch op, id
|
watcher.watch op, id
|
||||||
|
|
||||||
@ -1024,84 +1163,9 @@ qr =
|
|||||||
else if isQR
|
else if isQR
|
||||||
if !e then @submit()
|
if !e then @submit()
|
||||||
$('#error', qr.el).textContent = ''
|
$('#error', qr.el).textContent = ''
|
||||||
qr.autohide.set()
|
$('#autohide', qr.el).checked = true if conf['Auto Hide QR']
|
||||||
qr.sage = /sage/i.test $('input[name=email]', @).value
|
qr.sage = /sage/i.test $('input[name=email]', @).value
|
||||||
|
|
||||||
quote: (link) ->
|
|
||||||
if qr.el
|
|
||||||
qr.autohide.unset()
|
|
||||||
else
|
|
||||||
qr.dialog link
|
|
||||||
|
|
||||||
id = link.textContent
|
|
||||||
text = ">>#{id}\n"
|
|
||||||
|
|
||||||
selection = window.getSelection()
|
|
||||||
if s = selection.toString()
|
|
||||||
selectionID = $.x('preceding::input[@type="checkbox"][1]', selection.anchorNode)?.name
|
|
||||||
if selectionID == id
|
|
||||||
s = s.replace /\n/g, '\n>'
|
|
||||||
text += ">#{s}\n"
|
|
||||||
|
|
||||||
ta = $ 'textarea', qr.el
|
|
||||||
ta.focus()
|
|
||||||
ta.value += text
|
|
||||||
|
|
||||||
refresh: ->
|
|
||||||
$('form', qr.el).reset()
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
spoiler = if $('.postarea label') then '<label> [<input type=checkbox name=spoiler>Spoiler Image?]</label>' else ''
|
|
||||||
challenge = $('input[name=recaptcha_challenge_field]').value
|
|
||||||
html = "
|
|
||||||
<div class=move>
|
|
||||||
<input class=inputtext type=text name=name placeholder=Name form=qr_form>
|
|
||||||
Quick Reply
|
|
||||||
<input type=checkbox id=autohide title=autohide>
|
|
||||||
<a name=close title=close>X</a>
|
|
||||||
</div>
|
|
||||||
<form name=post action=http://sys.4chan.org/#{g.BOARD}/post method=POST enctype=multipart/form-data target=iframe id=qr_form>
|
|
||||||
<input type=hidden name=resto value=#{THREAD_ID}>
|
|
||||||
<input type=hidden name=recaptcha_challenge_field value=#{challenge}>
|
|
||||||
<div><input class=inputtext type=text name=email placeholder=E-mail>#{spoiler}</div>
|
|
||||||
<div><input class=inputtext type=text name=sub placeholder=Subject><input type=submit value=#{submitValue} id=com_submit #{submitDisabled}><label><input type=checkbox id=auto>auto</label></div>
|
|
||||||
<div><textarea class=inputtext name=com placeholder=Comment></textarea></div>
|
|
||||||
<div><img src=http://www.google.com/recaptcha/api/image?c=#{challenge}></div>
|
|
||||||
<div><input class=inputtext type=text name=recaptcha_response_field placeholder=Verification required autocomplete=off></div>
|
|
||||||
<div><input type=file name=upfile></div>
|
|
||||||
<div><input class=inputtext type=password name=pwd maxlength=8 placeholder=Password><input type=hidden name=mode value=regist></div>
|
|
||||||
</form>
|
|
||||||
<a id=error class=error></a>
|
|
||||||
"
|
|
||||||
qr.el = ui.dialog 'qr', top: '0px', left: '0px', html
|
|
||||||
|
|
||||||
qr.refresh()
|
|
||||||
|
|
||||||
$.bind $('input[name=name]', qr.el), 'mousedown', (e) -> e.stopPropagation()
|
|
||||||
$.bind $('#autohide', qr.el), 'click', qr.cb.autohide
|
|
||||||
$.bind $('a[name=close]', qr.el), 'click', qr.close
|
|
||||||
$.bind $('form', qr.el), 'submit', qr.submit
|
|
||||||
$.bind $('img', qr.el), 'click', Recaptcha.reload
|
|
||||||
$.bind $('input[name=recaptcha_response_field]', qr.el), 'keydown', Recaptcha.listener
|
|
||||||
|
|
||||||
$.append d.body, qr.el
|
|
||||||
|
|
||||||
persist: ->
|
|
||||||
qr.dialog()
|
|
||||||
qr.autohide.set()
|
|
||||||
|
|
||||||
close: ->
|
|
||||||
$.rm qr.el
|
|
||||||
qr.el = null
|
|
||||||
|
|
||||||
sys: ->
|
sys: ->
|
||||||
if recaptcha = $ '#recaptcha_response_field' #post reporting
|
if recaptcha = $ '#recaptcha_response_field' #post reporting
|
||||||
$.bind recaptcha, 'keydown', Recaptcha.listener
|
$.bind recaptcha, 'keydown', Recaptcha.listener
|
||||||
@ -1126,23 +1190,26 @@ qr =
|
|||||||
if c.nodeType is 8 #comment node
|
if c.nodeType is 8 #comment node
|
||||||
[_, thread, id] = c.textContent.match(/thread:(\d+),no:(\d+)/)
|
[_, thread, id] = c.textContent.match(/thread:(\d+),no:(\d+)/)
|
||||||
|
|
||||||
noko = /auto_noko/.test location.search
|
{search} = location
|
||||||
if thread is '0'
|
cooldown = /cooldown/.test search
|
||||||
if /auto_watch/.test location.search
|
noko = /noko/ .test search
|
||||||
window.location = "http://boards.4chan.org/#{g.BOARD}/res/#{id}#watch"
|
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
|
else if noko
|
||||||
window.location = "http://boards.4chan.org/#{g.BOARD}/res/#{id}"
|
url += '/res/'
|
||||||
else if /cooldown/.test location.search
|
url += if thread is '0' then id else thread
|
||||||
duration = Date.now() + 30000
|
if cooldown
|
||||||
duration += 30000 if /sage/.test location.search
|
duration = Date.now() + (if sage then 60 else 30) * 1000
|
||||||
|
url += '?cooldown=' + duration
|
||||||
if noko
|
if noko
|
||||||
window.location = "http://boards.4chan.org/#{g.BOARD}/res/#{thread}?cooldown=#{duration}##{id}"
|
url += '#' + id
|
||||||
else
|
|
||||||
window.location = "http://boards.4chan.org/#{g.BOARD}?cooldown=#{duration}"
|
window.location = url
|
||||||
else if noko
|
|
||||||
window.location = "http://boards.4chan.org/#{g.BOARD}/res/#{thread}##{id}"
|
|
||||||
else
|
|
||||||
window.location = "http://boards.4chan.org/#{g.BOARD}"
|
|
||||||
|
|
||||||
threading =
|
threading =
|
||||||
init: ->
|
init: ->
|
||||||
@ -1254,6 +1321,9 @@ threadHiding =
|
|||||||
|
|
||||||
updater =
|
updater =
|
||||||
init: ->
|
init: ->
|
||||||
|
if conf['Scrolling']
|
||||||
|
$.bind window, 'focus', (-> updater.focus = true)
|
||||||
|
$.bind window, 'blur', (-> updater.focus = false)
|
||||||
html = "<div class=move><span id=count></span> <span id=timer>-#{conf['Interval']}</span></div>"
|
html = "<div class=move><span id=count></span> <span id=timer>-#{conf['Interval']}</span></div>"
|
||||||
{checkbox} = config.updater
|
{checkbox} = config.updater
|
||||||
for name of checkbox
|
for name of checkbox
|
||||||
@ -1329,7 +1399,7 @@ updater =
|
|||||||
while (reply = replies.pop()) and (reply.id > id)
|
while (reply = replies.pop()) and (reply.id > id)
|
||||||
arr.push reply.parentNode.parentNode.parentNode #table
|
arr.push reply.parentNode.parentNode.parentNode #table
|
||||||
|
|
||||||
scroll = conf['Scrolling'] && arr.length && (d.body.scrollHeight - d.body.clientHeight - window.scrollY < 20)
|
scroll = conf['Scrolling'] && updater.focus && arr.length && (d.body.scrollHeight - d.body.clientHeight - window.scrollY < 20)
|
||||||
|
|
||||||
updater.timer.textContent = '-' + conf['Interval']
|
updater.timer.textContent = '-' + conf['Interval']
|
||||||
if conf['Verbose']
|
if conf['Verbose']
|
||||||
@ -1499,9 +1569,10 @@ Time =
|
|||||||
$.replace s, time
|
$.replace s, time
|
||||||
foo: ->
|
foo: ->
|
||||||
code = conf['time'].replace /%([A-Za-z])/g, (s, c) ->
|
code = conf['time'].replace /%([A-Za-z])/g, (s, c) ->
|
||||||
switch c
|
if c of Time.formatters
|
||||||
when 'a', 'A', 'b', 'B', 'd', 'H', 'I', 'm', 'M', 'p', 'P', 'y' then "' + Time.#{c}() + '"
|
"' + Time.formatters.#{c}() + '"
|
||||||
else s
|
else
|
||||||
|
s
|
||||||
Time.funk = Function 'Time', "return '#{code}'"
|
Time.funk = Function 'Time', "return '#{code}'"
|
||||||
day: [
|
day: [
|
||||||
'Sunday'
|
'Sunday'
|
||||||
@ -1527,18 +1598,22 @@ Time =
|
|||||||
'December'
|
'December'
|
||||||
]
|
]
|
||||||
zeroPad: (n) -> if n < 10 then '0' + n else n
|
zeroPad: (n) -> if n < 10 then '0' + n else n
|
||||||
a: -> @day[@date.getDay()][...3]
|
formatters:
|
||||||
A: -> @day[@date.getDay()]
|
a: -> Time.day[Time.date.getDay()][...3]
|
||||||
b: -> @month[@date.getMonth()][...3]
|
A: -> Time.day[Time.date.getDay()]
|
||||||
B: -> @month[@date.getMonth()]
|
b: -> Time.month[Time.date.getMonth()][...3]
|
||||||
d: -> @zeroPad @date.getDate()
|
B: -> Time.month[Time.date.getMonth()]
|
||||||
H: -> @zeroPad @date.getHours()
|
d: -> Time.zeroPad Time.date.getDate()
|
||||||
I: -> @zeroPad @date.getHours() % 12 or 12
|
e: -> Time.date.getDate()
|
||||||
m: -> @zeroPad @date.getMonth() + 1
|
H: -> Time.zeroPad Time.date.getHours()
|
||||||
M: -> @zeroPad @date.getMinutes()
|
I: -> Time.zeroPad Time.date.getHours() % 12 or 12
|
||||||
p: -> if @date.getHours() < 12 then 'AM' else 'PM'
|
k: -> Time.date.getHours()
|
||||||
P: -> if @date.getHours() < 12 then 'am' else 'pm'
|
l: -> Time.date.getHours() % 12 or 12
|
||||||
y: -> @date.getFullYear() - 2000
|
m: -> Time.zeroPad Time.date.getMonth() + 1
|
||||||
|
M: -> Time.zeroPad Time.date.getMinutes()
|
||||||
|
p: -> if Time.date.getHours() < 12 then 'AM' else 'PM'
|
||||||
|
P: -> if Time.date.getHours() < 12 then 'am' else 'pm'
|
||||||
|
y: -> Time.date.getFullYear() - 2000
|
||||||
|
|
||||||
titlePost =
|
titlePost =
|
||||||
init: ->
|
init: ->
|
||||||
@ -1558,7 +1633,7 @@ quoteBacklink =
|
|||||||
#duplicate quotes get overwritten
|
#duplicate quotes get overwritten
|
||||||
quotes[qid] = quote
|
quotes[qid] = quote
|
||||||
for qid of quotes
|
for qid of quotes
|
||||||
continue unless el = d.getElementById qid
|
continue unless el = $.id qid
|
||||||
#don't backlink the op
|
#don't backlink the op
|
||||||
continue if !conf['OP Backlinks'] and el.className is 'op'
|
continue if !conf['OP Backlinks'] and el.className is 'op'
|
||||||
link = $.el 'a',
|
link = $.el 'a',
|
||||||
@ -1585,30 +1660,19 @@ quoteInline =
|
|||||||
quote.removeAttribute 'onclick'
|
quote.removeAttribute 'onclick'
|
||||||
$.bind quote, 'click', quoteInline.toggle
|
$.bind quote, 'click', quoteInline.toggle
|
||||||
toggle: (e) ->
|
toggle: (e) ->
|
||||||
|
return if e.shiftKey or e.altKey or e.ctrlKey or e.button isnt 0
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
###
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=674955
|
|
||||||
`mouseout` does not fire when element removed
|
|
||||||
RESOLVED INVALID
|
|
||||||
|
|
||||||
inline a post, then hover over an inlined quote / image, then remove
|
|
||||||
the inlined post by clicking `enter` on the still-focused link - the
|
|
||||||
mouseout event doesn't fire, and the quote preview / image hover remains.
|
|
||||||
|
|
||||||
we can prevent this sequence by `blur`-ing the clicked links. chrome
|
|
||||||
doesn't focus clicked links anyway.
|
|
||||||
###
|
|
||||||
@blur()
|
|
||||||
id = @hash[1..]
|
id = @hash[1..]
|
||||||
if table = $ "#i#{id}", $.x 'ancestor::td[1]', @
|
if table = $ "#i#{id}", $.x 'ancestor::td[1]', @
|
||||||
$.rm table
|
$.rm table
|
||||||
$.removeClass @, 'inlined'
|
$.removeClass @, 'inlined'
|
||||||
for inlined in $$ 'input', table
|
for inlined in $$ 'input', table
|
||||||
if hidden = d.getElementById inlined.name
|
if hidden = $.id inlined.name
|
||||||
$.show $.x 'ancestor::table[1]', hidden
|
$.show $.x 'ancestor::table[1]', hidden
|
||||||
return
|
return
|
||||||
root = if @parentNode.nodeName is 'FONT' then @parentNode else if @nextSibling then @nextSibling else @
|
root = if @parentNode.nodeName is 'FONT' then @parentNode else if @nextSibling then @nextSibling else @
|
||||||
if el = d.getElementById id
|
if el = $.id id
|
||||||
inline = quoteInline.table id, el.innerHTML
|
inline = quoteInline.table id, el.innerHTML
|
||||||
if @className is 'backlink'
|
if @className is 'backlink'
|
||||||
return if $("a.backlink[href='##{id}']", el)
|
return if $("a.backlink[href='##{id}']", el)
|
||||||
@ -1670,7 +1734,7 @@ quotePreview =
|
|||||||
$.append d.body, qp
|
$.append d.body, qp
|
||||||
|
|
||||||
id = @hash[1..]
|
id = @hash[1..]
|
||||||
if el = d.getElementById id
|
if el = $.id id
|
||||||
qp.innerHTML = el.innerHTML
|
qp.innerHTML = el.innerHTML
|
||||||
$.addClass el, 'qphl' if conf['Quote Highlighting']
|
$.addClass el, 'qphl' if conf['Quote Highlighting']
|
||||||
if /backlink/.test @className
|
if /backlink/.test @className
|
||||||
@ -1683,7 +1747,7 @@ quotePreview =
|
|||||||
threadID = @pathname.split('/').pop() or $.x('ancestor::div[@class="thread"]/div', @).id
|
threadID = @pathname.split('/').pop() or $.x('ancestor::div[@class="thread"]/div', @).id
|
||||||
$.cache @pathname, (-> quotePreview.parse @, id, threadID)
|
$.cache @pathname, (-> quotePreview.parse @, id, threadID)
|
||||||
mouseout: ->
|
mouseout: ->
|
||||||
$.removeClass el, 'qphl' if el = d.getElementById @hash[1..]
|
$.removeClass el, 'qphl' if el = $.id @hash[1..]
|
||||||
ui.hoverend()
|
ui.hoverend()
|
||||||
parse: (req, id, threadID) ->
|
parse: (req, id, threadID) ->
|
||||||
return unless (qp = ui.el) and (qp.innerHTML is "Loading #{id}...")
|
return unless (qp = ui.el) and (qp.innerHTML is "Loading #{id}...")
|
||||||
@ -1767,6 +1831,7 @@ unread =
|
|||||||
Favicon.update()
|
Favicon.update()
|
||||||
|
|
||||||
scroll: (e) ->
|
scroll: (e) ->
|
||||||
|
updater.focus = true
|
||||||
height = d.body.clientHeight
|
height = d.body.clientHeight
|
||||||
for reply, i in unread.replies
|
for reply, i in unread.replies
|
||||||
{bottom} = reply.getBoundingClientRect()
|
{bottom} = reply.getBoundingClientRect()
|
||||||
@ -1826,21 +1891,12 @@ Recaptcha =
|
|||||||
#hack to tab from comment straight to recaptcha
|
#hack to tab from comment straight to recaptcha
|
||||||
for el in $$ '#recaptcha_table a'
|
for el in $$ '#recaptcha_table a'
|
||||||
el.tabIndex = 1
|
el.tabIndex = 1
|
||||||
$.bind $('#recaptcha_challenge_field_holder'), 'DOMNodeInserted', Recaptcha.reloaded
|
|
||||||
$.bind $('#recaptcha_response_field'), 'keydown', Recaptcha.listener
|
$.bind $('#recaptcha_response_field'), 'keydown', Recaptcha.listener
|
||||||
listener: (e) ->
|
listener: (e) ->
|
||||||
if e.keyCode is 8 and @value is '' # backspace to reload
|
if e.keyCode is 8 and @value is '' # backspace to reload
|
||||||
Recaptcha.reload()
|
Recaptcha.reload()
|
||||||
if e.keyCode is 13 and cooldown.duration # press enter to enable auto-post if cooldown is still running
|
|
||||||
$('#auto', qr.el).checked = true
|
|
||||||
qr.autohide.set()
|
|
||||||
reload: ->
|
reload: ->
|
||||||
window.location = 'javascript:Recaptcha.reload()'
|
window.location = 'javascript:Recaptcha.reload()'
|
||||||
reloaded: (e) ->
|
|
||||||
return unless qr.el
|
|
||||||
{target} = e
|
|
||||||
$('img', qr.el).src = "http://www.google.com/recaptcha/api/image?c=" + target.value
|
|
||||||
$('input[name=recaptcha_challenge_field]', qr.el).value = target.value
|
|
||||||
|
|
||||||
nodeInserted = (e) ->
|
nodeInserted = (e) ->
|
||||||
{target} = e
|
{target} = e
|
||||||
@ -2090,17 +2146,23 @@ main =
|
|||||||
|
|
||||||
$.addStyle main.css
|
$.addStyle main.css
|
||||||
|
|
||||||
if (form = $ 'form[name=post]') and canPost = !!$ '#recaptcha_response_field'
|
#recaptcha may be blocked, eg by noscript
|
||||||
|
if (form = $ 'form[name=post]') and (canPost = !!$ '#recaptcha_response_field')
|
||||||
Recaptcha.init()
|
Recaptcha.init()
|
||||||
$.bind form, 'submit', qr.submit
|
$.bind form, 'submit', qr.submit
|
||||||
|
|
||||||
#major features
|
#major features
|
||||||
threading.init()
|
threading.init()
|
||||||
|
|
||||||
if conf['Auto Noko']
|
# scroll to bottom if post isn't found
|
||||||
$('.postarea form').action += '?auto_noko'
|
# thumbnail generation takes time
|
||||||
|
if g.REPLY and (id = location.hash[1..]) and /\d/.test(id[0]) and !$.id(id)
|
||||||
|
scrollTo 0, d.body.scrollHeight
|
||||||
|
|
||||||
if conf['Cooldown']
|
if conf['Auto Noko'] and canPost
|
||||||
|
form.action += '?noko'
|
||||||
|
|
||||||
|
if conf['Cooldown'] and canPost
|
||||||
cooldown.init()
|
cooldown.init()
|
||||||
|
|
||||||
if conf['Image Expansion']
|
if conf['Image Expansion']
|
||||||
@ -2127,7 +2189,7 @@ main =
|
|||||||
if conf['Reply Hiding']
|
if conf['Reply Hiding']
|
||||||
replyHiding.init()
|
replyHiding.init()
|
||||||
|
|
||||||
if canPost and conf['Quick Reply']
|
if conf['Quick Reply'] and canPost
|
||||||
qr.init()
|
qr.init()
|
||||||
|
|
||||||
if conf['Report Button']
|
if conf['Report Button']
|
||||||
@ -2158,8 +2220,10 @@ main =
|
|||||||
if conf['Image Preloading']
|
if conf['Image Preloading']
|
||||||
imgPreloading.init()
|
imgPreloading.init()
|
||||||
|
|
||||||
if conf['Quick Reply'] and conf['Persistent QR']
|
if conf['Quick Reply'] and conf['Persistent QR'] and canPost
|
||||||
qr.persist()
|
qr.dialog()
|
||||||
|
if conf['Auto Hide QR']
|
||||||
|
$('#autohide', qr.el).checked = true
|
||||||
|
|
||||||
if conf['Post in Title']
|
if conf['Post in Title']
|
||||||
titlePost.init()
|
titlePost.init()
|
||||||
@ -2174,7 +2238,7 @@ main =
|
|||||||
nav.init()
|
nav.init()
|
||||||
|
|
||||||
if conf['Auto Watch'] and conf['Thread Watcher'] and
|
if conf['Auto Watch'] and conf['Thread Watcher'] and
|
||||||
location.hash is '#watch' and $('img.favicon').src is Favicon.empty
|
/watch/.test(location.search) and $('img.favicon').src is Favicon.empty
|
||||||
watcher.watch null, g.THREAD_ID
|
watcher.watch null, g.THREAD_ID
|
||||||
|
|
||||||
else #not reply
|
else #not reply
|
||||||
@ -2191,7 +2255,7 @@ main =
|
|||||||
expandComment.init()
|
expandComment.init()
|
||||||
|
|
||||||
if conf['Auto Watch']
|
if conf['Auto Watch']
|
||||||
$('.postarea form').action += '?auto_watch'
|
$('.postarea form').action += '?watch'
|
||||||
|
|
||||||
for op in $$ 'div.op'
|
for op in $$ 'div.op'
|
||||||
for callback in g.callbacks
|
for callback in g.callbacks
|
||||||
@ -2303,6 +2367,9 @@ main =
|
|||||||
|
|
||||||
#qr {
|
#qr {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
max-height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
#qr > div.move {
|
#qr > div.move {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@ -2320,7 +2387,10 @@ main =
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
}
|
}
|
||||||
#qr.auto:not(:hover) > form {
|
#qr #close, #qr #autohide {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
#qr:not(:hover) > #autohide:checked ~ form {
|
||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@ -2389,6 +2459,10 @@ main =
|
|||||||
[hidden] {
|
[hidden] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#files > input {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
main.init()
|
main.init()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user