Merge branch 'menu'
This commit is contained in:
commit
a63dad61b6
538
4chan_x.user.js
538
4chan_x.user.js
@ -77,7 +77,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var $, $$, Anonymize, AutoGif, Conf, Config, DeleteButton, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base;
|
var $, $$, Anonymize, ArchiveLink, AutoGif, Conf, Config, DeleteLink, DownloadLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base;
|
||||||
|
|
||||||
Config = {
|
Config = {
|
||||||
main: {
|
main: {
|
||||||
@ -86,8 +86,6 @@
|
|||||||
'Keybinds': [true, 'Binds actions to keys'],
|
'Keybinds': [true, 'Binds actions to keys'],
|
||||||
'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time'],
|
'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time'],
|
||||||
'File Info Formatting': [true, 'Reformats the file information'],
|
'File Info Formatting': [true, 'Reformats the file information'],
|
||||||
'Report Button': [true, 'Add report buttons'],
|
|
||||||
'Delete Button': [false, 'Add delete buttons'],
|
|
||||||
'Comment Expansion': [true, 'Expand too long comments'],
|
'Comment Expansion': [true, 'Expand too long comments'],
|
||||||
'Thread Expansion': [true, 'View all replies'],
|
'Thread Expansion': [true, 'View all replies'],
|
||||||
'Index Navigation': [true, 'Navigate to previous / next thread'],
|
'Index Navigation': [true, 'Navigate to previous / next thread'],
|
||||||
@ -110,6 +108,13 @@
|
|||||||
'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'],
|
'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'],
|
||||||
'Expand From Current': [false, 'Expand images from current position to thread end.']
|
'Expand From Current': [false, 'Expand images from current position to thread end.']
|
||||||
},
|
},
|
||||||
|
Menu: {
|
||||||
|
'Menu': [true, 'Add a drop-down menu in posts.'],
|
||||||
|
'Report Link': [true, 'Add a report link to the menu.'],
|
||||||
|
'Delete Link': [true, 'Add a delete link to the menu.'],
|
||||||
|
'Download Link': [true, 'Add a download with original filename link to the menu. Chrome-only currently.'],
|
||||||
|
'Archive Link': [true, 'Add an archive link to the menu.']
|
||||||
|
},
|
||||||
Monitoring: {
|
Monitoring: {
|
||||||
'Thread Updater': [true, 'Update threads. Has more options in its own dialog.'],
|
'Thread Updater': [true, 'Update threads. Has more options in its own dialog.'],
|
||||||
'Unread Count': [true, 'Show unread post count in tab title'],
|
'Unread Count': [true, 'Show unread post count in tab title'],
|
||||||
@ -419,7 +424,7 @@
|
|||||||
},
|
},
|
||||||
nodes: function(nodes) {
|
nodes: function(nodes) {
|
||||||
var frag, node, _i, _len;
|
var frag, node, _i, _len;
|
||||||
if (nodes instanceof Node) {
|
if (!(nodes instanceof Array)) {
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
frag = d.createDocumentFragment();
|
frag = d.createDocumentFragment();
|
||||||
@ -712,8 +717,12 @@
|
|||||||
filename: function(post) {
|
filename: function(post) {
|
||||||
var file, fileInfo;
|
var file, fileInfo;
|
||||||
fileInfo = post.fileInfo;
|
fileInfo = post.fileInfo;
|
||||||
if (fileInfo && (file = $('.fileText > span', fileInfo))) {
|
if (fileInfo) {
|
||||||
return file.title;
|
if (file = $('.fileText > span', fileInfo)) {
|
||||||
|
return file.title;
|
||||||
|
} else {
|
||||||
|
return fileInfo.firstElementChild.dataset.filename;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -740,6 +749,74 @@
|
|||||||
return img.dataset.md5;
|
return img.dataset.md5;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
},
|
||||||
|
menuInit: function() {
|
||||||
|
var div, entry, type, _i, _len, _ref;
|
||||||
|
div = $.el('div', {
|
||||||
|
textContent: 'Filter'
|
||||||
|
});
|
||||||
|
entry = {
|
||||||
|
el: div,
|
||||||
|
open: function() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
_ref = [['Name', 'name'], ['Unique ID', 'uniqueid'], ['Tripcode', 'tripcode'], ['Admin/Mod', 'mod'], ['E-mail', 'email'], ['Subject', 'subject'], ['Comment', 'comment'], ['Country', 'country'], ['Filename', 'filename'], ['Image dimensions', 'dimensions'], ['Filesize', 'filesize'], ['Image MD5', 'md5']];
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
type = _ref[_i];
|
||||||
|
entry.children.push(Filter.createSubEntry(type[0], type[1]));
|
||||||
|
}
|
||||||
|
return Menu.addEntry(entry);
|
||||||
|
},
|
||||||
|
createSubEntry: function(text, type) {
|
||||||
|
var el, onclick, open;
|
||||||
|
el = $.el('a', {
|
||||||
|
href: 'javascript:;',
|
||||||
|
textContent: text
|
||||||
|
});
|
||||||
|
onclick = null;
|
||||||
|
open = function(post) {
|
||||||
|
var value;
|
||||||
|
value = Filter[type](post);
|
||||||
|
if (value === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$.off(el, 'click', onclick);
|
||||||
|
onclick = function() {
|
||||||
|
var re, save, select, ta, tl;
|
||||||
|
re = type === 'md5' ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) {
|
||||||
|
if (c === '\n') {
|
||||||
|
return '\\n';
|
||||||
|
} else if (c === '\\') {
|
||||||
|
return '\\\\';
|
||||||
|
} else {
|
||||||
|
return "\\" + c;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
re = type === 'md5' ? "/" + value + "/" : "/^" + re + "$/";
|
||||||
|
if (/\bop\b/.test(post["class"])) {
|
||||||
|
re += ';op:yes';
|
||||||
|
}
|
||||||
|
save = (save = $.get(type, '')) ? "" + save + "\n" + re : re;
|
||||||
|
$.set(type, save);
|
||||||
|
Options.dialog();
|
||||||
|
select = $('select[name=filter]', $.id('options'));
|
||||||
|
select.value = type;
|
||||||
|
$.event(select, new Event('change'));
|
||||||
|
$.id('filter_tab').checked = true;
|
||||||
|
ta = select.nextElementSibling;
|
||||||
|
tl = ta.textLength;
|
||||||
|
ta.setSelectionRange(tl, tl);
|
||||||
|
return ta.focus();
|
||||||
|
};
|
||||||
|
$.on(el, 'click', onclick);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
el: el,
|
||||||
|
open: open
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -910,7 +987,7 @@
|
|||||||
quote.href = "res/" + href;
|
quote.href = "res/" + href;
|
||||||
}
|
}
|
||||||
id = reply.id.slice(2);
|
id = reply.id.slice(2);
|
||||||
link = $('.postInfo > .postNum > a[title="Highlight this post"]', reply);
|
link = $('.postNum > a[title="Highlight this post"]', reply);
|
||||||
link.href = "res/" + threadID + "#p" + id;
|
link.href = "res/" + threadID + "#p" + id;
|
||||||
link.nextSibling.href = "res/" + threadID + "#q" + id;
|
link.nextSibling.href = "res/" + threadID + "#q" + id;
|
||||||
nodes.push(reply);
|
nodes.push(reply);
|
||||||
@ -951,7 +1028,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
cb: function() {
|
cb: function() {
|
||||||
return ThreadHiding.toggle(this.parentNode);
|
return ThreadHiding.toggle($.x('ancestor::div[parent::div[@class="board"]]', this));
|
||||||
},
|
},
|
||||||
toggle: function(thread) {
|
toggle: function(thread) {
|
||||||
var hiddenThreads, id;
|
var hiddenThreads, id;
|
||||||
@ -967,7 +1044,7 @@
|
|||||||
return $.set("hiddenThreads/" + g.BOARD + "/", hiddenThreads);
|
return $.set("hiddenThreads/" + g.BOARD + "/", hiddenThreads);
|
||||||
},
|
},
|
||||||
hide: function(thread, show_stub) {
|
hide: function(thread, show_stub) {
|
||||||
var a, num, opInfo, span, text;
|
var a, menuButton, num, opInfo, span, stub, text;
|
||||||
if (show_stub == null) {
|
if (show_stub == null) {
|
||||||
show_stub = Conf['Show Stubs'];
|
show_stub = Conf['Show Stubs'];
|
||||||
}
|
}
|
||||||
@ -986,19 +1063,24 @@
|
|||||||
num += $$('.opContainer ~ .replyContainer', thread).length;
|
num += $$('.opContainer ~ .replyContainer', thread).length;
|
||||||
text = num === 1 ? '1 reply' : "" + num + " replies";
|
text = num === 1 ? '1 reply' : "" + num + " replies";
|
||||||
opInfo = $('.op > .postInfo > .nameBlock', thread).textContent;
|
opInfo = $('.op > .postInfo > .nameBlock', thread).textContent;
|
||||||
a = $.el('a', {
|
stub = $.el('div', {
|
||||||
className: 'hide_thread_button hidden_thread',
|
className: 'hide_thread_button hidden_thread',
|
||||||
innerHTML: '<span>[ + ]</span>',
|
innerHTML: '<a href="javascript:;"><span>[ + ]</span> </a>'
|
||||||
href: 'javascript:;'
|
|
||||||
});
|
});
|
||||||
$.add(a, $.tn(" " + opInfo + " (" + text + ")"));
|
a = stub.firstChild;
|
||||||
$.on(a, 'click', ThreadHiding.cb);
|
$.on(a, 'click', ThreadHiding.cb);
|
||||||
return $.prepend(thread, a);
|
$.add(a, $.tn("" + opInfo + " (" + text + ")"));
|
||||||
|
if (Conf['Menu']) {
|
||||||
|
menuButton = Menu.a.cloneNode(true);
|
||||||
|
$.on(menuButton, 'click', Menu.toggle);
|
||||||
|
$.add(stub, [$.tn(' '), menuButton]);
|
||||||
|
}
|
||||||
|
return $.prepend(thread, stub);
|
||||||
},
|
},
|
||||||
show: function(thread) {
|
show: function(thread) {
|
||||||
var a;
|
var stub;
|
||||||
if (a = $('.hidden_thread', thread)) {
|
if (stub = $('.hidden_thread', thread)) {
|
||||||
$.rm(a);
|
$.rm(stub);
|
||||||
}
|
}
|
||||||
thread.hidden = false;
|
thread.hidden = false;
|
||||||
return thread.nextElementSibling.hidden = false;
|
return thread.nextElementSibling.hidden = false;
|
||||||
@ -1046,7 +1128,7 @@
|
|||||||
return $.set("hiddenReplies/" + g.BOARD + "/", g.hiddenReplies);
|
return $.set("hiddenReplies/" + g.BOARD + "/", g.hiddenReplies);
|
||||||
},
|
},
|
||||||
hide: function(root, show_stub) {
|
hide: function(root, show_stub) {
|
||||||
var a, el, side, stub;
|
var a, el, menuButton, side, stub;
|
||||||
if (show_stub == null) {
|
if (show_stub == null) {
|
||||||
show_stub = Conf['Show Stubs'];
|
show_stub = Conf['Show Stubs'];
|
||||||
}
|
}
|
||||||
@ -1065,8 +1147,13 @@
|
|||||||
innerHTML: '<a href="javascript:;"><span>[ + ]</span> </a>'
|
innerHTML: '<a href="javascript:;"><span>[ + ]</span> </a>'
|
||||||
});
|
});
|
||||||
a = stub.firstChild;
|
a = stub.firstChild;
|
||||||
$.add(a, $.tn($('.nameBlock', el).textContent));
|
|
||||||
$.on(a, 'click', ReplyHiding.toggle);
|
$.on(a, 'click', ReplyHiding.toggle);
|
||||||
|
$.add(a, $.tn($('.nameBlock', el).textContent));
|
||||||
|
if (Conf['Menu']) {
|
||||||
|
menuButton = Menu.a.cloneNode(true);
|
||||||
|
$.on(menuButton, 'click', Menu.toggle);
|
||||||
|
$.add(stub, [$.tn(' '), menuButton]);
|
||||||
|
}
|
||||||
return $.prepend(root, stub);
|
return $.prepend(root, stub);
|
||||||
},
|
},
|
||||||
show: function(root) {
|
show: function(root) {
|
||||||
@ -1079,6 +1166,186 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Menu = {
|
||||||
|
entries: [],
|
||||||
|
init: function() {
|
||||||
|
this.a = $.el('a', {
|
||||||
|
className: 'menu_button',
|
||||||
|
href: 'javascript:;',
|
||||||
|
innerHTML: '[<span></span>]'
|
||||||
|
});
|
||||||
|
this.el = $.el('div', {
|
||||||
|
className: 'reply dialog',
|
||||||
|
id: 'menu',
|
||||||
|
tabIndex: 0
|
||||||
|
});
|
||||||
|
$.on(this.el, 'click', function(e) {
|
||||||
|
return e.stopPropagation();
|
||||||
|
});
|
||||||
|
$.on(this.el, 'keydown', this.keybinds);
|
||||||
|
$.on(d, 'AddMenuEntry', function(e) {
|
||||||
|
return Menu.addEntry(e.detail);
|
||||||
|
});
|
||||||
|
return Main.callbacks.push(this.node);
|
||||||
|
},
|
||||||
|
node: function(post) {
|
||||||
|
var a;
|
||||||
|
if (post.isInlined && !post.isCrosspost) {
|
||||||
|
a = $('.menu_button', post.el);
|
||||||
|
} else {
|
||||||
|
a = Menu.a.cloneNode(true);
|
||||||
|
$.add($('.postInfo', post.el), a);
|
||||||
|
}
|
||||||
|
return $.on(a, 'click', Menu.toggle);
|
||||||
|
},
|
||||||
|
toggle: function(e) {
|
||||||
|
var lastOpener, post;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (Menu.el.parentNode) {
|
||||||
|
lastOpener = Menu.lastOpener;
|
||||||
|
Menu.close();
|
||||||
|
if (lastOpener === this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Menu.lastOpener = this;
|
||||||
|
post = /\bhidden_thread\b/.test(this.parentNode.className) ? $.x('ancestor::div[parent::div[@class="board"]]/child::div[contains(@class,"opContainer")]', this) : $.x('ancestor::div[contains(@class,"postContainer")][1]', this);
|
||||||
|
return Menu.open(this, Main.preParse(post));
|
||||||
|
},
|
||||||
|
open: function(button, post) {
|
||||||
|
var bLeft, bRect, bTop, el, entry, funk, mRect, _i, _len, _ref;
|
||||||
|
el = Menu.el;
|
||||||
|
el.setAttribute('data-id', post.ID);
|
||||||
|
el.setAttribute('data-rootid', post.root.id);
|
||||||
|
funk = function(entry, parent) {
|
||||||
|
var child, children, subMenu, _i, _len;
|
||||||
|
children = entry.children;
|
||||||
|
if (!entry.open(post)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.add(parent, entry.el);
|
||||||
|
if (!children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subMenu = $('.subMenu', entry.el)) {
|
||||||
|
$.rm(subMenu);
|
||||||
|
}
|
||||||
|
subMenu = $.el('div', {
|
||||||
|
className: 'reply dialog subMenu'
|
||||||
|
});
|
||||||
|
$.add(entry.el, subMenu);
|
||||||
|
for (_i = 0, _len = children.length; _i < _len; _i++) {
|
||||||
|
child = children[_i];
|
||||||
|
funk(child, subMenu);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_ref = Menu.entries;
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
entry = _ref[_i];
|
||||||
|
funk(entry, el);
|
||||||
|
}
|
||||||
|
Menu.focus($('.entry', Menu.el));
|
||||||
|
$.on(d, 'click', Menu.close);
|
||||||
|
$.add(d.body, el);
|
||||||
|
mRect = el.getBoundingClientRect();
|
||||||
|
bRect = button.getBoundingClientRect();
|
||||||
|
bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top;
|
||||||
|
bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left;
|
||||||
|
el.style.top = bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight ? bTop + bRect.height + 2 + 'px' : bTop - mRect.height - 2 + 'px';
|
||||||
|
el.style.left = bRect.left + mRect.width < d.documentElement.clientWidth ? bLeft + 'px' : bLeft + bRect.width - mRect.width + 'px';
|
||||||
|
return el.focus();
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
var el, focused, _i, _len, _ref;
|
||||||
|
el = Menu.el;
|
||||||
|
$.rm(el);
|
||||||
|
_ref = $$('.focused.entry', el);
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
focused = _ref[_i];
|
||||||
|
$.rmClass(focused, 'focused');
|
||||||
|
}
|
||||||
|
el.innerHTML = null;
|
||||||
|
el.removeAttribute('style');
|
||||||
|
delete Menu.lastOpener;
|
||||||
|
delete Menu.focusedEntry;
|
||||||
|
return $.off(d, 'click', Menu.close);
|
||||||
|
},
|
||||||
|
keybinds: function(e) {
|
||||||
|
var el, next, subMenu;
|
||||||
|
el = Menu.focusedEntry;
|
||||||
|
switch (Keybinds.keyCode(e) || e.keyCode) {
|
||||||
|
case 'Esc':
|
||||||
|
Menu.lastOpener.focus();
|
||||||
|
Menu.close();
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
case 32:
|
||||||
|
el.click();
|
||||||
|
break;
|
||||||
|
case 'Up':
|
||||||
|
if (next = el.previousElementSibling) {
|
||||||
|
Menu.focus(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Down':
|
||||||
|
if (next = el.nextElementSibling) {
|
||||||
|
Menu.focus(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Right':
|
||||||
|
if ((subMenu = $('.subMenu', el)) && (next = subMenu.firstElementChild)) {
|
||||||
|
Menu.focus(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Left':
|
||||||
|
if (next = $.x('parent::*[contains(@class,"subMenu")]/parent::*', el)) {
|
||||||
|
Menu.focus(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
return e.stopPropagation();
|
||||||
|
},
|
||||||
|
focus: function(el) {
|
||||||
|
var focused, _i, _len, _ref;
|
||||||
|
if (focused = $.x('parent::*/child::*[contains(@class,"focused")]', el)) {
|
||||||
|
$.rmClass(focused, 'focused');
|
||||||
|
}
|
||||||
|
_ref = $$('.focused', el);
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
focused = _ref[_i];
|
||||||
|
$.rmClass(focused, 'focused');
|
||||||
|
}
|
||||||
|
Menu.focusedEntry = el;
|
||||||
|
return $.addClass(el, 'focused');
|
||||||
|
},
|
||||||
|
addEntry: function(entry) {
|
||||||
|
var funk;
|
||||||
|
funk = function(entry) {
|
||||||
|
var child, children, el, _i, _len;
|
||||||
|
el = entry.el, children = entry.children;
|
||||||
|
$.addClass(el, 'entry');
|
||||||
|
$.on(el, 'focus mouseover', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
return Menu.focus(this);
|
||||||
|
});
|
||||||
|
if (!children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.addClass(el, 'hasSubMenu');
|
||||||
|
for (_i = 0, _len = children.length; _i < _len; _i++) {
|
||||||
|
child = children[_i];
|
||||||
|
funk(child);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
funk(entry);
|
||||||
|
return Menu.entries.push(entry);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Keybinds = {
|
Keybinds = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var node, _i, _len, _ref;
|
var node, _i, _len, _ref;
|
||||||
@ -1300,7 +1567,7 @@
|
|||||||
},
|
},
|
||||||
qr: function(thread, quote) {
|
qr: function(thread, quote) {
|
||||||
if (quote) {
|
if (quote) {
|
||||||
QR.quote.call($('.postInfo > .postNum > a[title="Quote this post"]', $('.post.highlight', thread) || thread));
|
QR.quote.call($('.postNum > a[title="Quote this post"]', $('.post.highlight', thread) || thread));
|
||||||
} else {
|
} else {
|
||||||
QR.open();
|
QR.open();
|
||||||
}
|
}
|
||||||
@ -1455,7 +1722,7 @@
|
|||||||
return $.on(d, 'dragstart dragend', QR.drag);
|
return $.on(d, 'dragstart dragend', QR.drag);
|
||||||
},
|
},
|
||||||
node: function(post) {
|
node: function(post) {
|
||||||
return $.on($('.postInfo > .postNum > a[title="Quote this post"]', post.el), 'click', QR.quote);
|
return $.on($('.postNum > a[title="Quote this post"]', post.el), 'click', QR.quote);
|
||||||
},
|
},
|
||||||
open: function() {
|
open: function() {
|
||||||
if (QR.el) {
|
if (QR.el) {
|
||||||
@ -3078,6 +3345,7 @@
|
|||||||
fullname: span.title,
|
fullname: span.title,
|
||||||
shortname: span.textContent
|
shortname: span.textContent
|
||||||
};
|
};
|
||||||
|
node.setAttribute('data-filename', span.title);
|
||||||
return node.innerHTML = FileInfo.funk(FileInfo);
|
return node.innerHTML = FileInfo.funk(FileInfo);
|
||||||
},
|
},
|
||||||
setFormats: function() {
|
setFormats: function() {
|
||||||
@ -3211,7 +3479,7 @@
|
|||||||
}
|
}
|
||||||
quote.href = "/" + board + "/res/" + href;
|
quote.href = "/" + board + "/res/" + href;
|
||||||
}
|
}
|
||||||
link = $('.postInfo > .postNum > a[title="Highlight this post"]', pc);
|
link = $('.postNum > a[title="Highlight this post"]', pc);
|
||||||
link.href = "/" + board + "/res/" + threadID + "#p" + postID;
|
link.href = "/" + board + "/res/" + threadID + "#p" + postID;
|
||||||
link.nextSibling.href = "/" + board + "/res/" + threadID + "#q" + postID;
|
link.nextSibling.href = "/" + board + "/res/" + threadID + "#q" + postID;
|
||||||
$.replace(root.firstChild, pc);
|
$.replace(root.firstChild, pc);
|
||||||
@ -3369,13 +3637,13 @@
|
|||||||
}));
|
}));
|
||||||
$.after((isOP ? piM : pi), file);
|
$.after((isOP ? piM : pi), file);
|
||||||
}
|
}
|
||||||
$.replace(root.firstChild, pc);
|
$.replace(root.firstChild, Get.cleanPost(pc));
|
||||||
if (cb) {
|
if (cb) {
|
||||||
return cb();
|
return cb();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cleanPost: function(root) {
|
cleanPost: function(root) {
|
||||||
var child, el, inline, inlined, now, post, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3;
|
var child, el, els, inline, inlined, now, post, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2;
|
||||||
post = $('.post', root);
|
post = $('.post', root);
|
||||||
_ref = Array.prototype.slice.call(root.childNodes);
|
_ref = Array.prototype.slice.call(root.childNodes);
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
@ -3395,9 +3663,10 @@
|
|||||||
$.rmClass(inlined, 'inlined');
|
$.rmClass(inlined, 'inlined');
|
||||||
}
|
}
|
||||||
now = Date.now();
|
now = Date.now();
|
||||||
_ref3 = $$('[id]', root);
|
els = $$('[id]', root);
|
||||||
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
|
els.push(root);
|
||||||
el = _ref3[_l];
|
for (_l = 0, _len3 = els.length; _l < _len3; _l++) {
|
||||||
|
el = els[_l];
|
||||||
el.id = "" + now + "_" + el.id;
|
el.id = "" + now + "_" + el.id;
|
||||||
}
|
}
|
||||||
$.rmClass(root, 'forwarded');
|
$.rmClass(root, 'forwarded');
|
||||||
@ -3754,7 +4023,7 @@
|
|||||||
nodes.push($.tn(text));
|
nodes.push($.tn(text));
|
||||||
}
|
}
|
||||||
id = quote.match(/\d+$/)[0];
|
id = quote.match(/\d+$/)[0];
|
||||||
board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : $('.postInfo > .postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1];
|
board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1];
|
||||||
nodes.push(a = $.el('a', {
|
nodes.push(a = $.el('a', {
|
||||||
textContent: "" + quote + "\u00A0(Dead)"
|
textContent: "" + quote + "\u00A0(Dead)"
|
||||||
}));
|
}));
|
||||||
@ -3782,46 +4051,44 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
DeleteButton = {
|
DeleteLink = {
|
||||||
init: function() {
|
init: function() {
|
||||||
this.a = $.el('a', {
|
var a;
|
||||||
className: 'delete_button',
|
a = $.el('a', {
|
||||||
innerHTML: '[ × ]',
|
className: 'delete_link',
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
});
|
});
|
||||||
return Main.callbacks.push(this.node);
|
return Menu.addEntry({
|
||||||
},
|
el: a,
|
||||||
node: function(post) {
|
open: function(post) {
|
||||||
var a;
|
if (post.isArchived) {
|
||||||
if (!(a = $('.delete_button', post.el))) {
|
return false;
|
||||||
a = DeleteButton.a.cloneNode(true);
|
}
|
||||||
$.add($('.postInfo', post.el), a);
|
a.textContent = 'Delete this post';
|
||||||
}
|
$.on(a, 'click', DeleteLink["delete"]);
|
||||||
return $.on(a, 'click', DeleteButton["delete"]);
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
"delete": function() {
|
"delete": function() {
|
||||||
var board, form, id, m, pwd, self;
|
var board, form, id, m, pwd, self;
|
||||||
$.off(this, 'click', DeleteButton["delete"]);
|
$.off(this, 'click', DeleteLink["delete"]);
|
||||||
this.innerHTML = '[ Deleting... ]';
|
this.textContent = 'Deleting...';
|
||||||
if (m = d.cookie.match(/4chan_pass=([^;]+)/)) {
|
pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $.id('delPassword').value;
|
||||||
pwd = decodeURIComponent(m[1]);
|
id = this.parentNode.dataset.id;
|
||||||
} else {
|
board = $('.postNum > a[title="Highlight this post"]', $.id(this.parentNode.dataset.rootid)).pathname.split('/')[1];
|
||||||
pwd = $.id('delPassword').value;
|
|
||||||
}
|
|
||||||
id = $.x('preceding-sibling::input', this).name;
|
|
||||||
board = $.x('preceding-sibling::span[1]/a', this).pathname.match(/\w+/)[0];
|
|
||||||
self = this;
|
self = this;
|
||||||
form = {
|
form = {
|
||||||
mode: 'usrdel',
|
mode: 'usrdel',
|
||||||
pwd: pwd
|
pwd: pwd
|
||||||
};
|
};
|
||||||
form[id] = 'delete';
|
form[id] = 'delete';
|
||||||
return $.ajax("https://sys.4chan.org/" + board + "/imgboard.php", {
|
return $.ajax($.id('delform').action.replace("/" + g.BOARD + "/", "/" + board + "/"), {
|
||||||
onload: function() {
|
onload: function() {
|
||||||
return DeleteButton.load(self, this.response);
|
return DeleteLink.load(self, this.response);
|
||||||
},
|
},
|
||||||
onerror: function() {
|
onerror: function() {
|
||||||
return DeleteButton.error(self);
|
return DeleteLink.error(self);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
form: $.formData(form)
|
form: $.formData(form)
|
||||||
@ -3835,44 +4102,93 @@
|
|||||||
s = 'Banned!';
|
s = 'Banned!';
|
||||||
} else if (msg = doc.getElementById('errmsg')) {
|
} else if (msg = doc.getElementById('errmsg')) {
|
||||||
s = msg.textContent;
|
s = msg.textContent;
|
||||||
$.on(self, 'click', DeleteButton["delete"]);
|
$.on(self, 'click', DeleteLink["delete"]);
|
||||||
} else {
|
} else {
|
||||||
s = 'Deleted';
|
s = 'Deleted';
|
||||||
}
|
}
|
||||||
return self.innerHTML = "[ " + s + " ]";
|
return self.textContent = s;
|
||||||
},
|
},
|
||||||
error: function(self) {
|
error: function(self) {
|
||||||
self.innerHTML = '[ Connection error, please retry. ]';
|
self.textContent = 'Connection error, please retry.';
|
||||||
return $.on(self, 'click', DeleteButton["delete"]);
|
return $.on(self, 'click', DeleteLink["delete"]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ReportButton = {
|
ReportLink = {
|
||||||
init: function() {
|
init: function() {
|
||||||
this.a = $.el('a', {
|
|
||||||
className: 'report_button',
|
|
||||||
innerHTML: '[ ! ]',
|
|
||||||
href: 'javascript:;'
|
|
||||||
});
|
|
||||||
return Main.callbacks.push(this.node);
|
|
||||||
},
|
|
||||||
node: function(post) {
|
|
||||||
var a;
|
var a;
|
||||||
if (!(a = $('.report_button', post.el))) {
|
a = $.el('a', {
|
||||||
a = ReportButton.a.cloneNode(true);
|
className: 'report_link',
|
||||||
$.add($('.postInfo', post.el), a);
|
href: 'javascript:;',
|
||||||
}
|
textContent: 'Report this post'
|
||||||
return $.on(a, 'click', ReportButton.report);
|
});
|
||||||
|
$.on(a, 'click', this.report);
|
||||||
|
return Menu.addEntry({
|
||||||
|
el: a,
|
||||||
|
open: function(post) {
|
||||||
|
return post.isArchived === false;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
report: function() {
|
report: function() {
|
||||||
var id, set, url;
|
var a, id, set, url;
|
||||||
url = "//sys.4chan.org/" + g.BOARD + "/imgboard.php?mode=report&no=" + ($.x('preceding-sibling::input', this).name);
|
a = $('.postNum > a[title="Highlight this post"]', $.id(this.parentNode.dataset.rootid));
|
||||||
|
url = "//sys.4chan.org/" + (a.pathname.split('/')[1]) + "/imgboard.php?mode=report&no=" + this.parentNode.dataset.id;
|
||||||
id = Date.now();
|
id = Date.now();
|
||||||
set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200";
|
set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200";
|
||||||
return window.open(url, id, set);
|
return window.open(url, id, set);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DownloadLink = {
|
||||||
|
init: function() {
|
||||||
|
var a;
|
||||||
|
if ($.el('a').download === void 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
a = $.el('a', {
|
||||||
|
className: 'download_link',
|
||||||
|
textContent: 'Download file'
|
||||||
|
});
|
||||||
|
return Menu.addEntry({
|
||||||
|
el: a,
|
||||||
|
open: function(post) {
|
||||||
|
var fileText;
|
||||||
|
if (!post.img) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
a.href = post.img.parentNode.href;
|
||||||
|
fileText = post.fileInfo.firstElementChild;
|
||||||
|
a.download = Conf['File Info Formatting'] ? fileText.dataset.filename : $('span', fileText).title;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ArchiveLink = {
|
||||||
|
init: function() {
|
||||||
|
var a;
|
||||||
|
a = $.el('a', {
|
||||||
|
className: 'archive_link',
|
||||||
|
target: '_blank',
|
||||||
|
textContent: 'Archived post'
|
||||||
|
});
|
||||||
|
return Menu.addEntry({
|
||||||
|
el: a,
|
||||||
|
open: function(post) {
|
||||||
|
var href, path;
|
||||||
|
path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/');
|
||||||
|
if ((href = Redirect.thread(path[1], path[3], post.ID)) === ("//boards.4chan.org/" + path[1] + "/")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
a.href = href;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ThreadStats = {
|
ThreadStats = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var dialog;
|
var dialog;
|
||||||
@ -4531,11 +4847,23 @@
|
|||||||
if (Conf['Image Hover']) {
|
if (Conf['Image Hover']) {
|
||||||
ImageHover.init();
|
ImageHover.init();
|
||||||
}
|
}
|
||||||
if (Conf['Report Button']) {
|
if (Conf['Menu']) {
|
||||||
ReportButton.init();
|
Menu.init();
|
||||||
}
|
if (Conf['Report Link']) {
|
||||||
if (Conf['Delete Button']) {
|
ReportLink.init();
|
||||||
DeleteButton.init();
|
}
|
||||||
|
if (Conf['Delete Link']) {
|
||||||
|
DeleteLink.init();
|
||||||
|
}
|
||||||
|
if (Conf['Filter']) {
|
||||||
|
Filter.menuInit();
|
||||||
|
}
|
||||||
|
if (Conf['Download Link']) {
|
||||||
|
DownloadLink.init();
|
||||||
|
}
|
||||||
|
if (Conf['Archive Link']) {
|
||||||
|
ArchiveLink.init();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Conf['Resurrect Quotes']) {
|
if (Conf['Resurrect Quotes']) {
|
||||||
Quotify.init();
|
Quotify.init();
|
||||||
@ -4799,6 +5127,60 @@ a[href="javascript:;"] {\
|
|||||||
display: none !important;\
|
display: none !important;\
|
||||||
}\
|
}\
|
||||||
\
|
\
|
||||||
|
.menu_button {\
|
||||||
|
display: inline-block;\
|
||||||
|
}\
|
||||||
|
.menu_button > span {\
|
||||||
|
border-top: .5em solid;\
|
||||||
|
border-right: .3em solid transparent;\
|
||||||
|
border-left: .3em solid transparent;\
|
||||||
|
display: inline-block;\
|
||||||
|
margin: 2px;\
|
||||||
|
vertical-align: middle;\
|
||||||
|
}\
|
||||||
|
#menu {\
|
||||||
|
position: absolute;\
|
||||||
|
outline: none;\
|
||||||
|
}\
|
||||||
|
.entry {\
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, .25);\
|
||||||
|
cursor: pointer;\
|
||||||
|
display: block;\
|
||||||
|
outline: none;\
|
||||||
|
padding: 3px 7px;\
|
||||||
|
position: relative;\
|
||||||
|
text-decoration: none;\
|
||||||
|
white-space: nowrap;\
|
||||||
|
}\
|
||||||
|
.entry:last-child {\
|
||||||
|
border: none;\
|
||||||
|
}\
|
||||||
|
.focused.entry {\
|
||||||
|
background: rgba(255, 255, 255, .33);\
|
||||||
|
}\
|
||||||
|
.entry.hasSubMenu {\
|
||||||
|
padding-right: 1.5em;\
|
||||||
|
}\
|
||||||
|
.hasSubMenu::after {\
|
||||||
|
content: "";\
|
||||||
|
border-left: .5em solid;\
|
||||||
|
border-top: .3em solid transparent;\
|
||||||
|
border-bottom: .3em solid transparent;\
|
||||||
|
display: inline-block;\
|
||||||
|
margin: .3em;\
|
||||||
|
position: absolute;\
|
||||||
|
right: 3px;\
|
||||||
|
}\
|
||||||
|
.hasSubMenu:not(.focused) > .subMenu {\
|
||||||
|
display: none;\
|
||||||
|
}\
|
||||||
|
.subMenu {\
|
||||||
|
position: absolute;\
|
||||||
|
left: 100%;\
|
||||||
|
top: 0;\
|
||||||
|
margin-top: -1px;\
|
||||||
|
}\
|
||||||
|
\
|
||||||
h1 {\
|
h1 {\
|
||||||
text-align: center;\
|
text-align: center;\
|
||||||
}\
|
}\
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
master
|
master
|
||||||
- Mayhem
|
- Mayhem
|
||||||
|
New feature: Menu, which
|
||||||
|
- replaces and includes Report Button and Delete Button.
|
||||||
|
- add one-click Filter buttons.
|
||||||
|
- add download links to automatically save the file with its original filename. Chrome-only currently.
|
||||||
|
- add archive links.
|
||||||
|
- can integrate features from external userscripts/extensions, see https://github.com/MayhemYDG/4chan-x/wiki/Menu-API
|
||||||
The updater's refresh interval will now increase gradually in inactive threads.
|
The updater's refresh interval will now increase gradually in inactive threads.
|
||||||
The updater's refresh interval is now limited to 5 seconds minimum.
|
The updater's refresh interval is now limited to 5 seconds minimum.
|
||||||
|
|
||||||
|
|||||||
499
script.coffee
499
script.coffee
@ -5,8 +5,6 @@ Config =
|
|||||||
'Keybinds': [true, 'Binds actions to keys']
|
'Keybinds': [true, 'Binds actions to keys']
|
||||||
'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time']
|
'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time']
|
||||||
'File Info Formatting': [true, 'Reformats the file information']
|
'File Info Formatting': [true, 'Reformats the file information']
|
||||||
'Report Button': [true, 'Add report buttons']
|
|
||||||
'Delete Button': [false, 'Add delete buttons']
|
|
||||||
'Comment Expansion': [true, 'Expand too long comments']
|
'Comment Expansion': [true, 'Expand too long comments']
|
||||||
'Thread Expansion': [true, 'View all replies']
|
'Thread Expansion': [true, 'View all replies']
|
||||||
'Index Navigation': [true, 'Navigate to previous / next thread']
|
'Index Navigation': [true, 'Navigate to previous / next thread']
|
||||||
@ -26,6 +24,12 @@ Config =
|
|||||||
'Sauce': [true, 'Add sauce to images']
|
'Sauce': [true, 'Add sauce to images']
|
||||||
'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail']
|
'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail']
|
||||||
'Expand From Current': [false, 'Expand images from current position to thread end.']
|
'Expand From Current': [false, 'Expand images from current position to thread end.']
|
||||||
|
Menu:
|
||||||
|
'Menu': [true, 'Add a drop-down menu in posts.']
|
||||||
|
'Report Link': [true, 'Add a report link to the menu.']
|
||||||
|
'Delete Link': [true, 'Add a delete link to the menu.']
|
||||||
|
'Download Link': [true, 'Add a download with original filename link to the menu. Chrome-only currently.']
|
||||||
|
'Archive Link': [true, 'Add an archive link to the menu.']
|
||||||
Monitoring:
|
Monitoring:
|
||||||
'Thread Updater': [true, 'Update threads. Has more options in its own dialog.']
|
'Thread Updater': [true, 'Update threads. Has more options in its own dialog.']
|
||||||
'Unread Count': [true, 'Show unread post count in tab title']
|
'Unread Count': [true, 'Show unread post count in tab title']
|
||||||
@ -325,7 +329,10 @@ $.extend $,
|
|||||||
tn: (s) ->
|
tn: (s) ->
|
||||||
d.createTextNode s
|
d.createTextNode s
|
||||||
nodes: (nodes) ->
|
nodes: (nodes) ->
|
||||||
if nodes instanceof Node
|
# In (at least) Chrome, elements created inside different
|
||||||
|
# scripts/window contexts inherit from unequal prototypes.
|
||||||
|
# window_ext1.Node !== window_ext2.Node
|
||||||
|
unless nodes instanceof Array
|
||||||
return nodes
|
return nodes
|
||||||
frag = d.createDocumentFragment()
|
frag = d.createDocumentFragment()
|
||||||
for node in nodes
|
for node in nodes
|
||||||
@ -562,8 +569,11 @@ Filter =
|
|||||||
false
|
false
|
||||||
filename: (post) ->
|
filename: (post) ->
|
||||||
{fileInfo} = post
|
{fileInfo} = post
|
||||||
if fileInfo and file = $ '.fileText > span', fileInfo
|
if fileInfo
|
||||||
return file.title
|
if file = $ '.fileText > span', fileInfo
|
||||||
|
return file.title
|
||||||
|
else
|
||||||
|
return fileInfo.firstElementChild.dataset.filename
|
||||||
false
|
false
|
||||||
dimensions: (post) ->
|
dimensions: (post) ->
|
||||||
{fileInfo} = post
|
{fileInfo} = post
|
||||||
@ -581,6 +591,99 @@ Filter =
|
|||||||
return img.dataset.md5
|
return img.dataset.md5
|
||||||
false
|
false
|
||||||
|
|
||||||
|
menuInit: ->
|
||||||
|
div = $.el 'div',
|
||||||
|
textContent: 'Filter'
|
||||||
|
|
||||||
|
entry =
|
||||||
|
el: div
|
||||||
|
open: -> true
|
||||||
|
children: []
|
||||||
|
|
||||||
|
for type in [
|
||||||
|
['Name', 'name']
|
||||||
|
['Unique ID', 'uniqueid']
|
||||||
|
['Tripcode', 'tripcode']
|
||||||
|
['Admin/Mod', 'mod']
|
||||||
|
['E-mail', 'email']
|
||||||
|
['Subject', 'subject']
|
||||||
|
['Comment', 'comment']
|
||||||
|
['Country', 'country']
|
||||||
|
['Filename', 'filename']
|
||||||
|
['Image dimensions', 'dimensions']
|
||||||
|
['Filesize', 'filesize']
|
||||||
|
['Image MD5', 'md5']
|
||||||
|
]
|
||||||
|
# Add a sub entry for each filter type.
|
||||||
|
entry.children.push Filter.createSubEntry type[0], type[1]
|
||||||
|
|
||||||
|
Menu.addEntry entry
|
||||||
|
|
||||||
|
createSubEntry: (text, type) ->
|
||||||
|
el = $.el 'a',
|
||||||
|
href: 'javascript:;'
|
||||||
|
textContent: text
|
||||||
|
# Define the onclick var outside of open's scope to $.off it properly.
|
||||||
|
onclick = null
|
||||||
|
|
||||||
|
open = (post) ->
|
||||||
|
value = Filter[type] post
|
||||||
|
return false if value is false
|
||||||
|
$.off el, 'click', onclick
|
||||||
|
onclick = ->
|
||||||
|
# Convert value -> regexp, unless type is md5
|
||||||
|
re = if type is 'md5' then value else value.replace ///
|
||||||
|
/
|
||||||
|
| \\
|
||||||
|
| \^
|
||||||
|
| \$
|
||||||
|
| \n
|
||||||
|
| \.
|
||||||
|
| \(
|
||||||
|
| \)
|
||||||
|
| \{
|
||||||
|
| \}
|
||||||
|
| \[
|
||||||
|
| \]
|
||||||
|
| \?
|
||||||
|
| \*
|
||||||
|
| \+
|
||||||
|
| \|
|
||||||
|
///g, (c) ->
|
||||||
|
if c is '\n'
|
||||||
|
'\\n'
|
||||||
|
else if c is '\\'
|
||||||
|
'\\\\'
|
||||||
|
else
|
||||||
|
"\\#{c}"
|
||||||
|
|
||||||
|
re =
|
||||||
|
if type is 'md5'
|
||||||
|
"/#{value}/"
|
||||||
|
else
|
||||||
|
"/^#{re}$/"
|
||||||
|
if /\bop\b/.test post.class
|
||||||
|
re += ';op:yes'
|
||||||
|
|
||||||
|
# Add a new line before the regexp unless the text is empty.
|
||||||
|
save = if save = $.get type, '' then "#{save}\n#{re}" else re
|
||||||
|
$.set type, save
|
||||||
|
|
||||||
|
# Open the options and display & focus the relevant filter textarea.
|
||||||
|
Options.dialog()
|
||||||
|
select = $ 'select[name=filter]', $.id 'options'
|
||||||
|
select.value = type
|
||||||
|
$.event select, new Event 'change'
|
||||||
|
$.id('filter_tab').checked = true
|
||||||
|
ta = select.nextElementSibling
|
||||||
|
tl = ta.textLength
|
||||||
|
ta.setSelectionRange tl, tl
|
||||||
|
ta.focus()
|
||||||
|
$.on el, 'click', onclick
|
||||||
|
true
|
||||||
|
|
||||||
|
return el: el, open: open
|
||||||
|
|
||||||
StrikethroughQuotes =
|
StrikethroughQuotes =
|
||||||
init: ->
|
init: ->
|
||||||
Main.callbacks.push @node
|
Main.callbacks.push @node
|
||||||
@ -696,7 +799,7 @@ ExpandThread =
|
|||||||
continue if href[0] is '/' # Cross-board quote
|
continue if href[0] is '/' # Cross-board quote
|
||||||
quote.href = "res/#{href}" # Fix pathnames
|
quote.href = "res/#{href}" # Fix pathnames
|
||||||
id = reply.id[2..]
|
id = reply.id[2..]
|
||||||
link = $ '.postInfo > .postNum > a[title="Highlight this post"]', reply
|
link = $ '.postNum > a[title="Highlight this post"]', reply
|
||||||
link.href = "res/#{threadID}#p#{id}"
|
link.href = "res/#{threadID}#p#{id}"
|
||||||
link.nextSibling.href = "res/#{threadID}#q#{id}"
|
link.nextSibling.href = "res/#{threadID}#q#{id}"
|
||||||
nodes.push reply
|
nodes.push reply
|
||||||
@ -724,7 +827,7 @@ ThreadHiding =
|
|||||||
return
|
return
|
||||||
|
|
||||||
cb: ->
|
cb: ->
|
||||||
ThreadHiding.toggle @parentNode
|
ThreadHiding.toggle $.x 'ancestor::div[parent::div[@class="board"]]', @
|
||||||
|
|
||||||
toggle: (thread) ->
|
toggle: (thread) ->
|
||||||
hiddenThreads = $.get "hiddenThreads/#{g.BOARD}/", {}
|
hiddenThreads = $.get "hiddenThreads/#{g.BOARD}/", {}
|
||||||
@ -752,17 +855,21 @@ ThreadHiding =
|
|||||||
text = if num is 1 then '1 reply' else "#{num} replies"
|
text = if num is 1 then '1 reply' else "#{num} replies"
|
||||||
opInfo = $('.op > .postInfo > .nameBlock', thread).textContent
|
opInfo = $('.op > .postInfo > .nameBlock', thread).textContent
|
||||||
|
|
||||||
a = $.el 'a',
|
stub = $.el 'div',
|
||||||
className: 'hide_thread_button hidden_thread'
|
className: 'hide_thread_button hidden_thread'
|
||||||
innerHTML: '<span>[ + ]</span>'
|
innerHTML: '<a href="javascript:;"><span>[ + ]</span> </a>'
|
||||||
href: 'javascript:;'
|
a = stub.firstChild
|
||||||
$.add a, $.tn " #{opInfo} (#{text})"
|
$.on a, 'click', ThreadHiding.cb
|
||||||
$.on a, 'click', ThreadHiding.cb
|
$.add a, $.tn "#{opInfo} (#{text})"
|
||||||
$.prepend thread, a
|
if Conf['Menu']
|
||||||
|
menuButton = Menu.a.cloneNode true
|
||||||
|
$.on menuButton, 'click', Menu.toggle
|
||||||
|
$.add stub, [$.tn(' '), menuButton]
|
||||||
|
$.prepend thread, stub
|
||||||
|
|
||||||
show: (thread) ->
|
show: (thread) ->
|
||||||
if a = $ '.hidden_thread', thread
|
if stub = $ '.hidden_thread', thread
|
||||||
$.rm a
|
$.rm stub
|
||||||
thread.hidden = false
|
thread.hidden = false
|
||||||
thread.nextElementSibling.hidden = false
|
thread.nextElementSibling.hidden = false
|
||||||
|
|
||||||
@ -810,8 +917,12 @@ ReplyHiding =
|
|||||||
className: 'hide_reply_button stub'
|
className: 'hide_reply_button stub'
|
||||||
innerHTML: '<a href="javascript:;"><span>[ + ]</span> </a>'
|
innerHTML: '<a href="javascript:;"><span>[ + ]</span> </a>'
|
||||||
a = stub.firstChild
|
a = stub.firstChild
|
||||||
$.add a, $.tn $('.nameBlock', el).textContent
|
|
||||||
$.on a, 'click', ReplyHiding.toggle
|
$.on a, 'click', ReplyHiding.toggle
|
||||||
|
$.add a, $.tn $('.nameBlock', el).textContent
|
||||||
|
if Conf['Menu']
|
||||||
|
menuButton = Menu.a.cloneNode true
|
||||||
|
$.on menuButton, 'click', Menu.toggle
|
||||||
|
$.add stub, [$.tn(' '), menuButton]
|
||||||
$.prepend root, stub
|
$.prepend root, stub
|
||||||
|
|
||||||
show: (root) ->
|
show: (root) ->
|
||||||
@ -820,6 +931,155 @@ ReplyHiding =
|
|||||||
$('.sideArrows', root).hidden = false
|
$('.sideArrows', root).hidden = false
|
||||||
$('.post', root).hidden = false
|
$('.post', root).hidden = false
|
||||||
|
|
||||||
|
Menu =
|
||||||
|
entries: []
|
||||||
|
init: ->
|
||||||
|
@a = $.el 'a',
|
||||||
|
className: 'menu_button'
|
||||||
|
href: 'javascript:;'
|
||||||
|
innerHTML: '[<span></span>]'
|
||||||
|
@el = $.el 'div',
|
||||||
|
className: 'reply dialog'
|
||||||
|
id: 'menu'
|
||||||
|
tabIndex: 0
|
||||||
|
$.on @el, 'click', (e) -> e.stopPropagation()
|
||||||
|
$.on @el, 'keydown', @keybinds
|
||||||
|
|
||||||
|
# Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API
|
||||||
|
$.on d, 'AddMenuEntry', (e) -> Menu.addEntry e.detail
|
||||||
|
|
||||||
|
Main.callbacks.push @node
|
||||||
|
node: (post) ->
|
||||||
|
if post.isInlined and !post.isCrosspost
|
||||||
|
a = $ '.menu_button', post.el
|
||||||
|
else
|
||||||
|
a = Menu.a.cloneNode true
|
||||||
|
$.add $('.postInfo', post.el), a
|
||||||
|
$.on a, 'click', Menu.toggle
|
||||||
|
|
||||||
|
toggle: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
if Menu.el.parentNode
|
||||||
|
# Close if it's already opened.
|
||||||
|
# Reopen if we clicked on another button.
|
||||||
|
{lastOpener} = Menu
|
||||||
|
Menu.close()
|
||||||
|
return if lastOpener is @
|
||||||
|
|
||||||
|
Menu.lastOpener = @
|
||||||
|
post =
|
||||||
|
if /\bhidden_thread\b/.test @parentNode.className
|
||||||
|
$.x 'ancestor::div[parent::div[@class="board"]]/child::div[contains(@class,"opContainer")]', @
|
||||||
|
else
|
||||||
|
$.x 'ancestor::div[contains(@class,"postContainer")][1]', @
|
||||||
|
Menu.open @, Main.preParse post
|
||||||
|
open: (button, post) ->
|
||||||
|
{el} = Menu
|
||||||
|
# XXX GM/Scriptish require setAttribute
|
||||||
|
el.setAttribute 'data-id', post.ID
|
||||||
|
el.setAttribute 'data-rootid', post.root.id
|
||||||
|
|
||||||
|
funk = (entry, parent) ->
|
||||||
|
{children} = entry
|
||||||
|
return unless entry.open post
|
||||||
|
$.add parent, entry.el
|
||||||
|
|
||||||
|
return unless children
|
||||||
|
if subMenu = $ '.subMenu', entry.el
|
||||||
|
# Reset sub menu, remove irrelevant entries.
|
||||||
|
$.rm subMenu
|
||||||
|
subMenu = $.el 'div',
|
||||||
|
className: 'reply dialog subMenu'
|
||||||
|
$.add entry.el, subMenu
|
||||||
|
for child in children
|
||||||
|
funk child, subMenu
|
||||||
|
return
|
||||||
|
for entry in Menu.entries
|
||||||
|
funk entry, el
|
||||||
|
|
||||||
|
Menu.focus $ '.entry', Menu.el
|
||||||
|
$.on d, 'click', Menu.close
|
||||||
|
$.add d.body, el
|
||||||
|
|
||||||
|
# Position
|
||||||
|
mRect = el.getBoundingClientRect()
|
||||||
|
bRect = button.getBoundingClientRect()
|
||||||
|
bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top
|
||||||
|
bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left
|
||||||
|
el.style.top =
|
||||||
|
if bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight
|
||||||
|
bTop + bRect.height + 2 + 'px'
|
||||||
|
else
|
||||||
|
bTop - mRect.height - 2 + 'px'
|
||||||
|
el.style.left =
|
||||||
|
if bRect.left + mRect.width < d.documentElement.clientWidth
|
||||||
|
bLeft + 'px'
|
||||||
|
else
|
||||||
|
bLeft + bRect.width - mRect.width + 'px'
|
||||||
|
|
||||||
|
el.focus()
|
||||||
|
close: ->
|
||||||
|
{el} = Menu
|
||||||
|
$.rm el
|
||||||
|
for focused in $$ '.focused.entry', el
|
||||||
|
$.rmClass focused, 'focused'
|
||||||
|
el.innerHTML = null
|
||||||
|
el.removeAttribute 'style'
|
||||||
|
delete Menu.lastOpener
|
||||||
|
delete Menu.focusedEntry
|
||||||
|
$.off d, 'click', Menu.close
|
||||||
|
|
||||||
|
keybinds: (e) ->
|
||||||
|
el = Menu.focusedEntry
|
||||||
|
|
||||||
|
switch Keybinds.keyCode(e) or e.keyCode
|
||||||
|
when 'Esc'
|
||||||
|
Menu.lastOpener.focus()
|
||||||
|
Menu.close()
|
||||||
|
when 13, 32 # 'Enter', 'Space'
|
||||||
|
el.click()
|
||||||
|
when 'Up'
|
||||||
|
if next = el.previousElementSibling
|
||||||
|
Menu.focus next
|
||||||
|
when 'Down'
|
||||||
|
if next = el.nextElementSibling
|
||||||
|
Menu.focus next
|
||||||
|
when 'Right'
|
||||||
|
if (subMenu = $ '.subMenu', el) and next = subMenu.firstElementChild
|
||||||
|
Menu.focus next
|
||||||
|
when 'Left'
|
||||||
|
if next = $.x 'parent::*[contains(@class,"subMenu")]/parent::*', el
|
||||||
|
Menu.focus next
|
||||||
|
else
|
||||||
|
return
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
focus: (el) ->
|
||||||
|
if focused = $.x 'parent::*/child::*[contains(@class,"focused")]', el
|
||||||
|
$.rmClass focused, 'focused'
|
||||||
|
for focused in $$ '.focused', el
|
||||||
|
$.rmClass focused, 'focused'
|
||||||
|
Menu.focusedEntry = el
|
||||||
|
$.addClass el, 'focused'
|
||||||
|
|
||||||
|
addEntry: (entry) ->
|
||||||
|
funk = (entry) ->
|
||||||
|
{el, children} = entry
|
||||||
|
$.addClass el, 'entry'
|
||||||
|
$.on el, 'focus mouseover', (e) ->
|
||||||
|
e.stopPropagation()
|
||||||
|
Menu.focus @
|
||||||
|
return unless children
|
||||||
|
$.addClass el, 'hasSubMenu'
|
||||||
|
for child in children
|
||||||
|
funk child
|
||||||
|
return
|
||||||
|
funk entry
|
||||||
|
Menu.entries.push entry
|
||||||
|
|
||||||
Keybinds =
|
Keybinds =
|
||||||
init: ->
|
init: ->
|
||||||
for node in $$ '[accesskey]'
|
for node in $$ '[accesskey]'
|
||||||
@ -951,7 +1211,7 @@ Keybinds =
|
|||||||
|
|
||||||
qr: (thread, quote) ->
|
qr: (thread, quote) ->
|
||||||
if quote
|
if quote
|
||||||
QR.quote.call $ '.postInfo > .postNum > a[title="Quote this post"]', $('.post.highlight', thread) or thread
|
QR.quote.call $ '.postNum > a[title="Quote this post"]', $('.post.highlight', thread) or thread
|
||||||
else
|
else
|
||||||
QR.open()
|
QR.open()
|
||||||
$('textarea', QR.el).focus()
|
$('textarea', QR.el).focus()
|
||||||
@ -1072,7 +1332,7 @@ QR =
|
|||||||
$.on d, 'dragstart dragend', QR.drag
|
$.on d, 'dragstart dragend', QR.drag
|
||||||
|
|
||||||
node: (post) ->
|
node: (post) ->
|
||||||
$.on $('.postInfo > .postNum > a[title="Quote this post"]', post.el), 'click', QR.quote
|
$.on $('.postNum > a[title="Quote this post"]', post.el), 'click', QR.quote
|
||||||
|
|
||||||
open: ->
|
open: ->
|
||||||
if QR.el
|
if QR.el
|
||||||
@ -1358,9 +1618,9 @@ QR =
|
|||||||
QR.characterCount.call $ 'textarea', QR.el
|
QR.characterCount.call $ 'textarea', QR.el
|
||||||
$('#spoiler', QR.el).checked = @spoiler
|
$('#spoiler', QR.el).checked = @spoiler
|
||||||
dragStart: ->
|
dragStart: ->
|
||||||
$.addClass @, 'drag'
|
$.addClass @, 'drag'
|
||||||
dragEnter: ->
|
dragEnter: ->
|
||||||
$.addClass @, 'over'
|
$.addClass @, 'over'
|
||||||
dragLeave: ->
|
dragLeave: ->
|
||||||
$.rmClass @, 'over'
|
$.rmClass @, 'over'
|
||||||
dragOver: (e) ->
|
dragOver: (e) ->
|
||||||
@ -2390,6 +2650,8 @@ FileInfo =
|
|||||||
resolution: span.previousSibling.textContent.match(/\d+x\d+|PDF/)[0]
|
resolution: span.previousSibling.textContent.match(/\d+x\d+|PDF/)[0]
|
||||||
fullname: span.title
|
fullname: span.title
|
||||||
shortname: span.textContent
|
shortname: span.textContent
|
||||||
|
# XXX GM/Scriptish
|
||||||
|
node.setAttribute 'data-filename', span.title
|
||||||
node.innerHTML = FileInfo.funk FileInfo
|
node.innerHTML = FileInfo.funk FileInfo
|
||||||
setFormats: ->
|
setFormats: ->
|
||||||
code = Conf['fileInfo'].replace /%([BKlLMnNprs])/g, (s, c) ->
|
code = Conf['fileInfo'].replace /%([BKlLMnNprs])/g, (s, c) ->
|
||||||
@ -2473,7 +2735,7 @@ Get =
|
|||||||
href = quote.getAttribute 'href'
|
href = quote.getAttribute 'href'
|
||||||
continue if href[0] is '/' # Cross-board quote, or board link
|
continue if href[0] is '/' # Cross-board quote, or board link
|
||||||
quote.href = "/#{board}/res/#{href}" # Fix pathnames
|
quote.href = "/#{board}/res/#{href}" # Fix pathnames
|
||||||
link = $ '.postInfo > .postNum > a[title="Highlight this post"]', pc
|
link = $ '.postNum > a[title="Highlight this post"]', pc
|
||||||
link.href = "/#{board}/res/#{threadID}#p#{postID}"
|
link.href = "/#{board}/res/#{threadID}#p#{postID}"
|
||||||
link.nextSibling.href = "/#{board}/res/#{threadID}#q#{postID}"
|
link.nextSibling.href = "/#{board}/res/#{threadID}#q#{postID}"
|
||||||
|
|
||||||
@ -2640,7 +2902,7 @@ Get =
|
|||||||
innerHTML: "<img #{thumb_src} alt='#{if data.media_status isnt 'available' then "Error: #{data.media_status}, " else ''}#{if spoiler then 'Spoiler Image, ' else ''}#{filesize}' data-md5=#{data.media_hash} style='height: #{data.preview_h}px; width: #{data.preview_w}px;'>"
|
innerHTML: "<img #{thumb_src} alt='#{if data.media_status isnt 'available' then "Error: #{data.media_status}, " else ''}#{if spoiler then 'Spoiler Image, ' else ''}#{filesize}' data-md5=#{data.media_hash} style='height: #{data.preview_h}px; width: #{data.preview_w}px;'>"
|
||||||
$.after (if isOP then piM else pi), file
|
$.after (if isOP then piM else pi), file
|
||||||
|
|
||||||
$.replace root.firstChild, pc
|
$.replace root.firstChild, Get.cleanPost pc
|
||||||
cb() if cb
|
cb() if cb
|
||||||
cleanPost: (root) ->
|
cleanPost: (root) ->
|
||||||
post = $ '.post', root
|
post = $ '.post', root
|
||||||
@ -2655,7 +2917,9 @@ Get =
|
|||||||
|
|
||||||
# Don't mess with other features
|
# Don't mess with other features
|
||||||
now = Date.now()
|
now = Date.now()
|
||||||
for el in $$ '[id]', root
|
els = $$ '[id]', root
|
||||||
|
els.push root
|
||||||
|
for el in els
|
||||||
el.id = "#{now}_#{el.id}"
|
el.id = "#{now}_#{el.id}"
|
||||||
|
|
||||||
$.rmClass root, 'forwarded'
|
$.rmClass root, 'forwarded'
|
||||||
@ -2924,7 +3188,7 @@ Quotify =
|
|||||||
m[1]
|
m[1]
|
||||||
else
|
else
|
||||||
# Get the post's board, whether it's inlined or not.
|
# Get the post's board, whether it's inlined or not.
|
||||||
$('.postInfo > .postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1]
|
$('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1]
|
||||||
|
|
||||||
nodes.push a = $.el 'a',
|
nodes.push a = $.el 'a',
|
||||||
# \u00A0 is nbsp
|
# \u00A0 is nbsp
|
||||||
@ -2957,28 +3221,32 @@ Quotify =
|
|||||||
$.replace node, nodes
|
$.replace node, nodes
|
||||||
return
|
return
|
||||||
|
|
||||||
DeleteButton =
|
DeleteLink =
|
||||||
init: ->
|
init: ->
|
||||||
@a = $.el 'a',
|
a = $.el 'a',
|
||||||
className: 'delete_button'
|
className: 'delete_link'
|
||||||
innerHTML: '[ × ]'
|
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
Main.callbacks.push @node
|
Menu.addEntry
|
||||||
node: (post) ->
|
el: a
|
||||||
unless a = $ '.delete_button', post.el
|
open: (post) ->
|
||||||
a = DeleteButton.a.cloneNode true
|
if post.isArchived
|
||||||
$.add $('.postInfo', post.el), a
|
return false
|
||||||
$.on a, 'click', DeleteButton.delete
|
a.textContent = 'Delete this post'
|
||||||
|
$.on a, 'click', DeleteLink.delete
|
||||||
|
true
|
||||||
delete: ->
|
delete: ->
|
||||||
$.off @, 'click', DeleteButton.delete
|
$.off @, 'click', DeleteLink.delete
|
||||||
@innerHTML = '[ Deleting... ]'
|
@textContent = 'Deleting...'
|
||||||
|
|
||||||
if m = d.cookie.match /4chan_pass=([^;]+)/
|
pwd =
|
||||||
pwd = decodeURIComponent m[1]
|
if m = d.cookie.match /4chan_pass=([^;]+)/
|
||||||
else
|
decodeURIComponent m[1]
|
||||||
pwd = $.id('delPassword').value
|
else
|
||||||
id = $.x('preceding-sibling::input', @).name
|
$.id('delPassword').value
|
||||||
board = $.x('preceding-sibling::span[1]/a', @).pathname.match(/\w+/)[0]
|
|
||||||
|
id = @parentNode.dataset.id
|
||||||
|
board = $('.postNum > a[title="Highlight this post"]',
|
||||||
|
$.id @parentNode.dataset.rootid).pathname.split('/')[1]
|
||||||
self = this
|
self = this
|
||||||
|
|
||||||
form =
|
form =
|
||||||
@ -2986,13 +3254,12 @@ DeleteButton =
|
|||||||
pwd: pwd
|
pwd: pwd
|
||||||
form[id] = 'delete'
|
form[id] = 'delete'
|
||||||
|
|
||||||
$.ajax "https://sys.4chan.org/#{board}/imgboard.php", {
|
$.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{board}/"), {
|
||||||
onload: -> DeleteButton.load self, @response
|
onload: -> DeleteLink.load self, @response
|
||||||
onerror: -> DeleteButton.error self
|
onerror: -> DeleteLink.error self
|
||||||
}, {
|
}, {
|
||||||
form: $.formData form
|
form: $.formData form
|
||||||
}
|
}
|
||||||
|
|
||||||
load: (self, html) ->
|
load: (self, html) ->
|
||||||
doc = d.implementation.createHTMLDocument ''
|
doc = d.implementation.createHTMLDocument ''
|
||||||
doc.documentElement.innerHTML = html
|
doc.documentElement.innerHTML = html
|
||||||
@ -3000,32 +3267,68 @@ DeleteButton =
|
|||||||
s = 'Banned!'
|
s = 'Banned!'
|
||||||
else if msg = doc.getElementById 'errmsg' # error!
|
else if msg = doc.getElementById 'errmsg' # error!
|
||||||
s = msg.textContent
|
s = msg.textContent
|
||||||
$.on self, 'click', DeleteButton.delete
|
$.on self, 'click', DeleteLink.delete
|
||||||
else
|
else
|
||||||
s = 'Deleted'
|
s = 'Deleted'
|
||||||
self.innerHTML = "[ #{s} ]"
|
self.textContent = s
|
||||||
error: (self) ->
|
error: (self) ->
|
||||||
self.innerHTML = '[ Connection error, please retry. ]'
|
self.textContent = 'Connection error, please retry.'
|
||||||
$.on self, 'click', DeleteButton.delete
|
$.on self, 'click', DeleteLink.delete
|
||||||
|
|
||||||
ReportButton =
|
ReportLink =
|
||||||
init: ->
|
init: ->
|
||||||
@a = $.el 'a',
|
a = $.el 'a',
|
||||||
className: 'report_button'
|
className: 'report_link'
|
||||||
innerHTML: '[ ! ]'
|
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
Main.callbacks.push @node
|
textContent: 'Report this post'
|
||||||
node: (post) ->
|
$.on a, 'click', @report
|
||||||
unless a = $ '.report_button', post.el
|
Menu.addEntry
|
||||||
a = ReportButton.a.cloneNode true
|
el: a
|
||||||
$.add $('.postInfo', post.el), a
|
open: (post) ->
|
||||||
$.on a, 'click', ReportButton.report
|
post.isArchived is false
|
||||||
report: ->
|
report: ->
|
||||||
url = "//sys.4chan.org/#{g.BOARD}/imgboard.php?mode=report&no=#{$.x('preceding-sibling::input', @).name}"
|
a = $ '.postNum > a[title="Highlight this post"]', $.id @parentNode.dataset.rootid
|
||||||
|
url = "//sys.4chan.org/#{a.pathname.split('/')[1]}/imgboard.php?mode=report&no=#{@parentNode.dataset.id}"
|
||||||
id = Date.now()
|
id = Date.now()
|
||||||
set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200"
|
set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200"
|
||||||
window.open url, id, set
|
window.open url, id, set
|
||||||
|
|
||||||
|
DownloadLink =
|
||||||
|
init: ->
|
||||||
|
# Test for download feature support.
|
||||||
|
return if $.el('a').download is undefined
|
||||||
|
a = $.el 'a',
|
||||||
|
className: 'download_link'
|
||||||
|
textContent: 'Download file'
|
||||||
|
Menu.addEntry
|
||||||
|
el: a
|
||||||
|
open: (post) ->
|
||||||
|
unless post.img
|
||||||
|
return false
|
||||||
|
a.href = post.img.parentNode.href
|
||||||
|
fileText = post.fileInfo.firstElementChild
|
||||||
|
a.download =
|
||||||
|
if Conf['File Info Formatting']
|
||||||
|
fileText.dataset.filename
|
||||||
|
else
|
||||||
|
$('span', fileText).title
|
||||||
|
true
|
||||||
|
|
||||||
|
ArchiveLink =
|
||||||
|
init: ->
|
||||||
|
a = $.el 'a',
|
||||||
|
className: 'archive_link'
|
||||||
|
target: '_blank'
|
||||||
|
textContent: 'Archived post'
|
||||||
|
Menu.addEntry
|
||||||
|
el: a
|
||||||
|
open: (post) ->
|
||||||
|
path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split '/'
|
||||||
|
if (href = Redirect.thread path[1], path[3], post.ID) is "//boards.4chan.org/#{path[1]}/"
|
||||||
|
return false
|
||||||
|
a.href = href
|
||||||
|
true
|
||||||
|
|
||||||
ThreadStats =
|
ThreadStats =
|
||||||
init: ->
|
init: ->
|
||||||
dialog = UI.dialog 'stats', 'bottom: 0; left: 0;', '<div class=move><span id=postcount>0</span> / <span id=imagecount>0</span></div>'
|
dialog = UI.dialog 'stats', 'bottom: 0; left: 0;', '<div class=move><span id=postcount>0</span> / <span id=imagecount>0</span></div>'
|
||||||
@ -3506,11 +3809,23 @@ Main =
|
|||||||
if Conf['Image Hover']
|
if Conf['Image Hover']
|
||||||
ImageHover.init()
|
ImageHover.init()
|
||||||
|
|
||||||
if Conf['Report Button']
|
if Conf['Menu']
|
||||||
ReportButton.init()
|
Menu.init()
|
||||||
|
|
||||||
if Conf['Delete Button']
|
if Conf['Report Link']
|
||||||
DeleteButton.init()
|
ReportLink.init()
|
||||||
|
|
||||||
|
if Conf['Delete Link']
|
||||||
|
DeleteLink.init()
|
||||||
|
|
||||||
|
if Conf['Filter']
|
||||||
|
Filter.menuInit()
|
||||||
|
|
||||||
|
if Conf['Download Link']
|
||||||
|
DownloadLink.init()
|
||||||
|
|
||||||
|
if Conf['Archive Link']
|
||||||
|
ArchiveLink.init()
|
||||||
|
|
||||||
if Conf['Resurrect Quotes']
|
if Conf['Resurrect Quotes']
|
||||||
Quotify.init()
|
Quotify.init()
|
||||||
@ -3716,6 +4031,60 @@ a[href="javascript:;"] {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu_button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.menu_button > span {
|
||||||
|
border-top: .5em solid;
|
||||||
|
border-right: .3em solid transparent;
|
||||||
|
border-left: .3em solid transparent;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 2px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
#menu {
|
||||||
|
position: absolute;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.entry {
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, .25);
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
outline: none;
|
||||||
|
padding: 3px 7px;
|
||||||
|
position: relative;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.entry:last-child {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.focused.entry {
|
||||||
|
background: rgba(255, 255, 255, .33);
|
||||||
|
}
|
||||||
|
.entry.hasSubMenu {
|
||||||
|
padding-right: 1.5em;
|
||||||
|
}
|
||||||
|
.hasSubMenu::after {
|
||||||
|
content: "";
|
||||||
|
border-left: .5em solid;
|
||||||
|
border-top: .3em solid transparent;
|
||||||
|
border-bottom: .3em solid transparent;
|
||||||
|
display: inline-block;
|
||||||
|
margin: .3em;
|
||||||
|
position: absolute;
|
||||||
|
right: 3px;
|
||||||
|
}
|
||||||
|
.hasSubMenu:not(.focused) > .subMenu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.subMenu {
|
||||||
|
position: absolute;
|
||||||
|
left: 100%;
|
||||||
|
top: 0;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user