Merge @ccd0's capcha changes up to ccd0/4chan-x@bb806b453
This commit is contained in:
parent
29da4807e4
commit
168aa98096
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* appchan x - Version 2.9.40 - 2014-12-09
|
||||
* appchan x - Version 2.9.40 - 2014-12-12
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
// ==/UserScript==
|
||||
|
||||
/*
|
||||
* appchan x - Version 2.9.40 - 2014-12-09
|
||||
* appchan x - Version 2.9.40 - 2014-12-12
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||
@ -8819,6 +8819,9 @@
|
||||
return QR.captcha.destroy();
|
||||
},
|
||||
focusin: function() {
|
||||
if ($.hasClass(QR.nodes.el, 'autohide') && !$.hasClass(QR.nodes.el, 'focus')) {
|
||||
QR.captcha.setup();
|
||||
}
|
||||
return $.addClass(QR.nodes.el, 'focus');
|
||||
},
|
||||
focusout: function() {
|
||||
@ -8850,7 +8853,7 @@
|
||||
el.removeAttribute('style');
|
||||
}
|
||||
if (QR.captcha.isEnabled && /captcha|verification/i.test(el.textContent)) {
|
||||
QR.captcha.setup();
|
||||
QR.captcha.setup(true);
|
||||
}
|
||||
QR.notify(el);
|
||||
if (d.hidden) {
|
||||
@ -9007,6 +9010,9 @@
|
||||
},
|
||||
paste: function(e) {
|
||||
var blob, files, item, _i, _len, _ref;
|
||||
if (!e.clipboardData.items) {
|
||||
return;
|
||||
}
|
||||
files = [];
|
||||
_ref = e.clipboardData.items;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
@ -9028,45 +9034,16 @@
|
||||
QR.handleFiles(files);
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
},
|
||||
handleBlob: function(urlBlob, contentType, contentDisposition, url) {
|
||||
var blob, match, mime, name, _ref, _ref1, _ref2;
|
||||
name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0;
|
||||
mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
|
||||
match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0);
|
||||
if (match) {
|
||||
name = match.replace(/\\"/g, '"');
|
||||
}
|
||||
blob = new Blob([urlBlob], {
|
||||
type: mime
|
||||
});
|
||||
blob.name = name;
|
||||
return QR.handleFiles([blob]);
|
||||
},
|
||||
handleUrl: function() {
|
||||
var url;
|
||||
url = prompt("Enter a URL:");
|
||||
url = prompt('Enter a URL:');
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
return GM_xmlhttpRequest({
|
||||
method: "GET",
|
||||
url: url,
|
||||
overrideMimeType: "text/plain; charset=x-user-defined",
|
||||
onload: function(xhr) {
|
||||
var contentDisposition, contentType, data, h, i, r;
|
||||
r = xhr.responseText;
|
||||
h = xhr.responseHeaders;
|
||||
data = new Uint8Array(r.length);
|
||||
i = 0;
|
||||
while (i < r.length) {
|
||||
data[i] = r.charCodeAt(i);
|
||||
i++;
|
||||
}
|
||||
contentType = (h.match(/Content-Type:\s*(.*)/i) || [])[1];
|
||||
contentDisposition = (h.match(/Content-Disposition:\s*(.*)/i) || [])[1];
|
||||
return QR.handleBlob(data, contentType, contentDisposition, url);
|
||||
},
|
||||
onerror: function(xhr) {
|
||||
return CrossOrigin.file(url, function(blob) {
|
||||
if (blob) {
|
||||
return QR.handleFiles([blob]);
|
||||
} else {
|
||||
return QR.error("Can't load image.");
|
||||
}
|
||||
});
|
||||
@ -9090,12 +9067,12 @@
|
||||
}
|
||||
},
|
||||
handleFile: function(file, index, nfiles) {
|
||||
var isSingle, max, post, _ref;
|
||||
var err, isSingle, max, post, _ref;
|
||||
isSingle = nfiles === 1;
|
||||
if (/^text\//.test(file.type)) {
|
||||
if (isSingle) {
|
||||
post = QR.selected;
|
||||
} else if ((post = QR.posts[QR.posts.length - 1]).com) {
|
||||
} else if (index !== 0 || (post = QR.posts[QR.posts.length - 1]).com) {
|
||||
post = new QR.post();
|
||||
}
|
||||
post.pasteText(file);
|
||||
@ -9118,7 +9095,12 @@
|
||||
} else if ((post = QR.posts[QR.posts.length - 1]).file) {
|
||||
post = new QR.post();
|
||||
}
|
||||
return post.setFile(file);
|
||||
try {
|
||||
return post.setFile(file);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return console.log(err);
|
||||
}
|
||||
},
|
||||
openFileInput: function(e) {
|
||||
var _ref;
|
||||
@ -9126,10 +9108,12 @@
|
||||
if (e.shiftKey && e.type === 'click') {
|
||||
return QR.selected.rmFile();
|
||||
}
|
||||
if (e.ctrlKey && e.type === 'click') {
|
||||
if ((e.ctrlKey || e.metaKey) && e.type === 'click') {
|
||||
$.addClass(QR.nodes.filename, 'edit');
|
||||
QR.nodes.filename.focus();
|
||||
return;
|
||||
return $.on(QR.nodes.filename, 'blur', function() {
|
||||
return $.rmClass(QR.nodes.filename, 'edit');
|
||||
});
|
||||
}
|
||||
if (e.target.nodeName === 'INPUT' || (e.keyCode && ((_ref = e.keyCode) !== 32 && _ref !== 13)) || e.ctrlKey) {
|
||||
return;
|
||||
@ -9165,7 +9149,7 @@
|
||||
}
|
||||
},
|
||||
dialog: function() {
|
||||
var dialog, elm, event, i, items, name, node, nodes, prop, rules, save, setNode, _, _i, _len, _ref, _ref1, _ref2;
|
||||
var dialog, elm, event, i, items, match_max, match_min, name, node, nodes, rules, save, setNode;
|
||||
QR.nodes = nodes = {
|
||||
el: dialog = UI.dialog('qr', 'top:0;right:0;', "<div id=qrtab class=move><input type=checkbox id=autohide title=Auto-hide><div id=qr-thread-select><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><a href=javascript:; class='close fa' title=Close>\uf00d</a></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1><input name=email data-name=email list=\"list-email\" placeholder=Options class=field size=1><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1> </div><div class=textarea><textarea data-name=com placeholder=Comment class=field></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\">+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=0><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image'></label><span class=description>Spoiler</span><a id=url-button><i class=\"fa\">\uf0c1</i></a><span class=description>Post from URL</span><a id=dump-button title='Dump list'>+</a><span class=description>Dump</span><a id=qr-filerm href=javascript:; title='Remove file' class=fa>\uf00d</a><span class=description>Remove File</span></span></span><input type=submit></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>")
|
||||
};
|
||||
@ -9197,23 +9181,20 @@
|
||||
setNode('status', '[type=submit]');
|
||||
setNode('fileInput', '[type=file]');
|
||||
rules = $('ul.rules').textContent.trim();
|
||||
QR.min_width = QR.min_height = 1;
|
||||
QR.max_width = QR.max_height = 10000;
|
||||
try {
|
||||
_ref = rules.match(/.+smaller than (\d+)x(\d+).+/), _ = _ref[0], QR.min_width = _ref[1], QR.min_height = _ref[2];
|
||||
_ref1 = rules.match(/.+greater than (\d+)x(\d+).+/), _ = _ref1[0], QR.max_width = _ref1[1], QR.max_height = _ref1[2];
|
||||
_ref2 = ['min_width', 'min_height', 'max_width', 'max_height'];
|
||||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
||||
prop = _ref2[_i];
|
||||
QR[prop] = parseInt(QR[prop], 10);
|
||||
}
|
||||
} catch (_error) {
|
||||
null;
|
||||
}
|
||||
match_min = rules.match(/.+smaller than (\d+)x(\d+).+/);
|
||||
match_max = rules.match(/.+greater than (\d+)x(\d+).+/);
|
||||
QR.min_width = +(match_min != null ? match_min[1] : void 0) || 1;
|
||||
QR.min_height = +(match_min != null ? match_min[2] : void 0) || 1;
|
||||
QR.max_width = +(match_max != null ? match_max[1] : void 0) || 10000;
|
||||
QR.max_height = +(match_max != null ? match_max[2] : void 0) || 10000;
|
||||
nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value;
|
||||
QR.max_size_video = 3145728;
|
||||
QR.max_width_video = QR.max_height_video = 2048;
|
||||
QR.max_duration_video = 120;
|
||||
QR.forcedAnon = !!$('form[name="post"] input[name="name"][type="hidden"]');
|
||||
if (QR.forcedAnon) {
|
||||
$.addClass(QR.nodes.el, 'forced-anon');
|
||||
}
|
||||
QR.spoiler = !!$('.postForm input[name=spoiler]');
|
||||
if (QR.spoiler) {
|
||||
$.addClass(QR.nodes.el, 'has-spoiler');
|
||||
@ -9224,9 +9205,10 @@
|
||||
$.after(nodes.name.parentElement, nodes.dumpList.parentElement);
|
||||
nodes.addPost.tabIndex = 35;
|
||||
}
|
||||
if (g.BOARD.ID === 'f') {
|
||||
if (g.BOARD.ID === 'f' && g.VIEW !== 'thread') {
|
||||
nodes.flashTag = $.el('select', {
|
||||
name: 'filetag',
|
||||
name: 'filetag'
|
||||
}, {
|
||||
innerHTML: "<option value=0>Hentai</option>\n<option value=6>Porn</option>\n<option value=1>Japanese</option>\n<option value=2>Anime</option>\n<option value=3>Game</option>\n<option value=5>Loop</option>\n<option value=4 selected>Other</option>"
|
||||
});
|
||||
nodes.flashTag.dataset["default"] = '4';
|
||||
@ -9413,9 +9395,9 @@
|
||||
post.lock();
|
||||
formData = {
|
||||
resto: threadID,
|
||||
name: post.name,
|
||||
name: !QR.forcedAnon ? post.name : void 0,
|
||||
email: post.email,
|
||||
sub: post.sub,
|
||||
sub: !(QR.forcedAnon || threadID) ? post.sub : void 0,
|
||||
com: post.com,
|
||||
upfile: post.file,
|
||||
filetag: filetag,
|
||||
@ -9502,9 +9484,6 @@
|
||||
}
|
||||
QR.status();
|
||||
QR.error(err);
|
||||
if (QR.captcha.isEnabled) {
|
||||
QR.captcha.setup();
|
||||
}
|
||||
return;
|
||||
}
|
||||
h1 = $('h1', resDoc);
|
||||
@ -9555,7 +9534,7 @@
|
||||
QR.close();
|
||||
} else {
|
||||
post.rm();
|
||||
QR.captcha.setup();
|
||||
QR.captcha.setup(true);
|
||||
}
|
||||
QR.cooldown.set({
|
||||
req: req,
|
||||
@ -9607,7 +9586,7 @@
|
||||
|
||||
QR.captcha = {
|
||||
init: function() {
|
||||
var container, counter, section;
|
||||
var counter, root;
|
||||
if (d.cookie.indexOf('pass_enabled=1') >= 0) {
|
||||
return;
|
||||
}
|
||||
@ -9621,80 +9600,101 @@
|
||||
return QR.captcha.sync(captchas);
|
||||
});
|
||||
$.sync('captchas', this.sync.bind(this));
|
||||
section = $.el('div', {
|
||||
className: 'captcha-section'
|
||||
root = $.el('div', {
|
||||
className: 'captcha-root'
|
||||
});
|
||||
$.extend(section, {
|
||||
innerHTML: "<div class=\"captcha-container\"></div><div class=\"captcha-counter\"><a href=\"javascript:;\"></a></div>"
|
||||
$.extend(root, {
|
||||
innerHTML: "<div class=\"captcha-counter\"><a href=\"javascript:;\"></a></div>"
|
||||
});
|
||||
container = $('.captcha-container', section);
|
||||
counter = $('.captcha-counter > a', section);
|
||||
counter = $('.captcha-counter > a', root);
|
||||
this.nodes = {
|
||||
container: container,
|
||||
root: root,
|
||||
counter: counter
|
||||
};
|
||||
this.count();
|
||||
$.addClass(QR.nodes.el, 'has-captcha');
|
||||
$.after(QR.nodes.com.parentNode, section);
|
||||
new MutationObserver(this.afterSetup.bind(this)).observe(container, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
$.after(QR.nodes.com.parentNode, root);
|
||||
$.on(counter, 'click', this.toggle.bind(this));
|
||||
return $.on(window, 'captcha:success', this.save.bind(this));
|
||||
return $.on(window, 'captcha:success', (function(_this) {
|
||||
return function() {
|
||||
return _this.save(false);
|
||||
};
|
||||
})(this));
|
||||
},
|
||||
shouldFocus: false,
|
||||
timeouts: {},
|
||||
postsCount: 0,
|
||||
needed: function() {
|
||||
var captchaCount, postsCount;
|
||||
var captchaCount;
|
||||
captchaCount = this.captchas.length;
|
||||
if (this.nodes.container.dataset.widgetID && !this.timeouts.destroy) {
|
||||
if (this.nodes.container && !this.timeouts.destroy) {
|
||||
captchaCount++;
|
||||
}
|
||||
postsCount = QR.posts.length;
|
||||
if (postsCount === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) {
|
||||
postsCount = 0;
|
||||
this.postsCount = QR.posts.length;
|
||||
if (this.postsCount === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) {
|
||||
this.postsCount = 0;
|
||||
}
|
||||
return captchaCount < this.postsCount;
|
||||
},
|
||||
onPostChange: function() {
|
||||
if (this.postsCount === 0) {
|
||||
this.setup();
|
||||
}
|
||||
if (QR.posts.length === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) {
|
||||
return this.postsCount = 0;
|
||||
}
|
||||
return captchaCount < postsCount;
|
||||
},
|
||||
toggle: function() {
|
||||
if (this.nodes.container.dataset.widgetID && !this.timeouts.destroy) {
|
||||
if (this.nodes.container && !this.timeouts.destroy) {
|
||||
return this.destroy();
|
||||
} else {
|
||||
this.shouldFocus = true;
|
||||
return this.setup(true);
|
||||
return this.setup(true, true);
|
||||
}
|
||||
},
|
||||
setup: function(force) {
|
||||
setup: function(focus, force) {
|
||||
if (!(this.isEnabled && (this.needed() || force))) {
|
||||
return;
|
||||
}
|
||||
$.addClass(QR.nodes.el, 'captcha-open');
|
||||
if (focus) {
|
||||
this.shouldFocus = true;
|
||||
}
|
||||
if (this.timeouts.destroy) {
|
||||
clearTimeout(this.timeouts.destroy);
|
||||
delete this.timeouts.destroy;
|
||||
return this.reload();
|
||||
}
|
||||
if (this.nodes.container.dataset.widgetID) {
|
||||
if (this.nodes.container) {
|
||||
return;
|
||||
}
|
||||
return $.globalEval('(function() {\n var container = document.querySelector("#qr .captcha-container");\n container.dataset.widgetID = window.grecaptcha.render(container, {\n sitekey: \'6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc\',\n theme: document.documentElement.classList.contains(\'tomorrow\') ? \'dark\' : \'light\',\n callback: function(response) {\n window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));\n }\n });\n})();');
|
||||
this.nodes.container = $.el('div', {
|
||||
className: 'captcha-container'
|
||||
});
|
||||
$.prepend(this.nodes.root, this.nodes.container);
|
||||
new MutationObserver(this.afterSetup.bind(this)).observe(this.nodes.container, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
return $.globalEval('(function() {\n function render() {\n var container = document.querySelector("#qr .captcha-container");\n container.dataset.widgetID = window.grecaptcha.render(container, {\n sitekey: \'6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc\',\n theme: document.documentElement.classList.contains(\'tomorrow\') ? \'dark\' : \'light\',\n callback: function(response) {\n window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));\n }\n });\n }\n if (window.grecaptcha) {\n render();\n } else {\n var cbNative = window.onRecaptchaLoaded;\n window.onRecaptchaLoaded = function() {\n render();\n cbNative();\n }\n }\n})();');
|
||||
},
|
||||
afterSetup: function(mutations) {
|
||||
var iframe, mutation, node, _i, _j, _len, _len1, _ref;
|
||||
var iframe, mutation, node, textarea, _i, _j, _len, _len1, _ref;
|
||||
for (_i = 0, _len = mutations.length; _i < _len; _i++) {
|
||||
mutation = mutations[_i];
|
||||
_ref = mutation.addedNodes;
|
||||
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
||||
node = _ref[_j];
|
||||
if (node.nodeName === 'IFRAME') {
|
||||
iframe = node;
|
||||
if (iframe = $.x('./descendant-or-self::iframe', node)) {
|
||||
this.setupIFrame(iframe);
|
||||
}
|
||||
if (textarea = $.x('./descendant-or-self::textarea', node)) {
|
||||
this.setupTextArea(textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!iframe) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
setupIFrame: function(iframe) {
|
||||
this.setupTime = Date.now();
|
||||
if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) {
|
||||
QR.nodes.el.style.top = null;
|
||||
QR.nodes.el.style.bottom = '0px';
|
||||
@ -9704,16 +9704,28 @@
|
||||
}
|
||||
return this.shouldFocus = false;
|
||||
},
|
||||
setupTextArea: function(textarea) {
|
||||
return $.one(textarea, 'input', (function(_this) {
|
||||
return function() {
|
||||
return _this.save(true);
|
||||
};
|
||||
})(this));
|
||||
},
|
||||
destroy: function() {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
delete this.timeouts.destroy;
|
||||
$.rmClass(QR.nodes.el, 'captcha-open');
|
||||
$.rmAll(this.nodes.container);
|
||||
return this.nodes.container.removeAttribute('data-widget-i-d');
|
||||
if (this.nodes.container) {
|
||||
$.rm(this.nodes.container);
|
||||
}
|
||||
return delete this.nodes.container;
|
||||
},
|
||||
sync: function(captchas) {
|
||||
if (captchas == null) {
|
||||
captchas = [];
|
||||
}
|
||||
this.captchas = captchas;
|
||||
this.clear();
|
||||
return this.count();
|
||||
@ -9729,30 +9741,39 @@
|
||||
return null;
|
||||
}
|
||||
},
|
||||
save: function(e) {
|
||||
var _base;
|
||||
if (this.needed()) {
|
||||
save: function(pasted) {
|
||||
var reload, _base;
|
||||
$.forceSync('captchas');
|
||||
reload = (QR.cooldown.auto || Conf['Post on Captcha Completion']) && this.needed();
|
||||
this.captchas.push({
|
||||
response: $('textarea', this.nodes.container).value,
|
||||
timeout: (pasted ? this.setupTime : Date.now()) + 2 * $.MINUTE
|
||||
});
|
||||
this.count();
|
||||
$.set('captchas', this.captchas);
|
||||
if (reload) {
|
||||
this.shouldFocus = true;
|
||||
this.reload();
|
||||
} else {
|
||||
this.nodes.counter.focus();
|
||||
if ((_base = this.timeouts).destroy == null) {
|
||||
_base.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND);
|
||||
if (pasted) {
|
||||
this.destroy();
|
||||
} else {
|
||||
if ((_base = this.timeouts).destroy == null) {
|
||||
_base.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND);
|
||||
}
|
||||
}
|
||||
QR.nodes.status.focus();
|
||||
}
|
||||
if (Conf['Post on Captcha Completion'] && !QR.cooldown.auto) {
|
||||
return QR.submit();
|
||||
}
|
||||
$.forceSync('captchas');
|
||||
this.captchas.push({
|
||||
response: e.detail,
|
||||
timeout: Date.now() + 2 * $.MINUTE
|
||||
});
|
||||
this.count();
|
||||
return $.set('captchas', this.captchas);
|
||||
},
|
||||
clear: function() {
|
||||
var captcha, i, now, _i, _len, _ref;
|
||||
if (!this.captchas.length) {
|
||||
return;
|
||||
}
|
||||
$.forceSync('captchas');
|
||||
now = Date.now();
|
||||
_ref = this.captchas;
|
||||
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
||||
@ -9767,7 +9788,7 @@
|
||||
this.captchas = this.captchas.slice(i);
|
||||
this.count();
|
||||
$.set('captchas', this.captchas);
|
||||
return this.setup();
|
||||
return this.setup(true);
|
||||
},
|
||||
count: function() {
|
||||
this.nodes.counter.textContent = "Captchas: " + this.captchas.length;
|
||||
@ -10004,7 +10025,7 @@
|
||||
QR.post = (function() {
|
||||
function _Class(select) {
|
||||
this.select = __bind(this.select, this);
|
||||
var el, elm, event, prev, _i, _j, _len, _len1, _ref, _ref1, _ref2;
|
||||
var el, elm, event, prev, _i, _j, _len, _len1, _ref, _ref1;
|
||||
el = $.el('a', {
|
||||
className: 'qr-preview',
|
||||
draggable: true,
|
||||
@ -10051,9 +10072,8 @@
|
||||
$.on(el, event.toLowerCase(), this[event]);
|
||||
}
|
||||
this.thread = g.VIEW === 'thread' ? g.THREADID : 'new';
|
||||
_ref2 = QR.posts, prev = _ref2[_ref2.length - 1];
|
||||
prev = QR.posts[QR.posts.length - 1];
|
||||
QR.posts.push(this);
|
||||
QR.captcha.setup();
|
||||
this.nodes.spoiler.checked = this.spoiler = prev && Conf['Remember Spoiler'] ? prev.spoiler : false;
|
||||
QR.persona.get((function(_this) {
|
||||
return function(persona) {
|
||||
@ -10072,6 +10092,9 @@
|
||||
this.select();
|
||||
}
|
||||
this.unlock();
|
||||
$.queueTask(function() {
|
||||
return QR.captcha.setup();
|
||||
});
|
||||
}
|
||||
|
||||
_Class.prototype.rm = function() {
|
||||
@ -10162,7 +10185,7 @@
|
||||
return QR.status();
|
||||
case 'com':
|
||||
this.nodes.span.textContent = this.com;
|
||||
QR.captcha.setup();
|
||||
QR.captcha.onPostChange();
|
||||
QR.characterCount();
|
||||
if (QR.cooldown.auto && this === QR.posts[0] && (0 < (_ref = QR.cooldown.seconds) && _ref <= 5)) {
|
||||
return QR.cooldown.auto = false;
|
||||
@ -10202,7 +10225,7 @@
|
||||
if (QR.spoiler) {
|
||||
this.nodes.label.hidden = false;
|
||||
}
|
||||
QR.captcha.setup();
|
||||
QR.captcha.onPostChange();
|
||||
URL.revokeObjectURL(this.URL);
|
||||
if (this === QR.selected) {
|
||||
this.showFileData();
|
||||
@ -10220,55 +10243,55 @@
|
||||
var el, fileURL, isVideo;
|
||||
isVideo = /^video\//.test(this.file.type);
|
||||
el = $.el((isVideo ? 'video' : 'img'));
|
||||
$.on(el, (isVideo ? 'loadeddata' : 'load'), (function(_this) {
|
||||
return function() {
|
||||
var cv, error, errors, height, s, width, _i, _len;
|
||||
errors = _this.checkDimensions(el, isVideo);
|
||||
if (errors.length) {
|
||||
for (_i = 0, _len = errors.length; _i < _len; _i++) {
|
||||
error = errors[_i];
|
||||
QR.error(error);
|
||||
}
|
||||
_this.URL = fileURL;
|
||||
if ((QR.posts.length === 1) || (_this.com && _this.com.length)) {
|
||||
return _this.rmFile();
|
||||
} else {
|
||||
return _this.rm();
|
||||
}
|
||||
$.on(el, (isVideo ? 'loadeddata' : 'load'), function() {
|
||||
var cv, error, errors, height, s, width, _i, _len;
|
||||
errors = this.checkDimensions(el, isVideo);
|
||||
if (errors.length) {
|
||||
for (_i = 0, _len = errors.length; _i < _len; _i++) {
|
||||
error = errors[_i];
|
||||
QR.error(error);
|
||||
}
|
||||
s = 90 * 2 * window.devicePixelRatio;
|
||||
if (_this.file.type === 'image/gif') {
|
||||
s *= 3;
|
||||
}
|
||||
if (isVideo) {
|
||||
height = el.videoHeight;
|
||||
width = el.videoWidth;
|
||||
this.URL = fileURL;
|
||||
if ((QR.posts.length === 1) || (this.com && this.com.length)) {
|
||||
return this.rmFile();
|
||||
} else {
|
||||
height = el.height, width = el.width;
|
||||
if (height < s || width < s) {
|
||||
_this.URL = fileURL;
|
||||
_this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")";
|
||||
return;
|
||||
}
|
||||
return this.rm();
|
||||
}
|
||||
if (height <= width) {
|
||||
width = s / height * width;
|
||||
height = s;
|
||||
} else {
|
||||
height = s / width * height;
|
||||
width = s;
|
||||
}
|
||||
s = 90 * 2 * window.devicePixelRatio;
|
||||
if (this.file.type === 'image/gif') {
|
||||
s *= 3;
|
||||
}
|
||||
if (isVideo) {
|
||||
height = el.videoHeight;
|
||||
width = el.videoWidth;
|
||||
} else {
|
||||
height = el.height, width = el.width;
|
||||
if (height < s || width < s) {
|
||||
this.URL = fileURL;
|
||||
this.nodes.el.style.backgroundImage = "url(" + this.URL + ")";
|
||||
return;
|
||||
}
|
||||
cv = $.el('canvas');
|
||||
cv.height = el.height = height;
|
||||
cv.width = el.width = width;
|
||||
cv.getContext('2d').drawImage(el, 0, 0, width, height);
|
||||
URL.revokeObjectURL(fileURL);
|
||||
return cv.toBlob(function(blob) {
|
||||
}
|
||||
if (height <= width) {
|
||||
width = s / height * width;
|
||||
height = s;
|
||||
} else {
|
||||
height = s / width * height;
|
||||
width = s;
|
||||
}
|
||||
cv = $.el('canvas');
|
||||
cv.height = el.height = height;
|
||||
cv.width = el.width = width;
|
||||
cv.getContext('2d').drawImage(el, 0, 0, width, height);
|
||||
URL.revokeObjectURL(fileURL);
|
||||
return cv.toBlob((function(_this) {
|
||||
return function(blob) {
|
||||
_this.URL = URL.createObjectURL(blob);
|
||||
return _this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")";
|
||||
});
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
})(this));
|
||||
});
|
||||
fileURL = URL.createObjectURL(this.file);
|
||||
return el.src = fileURL;
|
||||
};
|
||||
@ -10326,7 +10349,7 @@
|
||||
|
||||
_Class.prototype.updateFilename = function() {
|
||||
var title;
|
||||
title = "" + this.filename + " (" + this.filesize + ")\nCtrl+click to edit filename. Shift+click to clear.";
|
||||
title = "" + this.filename + " (" + this.filesize + ")\nCtrl/\u2318+click to edit filename. Shift+click to clear.";
|
||||
this.nodes.el.title = title;
|
||||
if (this !== QR.selected) {
|
||||
return;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript
|
||||
/*
|
||||
* appchan x - Version 2.9.40 - 2014-12-09
|
||||
* appchan x - Version 2.9.40 - 2014-12-12
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||
@ -8841,6 +8841,9 @@
|
||||
return QR.captcha.destroy();
|
||||
},
|
||||
focusin: function() {
|
||||
if ($.hasClass(QR.nodes.el, 'autohide') && !$.hasClass(QR.nodes.el, 'focus')) {
|
||||
QR.captcha.setup();
|
||||
}
|
||||
return $.addClass(QR.nodes.el, 'focus');
|
||||
},
|
||||
focusout: function() {
|
||||
@ -8872,7 +8875,7 @@
|
||||
el.removeAttribute('style');
|
||||
}
|
||||
if (QR.captcha.isEnabled && /captcha|verification/i.test(el.textContent)) {
|
||||
QR.captcha.setup();
|
||||
QR.captcha.setup(true);
|
||||
}
|
||||
QR.notify(el);
|
||||
if (d.hidden) {
|
||||
@ -9038,6 +9041,9 @@
|
||||
},
|
||||
paste: function(e) {
|
||||
var blob, files, item, _i, _len, _ref;
|
||||
if (!e.clipboardData.items) {
|
||||
return;
|
||||
}
|
||||
files = [];
|
||||
_ref = e.clipboardData.items;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
@ -9059,42 +9065,19 @@
|
||||
QR.handleFiles(files);
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
},
|
||||
handleBlob: function(urlBlob, contentType, contentDisposition, url) {
|
||||
var blob, match, mime, name, _ref, _ref1, _ref2;
|
||||
name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0;
|
||||
mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
|
||||
match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0);
|
||||
if (match) {
|
||||
name = match.replace(/\\"/g, '"');
|
||||
}
|
||||
blob = new Blob([urlBlob], {
|
||||
type: mime
|
||||
});
|
||||
blob.name = name;
|
||||
return QR.handleFiles([blob]);
|
||||
},
|
||||
handleUrl: function() {
|
||||
var url, xhr;
|
||||
url = prompt("Enter a URL:");
|
||||
var url;
|
||||
url = prompt('Enter a URL:');
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.onload = function(e) {
|
||||
var contentDisposition, contentType;
|
||||
if (!(this.readyState === this.DONE && xhr.status === 200)) {
|
||||
return CrossOrigin.file(url, function(blob) {
|
||||
if (blob) {
|
||||
return QR.handleFiles([blob]);
|
||||
} else {
|
||||
return QR.error("Can't load image.");
|
||||
}
|
||||
contentType = this.getResponseHeader('Content-Type');
|
||||
contentDisposition = this.getResponseHeader('Content-Disposition');
|
||||
return QR.handleBlob(this.response, contentType, contentDisposition, url);
|
||||
};
|
||||
xhr.onerror = function(e) {
|
||||
return QR.error("Can't load image.");
|
||||
};
|
||||
return xhr.send();
|
||||
});
|
||||
},
|
||||
handleFiles: function(files) {
|
||||
var file, i, _i, _len;
|
||||
@ -9115,12 +9098,12 @@
|
||||
}
|
||||
},
|
||||
handleFile: function(file, index, nfiles) {
|
||||
var isSingle, max, post, _ref;
|
||||
var err, isSingle, max, post, _ref;
|
||||
isSingle = nfiles === 1;
|
||||
if (/^text\//.test(file.type)) {
|
||||
if (isSingle) {
|
||||
post = QR.selected;
|
||||
} else if ((post = QR.posts[QR.posts.length - 1]).com) {
|
||||
} else if (index !== 0 || (post = QR.posts[QR.posts.length - 1]).com) {
|
||||
post = new QR.post();
|
||||
}
|
||||
post.pasteText(file);
|
||||
@ -9143,7 +9126,12 @@
|
||||
} else if ((post = QR.posts[QR.posts.length - 1]).file) {
|
||||
post = new QR.post();
|
||||
}
|
||||
return post.setFile(file);
|
||||
try {
|
||||
return post.setFile(file);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return console.log(err);
|
||||
}
|
||||
},
|
||||
openFileInput: function(e) {
|
||||
var _ref;
|
||||
@ -9151,10 +9139,12 @@
|
||||
if (e.shiftKey && e.type === 'click') {
|
||||
return QR.selected.rmFile();
|
||||
}
|
||||
if (e.ctrlKey && e.type === 'click') {
|
||||
if ((e.ctrlKey || e.metaKey) && e.type === 'click') {
|
||||
$.addClass(QR.nodes.filename, 'edit');
|
||||
QR.nodes.filename.focus();
|
||||
return;
|
||||
return $.on(QR.nodes.filename, 'blur', function() {
|
||||
return $.rmClass(QR.nodes.filename, 'edit');
|
||||
});
|
||||
}
|
||||
if (e.target.nodeName === 'INPUT' || (e.keyCode && ((_ref = e.keyCode) !== 32 && _ref !== 13)) || e.ctrlKey) {
|
||||
return;
|
||||
@ -9190,7 +9180,7 @@
|
||||
}
|
||||
},
|
||||
dialog: function() {
|
||||
var dialog, elm, event, i, items, name, node, nodes, prop, rules, save, setNode, _, _i, _len, _ref, _ref1, _ref2;
|
||||
var dialog, elm, event, i, items, match_max, match_min, name, node, nodes, rules, save, setNode;
|
||||
QR.nodes = nodes = {
|
||||
el: dialog = UI.dialog('qr', 'top:0;right:0;', "<div id=qrtab class=move><input type=checkbox id=autohide title=Auto-hide><div id=qr-thread-select><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><a href=javascript:; class='close fa' title=Close>\uf00d</a></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1><input name=email data-name=email list=\"list-email\" placeholder=Options class=field size=1><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1> </div><div class=textarea><textarea data-name=com placeholder=Comment class=field></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\">+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=0><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image'></label><span class=description>Spoiler</span><a id=url-button><i class=\"fa\">\uf0c1</i></a><span class=description>Post from URL</span><a id=dump-button title='Dump list'>+</a><span class=description>Dump</span><a id=qr-filerm href=javascript:; title='Remove file' class=fa>\uf00d</a><span class=description>Remove File</span></span></span><input type=submit></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>")
|
||||
};
|
||||
@ -9222,23 +9212,20 @@
|
||||
setNode('status', '[type=submit]');
|
||||
setNode('fileInput', '[type=file]');
|
||||
rules = $('ul.rules').textContent.trim();
|
||||
QR.min_width = QR.min_height = 1;
|
||||
QR.max_width = QR.max_height = 10000;
|
||||
try {
|
||||
_ref = rules.match(/.+smaller than (\d+)x(\d+).+/), _ = _ref[0], QR.min_width = _ref[1], QR.min_height = _ref[2];
|
||||
_ref1 = rules.match(/.+greater than (\d+)x(\d+).+/), _ = _ref1[0], QR.max_width = _ref1[1], QR.max_height = _ref1[2];
|
||||
_ref2 = ['min_width', 'min_height', 'max_width', 'max_height'];
|
||||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
||||
prop = _ref2[_i];
|
||||
QR[prop] = parseInt(QR[prop], 10);
|
||||
}
|
||||
} catch (_error) {
|
||||
null;
|
||||
}
|
||||
match_min = rules.match(/.+smaller than (\d+)x(\d+).+/);
|
||||
match_max = rules.match(/.+greater than (\d+)x(\d+).+/);
|
||||
QR.min_width = +(match_min != null ? match_min[1] : void 0) || 1;
|
||||
QR.min_height = +(match_min != null ? match_min[2] : void 0) || 1;
|
||||
QR.max_width = +(match_max != null ? match_max[1] : void 0) || 10000;
|
||||
QR.max_height = +(match_max != null ? match_max[2] : void 0) || 10000;
|
||||
nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value;
|
||||
QR.max_size_video = 3145728;
|
||||
QR.max_width_video = QR.max_height_video = 2048;
|
||||
QR.max_duration_video = 120;
|
||||
QR.forcedAnon = !!$('form[name="post"] input[name="name"][type="hidden"]');
|
||||
if (QR.forcedAnon) {
|
||||
$.addClass(QR.nodes.el, 'forced-anon');
|
||||
}
|
||||
QR.spoiler = !!$('.postForm input[name=spoiler]');
|
||||
if (QR.spoiler) {
|
||||
$.addClass(QR.nodes.el, 'has-spoiler');
|
||||
@ -9249,9 +9236,10 @@
|
||||
$.after(nodes.name.parentElement, nodes.dumpList.parentElement);
|
||||
nodes.addPost.tabIndex = 35;
|
||||
}
|
||||
if (g.BOARD.ID === 'f') {
|
||||
if (g.BOARD.ID === 'f' && g.VIEW !== 'thread') {
|
||||
nodes.flashTag = $.el('select', {
|
||||
name: 'filetag',
|
||||
name: 'filetag'
|
||||
}, {
|
||||
innerHTML: "<option value=0>Hentai</option>\n<option value=6>Porn</option>\n<option value=1>Japanese</option>\n<option value=2>Anime</option>\n<option value=3>Game</option>\n<option value=5>Loop</option>\n<option value=4 selected>Other</option>"
|
||||
});
|
||||
nodes.flashTag.dataset["default"] = '4';
|
||||
@ -9427,9 +9415,9 @@
|
||||
post.lock();
|
||||
formData = {
|
||||
resto: threadID,
|
||||
name: post.name,
|
||||
name: !QR.forcedAnon ? post.name : void 0,
|
||||
email: post.email,
|
||||
sub: post.sub,
|
||||
sub: !(QR.forcedAnon || threadID) ? post.sub : void 0,
|
||||
com: post.com,
|
||||
upfile: post.file,
|
||||
filetag: filetag,
|
||||
@ -9516,9 +9504,6 @@
|
||||
}
|
||||
QR.status();
|
||||
QR.error(err);
|
||||
if (QR.captcha.isEnabled) {
|
||||
QR.captcha.setup();
|
||||
}
|
||||
return;
|
||||
}
|
||||
h1 = $('h1', resDoc);
|
||||
@ -9569,7 +9554,7 @@
|
||||
QR.close();
|
||||
} else {
|
||||
post.rm();
|
||||
QR.captcha.setup();
|
||||
QR.captcha.setup(true);
|
||||
}
|
||||
QR.cooldown.set({
|
||||
req: req,
|
||||
@ -9621,7 +9606,7 @@
|
||||
|
||||
QR.captcha = {
|
||||
init: function() {
|
||||
var container, counter, section;
|
||||
var counter, root;
|
||||
if (d.cookie.indexOf('pass_enabled=1') >= 0) {
|
||||
return;
|
||||
}
|
||||
@ -9635,80 +9620,101 @@
|
||||
return QR.captcha.sync(captchas);
|
||||
});
|
||||
$.sync('captchas', this.sync.bind(this));
|
||||
section = $.el('div', {
|
||||
className: 'captcha-section'
|
||||
root = $.el('div', {
|
||||
className: 'captcha-root'
|
||||
});
|
||||
$.extend(section, {
|
||||
innerHTML: "<div class=\"captcha-container\"></div><div class=\"captcha-counter\"><a href=\"javascript:;\"></a></div>"
|
||||
$.extend(root, {
|
||||
innerHTML: "<div class=\"captcha-counter\"><a href=\"javascript:;\"></a></div>"
|
||||
});
|
||||
container = $('.captcha-container', section);
|
||||
counter = $('.captcha-counter > a', section);
|
||||
counter = $('.captcha-counter > a', root);
|
||||
this.nodes = {
|
||||
container: container,
|
||||
root: root,
|
||||
counter: counter
|
||||
};
|
||||
this.count();
|
||||
$.addClass(QR.nodes.el, 'has-captcha');
|
||||
$.after(QR.nodes.com.parentNode, section);
|
||||
new MutationObserver(this.afterSetup.bind(this)).observe(container, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
$.after(QR.nodes.com.parentNode, root);
|
||||
$.on(counter, 'click', this.toggle.bind(this));
|
||||
return $.on(window, 'captcha:success', this.save.bind(this));
|
||||
return $.on(window, 'captcha:success', (function(_this) {
|
||||
return function() {
|
||||
return _this.save(false);
|
||||
};
|
||||
})(this));
|
||||
},
|
||||
shouldFocus: false,
|
||||
timeouts: {},
|
||||
postsCount: 0,
|
||||
needed: function() {
|
||||
var captchaCount, postsCount;
|
||||
var captchaCount;
|
||||
captchaCount = this.captchas.length;
|
||||
if (this.nodes.container.dataset.widgetID && !this.timeouts.destroy) {
|
||||
if (this.nodes.container && !this.timeouts.destroy) {
|
||||
captchaCount++;
|
||||
}
|
||||
postsCount = QR.posts.length;
|
||||
if (postsCount === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) {
|
||||
postsCount = 0;
|
||||
this.postsCount = QR.posts.length;
|
||||
if (this.postsCount === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) {
|
||||
this.postsCount = 0;
|
||||
}
|
||||
return captchaCount < this.postsCount;
|
||||
},
|
||||
onPostChange: function() {
|
||||
if (this.postsCount === 0) {
|
||||
this.setup();
|
||||
}
|
||||
if (QR.posts.length === 1 && !Conf['Auto-load captcha'] && !QR.posts[0].com && !QR.posts[0].file) {
|
||||
return this.postsCount = 0;
|
||||
}
|
||||
return captchaCount < postsCount;
|
||||
},
|
||||
toggle: function() {
|
||||
if (this.nodes.container.dataset.widgetID && !this.timeouts.destroy) {
|
||||
if (this.nodes.container && !this.timeouts.destroy) {
|
||||
return this.destroy();
|
||||
} else {
|
||||
this.shouldFocus = true;
|
||||
return this.setup(true);
|
||||
return this.setup(true, true);
|
||||
}
|
||||
},
|
||||
setup: function(force) {
|
||||
setup: function(focus, force) {
|
||||
if (!(this.isEnabled && (this.needed() || force))) {
|
||||
return;
|
||||
}
|
||||
$.addClass(QR.nodes.el, 'captcha-open');
|
||||
if (focus) {
|
||||
this.shouldFocus = true;
|
||||
}
|
||||
if (this.timeouts.destroy) {
|
||||
clearTimeout(this.timeouts.destroy);
|
||||
delete this.timeouts.destroy;
|
||||
return this.reload();
|
||||
}
|
||||
if (this.nodes.container.dataset.widgetID) {
|
||||
if (this.nodes.container) {
|
||||
return;
|
||||
}
|
||||
return $.globalEval('(function() {\n var container = document.querySelector("#qr .captcha-container");\n container.dataset.widgetID = window.grecaptcha.render(container, {\n sitekey: \'6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc\',\n theme: document.documentElement.classList.contains(\'tomorrow\') ? \'dark\' : \'light\',\n callback: function(response) {\n window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));\n }\n });\n})();');
|
||||
this.nodes.container = $.el('div', {
|
||||
className: 'captcha-container'
|
||||
});
|
||||
$.prepend(this.nodes.root, this.nodes.container);
|
||||
new MutationObserver(this.afterSetup.bind(this)).observe(this.nodes.container, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
return $.globalEval('(function() {\n function render() {\n var container = document.querySelector("#qr .captcha-container");\n container.dataset.widgetID = window.grecaptcha.render(container, {\n sitekey: \'6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc\',\n theme: document.documentElement.classList.contains(\'tomorrow\') ? \'dark\' : \'light\',\n callback: function(response) {\n window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));\n }\n });\n }\n if (window.grecaptcha) {\n render();\n } else {\n var cbNative = window.onRecaptchaLoaded;\n window.onRecaptchaLoaded = function() {\n render();\n cbNative();\n }\n }\n})();');
|
||||
},
|
||||
afterSetup: function(mutations) {
|
||||
var iframe, mutation, node, _i, _j, _len, _len1, _ref;
|
||||
var iframe, mutation, node, textarea, _i, _j, _len, _len1, _ref;
|
||||
for (_i = 0, _len = mutations.length; _i < _len; _i++) {
|
||||
mutation = mutations[_i];
|
||||
_ref = mutation.addedNodes;
|
||||
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
||||
node = _ref[_j];
|
||||
if (node.nodeName === 'IFRAME') {
|
||||
iframe = node;
|
||||
if (iframe = $.x('./descendant-or-self::iframe', node)) {
|
||||
this.setupIFrame(iframe);
|
||||
}
|
||||
if (textarea = $.x('./descendant-or-self::textarea', node)) {
|
||||
this.setupTextArea(textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!iframe) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
setupIFrame: function(iframe) {
|
||||
this.setupTime = Date.now();
|
||||
if (QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight) {
|
||||
QR.nodes.el.style.top = null;
|
||||
QR.nodes.el.style.bottom = '0px';
|
||||
@ -9718,16 +9724,28 @@
|
||||
}
|
||||
return this.shouldFocus = false;
|
||||
},
|
||||
setupTextArea: function(textarea) {
|
||||
return $.one(textarea, 'input', (function(_this) {
|
||||
return function() {
|
||||
return _this.save(true);
|
||||
};
|
||||
})(this));
|
||||
},
|
||||
destroy: function() {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
delete this.timeouts.destroy;
|
||||
$.rmClass(QR.nodes.el, 'captcha-open');
|
||||
$.rmAll(this.nodes.container);
|
||||
return this.nodes.container.removeAttribute('data-widget-i-d');
|
||||
if (this.nodes.container) {
|
||||
$.rm(this.nodes.container);
|
||||
}
|
||||
return delete this.nodes.container;
|
||||
},
|
||||
sync: function(captchas) {
|
||||
if (captchas == null) {
|
||||
captchas = [];
|
||||
}
|
||||
this.captchas = captchas;
|
||||
this.clear();
|
||||
return this.count();
|
||||
@ -9743,30 +9761,39 @@
|
||||
return null;
|
||||
}
|
||||
},
|
||||
save: function(e) {
|
||||
var _base;
|
||||
if (this.needed()) {
|
||||
save: function(pasted) {
|
||||
var reload, _base;
|
||||
$.forceSync('captchas');
|
||||
reload = (QR.cooldown.auto || Conf['Post on Captcha Completion']) && this.needed();
|
||||
this.captchas.push({
|
||||
response: $('textarea', this.nodes.container).value,
|
||||
timeout: (pasted ? this.setupTime : Date.now()) + 2 * $.MINUTE
|
||||
});
|
||||
this.count();
|
||||
$.set('captchas', this.captchas);
|
||||
if (reload) {
|
||||
this.shouldFocus = true;
|
||||
this.reload();
|
||||
} else {
|
||||
this.nodes.counter.focus();
|
||||
if ((_base = this.timeouts).destroy == null) {
|
||||
_base.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND);
|
||||
if (pasted) {
|
||||
this.destroy();
|
||||
} else {
|
||||
if ((_base = this.timeouts).destroy == null) {
|
||||
_base.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND);
|
||||
}
|
||||
}
|
||||
QR.nodes.status.focus();
|
||||
}
|
||||
if (Conf['Post on Captcha Completion'] && !QR.cooldown.auto) {
|
||||
return QR.submit();
|
||||
}
|
||||
$.forceSync('captchas');
|
||||
this.captchas.push({
|
||||
response: e.detail,
|
||||
timeout: Date.now() + 2 * $.MINUTE
|
||||
});
|
||||
this.count();
|
||||
return $.set('captchas', this.captchas);
|
||||
},
|
||||
clear: function() {
|
||||
var captcha, i, now, _i, _len, _ref;
|
||||
if (!this.captchas.length) {
|
||||
return;
|
||||
}
|
||||
$.forceSync('captchas');
|
||||
now = Date.now();
|
||||
_ref = this.captchas;
|
||||
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
||||
@ -9781,7 +9808,7 @@
|
||||
this.captchas = this.captchas.slice(i);
|
||||
this.count();
|
||||
$.set('captchas', this.captchas);
|
||||
return this.setup();
|
||||
return this.setup(true);
|
||||
},
|
||||
count: function() {
|
||||
this.nodes.counter.textContent = "Captchas: " + this.captchas.length;
|
||||
@ -10018,7 +10045,7 @@
|
||||
QR.post = (function() {
|
||||
function _Class(select) {
|
||||
this.select = __bind(this.select, this);
|
||||
var el, event, prev, _i, _len, _ref, _ref1;
|
||||
var el, event, prev, _i, _len, _ref;
|
||||
el = $.el('a', {
|
||||
className: 'qr-preview',
|
||||
draggable: true,
|
||||
@ -10059,9 +10086,8 @@
|
||||
$.on(el, event.toLowerCase(), this[event]);
|
||||
}
|
||||
this.thread = g.VIEW === 'thread' ? g.THREADID : 'new';
|
||||
_ref1 = QR.posts, prev = _ref1[_ref1.length - 1];
|
||||
prev = QR.posts[QR.posts.length - 1];
|
||||
QR.posts.push(this);
|
||||
QR.captcha.setup();
|
||||
this.nodes.spoiler.checked = this.spoiler = prev && Conf['Remember Spoiler'] ? prev.spoiler : false;
|
||||
QR.persona.get((function(_this) {
|
||||
return function(persona) {
|
||||
@ -10080,6 +10106,9 @@
|
||||
this.select();
|
||||
}
|
||||
this.unlock();
|
||||
$.queueTask(function() {
|
||||
return QR.captcha.setup();
|
||||
});
|
||||
}
|
||||
|
||||
_Class.prototype.rm = function() {
|
||||
@ -10170,7 +10199,7 @@
|
||||
return QR.status();
|
||||
case 'com':
|
||||
this.nodes.span.textContent = this.com;
|
||||
QR.captcha.setup();
|
||||
QR.captcha.onPostChange();
|
||||
QR.characterCount();
|
||||
if (QR.cooldown.auto && this === QR.posts[0] && (0 < (_ref = QR.cooldown.seconds) && _ref <= 5)) {
|
||||
return QR.cooldown.auto = false;
|
||||
@ -10210,7 +10239,7 @@
|
||||
if (QR.spoiler) {
|
||||
this.nodes.label.hidden = false;
|
||||
}
|
||||
QR.captcha.setup();
|
||||
QR.captcha.onPostChange();
|
||||
URL.revokeObjectURL(this.URL);
|
||||
if (this === QR.selected) {
|
||||
this.showFileData();
|
||||
@ -10228,55 +10257,55 @@
|
||||
var el, fileURL, isVideo;
|
||||
isVideo = /^video\//.test(this.file.type);
|
||||
el = $.el((isVideo ? 'video' : 'img'));
|
||||
$.on(el, (isVideo ? 'loadeddata' : 'load'), (function(_this) {
|
||||
return function() {
|
||||
var cv, error, errors, height, s, width, _i, _len;
|
||||
errors = _this.checkDimensions(el, isVideo);
|
||||
if (errors.length) {
|
||||
for (_i = 0, _len = errors.length; _i < _len; _i++) {
|
||||
error = errors[_i];
|
||||
QR.error(error);
|
||||
}
|
||||
_this.URL = fileURL;
|
||||
if ((QR.posts.length === 1) || (_this.com && _this.com.length)) {
|
||||
return _this.rmFile();
|
||||
} else {
|
||||
return _this.rm();
|
||||
}
|
||||
$.on(el, (isVideo ? 'loadeddata' : 'load'), function() {
|
||||
var cv, error, errors, height, s, width, _i, _len;
|
||||
errors = this.checkDimensions(el, isVideo);
|
||||
if (errors.length) {
|
||||
for (_i = 0, _len = errors.length; _i < _len; _i++) {
|
||||
error = errors[_i];
|
||||
QR.error(error);
|
||||
}
|
||||
s = 90 * 2 * window.devicePixelRatio;
|
||||
if (_this.file.type === 'image/gif') {
|
||||
s *= 3;
|
||||
}
|
||||
if (isVideo) {
|
||||
height = el.videoHeight;
|
||||
width = el.videoWidth;
|
||||
this.URL = fileURL;
|
||||
if ((QR.posts.length === 1) || (this.com && this.com.length)) {
|
||||
return this.rmFile();
|
||||
} else {
|
||||
height = el.height, width = el.width;
|
||||
if (height < s || width < s) {
|
||||
_this.URL = fileURL;
|
||||
_this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")";
|
||||
return;
|
||||
}
|
||||
return this.rm();
|
||||
}
|
||||
if (height <= width) {
|
||||
width = s / height * width;
|
||||
height = s;
|
||||
} else {
|
||||
height = s / width * height;
|
||||
width = s;
|
||||
}
|
||||
s = 90 * 2 * window.devicePixelRatio;
|
||||
if (this.file.type === 'image/gif') {
|
||||
s *= 3;
|
||||
}
|
||||
if (isVideo) {
|
||||
height = el.videoHeight;
|
||||
width = el.videoWidth;
|
||||
} else {
|
||||
height = el.height, width = el.width;
|
||||
if (height < s || width < s) {
|
||||
this.URL = fileURL;
|
||||
this.nodes.el.style.backgroundImage = "url(" + this.URL + ")";
|
||||
return;
|
||||
}
|
||||
cv = $.el('canvas');
|
||||
cv.height = el.height = height;
|
||||
cv.width = el.width = width;
|
||||
cv.getContext('2d').drawImage(el, 0, 0, width, height);
|
||||
URL.revokeObjectURL(fileURL);
|
||||
return cv.toBlob(function(blob) {
|
||||
}
|
||||
if (height <= width) {
|
||||
width = s / height * width;
|
||||
height = s;
|
||||
} else {
|
||||
height = s / width * height;
|
||||
width = s;
|
||||
}
|
||||
cv = $.el('canvas');
|
||||
cv.height = el.height = height;
|
||||
cv.width = el.width = width;
|
||||
cv.getContext('2d').drawImage(el, 0, 0, width, height);
|
||||
URL.revokeObjectURL(fileURL);
|
||||
return cv.toBlob((function(_this) {
|
||||
return function(blob) {
|
||||
_this.URL = URL.createObjectURL(blob);
|
||||
return _this.nodes.el.style.backgroundImage = "url(" + _this.URL + ")";
|
||||
});
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
})(this));
|
||||
});
|
||||
fileURL = URL.createObjectURL(this.file);
|
||||
return el.src = fileURL;
|
||||
};
|
||||
@ -10331,7 +10360,7 @@
|
||||
|
||||
_Class.prototype.updateFilename = function() {
|
||||
var title;
|
||||
title = "" + this.filename + " (" + this.filesize + ")\nCtrl+click to edit filename. Shift+click to clear.";
|
||||
title = "" + this.filename + " (" + this.filesize + ")\nCtrl/\u2318+click to edit filename. Shift+click to clear.";
|
||||
this.nodes.el.title = title;
|
||||
if (this !== QR.selected) {
|
||||
return;
|
||||
|
||||
99
src/General/CrossOrigin.coffee
Normal file
99
src/General/CrossOrigin.coffee
Normal file
@ -0,0 +1,99 @@
|
||||
CrossOrigin = do ->
|
||||
<% if (type === 'crx') { %>
|
||||
eventPageRequest = do ->
|
||||
callbacks = []
|
||||
chrome.runtime.onMessage.addListener (data) ->
|
||||
callbacks[data.id] data
|
||||
delete callbacks[data.id]
|
||||
(url, responseType, cb) ->
|
||||
chrome.runtime.sendMessage {url, responseType}, (id) ->
|
||||
callbacks[id] = cb
|
||||
<% } %>
|
||||
|
||||
file: do ->
|
||||
makeBlob = (urlBlob, contentType, contentDisposition, url) ->
|
||||
name = url.match(/([^\/]+)\/*$/)?[1]
|
||||
mime = contentType?.match(/[^;]*/)[0] or 'application/octet-stream'
|
||||
match =
|
||||
contentDisposition?.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)?[1] or
|
||||
contentType?.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)?[1]
|
||||
if match
|
||||
name = match.replace /\\"/g, '"'
|
||||
blob = new Blob([urlBlob], {type: mime})
|
||||
blob.name = name
|
||||
blob
|
||||
|
||||
(url, cb) ->
|
||||
<% if (type === 'crx') { %>
|
||||
if /^https:\/\//.test(url) or location.protocol is 'http:'
|
||||
$.ajax url,
|
||||
responseType: 'blob'
|
||||
onload: ->
|
||||
return cb null unless @readyState is @DONE and @status is 200
|
||||
contentType = @getResponseHeader 'Content-Type'
|
||||
contentDisposition = @getResponseHeader 'Content-Disposition'
|
||||
cb (makeBlob @response, contentType, contentDisposition, url)
|
||||
onerror: ->
|
||||
cb null
|
||||
else
|
||||
eventPageRequest url, 'arraybuffer', ({response, contentType, contentDisposition, error}) ->
|
||||
return cb null if error
|
||||
cb (makeBlob new Uint8Array(response), contentType, contentDisposition, url)
|
||||
<% } %>
|
||||
<% if (type === 'userscript') { %>
|
||||
GM_xmlhttpRequest
|
||||
method: "GET"
|
||||
url: url
|
||||
overrideMimeType: "text/plain; charset=x-user-defined"
|
||||
onload: (xhr) ->
|
||||
r = xhr.responseText
|
||||
data = new Uint8Array r.length
|
||||
i = 0
|
||||
while i < r.length
|
||||
data[i] = r.charCodeAt i
|
||||
i++
|
||||
contentType = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)?[1]
|
||||
contentDisposition = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)?[1]
|
||||
cb (makeBlob data, contentType, contentDisposition, url)
|
||||
onerror: ->
|
||||
cb null
|
||||
<% } %>
|
||||
|
||||
json: do ->
|
||||
callbacks = {}
|
||||
responses = {}
|
||||
(url, cb) ->
|
||||
<% if (type === 'crx') { %>
|
||||
if /^https:\/\//.test(url) or location.protocol is 'http:'
|
||||
return $.cache url, (-> cb @response), responseType: 'json'
|
||||
<% } %>
|
||||
if responses[url]
|
||||
cb responses[url]
|
||||
return
|
||||
if callbacks[url]
|
||||
callbacks[url].push cb
|
||||
return
|
||||
callbacks[url] = [cb]
|
||||
<% if (type === 'userscript') { %>
|
||||
GM_xmlhttpRequest
|
||||
method: "GET"
|
||||
url: url+''
|
||||
onload: (xhr) ->
|
||||
response = JSON.parse xhr.responseText
|
||||
cb response for cb in callbacks[url]
|
||||
delete callbacks[url]
|
||||
responses[url] = response
|
||||
onerror: ->
|
||||
delete callbacks[url]
|
||||
onabort: ->
|
||||
delete callbacks[url]
|
||||
<% } %>
|
||||
<% if (type === 'crx') { %>
|
||||
eventPageRequest url, 'json', ({response, error}) ->
|
||||
if error
|
||||
delete callbacks[url]
|
||||
else
|
||||
cb response for cb in callbacks[url]
|
||||
delete callbacks[url]
|
||||
responses[url] = response
|
||||
<% } %>
|
||||
@ -8,83 +8,108 @@ QR.captcha =
|
||||
QR.captcha.sync captchas
|
||||
$.sync 'captchas', @sync.bind @
|
||||
|
||||
section = $.el 'div', className: 'captcha-section'
|
||||
$.extend section, <%= html(
|
||||
'<div class="captcha-container"></div>' +
|
||||
root = $.el 'div', className: 'captcha-root'
|
||||
$.extend root, <%= html(
|
||||
'<div class="captcha-counter"><a href="javascript:;"></a></div>'
|
||||
) %>
|
||||
container = $ '.captcha-container', section
|
||||
counter = $ '.captcha-counter > a', section
|
||||
@nodes = {container, counter}
|
||||
counter = $ '.captcha-counter > a', root
|
||||
@nodes = {root, counter}
|
||||
@count()
|
||||
$.addClass QR.nodes.el, 'has-captcha'
|
||||
$.after QR.nodes.com.parentNode, section
|
||||
|
||||
new MutationObserver(@afterSetup.bind @).observe container,
|
||||
childList: true
|
||||
subtree: true
|
||||
$.after QR.nodes.com.parentNode, root
|
||||
|
||||
$.on counter, 'click', @toggle.bind @
|
||||
$.on window, 'captcha:success', @save.bind @
|
||||
$.on window, 'captcha:success', => @save false
|
||||
|
||||
shouldFocus: false
|
||||
timeouts: {}
|
||||
postsCount: 0
|
||||
|
||||
needed: ->
|
||||
captchaCount = @captchas.length
|
||||
captchaCount++ if @nodes.container.dataset.widgetID and !@timeouts.destroy
|
||||
postsCount = QR.posts.length
|
||||
postsCount = 0 if postsCount is 1 and !Conf['Auto-load captcha'] and !QR.posts[0].com and !QR.posts[0].file
|
||||
captchaCount < postsCount
|
||||
captchaCount++ if @nodes.container and !@timeouts.destroy
|
||||
@postsCount = QR.posts.length
|
||||
@postsCount = 0 if @postsCount is 1 and !Conf['Auto-load captcha'] and !QR.posts[0].com and !QR.posts[0].file
|
||||
captchaCount < @postsCount
|
||||
|
||||
onPostChange: ->
|
||||
@setup() if @postsCount is 0
|
||||
@postsCount = 0 if QR.posts.length is 1 and !Conf['Auto-load captcha'] and !QR.posts[0].com and !QR.posts[0].file
|
||||
|
||||
toggle: ->
|
||||
if @nodes.container.dataset.widgetID and !@timeouts.destroy
|
||||
if @nodes.container and !@timeouts.destroy
|
||||
@destroy()
|
||||
else
|
||||
@shouldFocus = true
|
||||
@setup true
|
||||
@setup true, true
|
||||
|
||||
setup: (force) ->
|
||||
setup: (focus, force) ->
|
||||
return unless @isEnabled and (@needed() or force)
|
||||
$.addClass QR.nodes.el, 'captcha-open'
|
||||
@shouldFocus = true if focus
|
||||
if @timeouts.destroy
|
||||
clearTimeout @timeouts.destroy
|
||||
delete @timeouts.destroy
|
||||
return @reload()
|
||||
return if @nodes.container.dataset.widgetID
|
||||
|
||||
return if @nodes.container
|
||||
|
||||
@nodes.container = $.el 'div', className: 'captcha-container'
|
||||
$.prepend @nodes.root, @nodes.container
|
||||
new MutationObserver(@afterSetup.bind @).observe @nodes.container,
|
||||
childList: true
|
||||
subtree: true
|
||||
|
||||
$.globalEval '''
|
||||
(function() {
|
||||
var container = document.querySelector("#qr .captcha-container");
|
||||
container.dataset.widgetID = window.grecaptcha.render(container, {
|
||||
sitekey: '<%= meta.recaptchaKey %>',
|
||||
theme: document.documentElement.classList.contains('tomorrow') ? 'dark' : 'light',
|
||||
callback: function(response) {
|
||||
window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));
|
||||
function render() {
|
||||
var container = document.querySelector("#qr .captcha-container");
|
||||
container.dataset.widgetID = window.grecaptcha.render(container, {
|
||||
sitekey: '<%= meta.recaptchaKey %>',
|
||||
theme: document.documentElement.classList.contains('tomorrow') ? 'dark' : 'light',
|
||||
callback: function(response) {
|
||||
window.dispatchEvent(new CustomEvent("captcha:success", {detail: response}));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (window.grecaptcha) {
|
||||
render();
|
||||
} else {
|
||||
var cbNative = window.onRecaptchaLoaded;
|
||||
window.onRecaptchaLoaded = function() {
|
||||
render();
|
||||
cbNative();
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
'''
|
||||
|
||||
afterSetup: (mutations) ->
|
||||
for mutation in mutations
|
||||
for node in mutation.addedNodes
|
||||
iframe = node if node.nodeName is 'IFRAME'
|
||||
return unless iframe
|
||||
@setupIFrame iframe if iframe = $.x './descendant-or-self::iframe', node
|
||||
@setupTextArea textarea if textarea = $.x './descendant-or-self::textarea', node
|
||||
return
|
||||
|
||||
setupIFrame: (iframe) ->
|
||||
@setupTime = Date.now()
|
||||
|
||||
if QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight
|
||||
QR.nodes.el.style.top = null
|
||||
QR.nodes.el.style.bottom = '0px'
|
||||
iframe.focus() if @shouldFocus
|
||||
@shouldFocus = false
|
||||
|
||||
setupTextArea: (textarea) ->
|
||||
$.one textarea, 'input', => @save true
|
||||
|
||||
destroy: ->
|
||||
return unless @isEnabled
|
||||
delete @timeouts.destroy
|
||||
$.rmClass QR.nodes.el, 'captcha-open'
|
||||
$.rmAll @nodes.container
|
||||
# XXX https://github.com/greasemonkey/greasemonkey/issues/1571
|
||||
@nodes.container.removeAttribute 'data-widget-i-d'
|
||||
$.rm @nodes.container if @nodes.container
|
||||
delete @nodes.container
|
||||
|
||||
sync: (captchas) ->
|
||||
sync: (captchas=[]) ->
|
||||
@captchas = captchas
|
||||
@clear()
|
||||
@count()
|
||||
@ -98,22 +123,30 @@ QR.captcha =
|
||||
else
|
||||
null
|
||||
|
||||
save: (e) ->
|
||||
if @needed()
|
||||
@shouldFocus = true
|
||||
@reload()
|
||||
else
|
||||
@nodes.counter.focus()
|
||||
@timeouts.destroy ?= setTimeout @destroy.bind(@), 3 * $.SECOND
|
||||
save: (pasted) ->
|
||||
$.forceSync 'captchas'
|
||||
reload = (QR.cooldown.auto or Conf['Post on Captcha Completion']) and @needed()
|
||||
@captchas.push
|
||||
response: e.detail
|
||||
timeout: Date.now() + 2 * $.MINUTE
|
||||
response: $('textarea', @nodes.container).value
|
||||
timeout: (if pasted then @setupTime else Date.now()) + 2 * $.MINUTE
|
||||
@count()
|
||||
$.set 'captchas', @captchas
|
||||
|
||||
if reload
|
||||
@shouldFocus = true
|
||||
@reload()
|
||||
else
|
||||
if pasted
|
||||
@destroy()
|
||||
else
|
||||
@timeouts.destroy ?= setTimeout @destroy.bind(@), 3 * $.SECOND
|
||||
QR.nodes.status.focus()
|
||||
|
||||
QR.submit() if Conf['Post on Captcha Completion'] and !QR.cooldown.auto
|
||||
|
||||
clear: ->
|
||||
return unless @captchas.length
|
||||
$.forceSync 'captchas'
|
||||
now = Date.now()
|
||||
for captcha, i in @captchas
|
||||
break if captcha.timeout > now
|
||||
@ -121,7 +154,7 @@ QR.captcha =
|
||||
@captchas = @captchas[i..]
|
||||
@count()
|
||||
$.set 'captchas', @captchas
|
||||
@setup()
|
||||
@setup true
|
||||
|
||||
count: ->
|
||||
@nodes.counter.textContent = "Captchas: #{@captchas.length}"
|
||||
|
||||
@ -108,6 +108,7 @@ QR =
|
||||
QR.captcha.destroy()
|
||||
|
||||
focusin: ->
|
||||
QR.captcha.setup() if $.hasClass(QR.nodes.el, 'autohide') and !$.hasClass(QR.nodes.el, 'focus')
|
||||
$.addClass QR.nodes.el, 'focus'
|
||||
|
||||
focusout: ->
|
||||
@ -136,7 +137,7 @@ QR =
|
||||
el = err
|
||||
el.removeAttribute 'style'
|
||||
if QR.captcha.isEnabled and /captcha|verification/i.test el.textContent
|
||||
QR.captcha.setup()
|
||||
QR.captcha.setup true
|
||||
QR.notify el
|
||||
alert el.textContent if d.hidden
|
||||
|
||||
@ -270,6 +271,7 @@ QR =
|
||||
QR.handleFiles e.dataTransfer.files
|
||||
|
||||
paste: (e) ->
|
||||
return unless e.clipboardData.items
|
||||
files = []
|
||||
for item in e.clipboardData.items when item.kind is 'file'
|
||||
blob = item.getAsFile()
|
||||
@ -280,66 +282,15 @@ QR =
|
||||
QR.open()
|
||||
QR.handleFiles files
|
||||
$.addClass QR.nodes.el, 'dump'
|
||||
|
||||
handleBlob: (urlBlob, contentType, contentDisposition, url) ->
|
||||
name = url.match(/([^\/]+)\/*$/)?[1]
|
||||
mime = contentType?.match(/[^;]*/)[0] or 'application/octet-stream'
|
||||
match =
|
||||
contentDisposition?.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)?[1] or
|
||||
contentType?.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)?[1]
|
||||
if match
|
||||
name = match.replace /\\"/g, '"'
|
||||
blob = new Blob([urlBlob], {type: mime})
|
||||
blob.name = name
|
||||
QR.handleFiles([blob])
|
||||
|
||||
handleUrl: ->
|
||||
url = prompt("Enter a URL:")
|
||||
url = prompt 'Enter a URL:'
|
||||
return if url is null
|
||||
|
||||
<% if (type === 'crx') { %>
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true)
|
||||
xhr.responseType = 'blob'
|
||||
xhr.onload = (e) ->
|
||||
return QR.error "Can't load image." unless @readyState is @DONE and xhr.status is 200
|
||||
|
||||
contentType = @getResponseHeader('Content-Type')
|
||||
contentDisposition = @getResponseHeader('Content-Disposition')
|
||||
QR.handleBlob @response, contentType, contentDisposition, url
|
||||
|
||||
xhr.onerror = (e) ->
|
||||
QR.error "Can't load image."
|
||||
xhr.send()
|
||||
|
||||
<% } else { %>
|
||||
|
||||
GM_xmlhttpRequest
|
||||
method: "GET"
|
||||
url: url
|
||||
|
||||
# FIXME: responseType: 'blob'
|
||||
# Could do it now, but don't wanna kill off legacy GM versions yet
|
||||
overrideMimeType: "text/plain; charset=x-user-defined"
|
||||
onload: (xhr) ->
|
||||
r = xhr.responseText
|
||||
h = xhr.responseHeaders
|
||||
data = new Uint8Array r.length
|
||||
i = 0
|
||||
|
||||
while i < r.length
|
||||
data[i] = r.charCodeAt i
|
||||
i++
|
||||
|
||||
contentType = (h.match(/Content-Type:\s*(.*)/i) or [])[1]
|
||||
contentDisposition = (h.match(/Content-Disposition:\s*(.*)/i) or [])[1]
|
||||
QR.handleBlob data, contentType, contentDisposition, url
|
||||
|
||||
onerror: (xhr) ->
|
||||
CrossOrigin.file url, (blob) ->
|
||||
if blob
|
||||
QR.handleFiles([blob])
|
||||
else
|
||||
QR.error "Can't load image."
|
||||
|
||||
<% } %>
|
||||
|
||||
handleFiles: (files) ->
|
||||
if @ isnt QR # file input
|
||||
files = [@files...]
|
||||
@ -355,7 +306,7 @@ QR =
|
||||
if /^text\//.test file.type
|
||||
if isSingle
|
||||
post = QR.selected
|
||||
else if (post = QR.posts[QR.posts.length - 1]).com
|
||||
else if index isnt 0 or (post = QR.posts[QR.posts.length - 1]).com
|
||||
post = new QR.post()
|
||||
post.pasteText file
|
||||
return
|
||||
@ -371,16 +322,19 @@ QR =
|
||||
post = QR.selected
|
||||
else if (post = QR.posts[QR.posts.length - 1]).file
|
||||
post = new QR.post()
|
||||
post.setFile file
|
||||
try
|
||||
post.setFile file
|
||||
catch err
|
||||
console.log err
|
||||
|
||||
openFileInput: (e) ->
|
||||
e.stopPropagation()
|
||||
if e.shiftKey and e.type is 'click'
|
||||
return QR.selected.rmFile()
|
||||
if e.ctrlKey and e.type is 'click'
|
||||
if (e.ctrlKey or e.metaKey) and e.type is 'click'
|
||||
$.addClass QR.nodes.filename, 'edit'
|
||||
QR.nodes.filename.focus()
|
||||
return
|
||||
return $.on QR.nodes.filename, 'blur', -> $.rmClass QR.nodes.filename, 'edit'
|
||||
return if e.target.nodeName is 'INPUT' or (e.keyCode and e.keyCode not in [32, 13]) or e.ctrlKey
|
||||
e.preventDefault()
|
||||
QR.nodes.fileInput.click()
|
||||
@ -439,15 +393,13 @@ QR =
|
||||
setNode 'fileInput', '[type=file]'
|
||||
|
||||
rules = $('ul.rules').textContent.trim()
|
||||
QR.min_width = QR.min_height = 1
|
||||
QR.max_width = QR.max_height = 10000
|
||||
try
|
||||
[_, QR.min_width, QR.min_height] = rules.match(/.+smaller than (\d+)x(\d+).+/)
|
||||
[_, QR.max_width, QR.max_height] = rules.match(/.+greater than (\d+)x(\d+).+/)
|
||||
for prop in ['min_width', 'min_height', 'max_width', 'max_height']
|
||||
QR[prop] = parseInt QR[prop], 10
|
||||
catch
|
||||
null
|
||||
|
||||
match_min = rules.match(/.+smaller than (\d+)x(\d+).+/)
|
||||
match_max = rules.match(/.+greater than (\d+)x(\d+).+/)
|
||||
QR.min_width = +match_min?[1] or 1
|
||||
QR.min_height = +match_min?[2] or 1
|
||||
QR.max_width = +match_max?[1] or 10000
|
||||
QR.max_height = +match_max?[2] or 10000
|
||||
|
||||
nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value
|
||||
|
||||
@ -455,6 +407,10 @@ QR =
|
||||
QR.max_width_video = QR.max_height_video = 2048
|
||||
QR.max_duration_video = 120
|
||||
|
||||
QR.forcedAnon = !!$ 'form[name="post"] input[name="name"][type="hidden"]'
|
||||
if QR.forcedAnon
|
||||
$.addClass QR.nodes.el, 'forced-anon'
|
||||
|
||||
QR.spoiler = !!$ '.postForm input[name=spoiler]'
|
||||
if QR.spoiler
|
||||
$.addClass QR.nodes.el, 'has-spoiler'
|
||||
@ -465,9 +421,8 @@ QR =
|
||||
$.after nodes.name.parentElement, nodes.dumpList.parentElement
|
||||
nodes.addPost.tabIndex = 35
|
||||
|
||||
if g.BOARD.ID is 'f'
|
||||
nodes.flashTag = $.el 'select',
|
||||
name: 'filetag'
|
||||
if g.BOARD.ID is 'f' and g.VIEW isnt 'thread'
|
||||
nodes.flashTag = $.el 'select', name: 'filetag',
|
||||
innerHTML: """
|
||||
<option value=0>Hentai</option>
|
||||
<option value=6>Porn</option>
|
||||
@ -660,9 +615,11 @@ QR =
|
||||
|
||||
formData =
|
||||
resto: threadID
|
||||
name: post.name
|
||||
|
||||
name: post.name unless QR.forcedAnon
|
||||
email: post.email
|
||||
sub: post.sub
|
||||
|
||||
sub: post.sub unless QR.forcedAnon or threadID
|
||||
com: post.com
|
||||
upfile: post.file
|
||||
filetag: filetag
|
||||
@ -766,7 +723,6 @@ QR =
|
||||
QR.cooldown.auto = false
|
||||
QR.status()
|
||||
QR.error err
|
||||
QR.captcha.setup() if QR.captcha.isEnabled
|
||||
return
|
||||
|
||||
h1 = $ 'h1', resDoc
|
||||
@ -817,7 +773,7 @@ QR =
|
||||
QR.close()
|
||||
else
|
||||
post.rm()
|
||||
QR.captcha.setup()
|
||||
QR.captcha.setup true
|
||||
|
||||
QR.cooldown.set {req, post, isReply, threadID}
|
||||
|
||||
|
||||
@ -35,9 +35,8 @@ QR.post = class
|
||||
else
|
||||
'new'
|
||||
|
||||
[..., prev] = QR.posts
|
||||
prev = QR.posts[QR.posts.length - 1]
|
||||
QR.posts.push @
|
||||
QR.captcha.setup()
|
||||
@nodes.spoiler.checked = @spoiler = if prev and Conf['Remember Spoiler']
|
||||
prev.spoiler
|
||||
else
|
||||
@ -72,6 +71,8 @@ QR.post = class
|
||||
@load() if QR.selected is @ # load persona
|
||||
@select() if select
|
||||
@unlock()
|
||||
# Post count temporarily off by 1 when called from QR.post.rm
|
||||
$.queueTask -> QR.captcha.setup()
|
||||
|
||||
rm: ->
|
||||
@delete()
|
||||
@ -136,7 +137,7 @@ QR.post = class
|
||||
QR.status()
|
||||
when 'com'
|
||||
@nodes.span.textContent = @com
|
||||
QR.captcha.setup()
|
||||
QR.captcha.onPostChange()
|
||||
QR.characterCount()
|
||||
# Disable auto-posting if you're typing in the first post
|
||||
# during the last 5 seconds of the cooldown.
|
||||
@ -165,7 +166,7 @@ QR.post = class
|
||||
@filename = file.name
|
||||
@filesize = $.bytesToString file.size
|
||||
@nodes.label.hidden = false if QR.spoiler
|
||||
QR.captcha.setup()
|
||||
QR.captcha.onPostChange()
|
||||
URL.revokeObjectURL @URL
|
||||
if @ is QR.selected
|
||||
@showFileData()
|
||||
@ -181,7 +182,7 @@ QR.post = class
|
||||
isVideo = /^video\//.test @file.type
|
||||
el = $.el (if isVideo then 'video' else 'img')
|
||||
|
||||
$.on el, (if isVideo then 'loadeddata' else 'load'), =>
|
||||
$.on el, (if isVideo then 'loadeddata' else 'load'), ->
|
||||
# Verify element dimensions.
|
||||
errors = @checkDimensions el, isVideo
|
||||
if errors.length
|
||||
@ -195,6 +196,7 @@ QR.post = class
|
||||
# to avoid crappy resized quality.
|
||||
s = 90 * 2 * window.devicePixelRatio
|
||||
s *= 3 if @file.type is 'image/gif' # let them animate
|
||||
|
||||
if isVideo
|
||||
height = el.videoHeight
|
||||
width = el.videoWidth
|
||||
@ -204,12 +206,14 @@ QR.post = class
|
||||
@URL = fileURL
|
||||
@nodes.el.style.backgroundImage = "url(#{@URL})"
|
||||
return
|
||||
|
||||
if height <= width
|
||||
width = s / height * width
|
||||
height = s
|
||||
else
|
||||
height = s / width * height
|
||||
width = s
|
||||
|
||||
cv = $.el 'canvas'
|
||||
cv.height = el.height = height
|
||||
cv.width = el.width = width
|
||||
@ -226,8 +230,14 @@ QR.post = class
|
||||
err = []
|
||||
if video
|
||||
{videoHeight, videoWidth, duration} = el
|
||||
max_height = if QR.max_height < QR.max_height_video then QR.max_height else QR.max_height_video
|
||||
max_width = if QR.max_width < QR.max_width_video then QR.max_width else QR.max_width_video
|
||||
max_height = if QR.max_height < QR.max_height_video
|
||||
QR.max_height
|
||||
else
|
||||
QR.max_height_video
|
||||
max_width = if QR.max_width < QR.max_width_video
|
||||
QR.max_width
|
||||
else
|
||||
QR.max_width_video
|
||||
if videoHeight > max_height or videoWidth > max_width
|
||||
err.push "#{@file.name}: Video too large (video: #{videoHeight}x#{videoWidth}px, max: #{max_height}x#{max_width}px)"
|
||||
if videoHeight < QR.min_height or videoWidth < QR.min_width
|
||||
@ -261,7 +271,7 @@ QR.post = class
|
||||
URL.revokeObjectURL @URL
|
||||
|
||||
updateFilename: ->
|
||||
title = "#{@filename} (#{@filesize})\nCtrl+click to edit filename. Shift+click to clear."
|
||||
title = "#{@filename} (#{@filesize})\nCtrl/\u2318+click to edit filename. Shift+click to clear."
|
||||
@nodes.el.title = title
|
||||
return unless @ is QR.selected
|
||||
QR.nodes.fileContainer.title = title
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user