Add Menu. Close #627. Add Report/Delete/Download/Archive Link.
This commit is contained in:
parent
ddbab74efb
commit
7bf2e7dd45
490
4chan_x.user.js
490
4chan_x.user.js
@ -20,7 +20,7 @@
|
|||||||
// @icon https://github.com/MayhemYDG/4chan-x/raw/stable/img/icon.gif
|
// @icon https://github.com/MayhemYDG/4chan-x/raw/stable/img/icon.gif
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
/* 4chan X Alpha - Version 3.0.0 - 2013-01-24
|
/* 4chan X Alpha - Version 3.0.0 - 2013-01-25
|
||||||
* http://mayhemydg.github.com/4chan-x/
|
* http://mayhemydg.github.com/4chan-x/
|
||||||
*
|
*
|
||||||
* Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
|
* Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
|
||||||
@ -43,7 +43,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var $, $$, Anonymize, AutoGIF, Board, Build, Clone, Conf, Config, FileInfo, Get, ImageHover, Main, Post, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Recursive, Redirect, ReplyHiding, RevealSpoilers, Sauce, Thread, ThreadHiding, ThreadUpdater, Time, UI, d, g, _base,
|
var $, $$, Anonymize, ArchiveLink, AutoGIF, Board, Build, Clone, Conf, Config, DeleteLink, DownloadLink, FileInfo, Get, ImageHover, Main, Menu, Post, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Recursive, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, Thread, ThreadHiding, ThreadUpdater, Time, UI, d, g, _base,
|
||||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||||
__hasProp = {}.hasOwnProperty,
|
__hasProp = {}.hasOwnProperty,
|
||||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||||
@ -1014,9 +1014,450 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Menu = {
|
||||||
|
entries: [],
|
||||||
|
init: function() {
|
||||||
|
$.on(d, 'AddMenuEntry', function(e) {
|
||||||
|
return Menu.addEntry(e.detail);
|
||||||
|
});
|
||||||
|
return Post.prototype.callbacks.push({
|
||||||
|
name: 'Menu',
|
||||||
|
cb: this.node
|
||||||
|
});
|
||||||
|
},
|
||||||
|
node: function() {
|
||||||
|
var a;
|
||||||
|
if (this.isClone) {
|
||||||
|
a = $('.menu-button', this.nodes.info);
|
||||||
|
a.setAttribute('data-clone', true);
|
||||||
|
$.on(a, 'click', Menu.toggle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
a = Menu.makeButton(this);
|
||||||
|
return $.add(this.nodes.info, [$.tn('\u00A0'), a]);
|
||||||
|
},
|
||||||
|
makeButton: function(post) {
|
||||||
|
var a;
|
||||||
|
a = $.el('a', {
|
||||||
|
className: 'menu-button',
|
||||||
|
innerHTML: '[<span></span>]',
|
||||||
|
href: 'javascript:;'
|
||||||
|
});
|
||||||
|
a.setAttribute('data-postid', post.fullID);
|
||||||
|
$.on(a, 'click', Menu.toggle);
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
makeMenu: function() {
|
||||||
|
var menu;
|
||||||
|
menu = $.el('div', {
|
||||||
|
className: 'reply dialog',
|
||||||
|
id: 'menu',
|
||||||
|
tabIndex: 0
|
||||||
|
});
|
||||||
|
$.on(menu, 'click', function(e) {
|
||||||
|
return e.stopPropagation();
|
||||||
|
});
|
||||||
|
$.on(menu, 'keydown', Menu.keybinds);
|
||||||
|
return menu;
|
||||||
|
},
|
||||||
|
toggle: function(e) {
|
||||||
|
var lastToggledButton, post;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (Menu.currentMenu) {
|
||||||
|
lastToggledButton = Menu.lastToggledButton;
|
||||||
|
Menu.close();
|
||||||
|
if (lastToggledButton === this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Menu.lastToggledButton = this;
|
||||||
|
post = this.dataset.clone ? Get.postFromRoot($.x('ancestor::div[contains(@class,"postContainer")][1]', this)) : g.posts[this.dataset.postid];
|
||||||
|
return Menu.open(this, post);
|
||||||
|
},
|
||||||
|
open: function(button, post) {
|
||||||
|
var bLeft, bRect, bTop, entry, mRect, menu, _i, _len, _ref;
|
||||||
|
menu = Menu.makeMenu();
|
||||||
|
Menu.currentMenu = menu;
|
||||||
|
_ref = Menu.entries;
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
entry = _ref[_i];
|
||||||
|
Menu.insertEntry(entry, menu, post);
|
||||||
|
}
|
||||||
|
Menu.focus($('.entry', menu));
|
||||||
|
$.on(d, 'click', Menu.close);
|
||||||
|
$.add(d.body, menu);
|
||||||
|
mRect = menu.getBoundingClientRect();
|
||||||
|
bRect = button.getBoundingClientRect();
|
||||||
|
bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top;
|
||||||
|
bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left;
|
||||||
|
menu.style.top = bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight ? bTop + bRect.height + 2 + 'px' : bTop - mRect.height - 2 + 'px';
|
||||||
|
menu.style.left = bRect.left + mRect.width < d.documentElement.clientWidth ? bLeft + 'px' : bLeft + bRect.width - mRect.width + 'px';
|
||||||
|
return menu.focus();
|
||||||
|
},
|
||||||
|
insertEntry: function(entry, parent, post) {
|
||||||
|
var child, submenu, _i, _len, _ref;
|
||||||
|
if (!entry.open(post)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.add(parent, entry.el);
|
||||||
|
if (!entry.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (submenu = $('.submenu', entry.el)) {
|
||||||
|
$.rm(submenu);
|
||||||
|
}
|
||||||
|
submenu = $.el('div', {
|
||||||
|
className: 'reply dialog submenu'
|
||||||
|
});
|
||||||
|
$.add(entry.el, submenu);
|
||||||
|
_ref = entry.children;
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
child = _ref[_i];
|
||||||
|
Menu.insertEntry(child, submenu, post);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
$.rm(Menu.currentMenu);
|
||||||
|
delete Menu.currentMenu;
|
||||||
|
delete Menu.lastToggledButton;
|
||||||
|
return $.off(d, 'click', Menu.close);
|
||||||
|
},
|
||||||
|
keybinds: function(e) {
|
||||||
|
var entry, next, subEntry, submenu;
|
||||||
|
entry = $('.focused', Menu.currentMenu);
|
||||||
|
while (subEntry = $('.focused', entry)) {
|
||||||
|
entry = subEntry;
|
||||||
|
}
|
||||||
|
switch (Keybinds.keyCode(e) || e.keyCode) {
|
||||||
|
case 'Esc':
|
||||||
|
Menu.lastToggledButton.focus();
|
||||||
|
Menu.close();
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
case 32:
|
||||||
|
entry.click();
|
||||||
|
break;
|
||||||
|
case 'Up':
|
||||||
|
if (next = entry.previousElementSibling) {
|
||||||
|
Menu.focus(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Down':
|
||||||
|
if (next = entry.nextElementSibling) {
|
||||||
|
Menu.focus(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Right':
|
||||||
|
if ((submenu = $('.submenu', entry)) && (next = submenu.firstElementChild)) {
|
||||||
|
Menu.focus(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Left':
|
||||||
|
if (next = $.x('parent::*[contains(@class,"submenu")]/parent::*', entry)) {
|
||||||
|
Menu.focus(next);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
return e.stopPropagation();
|
||||||
|
},
|
||||||
|
focus: function(entry) {
|
||||||
|
var bottom, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref;
|
||||||
|
if (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) {
|
||||||
|
$.rmClass(focused, 'focused');
|
||||||
|
}
|
||||||
|
_ref = $$('.focused', entry);
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
focused = _ref[_i];
|
||||||
|
$.rmClass(focused, 'focused');
|
||||||
|
}
|
||||||
|
$.addClass(entry, 'focused');
|
||||||
|
if (!(submenu = $('.submenu', entry))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sRect = submenu.getBoundingClientRect();
|
||||||
|
eRect = entry.getBoundingClientRect();
|
||||||
|
if (eRect.top + sRect.height < d.documentElement.clientHeight) {
|
||||||
|
top = '0px';
|
||||||
|
bottom = 'auto';
|
||||||
|
} else {
|
||||||
|
top = 'auto';
|
||||||
|
bottom = '0px';
|
||||||
|
}
|
||||||
|
if (eRect.right + sRect.width < d.documentElement.clientWidth) {
|
||||||
|
left = '100%';
|
||||||
|
right = 'auto';
|
||||||
|
} else {
|
||||||
|
left = 'auto';
|
||||||
|
right = '100%';
|
||||||
|
}
|
||||||
|
style = submenu.style;
|
||||||
|
style.top = top;
|
||||||
|
style.bottom = bottom;
|
||||||
|
style.left = left;
|
||||||
|
return style.right = right;
|
||||||
|
},
|
||||||
|
addEntry: function(entry) {
|
||||||
|
Menu.parseEntry(entry);
|
||||||
|
return Menu.entries.push(entry);
|
||||||
|
},
|
||||||
|
parseEntry: 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, 'has-submenu');
|
||||||
|
for (_i = 0, _len = children.length; _i < _len; _i++) {
|
||||||
|
child = children[_i];
|
||||||
|
Menu.parseEntry(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ReportLink = {
|
||||||
|
init: function() {
|
||||||
|
var a;
|
||||||
|
a = $.el('a', {
|
||||||
|
className: 'report-link',
|
||||||
|
href: 'javascript:;',
|
||||||
|
textContent: 'Report this post'
|
||||||
|
});
|
||||||
|
$.on(a, 'click', ReportLink.report);
|
||||||
|
return Menu.addEntry({
|
||||||
|
el: a,
|
||||||
|
open: function(post) {
|
||||||
|
ReportLink.post = post;
|
||||||
|
return !post.isDead;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
report: function() {
|
||||||
|
var id, post, set, url;
|
||||||
|
post = ReportLink.post;
|
||||||
|
url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
|
||||||
|
id = Date.now();
|
||||||
|
set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200";
|
||||||
|
return window.open(url, id, set);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DeleteLink = {
|
||||||
|
init: function() {
|
||||||
|
var div, fileEl, fileEntry, postEl, postEntry;
|
||||||
|
div = $.el('div', {
|
||||||
|
className: 'delete-link',
|
||||||
|
textContent: 'Delete'
|
||||||
|
});
|
||||||
|
postEl = $.el('a', {
|
||||||
|
className: 'delete-post',
|
||||||
|
href: 'javascript:;'
|
||||||
|
});
|
||||||
|
fileEl = $.el('a', {
|
||||||
|
className: 'delete-file',
|
||||||
|
href: 'javascript:;'
|
||||||
|
});
|
||||||
|
postEntry = {
|
||||||
|
el: postEl,
|
||||||
|
open: function() {
|
||||||
|
postEl.textContent = 'Post';
|
||||||
|
$.on(postEl, 'click', DeleteLink["delete"]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fileEntry = {
|
||||||
|
el: fileEl,
|
||||||
|
open: function(post) {
|
||||||
|
fileEl.textContent = 'File';
|
||||||
|
$.on(fileEl, 'click', DeleteLink["delete"]);
|
||||||
|
return !!post.file;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Menu.addEntry({
|
||||||
|
el: div,
|
||||||
|
open: function(post) {
|
||||||
|
var node, seconds;
|
||||||
|
if (post.isDead) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DeleteLink.post = post;
|
||||||
|
node = div.firstChild;
|
||||||
|
if (seconds = DeleteLink.cooldown[post.fullID]) {
|
||||||
|
node.textContent = "Delete (" + seconds + ")";
|
||||||
|
DeleteLink.cooldown.el = node;
|
||||||
|
} else {
|
||||||
|
node.textContent = 'Delete';
|
||||||
|
delete DeleteLink.cooldown.el;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
children: [postEntry, fileEntry]
|
||||||
|
});
|
||||||
|
return $.on(d, 'QRPostSuccessful', this.cooldown.start);
|
||||||
|
},
|
||||||
|
"delete": function() {
|
||||||
|
var form, link, m, post, pwd;
|
||||||
|
post = DeleteLink.post;
|
||||||
|
if (DeleteLink.cooldown[post.fullID]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.off(this, 'click', DeleteLink["delete"]);
|
||||||
|
this.textContent = "Deleting " + this.textContent + "...";
|
||||||
|
pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $.id('delPassword').value;
|
||||||
|
form = {
|
||||||
|
mode: 'usrdel',
|
||||||
|
onlyimgdel: $.hasClass(this, 'delete-file'),
|
||||||
|
pwd: pwd
|
||||||
|
};
|
||||||
|
form[post.ID] = 'delete';
|
||||||
|
link = this;
|
||||||
|
return $.ajax($.id('delform').action.replace("/" + g.BOARD + "/", "/" + post.board + "/"), {
|
||||||
|
onload: function() {
|
||||||
|
return DeleteLink.load(link, this.response);
|
||||||
|
},
|
||||||
|
onerror: function() {
|
||||||
|
return DeleteLink.error(link);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
form: $.formData(form)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
load: function(link, html) {
|
||||||
|
var doc, msg, s;
|
||||||
|
doc = d.implementation.createHTMLDocument('');
|
||||||
|
doc.documentElement.innerHTML = html;
|
||||||
|
if (doc.title === '4chan - Banned') {
|
||||||
|
s = 'Banned!';
|
||||||
|
} else if (msg = doc.getElementById('errmsg')) {
|
||||||
|
s = msg.textContent;
|
||||||
|
$.on(link, 'click', DeleteLink["delete"]);
|
||||||
|
} else {
|
||||||
|
s = 'Deleted';
|
||||||
|
}
|
||||||
|
return link.textContent = s;
|
||||||
|
},
|
||||||
|
error: function(link) {
|
||||||
|
link.textContent = 'Connection error, please retry.';
|
||||||
|
return $.on(link, 'click', DeleteLink["delete"]);
|
||||||
|
},
|
||||||
|
cooldown: {
|
||||||
|
start: function(e) {
|
||||||
|
var fullID, seconds;
|
||||||
|
seconds = g.BOARD.ID === 'q' ? 600 : 30;
|
||||||
|
fullID = "" + g.BOARD + "." + e.detail.postID;
|
||||||
|
return DeleteLink.cooldown.count(fullID, seconds, seconds);
|
||||||
|
},
|
||||||
|
count: function(fullID, seconds, length) {
|
||||||
|
var el;
|
||||||
|
if (!((0 <= seconds && seconds <= length))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout(DeleteLink.cooldown.count, 1000, fullID, seconds - 1, length);
|
||||||
|
el = DeleteLink.cooldown.el;
|
||||||
|
if (seconds === 0) {
|
||||||
|
if (el != null) {
|
||||||
|
el.textContent = 'Delete';
|
||||||
|
}
|
||||||
|
delete DeleteLink.cooldown[fullID];
|
||||||
|
delete DeleteLink.cooldown.el;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (el != null) {
|
||||||
|
el.textContent = "Delete (" + seconds + ")";
|
||||||
|
}
|
||||||
|
return DeleteLink.cooldown[fullID] = seconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (!post.file) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
a.href = post.file.URL;
|
||||||
|
a.download = post.file.name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ArchiveLink = {
|
||||||
|
init: function() {
|
||||||
|
var div, entry, type, _i, _len, _ref;
|
||||||
|
div = $.el('div', {
|
||||||
|
textContent: 'Archive'
|
||||||
|
});
|
||||||
|
entry = {
|
||||||
|
el: div,
|
||||||
|
open: function(post) {
|
||||||
|
var redirect;
|
||||||
|
redirect = Redirect.to({
|
||||||
|
board: post.board,
|
||||||
|
threadID: post.thread,
|
||||||
|
postID: post.ID
|
||||||
|
});
|
||||||
|
return redirect !== ("//boards.4chan.org/" + post.board + "/");
|
||||||
|
},
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
_ref = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['E-mail', 'email'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'md5']];
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
type = _ref[_i];
|
||||||
|
entry.children.push(this.createSubEntry(type[0], type[1]));
|
||||||
|
}
|
||||||
|
return Menu.addEntry(entry);
|
||||||
|
},
|
||||||
|
createSubEntry: function(text, type) {
|
||||||
|
var el, open;
|
||||||
|
el = $.el('a', {
|
||||||
|
textContent: text,
|
||||||
|
target: '_blank'
|
||||||
|
});
|
||||||
|
if (type === 'post') {
|
||||||
|
open = function(post) {
|
||||||
|
el.href = Redirect.to({
|
||||||
|
board: post.board,
|
||||||
|
threadID: post.thread,
|
||||||
|
postID: post.ID
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
open = function(post) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
el: el,
|
||||||
|
open: open
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Redirect = {
|
Redirect = {
|
||||||
image: function(board, filename) {
|
image: function(board, filename) {
|
||||||
switch (board.ID) {
|
switch ("" + board) {
|
||||||
case 'a':
|
case 'a':
|
||||||
case 'co':
|
case 'co':
|
||||||
case 'jp':
|
case 'jp':
|
||||||
@ -1057,7 +1498,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
post: function(board, postID) {
|
post: function(board, postID) {
|
||||||
switch (board.ID) {
|
switch ("" + board) {
|
||||||
case 'a':
|
case 'a':
|
||||||
case 'co':
|
case 'co':
|
||||||
case 'jp':
|
case 'jp':
|
||||||
@ -1082,7 +1523,7 @@
|
|||||||
to: function(data) {
|
to: function(data) {
|
||||||
var board, url;
|
var board, url;
|
||||||
board = data.board;
|
board = data.board;
|
||||||
switch (board.ID) {
|
switch ("" + board) {
|
||||||
case 'a':
|
case 'a':
|
||||||
case 'co':
|
case 'co':
|
||||||
case 'jp':
|
case 'jp':
|
||||||
@ -1153,7 +1594,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
board = data.board, threadID = data.threadID, postID = data.postID;
|
board = data.board, threadID = data.threadID, postID = data.postID;
|
||||||
if (postID) {
|
if (typeof postID === 'string') {
|
||||||
postID = postID.match(/\d+/)[0];
|
postID = postID.match(/\d+/)[0];
|
||||||
}
|
}
|
||||||
path = threadID ? "" + board + "/thread/" + threadID : "" + board + "/post/" + postID;
|
path = threadID ? "" + board + "/thread/" + threadID : "" + board + "/post/" + postID;
|
||||||
@ -3052,6 +3493,41 @@
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
$.log(err, 'Recursive');
|
$.log(err, 'Recursive');
|
||||||
}
|
}
|
||||||
|
if (Conf['Menu']) {
|
||||||
|
try {
|
||||||
|
Menu.init();
|
||||||
|
} catch (err) {
|
||||||
|
$.log(err, 'Menu');
|
||||||
|
}
|
||||||
|
if (Conf['Report Link']) {
|
||||||
|
try {
|
||||||
|
ReportLink.init();
|
||||||
|
} catch (err) {
|
||||||
|
$.log(err, 'Report Link', err.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Conf['Delete Link']) {
|
||||||
|
try {
|
||||||
|
DeleteLink.init();
|
||||||
|
} catch (err) {
|
||||||
|
$.log(err, 'Delete Link');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Conf['Download Link']) {
|
||||||
|
try {
|
||||||
|
DownloadLink.init();
|
||||||
|
} catch (err) {
|
||||||
|
$.log(err, 'Download Link');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Conf['Archive Link']) {
|
||||||
|
try {
|
||||||
|
ArchiveLink.init();
|
||||||
|
} catch (err) {
|
||||||
|
$.log(err, 'Archive Link');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (Conf['Quote Inline']) {
|
if (Conf['Quote Inline']) {
|
||||||
try {
|
try {
|
||||||
QuoteInline.init();
|
QuoteInline.init();
|
||||||
@ -3205,7 +3681,7 @@
|
|||||||
settings: function() {
|
settings: function() {
|
||||||
return alert('Here be settings');
|
return alert('Here be settings');
|
||||||
},
|
},
|
||||||
css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\n}\n.warning {\n color: red;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\n display: block !important;\n}\n.post {\n overflow: visible !important;\n}\n[hidden] {\n display: none !important;\n}\n\n/* fixed, z-index */\n#qp, #ihover,\n#updater, #stats,\n#boardNavDesktop.reply,\n#qr, #watcher {\n position: fixed;\n}\n#qp, #ihover {\n z-index: 100;\n}\n#updater, #stats {\n z-index: 90;\n}\n#boardNavDesktop.reply:hover {\n z-index: 80;\n}\n#qr {\n z-index: 50;\n}\n#watcher {\n z-index: 30;\n}\n#boardNavDesktop.reply {\n z-index: 10;\n}\n\n\n/* header */\nbody.fourchan_x {\n margin-top: 2.5em;\n}\n#boardNavDesktop.reply {\n border-width: 0 0 1px;\n padding: 4px;\n top: 0;\n right: 0;\n left: 0;\n transition: opacity .1s ease-in-out;\n -o-transition: opacity .1s ease-in-out;\n -moz-transition: opacity .1s ease-in-out;\n -webkit-transition: opacity .1s ease-in-out;\n}\n#boardNavDesktop.reply:not(:hover) {\n opacity: .4;\n transition: opacity 1.5s .5s ease-in-out;\n -o-transition: opacity 1.5s .5s ease-in-out;\n -moz-transition: opacity 1.5s .5s ease-in-out;\n -webkit-transition: opacity 1.5s .5s ease-in-out;\n}\n#boardNavDesktop.reply a {\n margin: -1px;\n}\n#settings {\n float: right;\n}\n\n/* thread updater */\n#updater {\n text-align: right;\n}\n#updater:not(:hover) {\n background: none;\n border: none;\n}\n#updater input[type=number] {\n width: 4em;\n}\n#updater:not(:hover) > div:not(.move) {\n display: none;\n}\n.new {\n color: limegreen;\n}\n\n/* quote */\n.quotelink.deadlink {\n text-decoration: underline !important;\n}\n.deadlink:not(.quotelink) {\n text-decoration: none !important;\n}\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.filtered {\n text-decoration: underline line-through;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n box-shadow: 0 0 0 2px rgba(216, 94, 49, .7);\n}\n\n/* file */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull {\n display: none;\n}\n#ihover {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n max-height: 100%;\n max-width: 75%;\n padding-bottom: 16px;\n}\n\n/* thread & reply hiding */\n.hide-thread-button,\n.hide-reply-button {\n float: left;\n margin-right: 2px;\n}\n.stub ~ .sideArrows,\n.stub ~ .hide-reply-button,\n.stub ~ .post {\n display: none !important;\n}"
|
css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\n}\n.warning {\n color: red;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\n display: block !important;\n}\n.post {\n overflow: visible !important;\n}\n[hidden] {\n display: none !important;\n}\n\n/* fixed, z-index */\n#qp, #ihover,\n#updater, #stats,\n#boardNavDesktop.reply,\n#qr, #watcher {\n position: fixed;\n}\n#qp, #ihover {\n z-index: 100;\n}\n#updater, #stats {\n z-index: 90;\n}\n#boardNavDesktop.reply:hover {\n z-index: 80;\n}\n#qr {\n z-index: 50;\n}\n#watcher {\n z-index: 30;\n}\n#boardNavDesktop.reply {\n z-index: 10;\n}\n\n\n/* header */\nbody.fourchan_x {\n margin-top: 2.5em;\n}\n#boardNavDesktop.reply {\n border-width: 0 0 1px;\n padding: 4px;\n top: 0;\n right: 0;\n left: 0;\n transition: opacity .1s ease-in-out;\n -o-transition: opacity .1s ease-in-out;\n -moz-transition: opacity .1s ease-in-out;\n -webkit-transition: opacity .1s ease-in-out;\n}\n#boardNavDesktop.reply:not(:hover) {\n opacity: .4;\n transition: opacity 1.5s .5s ease-in-out;\n -o-transition: opacity 1.5s .5s ease-in-out;\n -moz-transition: opacity 1.5s .5s ease-in-out;\n -webkit-transition: opacity 1.5s .5s ease-in-out;\n}\n#boardNavDesktop.reply a {\n margin: -1px;\n}\n#settings {\n float: right;\n}\n\n/* thread updater */\n#updater {\n text-align: right;\n}\n#updater:not(:hover) {\n background: none;\n border: none;\n}\n#updater input[type=number] {\n width: 4em;\n}\n#updater:not(:hover) > div:not(.move) {\n display: none;\n}\n.new {\n color: limegreen;\n}\n\n/* quote */\n.quotelink.deadlink {\n text-decoration: underline !important;\n}\n.deadlink:not(.quotelink) {\n text-decoration: none !important;\n}\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.filtered {\n text-decoration: underline line-through;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n box-shadow: 0 0 0 2px rgba(216, 94, 49, .7);\n}\n\n/* file */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull {\n display: none;\n}\n#ihover {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n max-height: 100%;\n max-width: 75%;\n padding-bottom: 16px;\n}\n\n/* thread & reply hiding */\n.hide-thread-button,\n.hide-reply-button {\n float: left;\n margin-right: 2px;\n}\n.stub ~ .sideArrows,\n.stub ~ .hide-reply-button,\n.stub ~ .post {\n display: none !important;\n}\n\n/* Menu */\n.menu-button {\n display: inline-block;\n}\n.menu-button > span {\n border-top: 6px solid;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n display: inline-block;\n margin: 2px;\n vertical-align: middle;\n}\n#menu {\n position: absolute;\n outline: none;\n}\n.entry {\n border-bottom: 1px solid rgba(0, 0, 0, .25);\n cursor: pointer;\n display: block;\n outline: none;\n padding: 3px 7px;\n position: relative;\n text-decoration: none;\n white-space: nowrap;\n}\n.entry:last-child {\n border: none;\n}\n.focused.entry {\n background: rgba(255, 255, 255, .33);\n}\n.entry.has-submenu {\n padding-right: 20px;\n}\n.has-submenu::after {\n content: \"\";\n border-left: 6px solid;\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n display: inline-block;\n margin: 4px;\n position: absolute;\n right: 3px;\n}\n.has-submenu:not(.focused) > .submenu {\n display: none;\n}\n.submenu {\n position: absolute;\n margin: -1px 0;\n}"
|
||||||
};
|
};
|
||||||
|
|
||||||
Main.init();
|
Main.init();
|
||||||
|
|||||||
@ -174,3 +174,56 @@ body.fourchan_x {
|
|||||||
.stub ~ .post {
|
.stub ~ .post {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Menu */
|
||||||
|
.menu-button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.menu-button > span {
|
||||||
|
border-top: 6px solid;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
border-left: 4px 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.has-submenu {
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
.has-submenu::after {
|
||||||
|
content: "";
|
||||||
|
border-left: 6px solid;
|
||||||
|
border-top: 4px solid transparent;
|
||||||
|
border-bottom: 4px solid transparent;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 4px;
|
||||||
|
position: absolute;
|
||||||
|
right: 3px;
|
||||||
|
}
|
||||||
|
.has-submenu:not(.focused) > .submenu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.submenu {
|
||||||
|
position: absolute;
|
||||||
|
margin: -1px 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -244,11 +244,386 @@ Recursive =
|
|||||||
break
|
break
|
||||||
return
|
return
|
||||||
|
|
||||||
|
Menu =
|
||||||
|
entries: []
|
||||||
|
init: ->
|
||||||
|
# Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API
|
||||||
|
$.on d, 'AddMenuEntry', (e) -> Menu.addEntry e.detail
|
||||||
|
Post::callbacks.push
|
||||||
|
name: 'Menu'
|
||||||
|
cb: @node
|
||||||
|
|
||||||
|
node: ->
|
||||||
|
if @isClone
|
||||||
|
a = $ '.menu-button', @nodes.info
|
||||||
|
a.setAttribute 'data-clone', true
|
||||||
|
$.on a, 'click', Menu.toggle
|
||||||
|
return
|
||||||
|
a = Menu.makeButton @
|
||||||
|
$.add @nodes.info, [$.tn('\u00A0'), a]
|
||||||
|
|
||||||
|
makeButton: (post) ->
|
||||||
|
a = $.el 'a',
|
||||||
|
className: 'menu-button'
|
||||||
|
innerHTML: '[<span></span>]'
|
||||||
|
href: 'javascript:;'
|
||||||
|
a.setAttribute 'data-postid', post.fullID
|
||||||
|
$.on a, 'click', Menu.toggle
|
||||||
|
a
|
||||||
|
|
||||||
|
makeMenu: ->
|
||||||
|
menu = $.el 'div',
|
||||||
|
className: 'reply dialog'
|
||||||
|
id: 'menu'
|
||||||
|
tabIndex: 0
|
||||||
|
$.on menu, 'click', (e) -> e.stopPropagation()
|
||||||
|
$.on menu, 'keydown', Menu.keybinds
|
||||||
|
menu
|
||||||
|
|
||||||
|
toggle: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
if Menu.currentMenu
|
||||||
|
# Close if it's already opened.
|
||||||
|
# Reopen if we clicked on another button.
|
||||||
|
{lastToggledButton} = Menu
|
||||||
|
Menu.close()
|
||||||
|
return if lastToggledButton is @
|
||||||
|
|
||||||
|
Menu.lastToggledButton = @
|
||||||
|
post =
|
||||||
|
if @dataset.clone
|
||||||
|
Get.postFromRoot $.x 'ancestor::div[contains(@class,"postContainer")][1]', @
|
||||||
|
else
|
||||||
|
g.posts[@dataset.postid]
|
||||||
|
Menu.open @, post
|
||||||
|
|
||||||
|
open: (button, post) ->
|
||||||
|
menu = Menu.makeMenu()
|
||||||
|
Menu.currentMenu = menu
|
||||||
|
|
||||||
|
for entry in Menu.entries
|
||||||
|
Menu.insertEntry entry, menu, post
|
||||||
|
|
||||||
|
Menu.focus $ '.entry', menu
|
||||||
|
$.on d, 'click', Menu.close
|
||||||
|
$.add d.body, menu
|
||||||
|
|
||||||
|
# Position
|
||||||
|
mRect = menu.getBoundingClientRect()
|
||||||
|
bRect = button.getBoundingClientRect()
|
||||||
|
bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top
|
||||||
|
bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left
|
||||||
|
menu.style.top =
|
||||||
|
if bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight
|
||||||
|
bTop + bRect.height + 2 + 'px'
|
||||||
|
else
|
||||||
|
bTop - mRect.height - 2 + 'px'
|
||||||
|
menu.style.left =
|
||||||
|
if bRect.left + mRect.width < d.documentElement.clientWidth
|
||||||
|
bLeft + 'px'
|
||||||
|
else
|
||||||
|
bLeft + bRect.width - mRect.width + 'px'
|
||||||
|
|
||||||
|
menu.focus()
|
||||||
|
|
||||||
|
insertEntry: (entry, parent, post) ->
|
||||||
|
return unless entry.open post
|
||||||
|
$.add parent, entry.el
|
||||||
|
|
||||||
|
return unless entry.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 entry.children
|
||||||
|
Menu.insertEntry child, submenu, post
|
||||||
|
return
|
||||||
|
|
||||||
|
close: ->
|
||||||
|
$.rm Menu.currentMenu
|
||||||
|
delete Menu.currentMenu
|
||||||
|
delete Menu.lastToggledButton
|
||||||
|
$.off d, 'click', Menu.close
|
||||||
|
|
||||||
|
keybinds: (e) ->
|
||||||
|
entry = $ '.focused', Menu.currentMenu
|
||||||
|
while subEntry = $ '.focused', entry
|
||||||
|
entry = subEntry
|
||||||
|
|
||||||
|
switch Keybinds.keyCode(e) or e.keyCode
|
||||||
|
when 'Esc'
|
||||||
|
Menu.lastToggledButton.focus()
|
||||||
|
Menu.close()
|
||||||
|
when 13, 32 # 'Enter', 'Space'
|
||||||
|
entry.click()
|
||||||
|
when 'Up'
|
||||||
|
if next = entry.previousElementSibling
|
||||||
|
Menu.focus next
|
||||||
|
when 'Down'
|
||||||
|
if next = entry.nextElementSibling
|
||||||
|
Menu.focus next
|
||||||
|
when 'Right'
|
||||||
|
if (submenu = $ '.submenu', entry) and next = submenu.firstElementChild
|
||||||
|
Menu.focus next
|
||||||
|
when 'Left'
|
||||||
|
if next = $.x 'parent::*[contains(@class,"submenu")]/parent::*', entry
|
||||||
|
Menu.focus next
|
||||||
|
else
|
||||||
|
return
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
focus: (entry) ->
|
||||||
|
if focused = $.x 'parent::*/child::*[contains(@class,"focused")]', entry
|
||||||
|
$.rmClass focused, 'focused'
|
||||||
|
for focused in $$ '.focused', entry
|
||||||
|
$.rmClass focused, 'focused'
|
||||||
|
$.addClass entry, 'focused'
|
||||||
|
|
||||||
|
# Submenu positioning.
|
||||||
|
return unless submenu = $ '.submenu', entry
|
||||||
|
sRect = submenu.getBoundingClientRect()
|
||||||
|
eRect = entry.getBoundingClientRect()
|
||||||
|
if eRect.top + sRect.height < d.documentElement.clientHeight
|
||||||
|
top = '0px'
|
||||||
|
bottom = 'auto'
|
||||||
|
else
|
||||||
|
top = 'auto'
|
||||||
|
bottom = '0px'
|
||||||
|
if eRect.right + sRect.width < d.documentElement.clientWidth
|
||||||
|
left = '100%'
|
||||||
|
right = 'auto'
|
||||||
|
else
|
||||||
|
left = 'auto'
|
||||||
|
right = '100%'
|
||||||
|
{style} = submenu
|
||||||
|
style.top = top
|
||||||
|
style.bottom = bottom
|
||||||
|
style.left = left
|
||||||
|
style.right = right
|
||||||
|
|
||||||
|
addEntry: (entry) ->
|
||||||
|
Menu.parseEntry entry
|
||||||
|
Menu.entries.push entry
|
||||||
|
|
||||||
|
parseEntry: (entry) ->
|
||||||
|
{el, children} = entry
|
||||||
|
$.addClass el, 'entry'
|
||||||
|
$.on el, 'focus mouseover', (e) ->
|
||||||
|
e.stopPropagation()
|
||||||
|
Menu.focus @
|
||||||
|
return unless children
|
||||||
|
$.addClass el, 'has-submenu'
|
||||||
|
for child in children
|
||||||
|
Menu.parseEntry child
|
||||||
|
return
|
||||||
|
|
||||||
|
ReportLink =
|
||||||
|
init: ->
|
||||||
|
a = $.el 'a',
|
||||||
|
className: 'report-link'
|
||||||
|
href: 'javascript:;'
|
||||||
|
textContent: 'Report this post'
|
||||||
|
$.on a, 'click', ReportLink.report
|
||||||
|
Menu.addEntry
|
||||||
|
el: a
|
||||||
|
open: (post) ->
|
||||||
|
ReportLink.post = post
|
||||||
|
!post.isDead
|
||||||
|
report: ->
|
||||||
|
{post} = ReportLink
|
||||||
|
url = "//sys.4chan.org/#{post.board}/imgboard.php?mode=report&no=#{post}"
|
||||||
|
id = Date.now()
|
||||||
|
set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200"
|
||||||
|
window.open url, id, set
|
||||||
|
|
||||||
|
DeleteLink =
|
||||||
|
init: ->
|
||||||
|
div = $.el 'div',
|
||||||
|
className: 'delete-link'
|
||||||
|
textContent: 'Delete'
|
||||||
|
postEl = $.el 'a',
|
||||||
|
className: 'delete-post'
|
||||||
|
href: 'javascript:;'
|
||||||
|
fileEl = $.el 'a',
|
||||||
|
className: 'delete-file'
|
||||||
|
href: 'javascript:;'
|
||||||
|
|
||||||
|
postEntry =
|
||||||
|
el: postEl
|
||||||
|
open: ->
|
||||||
|
postEl.textContent = 'Post'
|
||||||
|
$.on postEl, 'click', DeleteLink.delete
|
||||||
|
true
|
||||||
|
fileEntry =
|
||||||
|
el: fileEl
|
||||||
|
open: (post) ->
|
||||||
|
fileEl.textContent = 'File'
|
||||||
|
$.on fileEl, 'click', DeleteLink.delete
|
||||||
|
!!post.file
|
||||||
|
|
||||||
|
Menu.addEntry
|
||||||
|
el: div
|
||||||
|
open: (post) ->
|
||||||
|
return false if post.isDead
|
||||||
|
DeleteLink.post = post
|
||||||
|
node = div.firstChild
|
||||||
|
if seconds = DeleteLink.cooldown[post.fullID]
|
||||||
|
node.textContent = "Delete (#{seconds})"
|
||||||
|
DeleteLink.cooldown.el = node
|
||||||
|
else
|
||||||
|
node.textContent = 'Delete'
|
||||||
|
delete DeleteLink.cooldown.el
|
||||||
|
true
|
||||||
|
children: [postEntry, fileEntry]
|
||||||
|
|
||||||
|
$.on d, 'QRPostSuccessful', @cooldown.start
|
||||||
|
|
||||||
|
delete: ->
|
||||||
|
{post} = DeleteLink
|
||||||
|
return if DeleteLink.cooldown[post.fullID]
|
||||||
|
|
||||||
|
$.off @, 'click', DeleteLink.delete
|
||||||
|
@textContent = "Deleting #{@textContent}..."
|
||||||
|
|
||||||
|
pwd =
|
||||||
|
if m = d.cookie.match /4chan_pass=([^;]+)/
|
||||||
|
decodeURIComponent m[1]
|
||||||
|
else
|
||||||
|
$.id('delPassword').value
|
||||||
|
|
||||||
|
form =
|
||||||
|
mode: 'usrdel'
|
||||||
|
onlyimgdel: $.hasClass @, 'delete-file'
|
||||||
|
pwd: pwd
|
||||||
|
form[post.ID] = 'delete'
|
||||||
|
|
||||||
|
link = @
|
||||||
|
$.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"), {
|
||||||
|
onload: -> DeleteLink.load link, @response
|
||||||
|
onerror: -> DeleteLink.error link
|
||||||
|
}, {
|
||||||
|
form: $.formData form
|
||||||
|
}
|
||||||
|
load: (link, html) ->
|
||||||
|
doc = d.implementation.createHTMLDocument ''
|
||||||
|
doc.documentElement.innerHTML = html
|
||||||
|
if doc.title is '4chan - Banned' # Ban/warn check
|
||||||
|
s = 'Banned!'
|
||||||
|
else if msg = doc.getElementById 'errmsg' # error!
|
||||||
|
s = msg.textContent
|
||||||
|
$.on link, 'click', DeleteLink.delete
|
||||||
|
else
|
||||||
|
s = 'Deleted'
|
||||||
|
link.textContent = s
|
||||||
|
error: (link) ->
|
||||||
|
link.textContent = 'Connection error, please retry.'
|
||||||
|
$.on link, 'click', DeleteLink.delete
|
||||||
|
|
||||||
|
cooldown:
|
||||||
|
start: (e) ->
|
||||||
|
seconds =
|
||||||
|
if g.BOARD.ID is 'q'
|
||||||
|
600
|
||||||
|
else
|
||||||
|
30
|
||||||
|
fullID = "#{g.BOARD}.#{e.detail.postID}"
|
||||||
|
DeleteLink.cooldown.count fullID, seconds, seconds
|
||||||
|
count: (fullID, seconds, length) ->
|
||||||
|
return unless 0 <= seconds <= length
|
||||||
|
setTimeout DeleteLink.cooldown.count, 1000, fullID, seconds-1, length
|
||||||
|
{el} = DeleteLink.cooldown
|
||||||
|
if seconds is 0
|
||||||
|
el?.textContent = 'Delete'
|
||||||
|
delete DeleteLink.cooldown[fullID]
|
||||||
|
delete DeleteLink.cooldown.el
|
||||||
|
return
|
||||||
|
el?.textContent = "Delete (#{seconds})"
|
||||||
|
DeleteLink.cooldown[fullID] = seconds
|
||||||
|
|
||||||
|
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) ->
|
||||||
|
return false unless post.file
|
||||||
|
a.href = post.file.URL
|
||||||
|
a.download = post.file.name
|
||||||
|
true
|
||||||
|
|
||||||
|
ArchiveLink =
|
||||||
|
init: ->
|
||||||
|
div = $.el 'div',
|
||||||
|
textContent: 'Archive'
|
||||||
|
|
||||||
|
entry =
|
||||||
|
el: div
|
||||||
|
open: (post) ->
|
||||||
|
redirect = Redirect.to
|
||||||
|
board: post.board
|
||||||
|
threadID: post.thread
|
||||||
|
postID: post.ID
|
||||||
|
redirect isnt "//boards.4chan.org/#{post.board}/"
|
||||||
|
children: []
|
||||||
|
|
||||||
|
for type in [
|
||||||
|
['Post', 'post']
|
||||||
|
['Name', 'name']
|
||||||
|
['Tripcode', 'tripcode']
|
||||||
|
['E-mail', 'email']
|
||||||
|
['Subject', 'subject']
|
||||||
|
['Filename', 'filename']
|
||||||
|
['Image MD5', 'md5']
|
||||||
|
]
|
||||||
|
# Add a sub entry for each type.
|
||||||
|
entry.children.push @createSubEntry type[0], type[1]
|
||||||
|
|
||||||
|
Menu.addEntry entry
|
||||||
|
|
||||||
|
createSubEntry: (text, type) ->
|
||||||
|
el = $.el 'a',
|
||||||
|
textContent: text
|
||||||
|
target: '_blank'
|
||||||
|
|
||||||
|
if type is 'post'
|
||||||
|
open = (post) ->
|
||||||
|
el.href = Redirect.to
|
||||||
|
board: post.board
|
||||||
|
threadID: post.thread
|
||||||
|
postID: post.ID
|
||||||
|
true
|
||||||
|
else
|
||||||
|
open = (post) ->
|
||||||
|
# value = Filter[type] post
|
||||||
|
# # We want to parse the exact same stuff as the filter does already.
|
||||||
|
# return false unless value
|
||||||
|
# el.href = Redirect.to
|
||||||
|
# board: post.board
|
||||||
|
# type: type
|
||||||
|
# value: value
|
||||||
|
# isSearch: true
|
||||||
|
true
|
||||||
|
|
||||||
|
return {
|
||||||
|
el: el
|
||||||
|
open: open
|
||||||
|
}
|
||||||
|
|
||||||
Redirect =
|
Redirect =
|
||||||
image: (board, filename) ->
|
image: (board, filename) ->
|
||||||
# XXX need to differentiate between thumbnail only and full_image for img src=
|
# XXX need to differentiate between thumbnail only and full_image for img src=
|
||||||
# Do not use g.BOARD, the image url can originate from a cross-quote.
|
# Do not use g.BOARD, the image url can originate from a cross-quote.
|
||||||
switch board.ID
|
switch "#{board}"
|
||||||
when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg'
|
when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg'
|
||||||
"//archive.foolz.us/#{board}/full_image/#{filename}"
|
"//archive.foolz.us/#{board}/full_image/#{filename}"
|
||||||
when 'u'
|
when 'u'
|
||||||
@ -266,7 +641,7 @@ Redirect =
|
|||||||
when 'c'
|
when 'c'
|
||||||
"//archive.nyafuu.org/#{board}/full_image/#{filename}"
|
"//archive.nyafuu.org/#{board}/full_image/#{filename}"
|
||||||
post: (board, postID) ->
|
post: (board, postID) ->
|
||||||
switch board.ID
|
switch "#{board}"
|
||||||
when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg', 'dev', 'foolz'
|
when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg', 'dev', 'foolz'
|
||||||
"//archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
|
"//archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
|
||||||
when 'u', 'kuku'
|
when 'u', 'kuku'
|
||||||
@ -277,7 +652,7 @@ Redirect =
|
|||||||
# https://github.com/eksopl/fuuka/issues/27
|
# https://github.com/eksopl/fuuka/issues/27
|
||||||
to: (data) ->
|
to: (data) ->
|
||||||
{board} = data
|
{board} = data
|
||||||
switch board.ID
|
switch "#{board}"
|
||||||
when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg', 'dev', 'foolz'
|
when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg', 'dev', 'foolz'
|
||||||
url = Redirect.path '//archive.foolz.us', 'foolfuuka', data
|
url = Redirect.path '//archive.foolz.us', 'foolfuuka', data
|
||||||
when 'u', 'kuku'
|
when 'u', 'kuku'
|
||||||
@ -318,7 +693,7 @@ Redirect =
|
|||||||
|
|
||||||
{board, threadID, postID} = data
|
{board, threadID, postID} = data
|
||||||
# keep the number only if the location.hash was sent f.e.
|
# keep the number only if the location.hash was sent f.e.
|
||||||
postID = postID.match(/\d+/)[0] if postID
|
postID = postID.match(/\d+/)[0] if typeof postID is 'string'
|
||||||
path =
|
path =
|
||||||
if threadID
|
if threadID
|
||||||
"#{board}/thread/#{threadID}"
|
"#{board}/thread/#{threadID}"
|
||||||
|
|||||||
@ -342,6 +342,41 @@ Main =
|
|||||||
# XXX handle error
|
# XXX handle error
|
||||||
$.log err, 'Recursive'
|
$.log err, 'Recursive'
|
||||||
|
|
||||||
|
if Conf['Menu']
|
||||||
|
try
|
||||||
|
Menu.init()
|
||||||
|
catch err
|
||||||
|
# XXX handle error
|
||||||
|
$.log err, 'Menu'
|
||||||
|
|
||||||
|
if Conf['Report Link']
|
||||||
|
try
|
||||||
|
ReportLink.init()
|
||||||
|
catch err
|
||||||
|
# XXX handle error
|
||||||
|
$.log err, 'Report Link', err.stack
|
||||||
|
|
||||||
|
if Conf['Delete Link']
|
||||||
|
try
|
||||||
|
DeleteLink.init()
|
||||||
|
catch err
|
||||||
|
# XXX handle error
|
||||||
|
$.log err, 'Delete Link'
|
||||||
|
|
||||||
|
if Conf['Download Link']
|
||||||
|
try
|
||||||
|
DownloadLink.init()
|
||||||
|
catch err
|
||||||
|
# XXX handle error
|
||||||
|
$.log err, 'Download Link'
|
||||||
|
|
||||||
|
if Conf['Archive Link']
|
||||||
|
try
|
||||||
|
ArchiveLink.init()
|
||||||
|
catch err
|
||||||
|
# XXX handle error
|
||||||
|
$.log err, 'Archive Link'
|
||||||
|
|
||||||
if Conf['Quote Inline']
|
if Conf['Quote Inline']
|
||||||
try
|
try
|
||||||
QuoteInline.init()
|
QuoteInline.init()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user