Merge branch 'v3' of git://github.com/MayhemYDG/4chan-x into v3

Conflicts:
	CHANGELOG.md
	html/General/Header.html
	package.json
	src/Filtering/PostHiding.coffee
	src/Filtering/ThreadHiding.coffee
	src/General/Get.coffee
	src/General/Header.coffee
	src/General/Main.coffee
	src/General/UI.coffee
	src/Menu/Menu.coffee
	src/Posting/QuickReply.coffee
	src/Quotelinks/QuoteOP.coffee
This commit is contained in:
Zixaphir 2013-08-14 03:02:03 -07:00
commit 1f011dfd02
21 changed files with 377 additions and 253 deletions

View File

@ -5,10 +5,12 @@
- It is now possible to open all watched threads via the `Open all threads` button in the Thread Watcher's menu. - It is now possible to open all watched threads via the `Open all threads` button in the Thread Watcher's menu.
- Added the `Current Board` setting to switch between showing watched threads from the current board or all boards, disabled by default. - Added the `Current Board` setting to switch between showing watched threads from the current board or all boards, disabled by default.
- About dead (404'd) threads: - About dead (404'd) threads:
- Dead threads will be typographically indicated with a strikethrough. <ul>
- Dead threads will directly link to the corresponding archive when available. <li> Dead threads will be typographically indicated with a strikethrough.
- A button to prune all 404'd threads from the list is now available. <li> Dead threads will directly link to the corresponding archive when available.
- Added the `Auto Prune` setting to automatically prune 404'd threads, disabled by default. <li> A button to prune all 404'd threads from the list is now available.
<li> Added the `Auto Prune` setting to automatically prune 404'd threads, disabled by default.
</ul>
- The current thread is now highlighted in the list of watched threads. - The current thread is now highlighted in the list of watched threads.
- Watching the current thread can be done in the Header's menu too. - Watching the current thread can be done in the Header's menu too.
- Removed the `Check for Updates` setting: - Removed the `Check for Updates` setting:

View File

