Merge branch 'v3' of git://github.com/MayhemYDG/4chan-x into v3
Conflicts: CHANGELOG.md LICENSE css/style.css html/Monitoring/ThreadWatcher.html json/archives.json package.json src/General/Config.coffee src/General/Header.coffee src/General/Main.coffee src/General/img/favicons/empty.gif src/Linkification/Linkify.coffee src/Miscellaneous/ExpandComment.coffee src/Monitoring/Favicon.coffee src/Monitoring/ThreadWatcher.coffee src/Monitoring/Unread.coffee src/Posting/QuickReply.coffee
This commit is contained in:
commit
ccc0335b79
@ -1,3 +1,8 @@
|
||||
|
||||
**MayhemYDG**:
|
||||
- New desktop notification:
|
||||
- The QR will now warn you when you are running low on cached captchas while auto-posting.
|
||||
|
||||
### v1.2.35
|
||||
*2013-08-20*
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ module.exports = (grunt) ->
|
||||
'src/Linkification/**/*'
|
||||
'src/Posting/**/*'
|
||||
'src/Images/**/*'
|
||||
'src/Linkification/**/*'
|
||||
'src/Menu/**/*'
|
||||
'src/Monitoring/**/*'
|
||||
'src/Archive/**/*'
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 4chan X - Version 1.2.35 - 2013-08-20
|
||||
* 4chan X - Version 1.2.35 - 2013-08-22
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
// ==/UserScript==
|
||||
|
||||
/*
|
||||
* 4chan X - Version 1.2.35 - 2013-08-20
|
||||
* 4chan X - Version 1.2.35 - 2013-08-22
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
@ -945,6 +945,7 @@
|
||||
Post.prototype.parseComment = function() {
|
||||
var bq, i, node, nodes, text;
|
||||
|
||||
this.nodes.comment.normalize();
|
||||
bq = this.nodes.comment.cloneNode(true);
|
||||
nodes = $$('.abbr, .capcodeReplies, .exif, b', bq);
|
||||
i = 0;
|
||||
@ -1872,7 +1873,7 @@
|
||||
return;
|
||||
}
|
||||
el = $.el('span', {
|
||||
innerHTML: "Desktop notification permissions are not granted:<br>\n<button>Authorize</button> or <button>Disable</button>"
|
||||
innerHTML: "Desktop notification permissions are not granted.\n[<a href='https://github.com/MayhemYDG/4chan-x/wiki/FAQ#desktop-notifications' target=_blank>FAQ</a>]<br>\n<button>Authorize</button> or <button>Disable</button>"
|
||||
});
|
||||
_ref = $$('button', el), authorize = _ref[0], disable = _ref[1];
|
||||
$.on(authorize, 'click', function() {
|
||||
@ -5505,7 +5506,7 @@
|
||||
}
|
||||
e.preventDefault();
|
||||
QR.open();
|
||||
QR.fileInput(e.dataTransfer.files);
|
||||
QR.handleFiles(e.dataTransfer.files);
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
},
|
||||
paste: function(e) {
|
||||
@ -5515,84 +5516,72 @@
|
||||
_ref = e.clipboardData.items;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
item = _ref[_i];
|
||||
if (item.kind === 'file') {
|
||||
blob = item.getAsFile();
|
||||
blob.name = 'file';
|
||||
if (blob.type) {
|
||||
blob.name += '.' + blob.type.split('/')[1];
|
||||
}
|
||||
files.push(blob);
|
||||
if (!(item.kind === 'file')) {
|
||||
continue;
|
||||
}
|
||||
blob = item.getAsFile();
|
||||
blob.name = 'file';
|
||||
if (blob.type) {
|
||||
blob.name += '.' + blob.type.split('/')[1];
|
||||
}
|
||||
files.push(blob);
|
||||
}
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
QR.open();
|
||||
return QR.fileInput(files);
|
||||
QR.handleFiles(files);
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
},
|
||||
openFileInput: function(e) {
|
||||
e.stopPropagation();
|
||||
if (e.shiftKey && e.type === 'click') {
|
||||
return QR.selected.rmFile();
|
||||
}
|
||||
if (e.ctrlKey && e.type === 'click') {
|
||||
$.addClass(QR.nodes.filename, 'edit');
|
||||
QR.nodes.filename.focus();
|
||||
return $.on(QR.nodes.filename, 'blur', function() {
|
||||
return $.rmClass(QR.nodes.filename, 'edit');
|
||||
});
|
||||
}
|
||||
if (e.target.nodeName === 'INPUT' || (e.keyCode && ![32, 13].contains(e.keyCode)) || e.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
return QR.nodes.fileInput.click();
|
||||
},
|
||||
fileInput: function(files) {
|
||||
var file, length, max, post, _i, _len;
|
||||
handleFiles: function(files) {
|
||||
var file, isSingle, max, _i, _len;
|
||||
|
||||
if (this instanceof Element) {
|
||||
if (this !== QR) {
|
||||
files = __slice.call(this.files);
|
||||
QR.nodes.fileInput.value = null;
|
||||
this.value = null;
|
||||
}
|
||||
length = files.length;
|
||||
if (!length) {
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
max = QR.nodes.fileInput.max;
|
||||
isSingle = files.length === 1;
|
||||
QR.cleanNotifications();
|
||||
if (length === 1) {
|
||||
file = files[0];
|
||||
if (/^text/.test(file.type)) {
|
||||
QR.selected.pasteText(file);
|
||||
} else if (file.size > max) {
|
||||
QR.error("File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ").");
|
||||
} else if (!QR.mimeTypes.contains(file.type)) {
|
||||
QR.error('Unsupported file type.');
|
||||
} else {
|
||||
QR.selected.setFile(file);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
file = files[_i];
|
||||
if (/^text/.test(file.type)) {
|
||||
if ((post = QR.posts[QR.posts.length - 1]).com) {
|
||||
post = new QR.post();
|
||||
}
|
||||
post.pasteText(file);
|
||||
} else if (file.size > max) {
|
||||
QR.error("" + file.name + ": File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ").");
|
||||
} else if (!QR.mimeTypes.contains(file.type)) {
|
||||
QR.error("" + file.name + ": Unsupported file type.");
|
||||
} else {
|
||||
if ((post = QR.posts[QR.posts.length - 1]).file) {
|
||||
post = new QR.post();
|
||||
}
|
||||
post.setFile(file);
|
||||
}
|
||||
QR.handleFile(file, isSingle, max);
|
||||
}
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
if (!isSingle) {
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
}
|
||||
},
|
||||
handleFile: function(file, isSingle, max) {
|
||||
var post;
|
||||
|
||||
if (file.size > max) {
|
||||
QR.error("" + file.name + ": File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ").");
|
||||
return;
|
||||
} else if (!QR.mimeTypes.contains(file.type)) {
|
||||
if (!/^text/.test(file.type)) {
|
||||
QR.error("" + file.name + ": Unsupported file type.");
|
||||
return;
|
||||
}
|
||||
if (isSingle) {
|
||||
post = QR.selected;
|
||||
} else if ((post = QR.posts[QR.posts.length - 1]).com) {
|
||||
post = new QR.post();
|
||||
}
|
||||
post.pasteText(file);
|
||||
return;
|
||||
}
|
||||
if (isSingle) {
|
||||
post = QR.selected;
|
||||
} else if ((post = QR.posts[QR.posts.length - 1]).file) {
|
||||
post = new QR.post();
|
||||
}
|
||||
return post.setFile(file);
|
||||
},
|
||||
openFileInput: function() {
|
||||
return QR.nodes.fileInput.click();
|
||||
},
|
||||
posts: [],
|
||||
post: (function() {
|
||||
@ -5892,7 +5881,8 @@
|
||||
return reader.readAsText(file);
|
||||
};
|
||||
|
||||
_Class.prototype.dragStart = function() {
|
||||
_Class.prototype.dragStart = function(e) {
|
||||
e.dataTransfer.setDragImage(this, e.layerX, e.layerY);
|
||||
return $.addClass(this, 'drag');
|
||||
};
|
||||
|
||||
@ -6198,7 +6188,7 @@
|
||||
$.on(nodes.spoiler, 'change', function() {
|
||||
return QR.selected.nodes.spoiler.click();
|
||||
});
|
||||
$.on(nodes.fileInput, 'change', QR.fileInput);
|
||||
$.on(nodes.fileInput, 'change', QR.handleFiles);
|
||||
items = ['name', 'email', 'sub', 'com', 'filename'];
|
||||
i = 0;
|
||||
while (name = items[i++]) {
|
||||
@ -6344,7 +6334,7 @@
|
||||
return QR.status();
|
||||
},
|
||||
response: function() {
|
||||
var URL, ban, board, err, h1, isReply, m, post, postID, req, resDoc, threadID, _, _ref, _ref1;
|
||||
var URL, ban, board, captchasCount, err, h1, isReply, m, notif, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1;
|
||||
|
||||
req = QR.req;
|
||||
delete QR.req;
|
||||
@ -6408,7 +6398,22 @@
|
||||
threadID: threadID,
|
||||
postID: postID
|
||||
});
|
||||
QR.cooldown.auto = QR.posts.length > 1 && isReply;
|
||||
postsCount = QR.posts.length;
|
||||
QR.cooldown.auto = postsCount > 1 && isReply;
|
||||
if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) {
|
||||
notif = new Notification('Quick reply warning', {
|
||||
body: "You are running low on cached captchas. Cache count: " + captchasCount + ".",
|
||||
icon: Favicon.logo
|
||||
});
|
||||
notif.onclick = function() {
|
||||
QR.open();
|
||||
QR.captcha.nodes.input.focus();
|
||||
return window.focus();
|
||||
};
|
||||
setTimeout(function() {
|
||||
return notif.close();
|
||||
}, 7 * $.SECOND);
|
||||
}
|
||||
if (!(Conf['Persistent QR'] || QR.cooldown.auto)) {
|
||||
QR.close();
|
||||
} else {
|
||||
@ -7367,7 +7372,6 @@
|
||||
return Favicon.unreadY = Favicon.unreadNSFWY;
|
||||
}
|
||||
},
|
||||
empty: 'data:image/gif;base64,R0lGODlhEAAQAJEAAAAAAP///9vb2////yH5BAEAAAMALAAAAAAQABAAAAIvnI+pq+D9DBAUoFkPFnbs7lFZKIJOJJ3MyraoB14jFpOcVMpzrnF3OKlZYsMWowAAOw==',
|
||||
dead: 'data:image/gif;base64,R0lGODlhEAAQAKECAAAAAP8AAP///////yH5BAEKAAIALAAAAAAQABAAAAIvlI+pq+D9DAgUoFkPDlbs7lFZKIJOJJ3MyraoB14jFpOcVMpzrnF3OKlZYsMWowAAOw==',
|
||||
logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAgMAAAC+UIlYAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAGlJREFUWMPtlkEKADEIA/tJP9lXLttQto2yHxgDHozTi0ToGK2WKZZ+HAQQMZc+xBwI4EZ+wAC2IfPuSIDOZJrSZQEAX9eVJhhwIuUYAnQe8rhAEMAZlTI2MID9f5Clyh0JeE1V1ZEAvB4qDfwuJTSGRAAAAABJRU5ErkJggg=='
|
||||
};
|
||||
@ -8168,7 +8172,7 @@
|
||||
return div;
|
||||
},
|
||||
refresh: function() {
|
||||
var boardID, data, list, nodes, refresher, thread, threadID, toggler, watched, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
|
||||
var boardID, data, helper, list, nodes, refresher, thread, threadID, toggler, watched, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
|
||||
|
||||
nodes = [];
|
||||
_ref = ThreadWatcher.getAll();
|
||||
@ -8187,7 +8191,9 @@
|
||||
boardID: thread.board.ID,
|
||||
threadID: threadID
|
||||
});
|
||||
$[watched ? 'addClass' : 'rmClass'](toggler, 'watched');
|
||||
helper = watched ? ['addClass', 'Unwatch'] : ['rmClass', 'Watch'];
|
||||
$[helper[0]](toggler, 'watched');
|
||||
toggler.title = "" + helper[1] + " Thread";
|
||||
}
|
||||
_ref3 = ThreadWatcher.menu.refreshers;
|
||||
for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
|
||||
@ -8390,7 +8396,7 @@
|
||||
|
||||
Unread = {
|
||||
init: function() {
|
||||
if (g.VIEW !== 'thread' || !Conf['Unread Count'] && !Conf['Unread Favicon']) {
|
||||
if (g.VIEW !== 'thread' || !Conf['Unread Count'] && !Conf['Unread Favicon'] && !Conf['Desktop Notifications']) {
|
||||
return;
|
||||
}
|
||||
this.db = new DataBoard('lastReadPosts', this.sync);
|
||||
@ -8432,18 +8438,18 @@
|
||||
}
|
||||
}
|
||||
Unread.addPosts(posts);
|
||||
if (Conf['Scroll to Last Read Post']) {
|
||||
return Unread.scroll();
|
||||
}
|
||||
return Unread.scroll();
|
||||
},
|
||||
scroll: function() {
|
||||
var checkPosition, hash, onload, post, posts, root;
|
||||
|
||||
if (!Conf['Scroll to Last Read Post']) {
|
||||
return;
|
||||
}
|
||||
if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) {
|
||||
return;
|
||||
}
|
||||
if (Unread.posts.length) {
|
||||
post = Unread.posts[0];
|
||||
if (post = Unread.posts[0]) {
|
||||
while (root = $.x('preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root)) {
|
||||
if (!(post = Get.postFromRoot(root)).isHidden) {
|
||||
break;
|
||||
@ -8467,10 +8473,7 @@
|
||||
};
|
||||
}
|
||||
checkPosition = function(target) {
|
||||
var height, top, _ref;
|
||||
|
||||
_ref = target.getBoundingClientRect(), top = _ref.top, height = _ref.height;
|
||||
return top + height - doc.clientHeight > 0;
|
||||
return target.getBoundingClientRect().bottom > doc.clientHeight;
|
||||
};
|
||||
return $.on(window, 'load', onload);
|
||||
},
|
||||
@ -8488,7 +8491,9 @@
|
||||
Unread.lastReadPost = lastReadPost;
|
||||
Unread.readArray(Unread.posts);
|
||||
Unread.readArray(Unread.postsQuotingYou);
|
||||
Unread.setLine();
|
||||
if (Conf['Unread Line']) {
|
||||
Unread.setLine();
|
||||
}
|
||||
return Unread.update();
|
||||
},
|
||||
addPosts: function(posts) {
|
||||
@ -8533,7 +8538,6 @@
|
||||
}
|
||||
Unread.postsQuotingYou.push(post);
|
||||
Unread.openNotification(post);
|
||||
return;
|
||||
}
|
||||
},
|
||||
openNotification: function(post) {
|
||||
@ -8553,7 +8557,7 @@
|
||||
};
|
||||
return setTimeout(function() {
|
||||
return notif.close();
|
||||
}, 5 * $.SECOND);
|
||||
}, 7 * $.SECOND);
|
||||
},
|
||||
onUpdate: function(e) {
|
||||
if (e.detail[404]) {
|
||||
@ -8590,7 +8594,7 @@
|
||||
return arr.splice(0, i);
|
||||
},
|
||||
read: $.debounce(50, function(e) {
|
||||
var ID, bottom, height, i, post, posts;
|
||||
var ID, height, i, post, posts;
|
||||
|
||||
if (d.hidden || !Unread.posts.length) {
|
||||
return;
|
||||
@ -8599,8 +8603,7 @@
|
||||
posts = Unread.posts;
|
||||
i = 0;
|
||||
while (post = posts[i]) {
|
||||
bottom = post.nodes.root.getBoundingClientRect().bottom;
|
||||
if (bottom < height) {
|
||||
if (post.nodes.root.getBoundingClientRect().bottom < height) {
|
||||
ID = post.ID;
|
||||
if (Conf['Mark Quotes of You']) {
|
||||
if (post.info.yours) {
|
||||
@ -8618,10 +8621,8 @@
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!Conf['Quote Threading']) {
|
||||
if (i) {
|
||||
posts.splice(0, i);
|
||||
}
|
||||
if (i && !Conf['Quote Threading']) {
|
||||
posts.splice(0, i);
|
||||
}
|
||||
if (!ID) {
|
||||
return;
|
||||
@ -8646,19 +8647,17 @@
|
||||
});
|
||||
}),
|
||||
setLine: function(force) {
|
||||
var post, root;
|
||||
var post;
|
||||
|
||||
if (!(d.hidden || force === true)) {
|
||||
return;
|
||||
}
|
||||
if (post = Unread.posts[0]) {
|
||||
root = post.nodes.root;
|
||||
if (root !== $('.thread > .replyContainer', root.parentNode)) {
|
||||
return $.before(root, Unread.hr);
|
||||
}
|
||||
} else {
|
||||
if (!(post = Unread.posts[0])) {
|
||||
return $.rm(Unread.hr);
|
||||
}
|
||||
if ($.x('preceding-sibling::div[contains(@class,"replyContainer")]', post.nodes.root)) {
|
||||
return $.before(post.nodes.root, Unread.hr);
|
||||
}
|
||||
},
|
||||
update: function() {
|
||||
var count;
|
||||
@ -8670,7 +8669,7 @@
|
||||
if (!Conf['Unread Favicon']) {
|
||||
return;
|
||||
}
|
||||
Favicon.el.href = g.DEAD ? Unread.postsQuotingYou.length ? Favicon.unreadDeadY : count ? Favicon.unreadDead : Favicon.dead : count ? Unread.postsQuotingYou.length ? Favicon.unreadY : Favicon.unread : Favicon["default"];
|
||||
Favicon.el.href = g.DEAD ? Unread.postsQuotingYou[0] ? Favicon.unreadDeadY : count ? Favicon.unreadDead : Favicon.dead : count ? Unread.postsQuotingYou[0] ? Favicon.unreadY : Favicon.unread : Favicon["default"];
|
||||
return $.add(d.head, Favicon.el);
|
||||
}
|
||||
};
|
||||
@ -11291,6 +11290,7 @@
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
new Notice('warning', 'Cookies need to be enabled on 4chan for 4chan X to properly function.', 30);
|
||||
Main.disableReports = true;
|
||||
}
|
||||
return $.event('4chanXInitFinished');
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript
|
||||
/*
|
||||
* 4chan X - Version 1.2.35 - 2013-08-20
|
||||
* 4chan X - Version 1.2.35 - 2013-08-22
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
@ -954,6 +954,7 @@
|
||||
Post.prototype.parseComment = function() {
|
||||
var bq, i, node, nodes, text;
|
||||
|
||||
this.nodes.comment.normalize();
|
||||
bq = this.nodes.comment.cloneNode(true);
|
||||
nodes = $$('.abbr, .capcodeReplies, .exif, b', bq);
|
||||
i = 0;
|
||||
@ -1886,7 +1887,7 @@
|
||||
return;
|
||||
}
|
||||
el = $.el('span', {
|
||||
innerHTML: "Desktop notification permissions are not granted:<br>\n<button>Authorize</button> or <button>Disable</button>"
|
||||
innerHTML: "Desktop notification permissions are not granted.\n[<a href='https://github.com/MayhemYDG/4chan-x/wiki/FAQ#desktop-notifications' target=_blank>FAQ</a>]<br>\n<button>Authorize</button> or <button>Disable</button>"
|
||||
});
|
||||
_ref = $$('button', el), authorize = _ref[0], disable = _ref[1];
|
||||
$.on(authorize, 'click', function() {
|
||||
@ -5179,7 +5180,7 @@
|
||||
return setTimeout(function() {
|
||||
notif.onclose = null;
|
||||
return notif.close();
|
||||
}, 5 * $.SECOND);
|
||||
}, 7 * $.SECOND);
|
||||
},
|
||||
notifications: [],
|
||||
cleanNotifications: function() {
|
||||
@ -5515,7 +5516,7 @@
|
||||
}
|
||||
e.preventDefault();
|
||||
QR.open();
|
||||
QR.fileInput(e.dataTransfer.files);
|
||||
QR.handleFiles(e.dataTransfer.files);
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
},
|
||||
paste: function(e) {
|
||||
@ -5525,84 +5526,72 @@
|
||||
_ref = e.clipboardData.items;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
item = _ref[_i];
|
||||
if (item.kind === 'file') {
|
||||
blob = item.getAsFile();
|
||||
blob.name = 'file';
|
||||
if (blob.type) {
|
||||
blob.name += '.' + blob.type.split('/')[1];
|
||||
}
|
||||
files.push(blob);
|
||||
if (!(item.kind === 'file')) {
|
||||
continue;
|
||||
}
|
||||
blob = item.getAsFile();
|
||||
blob.name = 'file';
|
||||
if (blob.type) {
|
||||
blob.name += '.' + blob.type.split('/')[1];
|
||||
}
|
||||
files.push(blob);
|
||||
}
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
QR.open();
|
||||
return QR.fileInput(files);
|
||||
QR.handleFiles(files);
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
},
|
||||
openFileInput: function(e) {
|
||||
e.stopPropagation();
|
||||
if (e.shiftKey && e.type === 'click') {
|
||||
return QR.selected.rmFile();
|
||||
}
|
||||
if (e.ctrlKey && e.type === 'click') {
|
||||
$.addClass(QR.nodes.filename, 'edit');
|
||||
QR.nodes.filename.focus();
|
||||
return $.on(QR.nodes.filename, 'blur', function() {
|
||||
return $.rmClass(QR.nodes.filename, 'edit');
|
||||
});
|
||||
}
|
||||
if (e.target.nodeName === 'INPUT' || (e.keyCode && ![32, 13].contains(e.keyCode)) || e.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
return QR.nodes.fileInput.click();
|
||||
},
|
||||
fileInput: function(files) {
|
||||
var file, length, max, post, _i, _len;
|
||||
handleFiles: function(files) {
|
||||
var file, isSingle, max, _i, _len;
|
||||
|
||||
if (this instanceof Element) {
|
||||
if (this !== QR) {
|
||||
files = __slice.call(this.files);
|
||||
QR.nodes.fileInput.value = null;
|
||||
this.value = null;
|
||||
}
|
||||
length = files.length;
|
||||
if (!length) {
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
max = QR.nodes.fileInput.max;
|
||||
isSingle = files.length === 1;
|
||||
QR.cleanNotifications();
|
||||
if (length === 1) {
|
||||
file = files[0];
|
||||
if (/^text/.test(file.type)) {
|
||||
QR.selected.pasteText(file);
|
||||
} else if (file.size > max) {
|
||||
QR.error("File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ").");
|
||||
} else if (!QR.mimeTypes.contains(file.type)) {
|
||||
QR.error('Unsupported file type.');
|
||||
} else {
|
||||
QR.selected.setFile(file);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (_i = 0, _len = files.length; _i < _len; _i++) {
|
||||
file = files[_i];
|
||||
if (/^text/.test(file.type)) {
|
||||
if ((post = QR.posts[QR.posts.length - 1]).com) {
|
||||
post = new QR.post();
|
||||
}
|
||||
post.pasteText(file);
|
||||
} else if (file.size > max) {
|
||||
QR.error("" + file.name + ": File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ").");
|
||||
} else if (!QR.mimeTypes.contains(file.type)) {
|
||||
QR.error("" + file.name + ": Unsupported file type.");
|
||||
} else {
|
||||
if ((post = QR.posts[QR.posts.length - 1]).file) {
|
||||
post = new QR.post();
|
||||
}
|
||||
post.setFile(file);
|
||||
}
|
||||
QR.handleFile(file, isSingle, max);
|
||||
}
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
if (!isSingle) {
|
||||
return $.addClass(QR.nodes.el, 'dump');
|
||||
}
|
||||
},
|
||||
handleFile: function(file, isSingle, max) {
|
||||
var post;
|
||||
|
||||
if (file.size > max) {
|
||||
QR.error("" + file.name + ": File too large (file: " + ($.bytesToString(file.size)) + ", max: " + ($.bytesToString(max)) + ").");
|
||||
return;
|
||||
} else if (!QR.mimeTypes.contains(file.type)) {
|
||||
if (!/^text/.test(file.type)) {
|
||||
QR.error("" + file.name + ": Unsupported file type.");
|
||||
return;
|
||||
}
|
||||
if (isSingle) {
|
||||
post = QR.selected;
|
||||
} else if ((post = QR.posts[QR.posts.length - 1]).com) {
|
||||
post = new QR.post();
|
||||
}
|
||||
post.pasteText(file);
|
||||
return;
|
||||
}
|
||||
if (isSingle) {
|
||||
post = QR.selected;
|
||||
} else if ((post = QR.posts[QR.posts.length - 1]).file) {
|
||||
post = new QR.post();
|
||||
}
|
||||
return post.setFile(file);
|
||||
},
|
||||
openFileInput: function() {
|
||||
return QR.nodes.fileInput.click();
|
||||
},
|
||||
posts: [],
|
||||
post: (function() {
|
||||
@ -5896,7 +5885,8 @@
|
||||
return reader.readAsText(file);
|
||||
};
|
||||
|
||||
_Class.prototype.dragStart = function() {
|
||||
_Class.prototype.dragStart = function(e) {
|
||||
e.dataTransfer.setDragImage(this, e.layerX, e.layerY);
|
||||
return $.addClass(this, 'drag');
|
||||
};
|
||||
|
||||
@ -6194,7 +6184,7 @@
|
||||
$.on(nodes.spoiler, 'change', function() {
|
||||
return QR.selected.nodes.spoiler.click();
|
||||
});
|
||||
$.on(nodes.fileInput, 'change', QR.fileInput);
|
||||
$.on(nodes.fileInput, 'change', QR.handleFiles);
|
||||
items = ['name', 'email', 'sub', 'com', 'filename'];
|
||||
i = 0;
|
||||
while (name = items[i++]) {
|
||||
@ -6329,7 +6319,7 @@
|
||||
return QR.status();
|
||||
},
|
||||
response: function() {
|
||||
var URL, ban, board, err, h1, isReply, m, post, postID, req, resDoc, threadID, _, _ref, _ref1;
|
||||
var URL, ban, board, captchasCount, err, h1, isReply, m, notif, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1;
|
||||
|
||||
req = QR.req;
|
||||
delete QR.req;
|
||||
@ -6393,7 +6383,22 @@
|
||||
threadID: threadID,
|
||||
postID: postID
|
||||
});
|
||||
QR.cooldown.auto = QR.posts.length > 1 && isReply;
|
||||
postsCount = QR.posts.length;
|
||||
QR.cooldown.auto = postsCount > 1 && isReply;
|
||||
if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) {
|
||||
notif = new Notification('Quick reply warning', {
|
||||
body: "You are running low on cached captchas. Cache count: " + captchasCount + ".",
|
||||
icon: Favicon.logo
|
||||
});
|
||||
notif.onclick = function() {
|
||||
QR.open();
|
||||
QR.captcha.nodes.input.focus();
|
||||
return window.focus();
|
||||
};
|
||||
setTimeout(function() {
|
||||
return notif.close();
|
||||
}, 7 * $.SECOND);
|
||||
}
|
||||
if (!(Conf['Persistent QR'] || QR.cooldown.auto)) {
|
||||
QR.close();
|
||||
} else {
|
||||
@ -7352,7 +7357,6 @@
|
||||
return Favicon.unreadY = Favicon.unreadNSFWY;
|
||||
}
|
||||
},
|
||||
empty: 'data:image/gif;base64,R0lGODlhEAAQAJEAAAAAAP///9vb2////yH5BAEAAAMALAAAAAAQABAAAAIvnI+pq+D9DBAUoFkPFnbs7lFZKIJOJJ3MyraoB14jFpOcVMpzrnF3OKlZYsMWowAAOw==',
|
||||
dead: 'data:image/gif;base64,R0lGODlhEAAQAKECAAAAAP8AAP///////yH5BAEKAAIALAAAAAAQABAAAAIvlI+pq+D9DAgUoFkPDlbs7lFZKIJOJJ3MyraoB14jFpOcVMpzrnF3OKlZYsMWowAAOw==',
|
||||
logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAgMAAAC+UIlYAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAGlJREFUWMPtlkEKADEIA/tJP9lXLttQto2yHxgDHozTi0ToGK2WKZZ+HAQQMZc+xBwI4EZ+wAC2IfPuSIDOZJrSZQEAX9eVJhhwIuUYAnQe8rhAEMAZlTI2MID9f5Clyh0JeE1V1ZEAvB4qDfwuJTSGRAAAAABJRU5ErkJggg=='
|
||||
};
|
||||
@ -8153,7 +8157,7 @@
|
||||
return div;
|
||||
},
|
||||
refresh: function() {
|
||||
var boardID, data, list, nodes, refresher, thread, threadID, toggler, watched, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
|
||||
var boardID, data, helper, list, nodes, refresher, thread, threadID, toggler, watched, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
|
||||
|
||||
nodes = [];
|
||||
_ref = ThreadWatcher.getAll();
|
||||
@ -8172,7 +8176,9 @@
|
||||
boardID: thread.board.ID,
|
||||
threadID: threadID
|
||||
});
|
||||
$[watched ? 'addClass' : 'rmClass'](toggler, 'watched');
|
||||
helper = watched ? ['addClass', 'Unwatch'] : ['rmClass', 'Watch'];
|
||||
$[helper[0]](toggler, 'watched');
|
||||
toggler.title = "" + helper[1] + " Thread";
|
||||
}
|
||||
_ref3 = ThreadWatcher.menu.refreshers;
|
||||
for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
|
||||
@ -8375,7 +8381,7 @@
|
||||
|
||||
Unread = {
|
||||
init: function() {
|
||||
if (g.VIEW !== 'thread' || !Conf['Unread Count'] && !Conf['Unread Favicon']) {
|
||||
if (g.VIEW !== 'thread' || !Conf['Unread Count'] && !Conf['Unread Favicon'] && !Conf['Desktop Notifications']) {
|
||||
return;
|
||||
}
|
||||
this.db = new DataBoard('lastReadPosts', this.sync);
|
||||
@ -8417,18 +8423,18 @@
|
||||
}
|
||||
}
|
||||
Unread.addPosts(posts);
|
||||
if (Conf['Scroll to Last Read Post']) {
|
||||
return Unread.scroll();
|
||||
}
|
||||
return Unread.scroll();
|
||||
},
|
||||
scroll: function() {
|
||||
var checkPosition, hash, onload, post, posts, root;
|
||||
|
||||
if (!Conf['Scroll to Last Read Post']) {
|
||||
return;
|
||||
}
|
||||
if ((hash = location.hash.match(/\d+/)) && hash[0] in Unread.thread.posts) {
|
||||
return;
|
||||
}
|
||||
if (Unread.posts.length) {
|
||||
post = Unread.posts[0];
|
||||
if (post = Unread.posts[0]) {
|
||||
while (root = $.x('preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root)) {
|
||||
if (!(post = Get.postFromRoot(root)).isHidden) {
|
||||
break;
|
||||
@ -8452,10 +8458,7 @@
|
||||
};
|
||||
}
|
||||
checkPosition = function(target) {
|
||||
var height, top, _ref;
|
||||
|
||||
_ref = target.getBoundingClientRect(), top = _ref.top, height = _ref.height;
|
||||
return top + height - doc.clientHeight > 0;
|
||||
return target.getBoundingClientRect().bottom > doc.clientHeight;
|
||||
};
|
||||
return $.on(window, 'load', onload);
|
||||
},
|
||||
@ -8473,7 +8476,9 @@
|
||||
Unread.lastReadPost = lastReadPost;
|
||||
Unread.readArray(Unread.posts);
|
||||
Unread.readArray(Unread.postsQuotingYou);
|
||||
Unread.setLine();
|
||||
if (Conf['Unread Line']) {
|
||||
Unread.setLine();
|
||||
}
|
||||
return Unread.update();
|
||||
},
|
||||
addPosts: function(posts) {
|
||||
@ -8518,7 +8523,6 @@
|
||||
}
|
||||
Unread.postsQuotingYou.push(post);
|
||||
Unread.openNotification(post);
|
||||
return;
|
||||
}
|
||||
},
|
||||
openNotification: function(post) {
|
||||
@ -8538,7 +8542,7 @@
|
||||
};
|
||||
return setTimeout(function() {
|
||||
return notif.close();
|
||||
}, 5 * $.SECOND);
|
||||
}, 7 * $.SECOND);
|
||||
},
|
||||
onUpdate: function(e) {
|
||||
if (e.detail[404]) {
|
||||
@ -8575,7 +8579,7 @@
|
||||
return arr.splice(0, i);
|
||||
},
|
||||
read: $.debounce(50, function(e) {
|
||||
var ID, bottom, height, i, post, posts;
|
||||
var ID, height, i, post, posts;
|
||||
|
||||
if (d.hidden || !Unread.posts.length) {
|
||||
return;
|
||||
@ -8584,8 +8588,7 @@
|
||||
posts = Unread.posts;
|
||||
i = 0;
|
||||
while (post = posts[i]) {
|
||||
bottom = post.nodes.root.getBoundingClientRect().bottom;
|
||||
if (bottom < height) {
|
||||
if (post.nodes.root.getBoundingClientRect().bottom < height) {
|
||||
ID = post.ID;
|
||||
if (Conf['Mark Quotes of You']) {
|
||||
if (post.info.yours) {
|
||||
@ -8603,10 +8606,8 @@
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!Conf['Quote Threading']) {
|
||||
if (i) {
|
||||
posts.splice(0, i);
|
||||
}
|
||||
if (i && !Conf['Quote Threading']) {
|
||||
posts.splice(0, i);
|
||||
}
|
||||
if (!ID) {
|
||||
return;
|
||||
@ -8631,19 +8632,17 @@
|
||||
});
|
||||
}),
|
||||
setLine: function(force) {
|
||||
var post, root;
|
||||
var post;
|
||||
|
||||
if (!(d.hidden || force === true)) {
|
||||
return;
|
||||
}
|
||||
if (post = Unread.posts[0]) {
|
||||
root = post.nodes.root;
|
||||
if (root !== $('.thread > .replyContainer', root.parentNode)) {
|
||||
return $.before(root, Unread.hr);
|
||||
}
|
||||
} else {
|
||||
if (!(post = Unread.posts[0])) {
|
||||
return $.rm(Unread.hr);
|
||||
}
|
||||
if ($.x('preceding-sibling::div[contains(@class,"replyContainer")]', post.nodes.root)) {
|
||||
return $.before(post.nodes.root, Unread.hr);
|
||||
}
|
||||
},
|
||||
update: function(dontrepeat) {
|
||||
var count;
|
||||
@ -8651,17 +8650,18 @@
|
||||
count = Unread.posts.length;
|
||||
if (Conf['Unread Count']) {
|
||||
d.title = "" + (Conf['Quoted Title'] && Unread.postsQuotingYou.length ? '(!) ' : '') + (count || !Conf['Hide Unread Count at (0)'] ? "(" + count + ") " : '') + (g.DEAD ? "/" + g.BOARD + "/ - 404" : "" + Unread.title);
|
||||
if (!dontrepeat) {
|
||||
setTimeout(function() {
|
||||
d.title = '';
|
||||
return Unread.update(true);
|
||||
}, $.SECOND);
|
||||
if (dontrepeat) {
|
||||
return;
|
||||
}
|
||||
setTimeout(function() {
|
||||
d.title = '';
|
||||
return Unread.update(true);
|
||||
}, $.SECOND);
|
||||
}
|
||||
if (!Conf['Unread Favicon']) {
|
||||
return;
|
||||
}
|
||||
return Favicon.el.href = g.DEAD ? Unread.postsQuotingYou.length ? Favicon.unreadDeadY : count ? Favicon.unreadDead : Favicon.dead : count ? Unread.postsQuotingYou.length ? Favicon.unreadY : Favicon.unread : Favicon["default"];
|
||||
return Favicon.el.href = g.DEAD ? Unread.postsQuotingYou[0] ? Favicon.unreadDeadY : count ? Favicon.unreadDead : Favicon.dead : count ? Unread.postsQuotingYou[0] ? Favicon.unreadY : Favicon.unread : Favicon["default"];
|
||||
}
|
||||
};
|
||||
|
||||
@ -11046,7 +11046,7 @@
|
||||
}));
|
||||
Main.logError({
|
||||
message: 'Chrome Storage API bug',
|
||||
error: new Error(chrome.runtime.lastError.message || 'no lastError.message')
|
||||
error: new Error('~')
|
||||
});
|
||||
}
|
||||
return Main.initFeatures();
|
||||
@ -11288,6 +11288,7 @@
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
new Notice('warning', 'Cookies need to be enabled on 4chan for 4chan X to properly function.', 30);
|
||||
Main.disableReports = true;
|
||||
}
|
||||
return $.event('4chanXInitFinished');
|
||||
},
|
||||
|
||||
@ -29,7 +29,7 @@ Header =
|
||||
@linkJustifyToggler = linkJustifyToggler.firstElementChild
|
||||
@headerToggler = headerToggler.firstElementChild
|
||||
@footerToggler = footerToggler.firstElementChild
|
||||
@shortcutToggler = shortcutToggler.firstElementChild
|
||||
@shortcutToggler = shortcutToggler.firstElementChild
|
||||
@customNavToggler = customNavToggler.firstElementChild
|
||||
|
||||
$.on menuButton, 'click', @menuToggle
|
||||
@ -362,7 +362,8 @@ Header =
|
||||
|
||||
el = $.el 'span',
|
||||
innerHTML: """
|
||||
Desktop notification permissions are not granted:<br>
|
||||
Desktop notification permissions are not granted.
|
||||
[<a href='https://github.com/MayhemYDG/4chan-x/wiki/FAQ#desktop-notifications' target=_blank>FAQ</a>]<br>
|
||||
<button>Authorize</button> or <button>Disable</button>
|
||||
"""
|
||||
[authorize, disable] = $$ 'button', el
|
||||
|
||||
@ -29,7 +29,7 @@ Main =
|
||||
# Track resolution of this bug.
|
||||
Main.logError
|
||||
message: 'Chrome Storage API bug'
|
||||
error: new Error chrome.runtime.lastError.message or 'no lastError.message'
|
||||
error: new Error '~'
|
||||
<% } %>
|
||||
Main.initFeatures()
|
||||
|
||||
@ -237,6 +237,7 @@ Main =
|
||||
localStorage.getItem '4chan-settings'
|
||||
catch err
|
||||
new Notice 'warning', 'Cookies need to be enabled on 4chan for <%= meta.name %> to properly function.', 30
|
||||
Main.disableReports = true
|
||||
|
||||
$.event '4chanXInitFinished'
|
||||
|
||||
|
||||
@ -62,6 +62,8 @@ class Post
|
||||
@kill() if that.isArchived
|
||||
|
||||
parseComment: ->
|
||||
# Merge text nodes and remove empty ones.
|
||||
@nodes.comment.normalize()
|
||||
# Get the comment's text.
|
||||
# <br> -> \n
|
||||
# Remove:
|
||||
|
||||
@ -398,4 +398,5 @@ Linkify =
|
||||
src: "//www.youtube.com/embed/#{a.dataset.uid}#{if a.dataset.option then '#' + a.dataset.option else ''}?wmode=opaque"
|
||||
title:
|
||||
api: (uid) -> "https://gdata.youtube.com/feeds/api/videos/#{uid}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"
|
||||
text: (data) -> data.entry.title.$t
|
||||
text: (data) -> data.entry.title.$t
|
||||
|
||||
|
||||
@ -45,6 +45,5 @@ Favicon =
|
||||
Favicon.unread = Favicon.unreadNSFW
|
||||
Favicon.unreadY = Favicon.unreadNSFWY
|
||||
|
||||
empty: 'data:image/gif;base64,<%= grunt.file.read("src/General/img/favicons/empty.gif", {encoding: "base64"}) %>'
|
||||
dead: 'data:image/gif;base64,<%= grunt.file.read("src/General/img/favicons/dead.gif", {encoding: "base64"}) %>'
|
||||
logo: 'data:image/png;base64,<%= grunt.file.read("src/General/img/icon128.png", {encoding: "base64"}) %>'
|
||||
|
||||
@ -177,7 +177,9 @@ ThreadWatcher =
|
||||
for threadID, thread of g.BOARD.threads
|
||||
toggler = $ '.watch-thread-link', thread.OP.nodes.post
|
||||
watched = ThreadWatcher.db.get {boardID: thread.board.ID, threadID}
|
||||
$[if watched then 'addClass' else 'rmClass'] toggler, 'watched'
|
||||
helper = if watched then ['addClass', 'Unwatch'] else ['rmClass', 'Watch']
|
||||
$[helper[0]] toggler, 'watched'
|
||||
toggler.title = "#{helper[1]} Thread"
|
||||
|
||||
for refresher in ThreadWatcher.menu.refreshers
|
||||
refresher()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
Unread =
|
||||
init: ->
|
||||
return if g.VIEW isnt 'thread' or !Conf['Unread Count'] and !Conf['Unread Favicon']
|
||||
return if g.VIEW isnt 'thread' or !Conf['Unread Count'] and !Conf['Unread Favicon'] and !Conf['Desktop Notifications']
|
||||
|
||||
@db = new DataBoard 'lastReadPosts', @sync
|
||||
@hr = $.el 'hr',
|
||||
@ -30,14 +30,14 @@ Unread =
|
||||
for ID, post of Unread.thread.posts
|
||||
posts.push post if post.isReply
|
||||
Unread.addPosts posts
|
||||
Unread.scroll() if Conf['Scroll to Last Read Post']
|
||||
Unread.scroll()
|
||||
|
||||
scroll: ->
|
||||
return unless Conf['Scroll to Last Read Post']
|
||||
# Let the header's onload callback handle it.
|
||||
return if (hash = location.hash.match /\d+/) and hash[0] of Unread.thread.posts
|
||||
if Unread.posts.length
|
||||
if post = Unread.posts[0]
|
||||
# Scroll to a non-hidden, non-OP post that's before the first unread post.
|
||||
post = Unread.posts[0]
|
||||
while root = $.x 'preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root
|
||||
break unless (post = Get.postFromRoot root).isHidden
|
||||
return unless root
|
||||
@ -48,11 +48,8 @@ Unread =
|
||||
{root} = Unread.thread.posts[posts[posts.length - 1]].nodes
|
||||
onload = -> Header.scrollToPost root if checkPosition root
|
||||
checkPosition = (target) ->
|
||||
# Don't scroll to the target if
|
||||
# - it's visible.
|
||||
# - we've scrolled past it.
|
||||
{top, height} = target.getBoundingClientRect()
|
||||
top + height - doc.clientHeight > 0
|
||||
# Scroll to the target unless we scrolled past it.
|
||||
target.getBoundingClientRect().bottom > doc.clientHeight
|
||||
# Prevent the browser to scroll back to
|
||||
# the previous scroll location on page load.
|
||||
$.on window, 'load', onload
|
||||
@ -66,7 +63,7 @@ Unread =
|
||||
Unread.lastReadPost = lastReadPost
|
||||
Unread.readArray Unread.posts
|
||||
Unread.readArray Unread.postsQuotingYou
|
||||
Unread.setLine()
|
||||
Unread.setLine() if Conf['Unread Line']
|
||||
Unread.update()
|
||||
|
||||
addPosts: (posts) ->
|
||||
@ -93,7 +90,8 @@ Unread =
|
||||
for quotelink in post.nodes.quotelinks when QR.db.get Get.postDataFromLink quotelink
|
||||
Unread.postsQuotingYou.push post
|
||||
Unread.openNotification post
|
||||
return
|
||||
return
|
||||
|
||||
openNotification: (post) ->
|
||||
return unless Header.areNotificationsEnabled
|
||||
name = if Conf['Anonymize']
|
||||
@ -108,7 +106,7 @@ Unread =
|
||||
window.focus()
|
||||
setTimeout ->
|
||||
notif.close()
|
||||
, 5 * $.SECOND
|
||||
, 7 * $.SECOND
|
||||
|
||||
onUpdate: (e) ->
|
||||
if e.detail[404]
|
||||
@ -138,8 +136,7 @@ Unread =
|
||||
i = 0
|
||||
|
||||
while post = posts[i]
|
||||
{bottom} = post.nodes.root.getBoundingClientRect()
|
||||
if bottom < height # post is completely read
|
||||
if post.nodes.root.getBoundingClientRect().bottom < height # post is not completely read
|
||||
{ID} = post
|
||||
if Conf['Mark Quotes of You']
|
||||
if post.info.yours
|
||||
@ -152,9 +149,8 @@ Unread =
|
||||
break
|
||||
i++
|
||||
|
||||
unless Conf['Quote Threading']
|
||||
if i
|
||||
posts.splice 0, i
|
||||
if i and !Conf['Quote Threading']
|
||||
posts.splice 0, i
|
||||
|
||||
return unless ID
|
||||
|
||||
@ -172,12 +168,9 @@ Unread =
|
||||
|
||||
setLine: (force) ->
|
||||
return unless d.hidden or force is true
|
||||
if post = Unread.posts[0]
|
||||
{root} = post.nodes
|
||||
if root isnt $ '.thread > .replyContainer', root.parentNode # not the first reply
|
||||
$.before root, Unread.hr
|
||||
else
|
||||
$.rm Unread.hr
|
||||
return $.rm Unread.hr unless post = Unread.posts[0]
|
||||
if $.x 'preceding-sibling::div[contains(@class,"replyContainer")]', post.nodes.root # not the first reply
|
||||
$.before post.nodes.root, Unread.hr
|
||||
|
||||
update: <% if (type === 'crx') { %>(dontrepeat) <% } %>->
|
||||
count = Unread.posts.length
|
||||
@ -189,18 +182,18 @@ Unread =
|
||||
# crbug.com/124381
|
||||
# Call it one second later,
|
||||
# but don't display outdated unread count.
|
||||
unless dontrepeat
|
||||
setTimeout ->
|
||||
d.title = ''
|
||||
Unread.update true
|
||||
, $.SECOND
|
||||
return if dontrepeat
|
||||
setTimeout ->
|
||||
d.title = ''
|
||||
Unread.update true
|
||||
, $.SECOND
|
||||
<% } %>
|
||||
|
||||
return unless Conf['Unread Favicon']
|
||||
|
||||
Favicon.el.href =
|
||||
if g.DEAD
|
||||
if Unread.postsQuotingYou.length
|
||||
if Unread.postsQuotingYou[0]
|
||||
Favicon.unreadDeadY
|
||||
else if count
|
||||
Favicon.unreadDead
|
||||
@ -208,7 +201,7 @@ Unread =
|
||||
Favicon.dead
|
||||
else
|
||||
if count
|
||||
if Unread.postsQuotingYou.length
|
||||
if Unread.postsQuotingYou[0]
|
||||
Favicon.unreadY
|
||||
else
|
||||
Favicon.unread
|
||||
|
||||
@ -165,7 +165,7 @@ QR =
|
||||
setTimeout ->
|
||||
notif.onclose = null
|
||||
notif.close()
|
||||
, 5 * $.SECOND
|
||||
, 7 * $.SECOND
|
||||
<% } %>
|
||||
|
||||
notifications: []
|
||||
@ -448,69 +448,56 @@ QR =
|
||||
return unless e.dataTransfer.files.length
|
||||
e.preventDefault()
|
||||
QR.open()
|
||||
QR.fileInput e.dataTransfer.files
|
||||
QR.handleFiles e.dataTransfer.files
|
||||
$.addClass QR.nodes.el, 'dump'
|
||||
|
||||
paste: (e) ->
|
||||
files = []
|
||||
for item in e.clipboardData.items
|
||||
if item.kind is 'file'
|
||||
blob = item.getAsFile()
|
||||
blob.name = 'file'
|
||||
blob.name += '.' + blob.type.split('/')[1] if blob.type
|
||||
files.push blob
|
||||
for item in e.clipboardData.items when item.kind is 'file'
|
||||
blob = item.getAsFile()
|
||||
blob.name = 'file'
|
||||
blob.name += '.' + blob.type.split('/')[1] if blob.type
|
||||
files.push blob
|
||||
return unless files.length
|
||||
QR.open()
|
||||
QR.fileInput files
|
||||
|
||||
openFileInput: (e) ->
|
||||
e.stopPropagation()
|
||||
if e.shiftKey and e.type is 'click'
|
||||
return QR.selected.rmFile()
|
||||
if e.ctrlKey and e.type is 'click'
|
||||
$.addClass QR.nodes.filename, 'edit'
|
||||
QR.nodes.filename.focus()
|
||||
return $.on QR.nodes.filename, 'blur', -> $.rmClass QR.nodes.filename, 'edit'
|
||||
return if e.target.nodeName is 'INPUT' or (e.keyCode and not [32, 13].contains e.keyCode) or e.ctrlKey
|
||||
e.preventDefault()
|
||||
QR.nodes.fileInput.click()
|
||||
|
||||
fileInput: (files) ->
|
||||
if @ instanceof Element # file input, revert to "files instanceof Event" after a Pale Moon update
|
||||
files = [@files...]
|
||||
QR.nodes.fileInput.value = null # Don't hold the files from being modified on windows
|
||||
{length} = files
|
||||
return unless length
|
||||
max = QR.nodes.fileInput.max
|
||||
QR.cleanNotifications()
|
||||
# Set or change current post's file.
|
||||
if length is 1
|
||||
file = files[0]
|
||||
if /^text/.test file.type
|
||||
QR.selected.pasteText file
|
||||
else if file.size > max
|
||||
QR.error "File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})."
|
||||
else unless QR.mimeTypes.contains file.type
|
||||
QR.error 'Unsupported file type.'
|
||||
else
|
||||
QR.selected.setFile file
|
||||
return
|
||||
# Create new posts with these files.
|
||||
for file in files
|
||||
if /^text/.test file.type
|
||||
if (post = QR.posts[QR.posts.length - 1]).com
|
||||
post = new QR.post()
|
||||
post.pasteText file
|
||||
else if file.size > max
|
||||
QR.error "#{file.name}: File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})."
|
||||
else unless QR.mimeTypes.contains file.type
|
||||
QR.error "#{file.name}: Unsupported file type."
|
||||
else
|
||||
if (post = QR.posts[QR.posts.length - 1]).file
|
||||
post = new QR.post()
|
||||
post.setFile file
|
||||
QR.handleFiles files
|
||||
$.addClass QR.nodes.el, 'dump'
|
||||
|
||||
handleFiles: (files) ->
|
||||
if @ isnt QR # file input
|
||||
files = [@files...]
|
||||
@value = null
|
||||
return unless files.length
|
||||
max = QR.nodes.fileInput.max
|
||||
isSingle = files.length is 1
|
||||
QR.cleanNotifications()
|
||||
for file in files
|
||||
QR.handleFile file, isSingle, max
|
||||
$.addClass QR.nodes.el, 'dump' unless isSingle
|
||||
|
||||
handleFile: (file, isSingle, max) ->
|
||||
if file.size > max
|
||||
QR.error "#{file.name}: File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})."
|
||||
return
|
||||
else unless QR.mimeTypes.contains file.type
|
||||
unless /^text/.test file.type
|
||||
QR.error "#{file.name}: Unsupported file type."
|
||||
return
|
||||
if isSingle
|
||||
post = QR.selected
|
||||
else if (post = QR.posts[QR.posts.length - 1]).com
|
||||
post = new QR.post()
|
||||
post.pasteText file
|
||||
return
|
||||
if isSingle
|
||||
post = QR.selected
|
||||
else if (post = QR.posts[QR.posts.length - 1]).file
|
||||
post = new QR.post()
|
||||
post.setFile file
|
||||
|
||||
openFileInput: ->
|
||||
QR.nodes.fileInput.click()
|
||||
|
||||
posts: []
|
||||
|
||||
post: class
|
||||
@ -748,7 +735,9 @@ QR =
|
||||
@nodes.span.textContent = @com
|
||||
reader.readAsText file
|
||||
|
||||
dragStart: -> $.addClass @, 'drag'
|
||||
dragStart: (e) ->
|
||||
e.dataTransfer.setDragImage @, e.layerX, e.layerY
|
||||
$.addClass @, 'drag'
|
||||
dragEnd: -> $.rmClass @, 'drag'
|
||||
dragEnter: -> $.addClass @, 'over'
|
||||
dragLeave: -> $.rmClass @, 'over'
|
||||
@ -987,7 +976,7 @@ QR =
|
||||
$.on nodes.fileRM, 'click', -> QR.selected.rmFile()
|
||||
$.on nodes.fileExtras, 'click', (e) -> e.stopPropagation()
|
||||
$.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click()
|
||||
$.on nodes.fileInput, 'change', QR.fileInput
|
||||
$.on nodes.fileInput, 'change', QR.handleFiles
|
||||
# save selected post's data
|
||||
items = ['name', 'email', 'sub', 'com', 'filename']
|
||||
i = 0
|
||||
@ -1213,7 +1202,19 @@ QR =
|
||||
}
|
||||
|
||||
# Enable auto-posting if we have stuff to post, disable it otherwise.
|
||||
QR.cooldown.auto = QR.posts.length > 1 and isReply
|
||||
postsCount = QR.posts.length
|
||||
QR.cooldown.auto = postsCount > 1 and isReply
|
||||
if QR.cooldown.auto and QR.captcha.isEnabled and (captchasCount = QR.captcha.captchas.length) < 3 and captchasCount < postsCount
|
||||
notif = new Notification 'Quick reply warning',
|
||||
body: "You are running low on cached captchas. Cache count: #{captchasCount}."
|
||||
icon: Favicon.logo
|
||||
notif.onclick = ->
|
||||
QR.open()
|
||||
QR.captcha.nodes.input.focus()
|
||||
window.focus()
|
||||
setTimeout ->
|
||||
notif.close()
|
||||
, 7 * $.SECOND
|
||||
|
||||
unless Conf['Persistent QR'] or QR.cooldown.auto
|
||||
QR.close()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user