@ -1415,10 +1415,7 @@
})(); })();
Polyfill = { Polyfill = {
init: function() { init: function() {},
Polyfill.toBlob();
return Polyfill.visibility();
},
toBlob: function() { toBlob: function() {
var _base; var _base;
@ -1460,11 +1457,11 @@
Header = { Header = {
init: function() { init: function() {
var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton,
_this = this; _this = this;
this.menu = new UI.Menu('header'); this.menu = new UI.Menu('header');
this.menuButton = $.el('span', { menuButton = $.el('span', {
className: 'menu-button', className: 'menu-button',
innerHTML: '<i></i>' innerHTML: '<i></i>'
}); });
@ -1496,7 +1493,7 @@
this.headerToggler = headerToggler.firstElementChild; this.headerToggler = headerToggler.firstElementChild;
this.footerToggler = footerToggler.firstElementChild; this.footerToggler = footerToggler.firstElementChild;
this.customNavToggler = customNavToggler.firstElementChild; this.customNavToggler = customNavToggler.firstElementChild;
$.on(this.menuButton, 'click', this.menuToggle); $.on(menuButton, 'click', this.menuToggle);
$.on(this.barFixedToggler, 'change', this.toggleBarFixed); $.on(this.barFixedToggler, 'change', this.toggleBarFixed);
$.on(this.barPositionToggler, 'change', this.toggleBarPosition); $.on(this.barPositionToggler, 'change', this.toggleBarPosition);
$.on(this.linkJustifyToggler, 'change', this.toggleLinkJustify); $.on(this.linkJustifyToggler, 'change', this.toggleLinkJustify);
@ -1511,7 +1508,7 @@
$.sync('Bottom Header', Header.setBarPosition); $.sync('Bottom Header', Header.setBarPosition);
$.sync('Header auto-hide', Header.setBarVisibility); $.sync('Header auto-hide', Header.setBarVisibility);
$.sync('Centered links', Header.setLinkJustify); $.sync('Centered links', Header.setLinkJustify);
this.addShortcut(Header.menuButton); this.addShortcut(menuButton);
$.event('AddMenuEntry', { $.event('AddMenuEntry', {
type: 'header', type: 'header',
el: $.el('span', { el: $.el('span', {
@ -2030,6 +2027,9 @@
threadFromRoot: function(root) { threadFromRoot: function(root) {
return g.threads["" + g.BOARD + "." + root.id.slice(1)]; return g.threads["" + g.BOARD + "." + root.id.slice(1)];
}, },
threadFromNode: function(node) {
return Get.threadFromRoot($.x('ancestor::div[@class="thread"]', node));
},
postFromRoot: function(root) { postFromRoot: function(root) {
var boardID, index, link, post, postID; var boardID, index, link, post, postID;
@ -2047,8 +2047,8 @@
postFromNode: function(root) { postFromNode: function(root) {
return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root)); return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root));
}, },
contextFromNode: function(quotelink) { contextFromNode: function(node) {
return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink)); return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', node));
}, },
postDataFromLink: function(link) { postDataFromLink: function(link) {
var boardID, path, postID, threadID, _ref; var boardID, path, postID, threadID, _ref;
@ -2496,7 +2496,7 @@
}; };
Menu.prototype.focus = function(entry) { Menu.prototype.focus = function(entry) {
var bottom, cHeight, cWidth, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref, _ref1, _ref2; var cHeight, cWidth, eRect, focused, sRect, submenu, _i, _len, _ref;
while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) { while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) {
$.rmClass(focused, 'focused'); $.rmClass(focused, 'focused');
@ -2514,13 +2514,20 @@
eRect = entry.getBoundingClientRect(); eRect = entry.getBoundingClientRect();
cHeight = doc.clientHeight; cHeight = doc.clientHeight;
cWidth = doc.clientWidth; cWidth = doc.clientWidth;
_ref1 = eRect.top + sRect.height < cHeight ? ['0px', 'auto'] : ['auto', '0px'], top = _ref1[0], bottom = _ref1[1]; if (eRect.top + sRect.height < cHeight) {
_ref2 = eRect.right + sRect.width < cWidth ? ['100%', 'auto'] : ['auto', '100%'], left = _ref2[0], right = _ref2[1]; $.addClass(submenu, 'top');
style = submenu.style; $.rmClass(submenu, 'bottom');
style.top = top; } else {
style.bottom = bottom; $.addClass(submenu, 'bottom');
style.left = left; $.rmClass(submenu, 'top');
return style.right = right; }
if (eRect.right + sRect.width < cWidth) {
$.addClass(submenu, 'left');
return $.rmClass(submenu, 'right');
} else {
$.addClass(submenu, 'right');
return $.rmClass(submenu, 'left');
}
}; };
Menu.prototype.addEntry = function(e) { Menu.prototype.addEntry = function(e) {
@ -3315,7 +3322,7 @@
post.nodes.stub = $.el('div', { post.nodes.stub = $.el('div', {
className: 'stub' className: 'stub'
}); });
$.add(post.nodes.stub, !Conf['Menu'] ? a : [a, $.tn(' '), button = Menu.makeButton(post)]); $.add(post.nodes.stub, Conf['Menu'] ? [a, $.tn(' '), button = Menu.makeButton(post)] : a);
return $.prepend(post.nodes.root, post.nodes.stub); return $.prepend(post.nodes.root, post.nodes.stub);
}, },
show: function(post, showRecursively) { show: function(post, showRecursively) {
@ -3646,7 +3653,7 @@
return ThreadHiding.saveHiddenState(thread); return ThreadHiding.saveHiddenState(thread);
}, },
hide: function(thread, makeStub) { hide: function(thread, makeStub) {
var OP, a, button, numReplies, opInfo, span, threadRoot; var OP, a, numReplies, opInfo, span, threadRoot;
if (makeStub == null) { if (makeStub == null) {
makeStub = Conf['Stubs']; makeStub = Conf['Stubs'];
@ -3666,7 +3673,7 @@
thread.stub = $.el('div', { thread.stub = $.el('div', {
className: 'stub' className: 'stub'
}); });
$.add(thread.stub, !Conf['Menu'] ? a : [a, $.tn(' '), button = Menu.makeButton(OP)]); $.add(thread.stub, Conf['Menu'] ? [a, $.tn(' '), Menu.makeButton()] : a);
return $.prepend(threadRoot, thread.stub); return $.prepend(threadRoot, thread.stub);
}, },
show: function(thread) { show: function(thread) {
@ -3776,26 +3783,23 @@
}); });
}, },
node: function() { node: function() {
var board, boardID, quotelink, quotelinks, quotes, thread, threadID, _i, _len, _ref, _ref1; var board, boardID, quotelink, thread, threadID, _i, _len, _ref, _ref1, _ref2;
if (this.isClone && this.thread === this.context.thread) { if (this.isClone && this.thread === this.context.thread) {
return; return;
} }
if (!(quotes = this.quotes).length) {
return;
}
quotelinks = this.nodes.quotelinks;
_ref = this.isClone ? this.context : this, board = _ref.board, thread = _ref.thread; _ref = this.isClone ? this.context : this, board = _ref.board, thread = _ref.thread;
for (_i = 0, _len = quotelinks.length; _i < _len; _i++) { _ref1 = this.nodes.quotelinks;
quotelink = quotelinks[_i]; for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
_ref1 = Get.postDataFromLink(quotelink), boardID = _ref1.boardID, threadID = _ref1.threadID; quotelink = _ref1[_i];
_ref2 = Get.postDataFromLink(quotelink), boardID = _ref2.boardID, threadID = _ref2.threadID;
if (!threadID) { if (!threadID) {
continue; continue;
} }
if (this.isClone) { if (this.isClone) {
quotelink.textContent = quotelink.textContent.replace(QuoteCT.text, ''); quotelink.textContent = quotelink.textContent.replace(QuoteCT.text, '');
} }
if (boardID === this.board.ID && threadID !== thread.ID) { if (boardID === board.ID && threadID !== thread.ID) {
$.add(quotelink, $.tn(QuoteCT.text)); $.add(quotelink, $.tn(QuoteCT.text));
} }
} }
@ -3942,7 +3946,7 @@
}); });
}, },
node: function() { node: function() {
var boardID, op, postID, quotelink, quotelinks, quotes, _i, _j, _len, _len1, _ref; var boardID, fullID, i, postID, quotelink, quotelinks, quotes, _ref;
if (this.isClone && this.thread === this.context.thread) { if (this.isClone && this.thread === this.context.thread) {
return; return;
@ -3952,19 +3956,19 @@
} }
quotelinks = this.nodes.quotelinks; quotelinks = this.nodes.quotelinks;
if (this.isClone && quotes.contains(this.thread.fullID)) { if (this.isClone && quotes.contains(this.thread.fullID)) {
for (_i = 0, _len = quotelinks.length; _i < _len; _i++) { i = 0;
quotelink = quotelinks[_i]; while (quotelink = quotelinks[i++]) {
quotelink.textContent = quotelink.textContent.replace(QuoteOP.text, ''); quotelink.textContent = quotelink.textContent.replace(QuoteOP.text, '');
} }
} }
op = (this.isClone ? this.context : this).thread.fullID; fullID = (this.isClone ? this.context : this).thread.fullID;
if (!quotes.contains(op)) { if (!quotes.contains(fullID)) {
return; return;
} }
for (_j = 0, _len1 = quotelinks.length; _j < _len1; _j++) { i = 0;
quotelink = quotelinks[_j]; while (quotelink = quotelinks[i++]) {
_ref = Get.postDataFromLink(quotelink), boardID = _ref.boardID, postID = _ref.postID; _ref = Get.postDataFromLink(quotelink), boardID = _ref.boardID, postID = _ref.postID;
if (("" + boardID + "." + postID) === op) { if (("" + boardID + "." + postID) === fullID) {
$.add(quotelink, $.tn(QuoteOP.text)); $.add(quotelink, $.tn(QuoteOP.text));
} }
} }
@ -5363,7 +5367,7 @@
} }
_ref = QR.nodes, com = _ref.com, thread = _ref.thread; _ref = QR.nodes, com = _ref.com, thread = _ref.thread;
if (!com.value) { if (!com.value) {
thread.value = Get.contextFromNode(this).thread; thread.value = Get.threadFromNode(this);
} }
caretPos = com.selectionStart; caretPos = com.selectionStart;
com.value = com.value.slice(0, caretPos) + text + com.value.slice(com.selectionEnd); com.value = com.value.slice(0, caretPos) + text + com.value.slice(com.selectionEnd);
@ -6341,7 +6345,7 @@
href: 'javascript:;' href: 'javascript:;'
}); });
$.on(this.EAI, 'click', ImageExpand.cb.toggleAll); $.on(this.EAI, 'click', ImageExpand.cb.toggleAll);
Header.addShortcut(this.EAI); Header.addShortcut(this.EAI, 2);
return Post.prototype.callbacks.push({ return Post.prototype.callbacks.push({
name: 'Image Expansion', name: 'Image Expansion',
cb: this.node cb: this.node
@ -7089,48 +7093,47 @@
} }
}; };
Menu = (function() { Menu = {
var a; init: function() {
if (g.VIEW === 'catalog' || !Conf['Menu']) {
return;
}
this.menu = new UI.Menu('post');
return Post.prototype.callbacks.push({
name: 'Menu',
cb: this.node
});
},
node: function() {
if (this.isClone) {
return $.on($('.menu-button', this.nodes.info), 'click', Menu.toggle);
} else {
return $.add(this.nodes.info, [$.tn('\u00A0'), Menu.makeButton()]);
}
},
makeButton: (function() {
var a;
a = $.el('a', { a = $.el('a', {
className: 'menu-button brackets-wrap', className: 'menu-button brackets-wrap',
innerHTML: '<i></i>', innerHTML: '<i></i>',
href: 'javascript:;' href: 'javascript:;'
}); });
return { return function() {
init: function() {
if (g.VIEW === 'catalog' || !Conf['Menu']) {
return;
}
this.menu = new UI.Menu('post');
return Post.prototype.callbacks.push({
name: 'Menu',
cb: this.node
});
},
node: function() {
var button; var button;
if (this.isClone) { button = a.cloneNode(true);
button = $('.menu-button', this.nodes.info); $.on(button, 'click', Menu.toggle);
} else { return button;
button = a.cloneNode(true); };
$.add(this.nodes.info, [$.tn('\u00A0'), button]); })(),
} toggle: function(e) {
return $.on(button, 'click', Menu.toggle); var post;
},
makeButton: function() {
var el;
el = a.cloneNode(true); post = Get.postFromNode(this);
$.on(el, 'click', Menu.toggle); return Menu.menu.toggle(e, this, post);
return el; }
}, };
toggle: function(e) {
return Menu.menu.toggle(e, this, Get.postFromNode(this));
}
};
})();
ReportLink = { ReportLink = {
init: function() { init: function() {
@ -7915,12 +7918,14 @@
fetching: 0 fetching: 0
}, },
fetchAllStatus: function() { fetchAllStatus: function() {
var thread, _i, _len, _ref; var thread, threads, _i, _len;
if (!(threads = ThreadWatcher.getAll()).length) {
return;
}
ThreadWatcher.status.textContent = '...'; ThreadWatcher.status.textContent = '...';
_ref = ThreadWatcher.getAll(); for (_i = 0, _len = threads.length; _i < _len; _i++) {
for (_i = 0, _len = _ref.length; _i < _len; _i++) { thread = threads[_i];
thread = _ref[_i];
ThreadWatcher.fetchStatus(thread); ThreadWatcher.fetchStatus(thread);
} }
}, },
@ -9113,7 +9118,7 @@
return ("" + status + " " + posts + " post" + (posts > 1 ? 's' : '')) + (+files ? " and " + files + " image repl" + (files > 1 ? 'ies' : 'y') : "") + (" " + (status === '-' ? 'shown' : 'omitted') + "."); return ("" + status + " " + posts + " post" + (posts > 1 ? 's' : '')) + (+files ? " and " + files + " image repl" + (files > 1 ? 'ies' : 'y') : "") + (" " + (status === '-' ? 'shown' : 'omitted') + ".");
}, },
cbToggle: function() { cbToggle: function() {
return ExpandThread.toggle(Get.threadFromRoot(this.parentNode)); return ExpandThread.toggle(Get.threadFromNode(this));
}, },
toggle: function(thread) { toggle: function(thread) {
var a, files, filesCount, inlined, num, post, posts, postsCount, reply, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; var a, files, filesCount, inlined, num, post, posts, postsCount, reply, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4;
@ -10748,9 +10753,27 @@
}; };
Main = { Main = {
init: function(items) { init: function() {
var db, flatten, _i, _len; var db, flatten, pathname, _i, _len, _ref;
pathname = location.pathname.split('/');
g.BOARD = new Board(pathname[1]);
if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') {
return;
}
g.VIEW = (function() {
switch (pathname[2]) {
case 'res':
return 'thread';
case 'catalog':
return 'catalog';
default:
return 'index';
}
})();
if (g.VIEW === 'thread') {
g.THREADID = +pathname[3];
}
flatten = function(parent, obj) { flatten = function(parent, obj) {
var key, val; var key, val;
@ -10774,16 +10797,19 @@
} }
Conf['selectedArchives'] = {}; Conf['selectedArchives'] = {};
Conf['CachedTitles'] = []; Conf['CachedTitles'] = [];
$.get(Conf, Main.initFeatures); $.get(Conf, function(items) {
$.extend(Conf, items);
return Main.initFeatures();
});
$.on(d, '4chanMainInit', Main.initStyle); $.on(d, '4chanMainInit', Main.initStyle);
return $.asap((function() { return $.asap((function() {
return d.head && $('link[rel="shortcut icon"]', d.head) || d.readyState !== 'loading'; return d.head && $('link[rel="shortcut icon"]', d.head) || d.readyState !== 'loading';
}), Main.initStyle); }), Main.initStyle);
}, },
initFeatures: function(items) { initFeatures: function() {
Conf;
var init, pathname, _ref; var init, pathname, _ref;
Conf = items;
pathname = location.pathname.split('/'); pathname = location.pathname.split('/');
g.BOARD = new Board(pathname[1]); g.BOARD = new Board(pathname[1]);
if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') { if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') {
@ -10950,7 +10976,7 @@
initReady: function() { initReady: function() {
var board, err, errors, href, passLink, postRoot, posts, styleSelector, thread, threadRoot, threads, _i, _j, _len, _len1, _ref, _ref1; var board, err, errors, href, passLink, postRoot, posts, styleSelector, thread, threadRoot, threads, _i, _j, _len, _len1, _ref, _ref1;
if (d.title === '4chan - 404 Not Found') { if (['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) {
if (Conf['404 Redirect'] && g.VIEW === 'thread') { if (Conf['404 Redirect'] && g.VIEW === 'thread') {
href = Redirect.to('thread', { href = Redirect.to('thread', {
boardID: g.BOARD.ID, boardID: g.BOARD.ID,
@ -10961,9 +10987,7 @@
} }
return; return;
} }
if (!$.hasClass(doc, 'fourchan-x')) { Main.initStyle();
Main.initStyle();
}
if (board = $('.board')) { if (board = $('.board')) {
threads = []; threads = [];
posts = []; posts = [];

View File

@ -1473,11 +1473,11 @@
Header = { Header = {
init: function() { init: function() {
var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton,
_this = this; _this = this;
this.menu = new UI.Menu('header'); this.menu = new UI.Menu('header');
this.menuButton = $.el('span', { menuButton = $.el('span', {
className: 'menu-button', className: 'menu-button',
innerHTML: '<i></i>' innerHTML: '<i></i>'
}); });
@ -1509,7 +1509,7 @@
this.headerToggler = headerToggler.firstElementChild; this.headerToggler = headerToggler.firstElementChild;
this.footerToggler = footerToggler.firstElementChild; this.footerToggler = footerToggler.firstElementChild;
this.customNavToggler = customNavToggler.firstElementChild; this.customNavToggler = customNavToggler.firstElementChild;
$.on(this.menuButton, 'click', this.menuToggle); $.on(menuButton, 'click', this.menuToggle);
$.on(this.barFixedToggler, 'change', this.toggleBarFixed); $.on(this.barFixedToggler, 'change', this.toggleBarFixed);
$.on(this.barPositionToggler, 'change', this.toggleBarPosition); $.on(this.barPositionToggler, 'change', this.toggleBarPosition);
$.on(this.linkJustifyToggler, 'change', this.toggleLinkJustify); $.on(this.linkJustifyToggler, 'change', this.toggleLinkJustify);
@ -1524,7 +1524,7 @@
$.sync('Bottom Header', Header.setBarPosition); $.sync('Bottom Header', Header.setBarPosition);
$.sync('Header auto-hide', Header.setBarVisibility); $.sync('Header auto-hide', Header.setBarVisibility);
$.sync('Centered links', Header.setLinkJustify); $.sync('Centered links', Header.setLinkJustify);
this.addShortcut(Header.menuButton); this.addShortcut(menuButton);
$.event('AddMenuEntry', { $.event('AddMenuEntry', {
type: 'header', type: 'header',
el: $.el('span', { el: $.el('span', {
@ -2043,6 +2043,9 @@
threadFromRoot: function(root) { threadFromRoot: function(root) {
return g.threads["" + g.BOARD + "." + root.id.slice(1)]; return g.threads["" + g.BOARD + "." + root.id.slice(1)];
}, },
threadFromNode: function(node) {
return Get.threadFromRoot($.x('ancestor::div[@class="thread"]', node));
},
postFromRoot: function(root) { postFromRoot: function(root) {
var boardID, index, link, post, postID; var boardID, index, link, post, postID;
@ -2060,8 +2063,8 @@
postFromNode: function(root) { postFromNode: function(root) {
return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root)); return Get.postFromRoot($.x('(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root));
}, },
contextFromNode: function(quotelink) { contextFromNode: function(node) {
return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink)); return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', node));
}, },
postDataFromLink: function(link) { postDataFromLink: function(link) {
var boardID, path, postID, threadID, _ref; var boardID, path, postID, threadID, _ref;
@ -2509,7 +2512,7 @@
}; };
Menu.prototype.focus = function(entry) { Menu.prototype.focus = function(entry) {
var bottom, cHeight, cWidth, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref, _ref1, _ref2; var cHeight, cWidth, eRect, focused, sRect, submenu, _i, _len, _ref;
while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) { while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) {
$.rmClass(focused, 'focused'); $.rmClass(focused, 'focused');
@ -2527,13 +2530,20 @@
eRect = entry.getBoundingClientRect(); eRect = entry.getBoundingClientRect();
cHeight = doc.clientHeight; cHeight = doc.clientHeight;
cWidth = doc.clientWidth; cWidth = doc.clientWidth;
_ref1 = eRect.top + sRect.height < cHeight ? ['0px', 'auto'] : ['auto', '0px'], top = _ref1[0], bottom = _ref1[1]; if (eRect.top + sRect.height < cHeight) {
_ref2 = eRect.right + sRect.width < cWidth ? ['100%', 'auto'] : ['auto', '100%'], left = _ref2[0], right = _ref2[1]; $.addClass(submenu, 'top');
style = submenu.style; $.rmClass(submenu, 'bottom');
style.top = top; } else {
style.bottom = bottom; $.addClass(submenu, 'bottom');
style.left = left; $.rmClass(submenu, 'top');
return style.right = right; }
if (eRect.right + sRect.width < cWidth) {
$.addClass(submenu, 'left');
return $.rmClass(submenu, 'right');
} else {
$.addClass(submenu, 'right');
return $.rmClass(submenu, 'left');
}
}; };
Menu.prototype.addEntry = function(e) { Menu.prototype.addEntry = function(e) {
@ -3321,7 +3331,7 @@
post.nodes.stub = $.el('div', { post.nodes.stub = $.el('div', {
className: 'stub' className: 'stub'
}); });
$.add(post.nodes.stub, !Conf['Menu'] ? a : [a, $.tn(' '), button = Menu.makeButton(post)]); $.add(post.nodes.stub, Conf['Menu'] ? [a, $.tn(' '), button = Menu.makeButton(post)] : a);
return $.prepend(post.nodes.root, post.nodes.stub); return $.prepend(post.nodes.root, post.nodes.stub);
}, },
show: function(post, showRecursively) { show: function(post, showRecursively) {
@ -3652,7 +3662,7 @@
return ThreadHiding.saveHiddenState(thread); return ThreadHiding.saveHiddenState(thread);
}, },
hide: function(thread, makeStub) { hide: function(thread, makeStub) {
var OP, a, button, numReplies, opInfo, span, threadRoot; var OP, a, numReplies, opInfo, span, threadRoot;
if (makeStub == null) { if (makeStub == null) {
makeStub = Conf['Stubs']; makeStub = Conf['Stubs'];
@ -3672,7 +3682,7 @@
thread.stub = $.el('div', { thread.stub = $.el('div', {
className: 'stub' className: 'stub'
}); });
$.add(thread.stub, !Conf['Menu'] ? a : [a, $.tn(' '), button = Menu.makeButton(OP)]); $.add(thread.stub, Conf['Menu'] ? [a, $.tn(' '), Menu.makeButton()] : a);
return $.prepend(threadRoot, thread.stub); return $.prepend(threadRoot, thread.stub);
}, },
show: function(thread) { show: function(thread) {
@ -3782,26 +3792,23 @@
}); });
}, },
node: function() { node: function() {
var board, boardID, quotelink, quotelinks, quotes, thread, threadID, _i, _len, _ref, _ref1; var board, boardID, quotelink, thread, threadID, _i, _len, _ref, _ref1, _ref2;
if (this.isClone && this.thread === this.context.thread) { if (this.isClone && this.thread === this.context.thread) {
return; return;
} }
if (!(quotes = this.quotes).length) {
return;
}
quotelinks = this.nodes.quotelinks;
_ref = this.isClone ? this.context : this, board = _ref.board, thread = _ref.thread; _ref = this.isClone ? this.context : this, board = _ref.board, thread = _ref.thread;
for (_i = 0, _len = quotelinks.length; _i < _len; _i++) { _ref1 = this.nodes.quotelinks;
quotelink = quotelinks[_i]; for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
_ref1 = Get.postDataFromLink(quotelink), boardID = _ref1.boardID, threadID = _ref1.threadID; quotelink = _ref1[_i];
_ref2 = Get.postDataFromLink(quotelink), boardID = _ref2.boardID, threadID = _ref2.threadID;
if (!threadID) { if (!threadID) {
continue; continue;
} }
if (this.isClone) { if (this.isClone) {
quotelink.textContent = quotelink.textContent.replace(QuoteCT.text, ''); quotelink.textContent = quotelink.textContent.replace(QuoteCT.text, '');
} }
if (boardID === this.board.ID && threadID !== thread.ID) { if (boardID === board.ID && threadID !== thread.ID) {
$.add(quotelink, $.tn(QuoteCT.text)); $.add(quotelink, $.tn(QuoteCT.text));
} }
} }
@ -3948,7 +3955,7 @@
}); });
}, },
node: function() { node: function() {
var boardID, op, postID, quotelink, quotelinks, quotes, _i, _j, _len, _len1, _ref; var boardID, fullID, i, postID, quotelink, quotelinks, quotes, _ref;
if (this.isClone && this.thread === this.context.thread) { if (this.isClone && this.thread === this.context.thread) {
return; return;
@ -3958,19 +3965,19 @@
} }
quotelinks = this.nodes.quotelinks; quotelinks = this.nodes.quotelinks;
if (this.isClone && quotes.contains(this.thread.fullID)) { if (this.isClone && quotes.contains(this.thread.fullID)) {
for (_i = 0, _len = quotelinks.length; _i < _len; _i++) { i = 0;
quotelink = quotelinks[_i]; while (quotelink = quotelinks[i++]) {
quotelink.textContent = quotelink.textContent.replace(QuoteOP.text, ''); quotelink.textContent = quotelink.textContent.replace(QuoteOP.text, '');
} }
} }
op = (this.isClone ? this.context : this).thread.fullID; fullID = (this.isClone ? this.context : this).thread.fullID;
if (!quotes.contains(op)) { if (!quotes.contains(fullID)) {
return; return;
} }
for (_j = 0, _len1 = quotelinks.length; _j < _len1; _j++) { i = 0;
quotelink = quotelinks[_j]; while (quotelink = quotelinks[i++]) {
_ref = Get.postDataFromLink(quotelink), boardID = _ref.boardID, postID = _ref.postID; _ref = Get.postDataFromLink(quotelink), boardID = _ref.boardID, postID = _ref.postID;
if (("" + boardID + "." + postID) === op) { if (("" + boardID + "." + postID) === fullID) {
$.add(quotelink, $.tn(QuoteOP.text)); $.add(quotelink, $.tn(QuoteOP.text));
} }
} }
@ -5370,7 +5377,7 @@
} }
_ref = QR.nodes, com = _ref.com, thread = _ref.thread; _ref = QR.nodes, com = _ref.com, thread = _ref.thread;
if (!com.value) { if (!com.value) {
thread.value = Get.contextFromNode(this).thread; thread.value = Get.threadFromNode(this);
} }
caretPos = com.selectionStart; caretPos = com.selectionStart;
com.value = com.value.slice(0, caretPos) + text + com.value.slice(com.selectionEnd); com.value = com.value.slice(0, caretPos) + text + com.value.slice(com.selectionEnd);
@ -6323,7 +6330,7 @@
href: 'javascript:;' href: 'javascript:;'
}); });
$.on(this.EAI, 'click', ImageExpand.cb.toggleAll); $.on(this.EAI, 'click', ImageExpand.cb.toggleAll);
Header.addShortcut(this.EAI); Header.addShortcut(this.EAI, 2);
return Post.prototype.callbacks.push({ return Post.prototype.callbacks.push({
name: 'Image Expansion', name: 'Image Expansion',
cb: this.node cb: this.node
@ -7071,48 +7078,47 @@
} }
}; };
Menu = (function() { Menu = {
var a; init: function() {
if (g.VIEW === 'catalog' || !Conf['Menu']) {
return;
}
this.menu = new UI.Menu('post');
return Post.prototype.callbacks.push({
name: 'Menu',
cb: this.node
});
},
node: function() {
if (this.isClone) {
return $.on($('.menu-button', this.nodes.info), 'click', Menu.toggle);
} else {
return $.add(this.nodes.info, [$.tn('\u00A0'), Menu.makeButton()]);
}
},
makeButton: (function() {
var a;
a = $.el('a', { a = $.el('a', {
className: 'menu-button brackets-wrap', className: 'menu-button brackets-wrap',
innerHTML: '<i></i>', innerHTML: '<i></i>',
href: 'javascript:;' href: 'javascript:;'
}); });
return { return function() {
init: function() {
if (g.VIEW === 'catalog' || !Conf['Menu']) {
return;
}
this.menu = new UI.Menu('post');
return Post.prototype.callbacks.push({
name: 'Menu',
cb: this.node
});
},
node: function() {
var button; var button;
if (this.isClone) { button = a.cloneNode(true);
button = $('.menu-button', this.nodes.info); $.on(button, 'click', Menu.toggle);
} else { return button;
button = a.cloneNode(true); };
$.add(this.nodes.info, [$.tn('\u00A0'), button]); })(),
} toggle: function(e) {
return $.on(button, 'click', Menu.toggle); var post;
},
makeButton: function() {
var el;
el = a.cloneNode(true); post = Get.postFromNode(this);
$.on(el, 'click', Menu.toggle); return Menu.menu.toggle(e, this, post);
return el; }
}, };
toggle: function(e) {
return Menu.menu.toggle(e, this, Get.postFromNode(this));
}
};
})();
ReportLink = { ReportLink = {
init: function() { init: function() {
@ -7897,12 +7903,14 @@
fetching: 0 fetching: 0
}, },
fetchAllStatus: function() { fetchAllStatus: function() {
var thread, _i, _len, _ref; var thread, threads, _i, _len;
if (!(threads = ThreadWatcher.getAll()).length) {
return;
}
ThreadWatcher.status.textContent = '...'; ThreadWatcher.status.textContent = '...';
_ref = ThreadWatcher.getAll(); for (_i = 0, _len = threads.length; _i < _len; _i++) {
for (_i = 0, _len = _ref.length; _i < _len; _i++) { thread = threads[_i];
thread = _ref[_i];
ThreadWatcher.fetchStatus(thread); ThreadWatcher.fetchStatus(thread);
} }
}, },
@ -9100,7 +9108,7 @@
return ("" + status + " " + posts + " post" + (posts > 1 ? 's' : '')) + (+files ? " and " + files + " image repl" + (files > 1 ? 'ies' : 'y') : "") + (" " + (status === '-' ? 'shown' : 'omitted') + "."); return ("" + status + " " + posts + " post" + (posts > 1 ? 's' : '')) + (+files ? " and " + files + " image repl" + (files > 1 ? 'ies' : 'y') : "") + (" " + (status === '-' ? 'shown' : 'omitted') + ".");
}, },
cbToggle: function() { cbToggle: function() {
return ExpandThread.toggle(Get.threadFromRoot(this.parentNode)); return ExpandThread.toggle(Get.threadFromNode(this));
}, },
toggle: function(thread) { toggle: function(thread) {
var a, files, filesCount, inlined, num, post, posts, postsCount, reply, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4; var a, files, filesCount, inlined, num, post, posts, postsCount, reply, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3, _ref4;
@ -10733,9 +10741,27 @@
}; };
Main = { Main = {
init: function(items) { init: function() {
var db, flatten, _i, _len; var db, flatten, pathname, _i, _len, _ref;
pathname = location.pathname.split('/');
g.BOARD = new Board(pathname[1]);
if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') {
return;
}
g.VIEW = (function() {
switch (pathname[2]) {
case 'res':
return 'thread';
case 'catalog':
return 'catalog';
default:
return 'index';
}
})();
if (g.VIEW === 'thread') {
g.THREADID = +pathname[3];
}
flatten = function(parent, obj) { flatten = function(parent, obj) {
var key, val; var key, val;
@ -10759,16 +10785,28 @@
} }
Conf['selectedArchives'] = {}; Conf['selectedArchives'] = {};
Conf['CachedTitles'] = []; Conf['CachedTitles'] = [];
$.get(Conf, Main.initFeatures); $.get(Conf, function(items) {
$.extend(Conf, items);
if (!items) {
new Notification('error', $.el('span', {
innerHTML: "It seems like your 4chan X settings became corrupted due to a <a href=\"https://code.google.com/p/chromium/issues/detail?id=261623\" target=_blank>Chrome bug</a>.<br>\nUnfortunately, you'll have to <a href=\"https://github.com/MayhemYDG/4chan-x/wiki/FAQ#known-problems\" target=_blank>fix it yourself</a>."
}));
Main.logError({
message: 'Chrome Storage API bug',
error: new Error(chrome.runtime.lastError.message || 'no lastError.message')
});
}
return Main.initFeatures();
});
$.on(d, '4chanMainInit', Main.initStyle); $.on(d, '4chanMainInit', Main.initStyle);
return $.asap((function() { return $.asap((function() {
return d.head && $('link[rel="shortcut icon"]', d.head) || d.readyState !== 'loading'; return d.head && $('link[rel="shortcut icon"]', d.head) || d.readyState !== 'loading';
}), Main.initStyle); }), Main.initStyle);
}, },
initFeatures: function(items) { initFeatures: function() {
Conf;
var init, pathname, _ref; var init, pathname, _ref;
Conf = items;
pathname = location.pathname.split('/'); pathname = location.pathname.split('/');
g.BOARD = new Board(pathname[1]); g.BOARD = new Board(pathname[1]);
if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') { if ((_ref = g.BOARD.ID) === 'z' || _ref === 'fk') {
@ -10935,7 +10973,7 @@
initReady: function() { initReady: function() {
var board, err, errors, href, passLink, postRoot, posts, styleSelector, thread, threadRoot, threads, _i, _j, _len, _len1, _ref, _ref1; var board, err, errors, href, passLink, postRoot, posts, styleSelector, thread, threadRoot, threads, _i, _j, _len, _len1, _ref, _ref1;
if (d.title === '4chan - 404 Not Found') { if (['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains(d.title)) {
if (Conf['404 Redirect'] && g.VIEW === 'thread') { if (Conf['404 Redirect'] && g.VIEW === 'thread') {
href = Redirect.to('thread', { href = Redirect.to('thread', {
boardID: g.BOARD.ID, boardID: g.BOARD.ID,
@ -10946,9 +10984,7 @@
} }
return; return;
} }
if (!$.hasClass(doc, 'fourchan-x')) { Main.initStyle();
Main.initStyle();
}
if (board = $('.board')) { if (board = $('.board')) {
threads = []; threads = [];
posts = []; posts = [];

View File

@ -189,9 +189,6 @@ a[href="javascript:;"] {
text-decoration: none; text-decoration: none;
padding: 1px; padding: 1px;
} }
#shortcuts:empty {
display: none;
}
.shortcut:not(:last-child)::after { .shortcut:not(:last-child)::after {
content: " / "; content: " / ";
} }
@ -888,6 +885,18 @@ a.hide-announcement {
position: absolute; position: absolute;
outline: none; outline: none;
} }
#menu.top {
top: 100%;
}
#menu.bottom {
bottom: 100%;
}
#menu.left {
left: 0;
}
#menu.right {
right: 0;
}
.entry { .entry {
cursor: pointer; cursor: pointer;
outline: none; outline: none;
@ -924,6 +933,18 @@ a.hide-announcement {
position: absolute; position: absolute;
margin: -1px 0; margin: -1px 0;
} }
.submenu.top {
top: 0;
}
.submenu.bottom {
bottom: 0;
}
.submenu.left {
left: 100%;
}
.submenu.right {
right: 100%;
}
.entry input { .entry input {
margin: 0; margin: 0;
} }

9
html/General/Header.html Normal file
View File

@ -0,0 +1,9 @@
<div id="header-bar" class="dialog">
<span id="shortcuts" class="brackets-wrap"></span>
<span id="board-list">
<span id="custom-board-list"></span>
<span id="full-board-list" hidden></span>
</span>
<div id="toggle-header-bar" title="Toggle the header auto-hiding."></div>
</div>
<div id="notifications"></div>

View File

@ -105,6 +105,6 @@
"https": true, "https": true,
"withCredentials": true, "withCredentials": true,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["a", "co", "gd", "h", "jp", "m", "mlp", "q", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], "boards": ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "q", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
"files": ["a", "gd", "h", "jp", "m", "q", "tg", "u", "vg", "vp", "vr", "wsg"] "files": ["a", "d", "gd", "h", "jp", "m", "q", "tg", "u", "vg", "vp", "vr", "wsg"]
}] }]

View File

@ -2,6 +2,7 @@
"name": "4chan-X", "name": "4chan-X",
"version": "1.2.25", "version": "1.2.25",
"description": "Cross-browser userscript for maximum lurking on 4chan.", "description": "Cross-browser userscript for maximum lurking on 4chan.",
"meta": { "meta": {
"name": "4chan X", "name": "4chan X",
"repo": "https://github.com/seaweedchan/4chan-x/", "repo": "https://github.com/seaweedchan/4chan-x/",

View File

@ -186,10 +186,10 @@ PostHiding =
$.add a, $.tn " #{postInfo}" $.add a, $.tn " #{postInfo}"
post.nodes.stub = $.el 'div', post.nodes.stub = $.el 'div',
className: 'stub' className: 'stub'
$.add post.nodes.stub, unless Conf['Menu'] $.add post.nodes.stub, if Conf['Menu']
a [a, $.tn(' '), button = Menu.makeButton post]
else else
[a, $.tn(' '), button = Menu.makeButton post] a
$.prepend post.nodes.root, post.nodes.stub $.prepend post.nodes.root, post.nodes.stub
show: (post, showRecursively=Conf['Recursive Hiding']) -> show: (post, showRecursively=Conf['Recursive Hiding']) ->
@ -204,4 +204,4 @@ PostHiding =
Recursive.rm PostHiding.hide, post Recursive.rm PostHiding.hide, post
for quotelink in Get.allQuotelinksLinkingTo post for quotelink in Get.allQuotelinksLinkingTo post
$.rmClass quotelink, 'filtered' $.rmClass quotelink, 'filtered'
return return

View File

@ -191,10 +191,10 @@ ThreadHiding =
$.add a, $.tn " #{opInfo} (#{numReplies})" $.add a, $.tn " #{opInfo} (#{numReplies})"
thread.stub = $.el 'div', thread.stub = $.el 'div',
className: 'stub' className: 'stub'
$.add thread.stub, unless Conf['Menu'] $.add thread.stub, if Conf['Menu']
a [a, $.tn(' '), Menu.makeButton()]
else else
[a, $.tn(' '), button = Menu.makeButton OP] a
$.prepend threadRoot, thread.stub $.prepend threadRoot, thread.stub
show: (thread) -> show: (thread) ->

View File

@ -10,6 +10,8 @@ Get =
"/#{thread.board}/ - #{excerpt}" "/#{thread.board}/ - #{excerpt}"
threadFromRoot: (root) -> threadFromRoot: (root) ->
g.threads["#{g.BOARD}.#{root.id[1..]}"] g.threads["#{g.BOARD}.#{root.id[1..]}"]
threadFromNode: (node) ->
Get.threadFromRoot $.x 'ancestor::div[@class="thread"]', node
postFromRoot: (root) -> postFromRoot: (root) ->
link = $ 'a[title="Highlight this post"]', root link = $ 'a[title="Highlight this post"]', root
boardID = link.pathname.split('/')[1] boardID = link.pathname.split('/')[1]
@ -19,8 +21,8 @@ Get =
if index then post.clones[index] else post if index then post.clones[index] else post
postFromNode: (root) -> postFromNode: (root) ->
Get.postFromRoot $.x '(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root Get.postFromRoot $.x '(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root
contextFromNode: (quotelink) -> contextFromNode: (node) ->
Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', node
postDataFromLink: (link) -> postDataFromLink: (link) ->
if link.hostname is 'boards.4chan.org' if link.hostname is 'boards.4chan.org'
path = link.pathname.split '/' path = link.pathname.split '/'

View File

@ -1,7 +1,8 @@
Header = Header =
init: -> init: ->
@menu = new UI.Menu 'header' @menu = new UI.Menu 'header'
@menuButton = $.el 'span',
menuButton = $.el 'span',
className: 'menu-button' className: 'menu-button'
innerHTML: '<i></i>' innerHTML: '<i></i>'
@ -28,7 +29,7 @@ Header =
@footerToggler = footerToggler.firstElementChild @footerToggler = footerToggler.firstElementChild
@customNavToggler = customNavToggler.firstElementChild @customNavToggler = customNavToggler.firstElementChild
$.on @menuButton, 'click', @menuToggle $.on menuButton, 'click', @menuToggle
$.on @barFixedToggler, 'change', @toggleBarFixed $.on @barFixedToggler, 'change', @toggleBarFixed
$.on @barPositionToggler, 'change', @toggleBarPosition $.on @barPositionToggler, 'change', @toggleBarPosition
$.on @linkJustifyToggler, 'change', @toggleLinkJustify $.on @linkJustifyToggler, 'change', @toggleLinkJustify
@ -46,7 +47,7 @@ Header =
$.sync 'Header auto-hide', Header.setBarVisibility $.sync 'Header auto-hide', Header.setBarVisibility
$.sync 'Centered links', Header.setLinkJustify $.sync 'Centered links', Header.setLinkJustify
@addShortcut Header.menuButton @addShortcut menuButton
$.event 'AddMenuEntry', $.event 'AddMenuEntry',
type: 'header' type: 'header'

View File

@ -1,5 +1,19 @@
Main = Main =
init: (items) -> init: ->
pathname = location.pathname.split '/'
g.BOARD = new Board pathname[1]
return if g.BOARD.ID in ['z', 'fk']
g.VIEW =
switch pathname[2]
when 'res'
'thread'
when 'catalog'
'catalog'
else
'index'
if g.VIEW is 'thread'
g.THREADID = +pathname[3]
# flatten Config into Conf # flatten Config into Conf
# and get saved or default values # and get saved or default values
flatten = (parent, obj) -> flatten = (parent, obj) ->
@ -16,15 +30,29 @@ Main =
Conf[db] = boards: {} Conf[db] = boards: {}
Conf['selectedArchives'] = {} Conf['selectedArchives'] = {}
Conf['CachedTitles'] = [] Conf['CachedTitles'] = []
$.get Conf, Main.initFeatures $.get Conf, (items) ->
$.extend Conf, items
<% if (type === 'crx') { %>
unless items
new Notification 'error', $.el 'span',
innerHTML: """
It seems like your <%= meta.name %> settings became corrupted due to a <a href="https://code.google.com/p/chromium/issues/detail?id=261623" target=_blank>Chrome bug</a>.<br>
Unfortunately, you'll have to <a href="https://github.com/MayhemYDG/4chan-x/wiki/FAQ#known-problems" target=_blank>fix it yourself</a>.
"""
# Track resolution of this bug.
Main.logError
message: 'Chrome Storage API bug'
error: new Error chrome.runtime.lastError.message or 'no lastError.message'
<% } %>
Main.initFeatures()
$.on d, '4chanMainInit', Main.initStyle $.on d, '4chanMainInit', Main.initStyle
$.asap (-> d.head and $('link[rel="shortcut icon"]', d.head) or d.readyState isnt 'loading'), $.asap (-> d.head and $('link[rel="shortcut icon"]', d.head) or d.readyState isnt 'loading'),
Main.initStyle Main.initStyle
initFeatures: (items) -> initFeatures: ->
Conf = items Conf
pathname = location.pathname.split '/' pathname = location.pathname.split '/'
g.BOARD = new Board pathname[1] g.BOARD = new Board pathname[1]
@ -171,7 +199,7 @@ Main =
attributeFilter: ['href'] attributeFilter: ['href']
initReady: -> initReady: ->
if d.title is '4chan - 404 Not Found' if ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains d.title
if Conf['404 Redirect'] and g.VIEW is 'thread' if Conf['404 Redirect'] and g.VIEW is 'thread'
href = Redirect.to 'thread', href = Redirect.to 'thread',
boardID: g.BOARD.ID boardID: g.BOARD.ID
@ -180,9 +208,8 @@ Main =
location.replace href or "/#{g.BOARD}/" location.replace href or "/#{g.BOARD}/"
return return
unless $.hasClass doc, 'fourchan-x' # Something might have gone wrong!
# Something might have gone wrong! Main.initStyle()
Main.initStyle()
if board = $ '.board' if board = $ '.board'
threads = [] threads = []

View File

@ -91,7 +91,7 @@ UI = do ->
$.addClass menu, 'left' $.addClass menu, 'left'
entry = $ '.entry', menu entry = $ '.entry', menu
# We've removed flexbox, so we don't user order anymore. # We've removed flexbox, so we don't use order anymore.
# while prevEntry = @findNextEntry entry, -1 # while prevEntry = @findNextEntry entry, -1
# entry = prevEntry # entry = prevEntry
@focus entry @focus entry
@ -171,19 +171,18 @@ UI = do ->
eRect = entry.getBoundingClientRect() eRect = entry.getBoundingClientRect()
cHeight = doc.clientHeight cHeight = doc.clientHeight
cWidth = doc.clientWidth cWidth = doc.clientWidth
[top, bottom] = if eRect.top + sRect.height < cHeight if eRect.top + sRect.height < cHeight
['0px', 'auto'] $.addClass submenu, 'top'
$.rmClass submenu, 'bottom'
else else
['auto', '0px'] $.addClass submenu, 'bottom'
[left, right] = if eRect.right + sRect.width < cWidth $.rmClass submenu, 'top'
['100%', 'auto'] if eRect.right + sRect.width < cWidth
$.addClass submenu, 'left'
$.rmClass submenu, 'right'
else else
['auto', '100%'] $.addClass submenu, 'right'
{style} = submenu $.rmClass submenu, 'left'
style.top = top
style.bottom = bottom
style.left = left
style.right = right
addEntry: (e) -> addEntry: (e) ->
entry = e.detail entry = e.detail

View File

@ -1,7 +1,9 @@
Polyfill = Polyfill =
init: -> init: ->
<% if (type === 'crx') { %>
Polyfill.toBlob() Polyfill.toBlob()
Polyfill.visibility() Polyfill.visibility()
<% } %>
toBlob: -> toBlob: ->
HTMLCanvasElement::toBlob or= (cb) -> HTMLCanvasElement::toBlob or= (cb) ->
data = atob @toDataURL()[22..] data = atob @toDataURL()[22..]

View File

@ -8,7 +8,7 @@ ImageExpand =
title: 'Expand All Images' title: 'Expand All Images'
href: 'javascript:;' href: 'javascript:;'
$.on @EAI, 'click', ImageExpand.cb.toggleAll $.on @EAI, 'click', ImageExpand.cb.toggleAll
Header.addShortcut @EAI Header.addShortcut @EAI, 2
Post::callbacks.push Post::callbacks.push
name: 'Image Expansion' name: 'Image Expansion'

View File

@ -1,9 +1,4 @@
Menu = do -> Menu =
a = $.el 'a',
className: 'menu-button brackets-wrap'
innerHTML: '<i></i>'
href: 'javascript:;'
init: -> init: ->
return if g.VIEW is 'catalog' or !Conf['Menu'] return if g.VIEW is 'catalog' or !Conf['Menu']
@ -14,16 +9,20 @@ Menu = do ->
node: -> node: ->
if @isClone if @isClone
button = $ '.menu-button', @nodes.info $.on $('.menu-button', @nodes.info), 'click', Menu.toggle
else else
button = a.cloneNode true $.add @nodes.info, [$.tn('\u00A0'), Menu.makeButton()]
$.add @nodes.info, [$.tn('\u00A0'), button]
$.on button, 'click', Menu.toggle
makeButton: -> makeButton: do ->
el = a.cloneNode true a = $.el 'a',
$.on el, 'click', Menu.toggle className: 'menu-button brackets-wrap'
el innerHTML: '<i></i>'
href: 'javascript:;'
->
button = a.cloneNode true
$.on button, 'click', Menu.toggle
button
toggle: (e) -> toggle: (e) ->
Menu.menu.toggle e, @, Get.postFromNode @ post = Get.postFromNode @
Menu.menu.toggle e, @, post

View File

@ -22,7 +22,7 @@ ExpandThread =
" #{if status is '-' then 'shown' else 'omitted'}." " #{if status is '-' then 'shown' else 'omitted'}."
cbToggle: -> cbToggle: ->
ExpandThread.toggle Get.threadFromRoot @parentNode ExpandThread.toggle Get.threadFromNode @
toggle: (thread) -> toggle: (thread) ->
threadRoot = thread.OP.nodes.root.parentNode threadRoot = thread.OP.nodes.root.parentNode

View File

@ -107,8 +107,9 @@ ThreadWatcher =
fetched: 0 fetched: 0
fetching: 0 fetching: 0
fetchAllStatus: -> fetchAllStatus: ->
return unless (threads = ThreadWatcher.getAll()).length
ThreadWatcher.status.textContent = '...' ThreadWatcher.status.textContent = '...'
for thread in ThreadWatcher.getAll() for thread in threads
ThreadWatcher.fetchStatus thread ThreadWatcher.fetchStatus thread
return return
fetchStatus: ({boardID, threadID, data}) -> fetchStatus: ({boardID, threadID, data}) ->

View File

@ -385,7 +385,7 @@ QR =
$.addClass QR.nodes.el, 'dump' $.addClass QR.nodes.el, 'dump'
QR.cooldown.auto = true QR.cooldown.auto = true
{com, thread} = QR.nodes {com, thread} = QR.nodes
thread.value = Get.contextFromNode(@).thread unless com.value thread.value = Get.threadFromNode @ unless com.value
caretPos = com.selectionStart caretPos = com.selectionStart
# Replace selection for text. # Replace selection for text.

View File

@ -13,16 +13,13 @@ QuoteCT =
node: -> node: ->
# Stop there if it's a clone of a post in the same thread. # Stop there if it's a clone of a post in the same thread.
return if @isClone and @thread is @context.thread return if @isClone and @thread is @context.thread
# Stop there if there's no quotes in that post.
return unless (quotes = @quotes).length
{quotelinks} = @nodes
{board, thread} = if @isClone then @context else @ {board, thread} = if @isClone then @context else @
for quotelink in quotelinks for quotelink in @nodes.quotelinks
{boardID, threadID} = Get.postDataFromLink quotelink {boardID, threadID} = Get.postDataFromLink quotelink
continue unless threadID # deadlink continue unless threadID # deadlink
if @isClone if @isClone
quotelink.textContent = quotelink.textContent.replace QuoteCT.text, '' quotelink.textContent = quotelink.textContent.replace QuoteCT.text, ''
if boardID is @board.ID and threadID isnt thread.ID if boardID is board.ID and threadID isnt thread.ID
$.add quotelink, $.tn QuoteCT.text $.add quotelink, $.tn QuoteCT.text
return return

View File

@ -10,6 +10,7 @@ QuoteOP =
Post::callbacks.push Post::callbacks.push
name: 'Mark OP Quotes' name: 'Mark OP Quotes'
cb: @node cb: @node
node: -> node: ->
# Stop there if it's a clone of a post in the same thread. # Stop there if it's a clone of a post in the same thread.
return if @isClone and @thread is @context.thread return if @isClone and @thread is @context.thread
@ -19,14 +20,16 @@ QuoteOP =
# rm (OP) from cross-thread quotes. # rm (OP) from cross-thread quotes.
if @isClone and quotes.contains @thread.fullID if @isClone and quotes.contains @thread.fullID
for quotelink in quotelinks i = 0
while quotelink = quotelinks[i++]
quotelink.textContent = quotelink.textContent.replace QuoteOP.text, '' quotelink.textContent = quotelink.textContent.replace QuoteOP.text, ''
op = (if @isClone then @context else @).thread.fullID {fullID} = (if @isClone then @context else @).thread
# add (OP) to quotes quoting this context's OP. # add (OP) to quotes quoting this context's OP.
return unless quotes.contains op return unless quotes.contains fullID
for quotelink in quotelinks i = 0
while quotelink = quotelinks[i++]
{boardID, postID} = Get.postDataFromLink quotelink {boardID, postID} = Get.postDataFromLink quotelink
if "#{boardID}.#{postID}" is op if "#{boardID}.#{postID}" is fullID
$.add quotelink, $.tn QuoteOP.text $.add quotelink, $.tn QuoteOP.text
return return