Merge branch 'mayhem' into v3

Conflicts:
	CHANGELOG.md
	package.json
	src/Archive/archives.json
	src/General/Config.coffee
	src/General/Header.coffee
	src/General/Main.coffee
	src/General/Settings.coffee
	src/General/UI.coffee
	src/Miscellaneous/Keybinds.coffee
	src/Monitoring/ThreadUpdater.coffee
	src/Monitoring/Unread.coffee
	src/Posting/QR.captcha.coffee
	src/Posting/QR.coffee
	src/Posting/QR.post.coffee
This commit is contained in:
Zixaphir 2014-06-22 07:04:25 -07:00
commit f103fd0301
38 changed files with 233 additions and 507 deletions

View File

@ -1,4 +1,7 @@
**MayhemYDG**: **MayhemYDG**:
- Compatibility fixes for Greasemonkey v2.
- The QR won't duplicate single-word captchas anymore if you've input numbers only (like a street address).
- Removed the `Download Link` feature on Chrome/Opera as it is not working as intended anymore.
- [Security fix](https://github.com/MayhemYDG/4chan-x/issues/1634). - [Security fix](https://github.com/MayhemYDG/4chan-x/issues/1634).
### v1.7.33 ### v1.7.33

View File

@ -1,5 +1,5 @@
/* /*
* 4chan X - Version 1.7.33 - 2014-05-29 * 4chan X - Version 1.7.33 - 2014-06-22
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE * https://github.com/ccd0/4chan-x/blob/master/LICENSE

View File

@ -24,7 +24,7 @@
// ==/UserScript== // ==/UserScript==
/* /*
* 4chan X - Version 1.7.33 - 2014-05-29 * 4chan X - Version 1.7.33 - 2014-06-22
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE * https://github.com/ccd0/4chan-x/blob/master/LICENSE
@ -215,7 +215,6 @@
'Persistent QR': [true, 'The Quick reply won\'t disappear after posting.'], 'Persistent QR': [true, 'The Quick reply won\'t disappear after posting.'],
'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'], 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'],
'Open Post in New Tab': [true, 'Open new threads or replies to a thread from the index in a new tab.'], 'Open Post in New Tab': [true, 'Open new threads or replies to a thread from the index in a new tab.'],
'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'],
'Remember QR Size': [false, 'Remember the size of the Quick reply.'], 'Remember QR Size': [false, 'Remember the size of the Quick reply.'],
'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'], 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'],
'Hide Original Post Form': [true, 'Hide the normal post form.'], 'Hide Original Post Form': [true, 'Hide the normal post form.'],
@ -655,6 +654,9 @@
if (root == null) { if (root == null) {
root = d; root = d;
} }
if ((detail != null) && typeof cloneInto === 'function') {
detail = cloneInto(detail, d.defaultView);
}
return root.dispatchEvent(new CustomEvent(event, { return root.dispatchEvent(new CustomEvent(event, {
bubbles: true, bubbles: true,
detail: detail detail: detail
@ -1961,8 +1963,8 @@
Header = { Header = {
init: function() { init: function() {
var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler; var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler;
this.menu = new UI.Menu('header'); this.menu = new UI.Menu();
menuButton = $.el('span', { menuButton = $.el('a', {
className: 'menu-button', className: 'menu-button',
innerHTML: '<i></i>' innerHTML: '<i></i>'
}); });
@ -2025,8 +2027,7 @@
$.sync('Header auto-hide', this.setBarVisibility); $.sync('Header auto-hide', this.setBarVisibility);
$.sync('Centered links', this.setLinkJustify); $.sync('Centered links', this.setLinkJustify);
this.addShortcut(menuButton); this.addShortcut(menuButton);
$.event('AddMenuEntry', { this.menu.addEntry({
type: 'header',
el: $.el('span', { el: $.el('span', {
textContent: 'Header' textContent: 'Header'
}), }),
@ -2473,7 +2474,7 @@
className: 'shortcut brackets-wrap' className: 'shortcut brackets-wrap'
}); });
$.add(shortcut, el); $.add(shortcut, el);
return $.prepend(Header.shortcuts, shortcut); return $.add(Header.shortcuts, shortcut);
}, },
rmShortcut: function(el) { rmShortcut: function(el) {
return $.rm(el.parentElement); return $.rm(el.parentElement);
@ -2482,12 +2483,9 @@
return Header.menu.toggle(e, this, g); return Header.menu.toggle(e, this, g);
}, },
createNotification: function(e) { createNotification: function(e) {
var cb, content, lifetime, notice, type, _ref; var content, lifetime, notice, type, _ref;
_ref = e.detail, type = _ref.type, content = _ref.content, lifetime = _ref.lifetime, cb = _ref.cb; _ref = e.detail, type = _ref.type, content = _ref.content, lifetime = _ref.lifetime;
notice = new Notice(type, content, lifetime); return notice = new Notice(type, content, lifetime);
if (cb) {
return cb(notice);
}
}, },
areNotificationsEnabled: false, areNotificationsEnabled: false,
enableDesktopNotifications: function() { enableDesktopNotifications: function() {
@ -2630,8 +2628,7 @@
$.on(input, 'change', this.cb.replies); $.on(input, 'change', this.cb.replies);
} }
} }
$.event('AddMenuEntry', { Header.menu.addEntry({
type: 'header',
el: $.el('span', { el: $.el('span', {
textContent: 'Index Navigation' textContent: 'Index Navigation'
}), }),
@ -2748,8 +2745,7 @@
if (g.VIEW !== 'index' || !Conf['Menu'] || g.BOARD.ID === 'f') { if (g.VIEW !== 'index' || !Conf['Menu'] || g.BOARD.ID === 'f') {
return; return;
} }
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: $.el('a', { el: $.el('a', {
href: 'javascript:;' href: 'javascript:;'
}), }),
@ -3023,7 +3019,7 @@
} }
onSameIndex = g.VIEW === 'index' && a.pathname.split('/')[1] === g.BOARD.ID; onSameIndex = g.VIEW === 'index' && a.pathname.split('/')[1] === g.BOARD.ID;
needChange = Index.cb.indexNav(a, onSameIndex); needChange = Index.cb.indexNav(a, onSameIndex);
if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || !onSameIndex) { if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || !onSameIndex || g.BOARD.ID === 'f') {
return; return;
} }
e.preventDefault(); e.preventDefault();
@ -3554,8 +3550,7 @@
if (!infinite) { if (!infinite) {
$.rmAll(Index.root); $.rmAll(Index.root);
} }
$.add(Index.root, nodes); return $.add(Index.root, nodes);
return $.event('IndexBuild', nodes);
}, },
isSearching: false, isSearching: false,
clearSearch: function() { clearSearch: function() {
@ -4216,15 +4211,11 @@
lastToggledButton = null; lastToggledButton = null;
function Menu(type) { function Menu() {
this.type = type;
this.rmEntry = __bind(this.rmEntry, this); this.rmEntry = __bind(this.rmEntry, this);
this.addEntry = __bind(this.addEntry, this);
this.onFocus = __bind(this.onFocus, this); this.onFocus = __bind(this.onFocus, this);
this.keybinds = __bind(this.keybinds, this); this.keybinds = __bind(this.keybinds, this);
this.close = __bind(this.close, this); this.close = __bind(this.close, this);
$.on(d, 'AddMenuEntry', this.addEntry);
$.on(d, 'rmMenuEntry', this.rmEntry);
this.entries = []; this.entries = [];
} }
@ -4422,12 +4413,7 @@
return style.right = right; return style.right = right;
}; };
Menu.prototype.addEntry = function(e) { Menu.prototype.addEntry = function(entry) {
var entry;
entry = e.detail;
if (entry.type !== this.type) {
return;
}
this.parseEntry(entry); this.parseEntry(entry);
return this.entries.push(entry); return this.entries.push(entry);
}; };
@ -4854,7 +4840,6 @@
textContent: 'Filter' textContent: 'Filter'
}); });
entry = { entry = {
type: 'post',
el: div, el: div,
order: 50, order: 50,
open: function(post) { open: function(post) {
@ -4868,7 +4853,7 @@
type = _ref[_i]; type = _ref[_i];
entry.subEntries.push(Filter.menu.createSubEntry(type[0], type[1])); entry.subEntries.push(Filter.menu.createSubEntry(type[0], type[1]));
} }
return $.event('AddMenuEntry', entry); return Menu.menu.addEntry(entry);
}, },
createSubEntry: function(text, type) { createSubEntry: function(text, type) {
var el; var el;
@ -5047,8 +5032,7 @@
innerHTML: "<input type=checkbox name=makeStub checked=" + Conf['Stubs'] + "> Make stub" innerHTML: "<input type=checkbox name=makeStub checked=" + Conf['Stubs'] + "> Make stub"
}) })
}; };
$.event('AddMenuEntry', { Menu.menu.addEntry({
type: 'post',
el: $.el('div', { el: $.el('div', {
textContent: 'Hide post', textContent: 'Hide post',
className: 'hide-post-link' className: 'hide-post-link'
@ -5099,8 +5083,7 @@
return true; return true;
} }
}; };
$.event('AddMenuEntry', { Menu.menu.addEntry({
type: 'post',
el: $.el('div', { el: $.el('div', {
textContent: 'Unhide post', textContent: 'Unhide post',
className: 'show-post-link' className: 'show-post-link'
@ -5124,8 +5107,7 @@
if (g.VIEW !== 'index') { if (g.VIEW !== 'index') {
return; return;
} }
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: $.el('a', { el: $.el('a', {
href: 'javascript:;' href: 'javascript:;'
}), }),
@ -5716,8 +5698,7 @@
}); });
input = $('input', this.controls); input = $('input', this.controls);
$.on(input, 'change', this.toggle); $.on(input, 'change', this.toggle);
$.event('AddMenuEntry', this.entry = { Header.menu.addEntry(this.entry = {
type: 'header',
el: this.controls, el: this.controls,
order: 98 order: 98
}); });
@ -6024,16 +6005,6 @@
} }
}); });
$.before($.id('togglePostFormLink'), link); $.before($.id('togglePostFormLink'), link);
$.on(d, 'QRGetSelectedPost', function(_arg) {
var cb;
cb = _arg.detail;
return cb(QR.selected);
});
$.on(d, 'QRAddPreSubmitHook', function(_arg) {
var cb;
cb = _arg.detail;
return QR.preSubmitHooks.push(cb);
});
$.on(d, 'dragover', QR.dragOver); $.on(d, 'dragover', QR.dragOver);
$.on(d, 'drop', QR.dropFile); $.on(d, 'drop', QR.dropFile);
$.on(d, 'dragstart dragend', QR.drag); $.on(d, 'dragstart dragend', QR.drag);
@ -6622,9 +6593,8 @@
return $.add(nodes.form, flag); return $.add(nodes.form, flag);
} }
}, },
preSubmitHooks: [],
submit: function(e) { submit: function(e) {
var challenge, err, extra, filetag, formData, hook, options, post, response, textOnly, thread, threadID, _i, _len, _ref, _ref1; var challenge, err, extra, filetag, formData, options, post, response, textOnly, thread, threadID, _ref;
if (e != null) { if (e != null) {
e.preventDefault(); e.preventDefault();
} }
@ -6657,17 +6627,9 @@
err = 'No file selected.'; err = 'No file selected.';
} else if (post.file && thread.fileLimit) { } else if (post.file && thread.fileLimit) {
err = 'Max limit of image replies has been reached.'; err = 'Max limit of image replies has been reached.';
} else {
_ref = QR.preSubmitHooks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
hook = _ref[_i];
if (err = hook(post, thread)) {
break;
}
}
} }
if (QR.captcha.isEnabled && !err) { if (QR.captcha.isEnabled && !err) {
_ref1 = QR.captcha.getOne(), challenge = _ref1.challenge, response = _ref1.response; _ref = QR.captcha.getOne(), challenge = _ref.challenge, response = _ref.response;
if (!response) { if (!response) {
err = 'No valid captcha.'; err = 'No valid captcha.';
} }
@ -6802,11 +6764,12 @@
}); });
ThreadUpdater.postID = postID; ThreadUpdater.postID = postID;
$.event('QRPostSuccessful', { $.event('QRPostSuccessful', {
board: g.BOARD, boardID: g.BOARD.ID,
threadID: threadID, threadID: threadID,
postID: postID postID: postID
}); });
$.event('QRPostSuccessful_', { $.event('QRPostSuccessful_', {
boardID: g.BOARD.ID,
threadID: threadID, threadID: threadID,
postID: postID postID: postID
}); });
@ -6978,7 +6941,7 @@
} }
if (response) { if (response) {
response = response.trim(); response = response.trim();
if (!/\s/.test(response)) { if (!/\s|^\d+$/.test(response)) {
response = "" + response + " " + response; response = "" + response + " " + response;
} }
} }
@ -7423,8 +7386,7 @@
rectEl = this.nodes.el.getBoundingClientRect(); rectEl = this.nodes.el.getBoundingClientRect();
rectList = this.nodes.el.parentNode.getBoundingClientRect(); rectList = this.nodes.el.parentNode.getBoundingClientRect();
this.nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width / 2 - rectList.left - rectList.width / 2; this.nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width / 2 - rectList.left - rectList.width / 2;
this.load(); return this.load();
return $.event('QRPostSelection', this);
}; };
_Class.prototype.load = function() { _Class.prototype.load = function() {
@ -7719,8 +7681,7 @@
}); });
FappeTyme[lc] = input = el.firstElementChild; FappeTyme[lc] = input = el.firstElementChild;
$.on(input, 'change', FappeTyme.cb.toggle.bind(input)); $.on(input, 'change', FappeTyme.cb.toggle.bind(input));
$.event('AddMenuEntry', { Header.menu.addEntry({
type: 'header',
el: el, el: el,
order: 97 order: 97
}); });
@ -7806,7 +7767,7 @@
nodes[key] = $(value, dialog); nodes[key] = $(value, dialog);
} }
menuButton = $('.menu-button', dialog); menuButton = $('.menu-button', dialog);
nodes.menu = new UI.Menu('gallery'); nodes.menu = new UI.Menu();
cb = Gallery.cb; cb = Gallery.cb;
$.on(nodes.frame, 'click', cb.blank); $.on(nodes.frame, 'click', cb.blank);
$.on(nodes.next, 'click', cb.advance); $.on(nodes.next, 'click', cb.advance);
@ -7819,8 +7780,7 @@
createSubEntry = Gallery.menu.createSubEntry; createSubEntry = Gallery.menu.createSubEntry;
for (name in Config.gallery) { for (name in Config.gallery) {
el = createSubEntry(name).el; el = createSubEntry(name).el;
$.event('AddMenuEntry', { nodes.menu.addEntry({
type: 'gallery',
el: el, el: el,
order: 0 order: 0
}); });
@ -8049,8 +8009,7 @@
for (name in Config.gallery) { for (name in Config.gallery) {
subEntries.push(createSubEntry(name)); subEntries.push(createSubEntry(name));
} }
return $.event('AddMenuEntry', { return Header.menu.addEntry({
type: 'header',
el: el, el: el,
order: 105, order: 105,
subEntries: subEntries subEntries: subEntries
@ -8423,8 +8382,7 @@
conf = _ref[name]; conf = _ref[name];
subEntries.push(createSubEntry(name, conf[1])); subEntries.push(createSubEntry(name, conf[1]));
} }
return $.event('AddMenuEntry', { return Header.menu.addEntry({
type: 'header',
el: el, el: el,
order: 105, order: 105,
subEntries: subEntries subEntries: subEntries
@ -8602,8 +8560,7 @@
}); });
this.el = prefetch.firstElementChild; this.el = prefetch.firstElementChild;
$.on(this.el, 'change', this.toggle); $.on(this.el, 'change', this.toggle);
return $.event('AddMenuEntry', { return Header.menu.addEntry({
type: 'header',
el: prefetch, el: prefetch,
order: 104 order: 104
}); });
@ -9363,7 +9320,6 @@
textContent: 'Archive' textContent: 'Archive'
}); });
entry = { entry = {
type: 'post',
el: div, el: div,
order: 90, order: 90,
open: function(_arg) { open: function(_arg) {
@ -9382,7 +9338,7 @@
type = _ref[_i]; type = _ref[_i];
entry.subEntries.push(this.createSubEntry(type[0], type[1])); entry.subEntries.push(this.createSubEntry(type[0], type[1]));
} }
return $.event('AddMenuEntry', entry); return Menu.menu.addEntry(entry);
}, },
createSubEntry: function(text, type) { createSubEntry: function(text, type) {
var el, open; var el, open;
@ -9459,8 +9415,7 @@
return true; return true;
} }
}; };
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: div, el: div,
order: 40, order: 40,
open: function(post) { open: function(post) {
@ -9568,8 +9523,7 @@
className: 'download-link', className: 'download-link',
textContent: 'Download file' textContent: 'Download file'
}); });
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: a, el: a,
order: 100, order: 100,
open: function(_arg) { open: function(_arg) {
@ -9591,8 +9545,7 @@
if (!Conf['Menu']) { if (!Conf['Menu']) {
return; return;
} }
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: $.el('div', { el: $.el('div', {
textContent: 'Labels' textContent: 'Labels'
}), }),
@ -9630,7 +9583,7 @@
innerHTML: '<i class="fa fa-bars"></i>', innerHTML: '<i class="fa fa-bars"></i>',
href: 'javascript:;' href: 'javascript:;'
}); });
this.menu = new UI.Menu('post'); this.menu = new UI.Menu();
Post.callbacks.push({ Post.callbacks.push({
name: 'Menu', name: 'Menu',
cb: this.node cb: this.node
@ -9683,8 +9636,7 @@
textContent: 'Report this post' textContent: 'Report this post'
}); });
$.on(a, 'click', ReportLink.report); $.on(a, 'click', ReportLink.report);
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: a, el: a,
order: 10, order: 10,
open: function(post) { open: function(post) {
@ -9945,8 +9897,7 @@
subEntries.push({ subEntries.push({
el: this.settings el: this.settings
}); });
$.event('AddMenuEntry', this.entry = { Header.menu.addEntry(this.entry = {
type: 'header',
el: $.el('span', { el: $.el('span', {
textContent: 'Updater' textContent: 'Updater'
}), }),
@ -10097,7 +10048,7 @@
ThreadUpdater.thread.kill(); ThreadUpdater.thread.kill();
$.event('ThreadUpdate', { $.event('ThreadUpdate', {
404: true, 404: true,
thread: ThreadUpdater.thread threadID: ThreadUpdater.thread.fullID
}); });
break; break;
default: default:
@ -10196,7 +10147,7 @@
return new Notice('info', "The thread is " + change + ".", 30); return new Notice('info', "The thread is " + change + ".", 30);
}, },
parse: function(postObjects) { parse: function(postObjects) {
var OP, count, deletedFiles, deletedPosts, files, index, node, num, post, postObject, posts, root, scroll, sendEvent, _i, _j, _len, _len1; var OP, count, files, index, node, num, post, postObject, posts, root, scroll, sendEvent, _i, _j, _len, _len1;
OP = postObjects[0]; OP = postObjects[0];
Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler;
ThreadUpdater.updateThreadStatus('Sticky', !!OP.sticky); ThreadUpdater.updateThreadStatus('Sticky', !!OP.sticky);
@ -10221,19 +10172,15 @@
node = Build.postFromObject(postObject, ThreadUpdater.thread.board.ID); node = Build.postFromObject(postObject, ThreadUpdater.thread.board.ID);
posts.push(new Post(node, ThreadUpdater.thread, ThreadUpdater.thread.board)); posts.push(new Post(node, ThreadUpdater.thread, ThreadUpdater.thread.board));
} }
deletedPosts = [];
deletedFiles = [];
ThreadUpdater.thread.posts.forEach(function(post) { ThreadUpdater.thread.posts.forEach(function(post) {
var ID; var ID;
ID = +post.ID; ID = +post.ID;
if (__indexOf.call(index, ID) < 0) { if (__indexOf.call(index, ID) < 0) {
post.kill(); post.kill();
deletedPosts.push(post);
} else if (post.isDead) { } else if (post.isDead) {
post.resurrect(); post.resurrect();
} else if (post.file && !(post.file.isDead || __indexOf.call(files, ID) >= 0)) { } else if (post.file && !post.file.isDead && __indexOf.call(files, ID) < 0) {
post.kill(true); post.kill(true);
deletedFiles.push(post);
} }
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) { if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
return ThreadUpdater.foundPost = true; return ThreadUpdater.foundPost = true;
@ -10242,10 +10189,10 @@
sendEvent = function() { sendEvent = function() {
return $.event('ThreadUpdate', { return $.event('ThreadUpdate', {
404: false, 404: false,
thread: ThreadUpdater.thread, threadID: ThreadUpdater.thread.fullID,
newPosts: posts, newPosts: posts.map(function(post) {
deletedPosts: deletedPosts, return post.fullID;
deletedFiles: deletedFiles, }),
postCount: OP.replies + 1, postCount: OP.replies + 1,
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead) fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead)
}); });
@ -10420,14 +10367,14 @@
return ThreadWatcher.rm(boardID, +threadID); return ThreadWatcher.rm(boardID, +threadID);
}, },
post: function(e) { post: function(e) {
var board, postID, threadID, _ref; var boardID, postID, threadID, _ref;
_ref = e.detail, board = _ref.board, postID = _ref.postID, threadID = _ref.threadID; _ref = e.detail, boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID;
if (postID === threadID) { if (postID === threadID) {
if (Conf['Auto Watch']) { if (Conf['Auto Watch']) {
return $.set('AutoWatch', threadID); return $.set('AutoWatch', threadID);
} }
} else if (Conf['Auto Watch Reply']) { } else if (Conf['Auto Watch Reply']) {
return ThreadWatcher.add(board.threads[threadID]); return ThreadWatcher.add(g.threads[boardID + '.' + threadID]);
} }
}, },
onIndexRefresh: function() { onIndexRefresh: function() {
@ -10456,7 +10403,7 @@
}, },
onThreadRefresh: function(e) { onThreadRefresh: function(e) {
var thread; var thread;
thread = e.detail.thread; thread = g.threads[e.detail.threadID];
if (!(e.detail[404] && ThreadWatcher.db.get({ if (!(e.detail[404] && ThreadWatcher.db.get({
boardID: thread.board.ID, boardID: thread.board.ID,
threadID: thread.ID threadID: thread.ID
@ -10668,12 +10615,12 @@
if (!Conf['Thread Watcher']) { if (!Conf['Thread Watcher']) {
return; return;
} }
menu = new UI.Menu('thread watcher'); menu = new UI.Menu();
$.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) { $.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) {
return menu.toggle(e, this, ThreadWatcher); return menu.toggle(e, this, ThreadWatcher);
}); });
this.addHeaderMenuEntry(); this.addHeaderMenuEntry();
return this.addMenuEntries(); return this.addMenuEntries(menu);
}, },
addHeaderMenuEntry: function() { addHeaderMenuEntry: function() {
var entryEl; var entryEl;
@ -10683,8 +10630,7 @@
entryEl = $.el('a', { entryEl = $.el('a', {
href: 'javascript:;' href: 'javascript:;'
}); });
$.event('AddMenuEntry', { Header.menu.addEntry({
type: 'header',
el: entryEl, el: entryEl,
order: 60 order: 60
}); });
@ -10699,13 +10645,12 @@
return entryEl.textContent = text; return entryEl.textContent = text;
}); });
}, },
addMenuEntries: function() { addMenuEntries: function(menu) {
var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1; var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1;
entries = []; entries = [];
entries.push({ entries.push({
cb: ThreadWatcher.cb.openAll, cb: ThreadWatcher.cb.openAll,
entry: { entry: {
type: 'thread watcher',
el: $.el('a', { el: $.el('a', {
textContent: 'Open all threads' textContent: 'Open all threads'
}) })
@ -10717,7 +10662,6 @@
entries.push({ entries.push({
cb: ThreadWatcher.cb.checkThreads, cb: ThreadWatcher.cb.checkThreads,
entry: { entry: {
type: 'thread watcher',
el: $.el('a', { el: $.el('a', {
textContent: 'Check 404\'d threads' textContent: 'Check 404\'d threads'
}) })
@ -10729,7 +10673,6 @@
entries.push({ entries.push({
cb: ThreadWatcher.cb.pruneDeads, cb: ThreadWatcher.cb.pruneDeads,
entry: { entry: {
type: 'thread watcher',
el: $.el('a', { el: $.el('a', {
textContent: 'Prune 404\'d threads' textContent: 'Prune 404\'d threads'
}) })
@ -10746,7 +10689,6 @@
} }
entries.push({ entries.push({
entry: { entry: {
type: 'thread watcher',
el: $.el('span', { el: $.el('span', {
textContent: 'Settings' textContent: 'Settings'
}), }),
@ -10764,7 +10706,7 @@
if (refresh) { if (refresh) {
this.refreshers.push(refresh.bind(entry)); this.refreshers.push(refresh.bind(entry));
} }
$.event('AddMenuEntry', entry); menu.addEntry(entry);
} }
}, },
createSubEntry: function(name, desc) { createSubEntry: function(name, desc) {
@ -10964,11 +10906,13 @@
onUpdate: function(e) { onUpdate: function(e) {
if (e.detail[404]) { if (e.detail[404]) {
return Unread.update(); return Unread.update();
} else if (!Conf['Quote Threading']) { } else if (Conf['Quote Threading']) {
return Unread.addPosts(e.detail.newPosts);
} else {
Unread.read(); Unread.read();
return Unread.update(); return Unread.update();
} else {
return Unread.addPosts(e.detail.newPosts.map(function(fullID) {
return g.posts[fullID];
}));
} }
}, },
readSinglePost: function(post) { readSinglePost: function(post) {
@ -11114,7 +11058,7 @@
} }
return Redirect.data = o; return Redirect.data = o;
}, },
archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","vg","vp","vr","wsg"],"files":["a","biz","diy","gd","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i","lgbt"],"files":["d","i","lgbt"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","mlp","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["asp","cm","h","hc","hm","n","p","r","s","soc","y"],"files":["asp","cm","h","hc","hm","n","p","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":19,"name":"Innovandalism Archive","domain":"boards.innovandalism.eu","http":true,"https":false,"software":"foolfuuka","boards":["v"],"files":[]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]},{"uid":19,"name":"Innovandalism Archive","domain":"boards.innovandalism.eu","http":true,"https":false,"software":"foolfuuka","boards":["v"],"files":[]}], archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","gd","int","jp","m","out","po","sci","sp","tg","tv","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","gd","jp","m","po","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i","lgbt"],"files":["d","i","lgbt"]},{"uid":8,"name":"Rebecca Black Tech","domain":"archive.rebeccablacktech.com","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","mlp","r9k","toy"]},{"uid":10,"name":"warosu","domain":"warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","cm","h","hc","hm","n","p","r","s","soc","y"],"files":["asp","cm","h","hc","hm","n","p","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":19,"name":"Deniable Plausibility","domain":"boards.deniableplausibility.net","http":true,"https":false,"software":"foolfuuka","boards":["v","vg"],"files":["v","vg"]},{"uid":19,"name":"Innovandalism Archive","domain":"boards.innovandalism.eu","http":true,"https":false,"software":"foolfuuka","boards":["v"],"files":[]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","c","co","d","diy","gd","int","jp","m","out","po","s4s","sci","sp","tg","tv","u","vg","vp","vr","wsg"],"files":["a","biz","c","co","d","diy","gd","jp","m","po","s4s","sci","tg","u","vg","vp","vr","wsg"]}],
to: function(dest, data) { to: function(dest, data) {
var archive; var archive;
archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID];
@ -11183,7 +11127,6 @@
return; return;
} }
entry = { entry = {
type: 'header',
el: $.el('a', { el: $.el('a', {
textContent: 'Show announcement', textContent: 'Show announcement',
className: 'show-announcement', className: 'show-announcement',
@ -11194,7 +11137,7 @@
return psa.hidden; return psa.hidden;
} }
}; };
$.event('AddMenuEntry', entry); Header.menu.addEntry(entry);
$.on(entry.el, 'click', PSAHiding.toggle); $.on(entry.el, 'click', PSAHiding.toggle);
PSAHiding.btn = btn = $.el('span', { PSAHiding.btn = btn = $.el('span', {
innerHTML: '[<a href=javascript:;>Dismiss</a>]', innerHTML: '[<a href=javascript:;>Dismiss</a>]',
@ -11352,8 +11295,7 @@
input = $('input', el); input = $('input', el);
$.on(input, 'change', this.toggle); $.on(input, 'change', this.toggle);
$.sync('Header catalog links', CatalogLinks.set); $.sync('Header catalog links', CatalogLinks.set);
$.event('AddMenuEntry', { Header.menu.addEntry({
type: 'header',
el: el, el: el,
order: 95 order: 95
}); });
@ -11885,7 +11827,7 @@
}); });
} }
if (board === 'sci') { if (board === 'sci') {
$.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(e.detail);\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);"); $.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(document.getElementById(e.detail));\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);");
return Post.callbacks.push({ return Post.callbacks.push({
name: 'Parse /sci/ math', name: 'Parse /sci/ math',
cb: this.math cb: this.math
@ -11912,7 +11854,7 @@
if (this.isClone || !$('.math', this.nodes.comment)) { if (this.isClone || !$('.math', this.nodes.comment)) {
return; return;
} }
return $.event('jsmath', this.nodes.post, window); return $.event('jsmath', this.nodes.post.id, window);
} }
}; };
@ -12215,31 +12157,31 @@
} }
break; break;
case Conf['Next thread']: case Conf['Next thread']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
Nav.scroll(+1); Nav.scroll(+1);
break; break;
case Conf['Previous thread']: case Conf['Previous thread']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
Nav.scroll(-1); Nav.scroll(-1);
break; break;
case Conf['Expand thread']: case Conf['Expand thread']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
ExpandThread.toggle(thread); ExpandThread.toggle(thread);
break; break;
case Conf['Open thread']: case Conf['Open thread']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
Keybinds.open(thread); Keybinds.open(thread);
break; break;
case Conf['Open thread tab']: case Conf['Open thread tab']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
Keybinds.open(thread, true); Keybinds.open(thread, true);
@ -12628,7 +12570,7 @@
var response; var response;
e.preventDefault(); e.preventDefault();
response = field.value.trim(); response = field.value.trim();
if (!/\s/.test(response)) { if (!/\s|^\d+$/.test(response)) {
field.value = "" + response + " " + response; field.value = "" + response + " " + response;
} }
return this.submit(); return this.submit();
@ -13174,7 +13116,6 @@
Settings.addSection('Sauce', Settings.sauce); Settings.addSection('Sauce', Settings.sauce);
Settings.addSection('Advanced', Settings.advanced); Settings.addSection('Advanced', Settings.advanced);
Settings.addSection('Keybinds', Settings.keybinds); Settings.addSection('Keybinds', Settings.keybinds);
$.on(d, 'AddSettingsSection', Settings.addSection);
$.on(d, 'OpenSettings', function(e) { $.on(d, 'OpenSettings', function(e) {
return Settings.open(e.detail); return Settings.open(e.detail);
}); });
@ -13238,10 +13179,7 @@
}, },
sections: [], sections: [],
addSection: function(title, open) { addSection: function(title, open) {
var hyphenatedTitle, _ref; var hyphenatedTitle;
if (typeof title !== 'string') {
_ref = title.detail, title = _ref.title, open = _ref.open;
}
hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-'); hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-');
return Settings.sections.push({ return Settings.sections.push({
title: title, title: title,
@ -13923,7 +13861,6 @@
init('Banner', Banner); init('Banner', Banner);
init('Navigate', Navigate); init('Navigate', Navigate);
init('Flash Features', Flash); init('Flash Features', Flash);
$.on(d, 'AddCallback', Main.addCallback);
return $.ready(Main.initReady); return $.ready(Main.initReady);
}, },
initStyle: function() { initStyle: function() {
@ -14071,25 +14008,6 @@
return $.set('previousversion', g.VERSION); return $.set('previousversion', g.VERSION);
}); });
}, },
addCallback: function(e) {
var Klass, obj;
obj = e.detail;
if (typeof obj.callback.name !== 'string') {
throw new Error("Invalid callback name: " + obj.callback.name);
}
switch (obj.type) {
case 'Post':
Klass = Post;
break;
case 'Thread':
Klass = Thread;
break;
default:
return;
}
obj.callback.isAddon = true;
return Klass.callbacks.push(obj.callback);
},
handleErrors: function(errors) { handleErrors: function(errors) {
var div, error, logs, _i, _len; var div, error, logs, _i, _len;
if (!(errors instanceof Array)) { if (!(errors instanceof Array)) {

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript // Generated by CoffeeScript
/* /*
* 4chan X - Version 1.7.33 - 2014-05-29 * 4chan X - Version 1.7.33 - 2014-06-22
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE * https://github.com/ccd0/4chan-x/blob/master/LICENSE
@ -170,7 +170,6 @@
'Report Link': [true, 'Add a report link to the menu.'], 'Report Link': [true, 'Add a report link to the menu.'],
'Post Hiding Link': [true, 'Add a link to hide posts.'], 'Post Hiding Link': [true, 'Add a link to hide posts.'],
'Delete Link': [true, 'Add post and image deletion links to the menu.'], 'Delete Link': [true, 'Add post and image deletion links to the menu.'],
'Download Link': [true, 'Add a download with original filename link to the menu. Chrome-only currently.'],
'Archive Link': [true, 'Add an archive link to the menu.'] 'Archive Link': [true, 'Add an archive link to the menu.']
}, },
'Monitoring': { 'Monitoring': {
@ -192,7 +191,6 @@
'Persistent QR': [true, 'The Quick reply won\'t disappear after posting.'], 'Persistent QR': [true, 'The Quick reply won\'t disappear after posting.'],
'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'], 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'],
'Open Post in New Tab': [true, 'Open new threads or replies to a thread from the index in a new tab.'], 'Open Post in New Tab': [true, 'Open new threads or replies to a thread from the index in a new tab.'],
'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'],
'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'], 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'],
'Hide Original Post Form': [true, 'Hide the normal post form.'], 'Hide Original Post Form': [true, 'Hide the normal post form.'],
'Cooldown': [true, 'Indicate the remaining time before posting again.'], 'Cooldown': [true, 'Indicate the remaining time before posting again.'],
@ -2020,8 +2018,8 @@
Header = { Header = {
init: function() { init: function() {
var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler; var barFixedToggler, barPositionToggler, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler;
this.menu = new UI.Menu('header'); this.menu = new UI.Menu();
menuButton = $.el('span', { menuButton = $.el('a', {
className: 'menu-button', className: 'menu-button',
innerHTML: '<i></i>' innerHTML: '<i></i>'
}); });
@ -2084,8 +2082,7 @@
$.sync('Header auto-hide', this.setBarVisibility); $.sync('Header auto-hide', this.setBarVisibility);
$.sync('Centered links', this.setLinkJustify); $.sync('Centered links', this.setLinkJustify);
this.addShortcut(menuButton); this.addShortcut(menuButton);
$.event('AddMenuEntry', { this.menu.addEntry({
type: 'header',
el: $.el('span', { el: $.el('span', {
textContent: 'Header' textContent: 'Header'
}), }),
@ -2532,7 +2529,7 @@
className: 'shortcut brackets-wrap' className: 'shortcut brackets-wrap'
}); });
$.add(shortcut, el); $.add(shortcut, el);
return $.prepend(Header.shortcuts, shortcut); return $.add(Header.shortcuts, shortcut);
}, },
rmShortcut: function(el) { rmShortcut: function(el) {
return $.rm(el.parentElement); return $.rm(el.parentElement);
@ -2541,12 +2538,9 @@
return Header.menu.toggle(e, this, g); return Header.menu.toggle(e, this, g);
}, },
createNotification: function(e) { createNotification: function(e) {
var cb, content, lifetime, notice, type, _ref; var content, lifetime, notice, type, _ref;
_ref = e.detail, type = _ref.type, content = _ref.content, lifetime = _ref.lifetime, cb = _ref.cb; _ref = e.detail, type = _ref.type, content = _ref.content, lifetime = _ref.lifetime;
notice = new Notice(type, content, lifetime); return notice = new Notice(type, content, lifetime);
if (cb) {
return cb(notice);
}
}, },
areNotificationsEnabled: false, areNotificationsEnabled: false,
enableDesktopNotifications: function() { enableDesktopNotifications: function() {
@ -2689,8 +2683,7 @@
$.on(input, 'change', this.cb.replies); $.on(input, 'change', this.cb.replies);
} }
} }
$.event('AddMenuEntry', { Header.menu.addEntry({
type: 'header',
el: $.el('span', { el: $.el('span', {
textContent: 'Index Navigation' textContent: 'Index Navigation'
}), }),
@ -2807,8 +2800,7 @@
if (g.VIEW !== 'index' || !Conf['Menu'] || g.BOARD.ID === 'f') { if (g.VIEW !== 'index' || !Conf['Menu'] || g.BOARD.ID === 'f') {
return; return;
} }
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: $.el('a', { el: $.el('a', {
href: 'javascript:;' href: 'javascript:;'
}), }),
@ -3082,7 +3074,7 @@
} }
onSameIndex = g.VIEW === 'index' && a.pathname.split('/')[1] === g.BOARD.ID; onSameIndex = g.VIEW === 'index' && a.pathname.split('/')[1] === g.BOARD.ID;
needChange = Index.cb.indexNav(a, onSameIndex); needChange = Index.cb.indexNav(a, onSameIndex);
if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || !onSameIndex) { if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || !onSameIndex || g.BOARD.ID === 'f') {
return; return;
} }
e.preventDefault(); e.preventDefault();
@ -3613,8 +3605,7 @@
if (!infinite) { if (!infinite) {
$.rmAll(Index.root); $.rmAll(Index.root);
} }
$.add(Index.root, nodes); return $.add(Index.root, nodes);
return $.event('IndexBuild', nodes);
}, },
isSearching: false, isSearching: false,
clearSearch: function() { clearSearch: function() {
@ -4275,15 +4266,11 @@
lastToggledButton = null; lastToggledButton = null;
function Menu(type) { function Menu() {
this.type = type;
this.rmEntry = __bind(this.rmEntry, this); this.rmEntry = __bind(this.rmEntry, this);
this.addEntry = __bind(this.addEntry, this);
this.onFocus = __bind(this.onFocus, this); this.onFocus = __bind(this.onFocus, this);
this.keybinds = __bind(this.keybinds, this); this.keybinds = __bind(this.keybinds, this);
this.close = __bind(this.close, this); this.close = __bind(this.close, this);
$.on(d, 'AddMenuEntry', this.addEntry);
$.on(d, 'rmMenuEntry', this.rmEntry);
this.entries = []; this.entries = [];
} }
@ -4481,12 +4468,7 @@
return style.right = right; return style.right = right;
}; };
Menu.prototype.addEntry = function(e) { Menu.prototype.addEntry = function(entry) {
var entry;
entry = e.detail;
if (entry.type !== this.type) {
return;
}
this.parseEntry(entry); this.parseEntry(entry);
return this.entries.push(entry); return this.entries.push(entry);
}; };
@ -4906,7 +4888,6 @@
textContent: 'Filter' textContent: 'Filter'
}); });
entry = { entry = {
type: 'post',
el: div, el: div,
order: 50, order: 50,
open: function(post) { open: function(post) {
@ -4920,7 +4901,7 @@
type = _ref[_i]; type = _ref[_i];
entry.subEntries.push(Filter.menu.createSubEntry(type[0], type[1])); entry.subEntries.push(Filter.menu.createSubEntry(type[0], type[1]));
} }
return $.event('AddMenuEntry', entry); return Menu.menu.addEntry(entry);
}, },
createSubEntry: function(text, type) { createSubEntry: function(text, type) {
var el; var el;
@ -5099,8 +5080,7 @@
innerHTML: "<input type=checkbox name=makeStub checked=" + Conf['Stubs'] + "> Make stub" innerHTML: "<input type=checkbox name=makeStub checked=" + Conf['Stubs'] + "> Make stub"
}) })
}; };
$.event('AddMenuEntry', { Menu.menu.addEntry({
type: 'post',
el: $.el('div', { el: $.el('div', {
textContent: 'Hide post', textContent: 'Hide post',
className: 'hide-post-link' className: 'hide-post-link'
@ -5151,8 +5131,7 @@
return true; return true;
} }
}; };
$.event('AddMenuEntry', { Menu.menu.addEntry({
type: 'post',
el: $.el('div', { el: $.el('div', {
textContent: 'Unhide post', textContent: 'Unhide post',
className: 'show-post-link' className: 'show-post-link'
@ -5176,8 +5155,7 @@
if (g.VIEW !== 'index') { if (g.VIEW !== 'index') {
return; return;
} }
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: $.el('a', { el: $.el('a', {
href: 'javascript:;' href: 'javascript:;'
}), }),
@ -5768,8 +5746,7 @@
}); });
input = $('input', this.controls); input = $('input', this.controls);
$.on(input, 'change', this.toggle); $.on(input, 'change', this.toggle);
$.event('AddMenuEntry', this.entry = { Header.menu.addEntry(this.entry = {
type: 'header',
el: this.controls, el: this.controls,
order: 98 order: 98
}); });
@ -6076,16 +6053,6 @@
} }
}); });
$.before($.id('togglePostFormLink'), link); $.before($.id('togglePostFormLink'), link);
$.on(d, 'QRGetSelectedPost', function(_arg) {
var cb;
cb = _arg.detail;
return cb(QR.selected);
});
$.on(d, 'QRAddPreSubmitHook', function(_arg) {
var cb;
cb = _arg.detail;
return QR.preSubmitHooks.push(cb);
});
$.on(d, 'paste', QR.paste); $.on(d, 'paste', QR.paste);
$.on(d, 'dragover', QR.dragOver); $.on(d, 'dragover', QR.dragOver);
$.on(d, 'drop', QR.dropFile); $.on(d, 'drop', QR.dropFile);
@ -6669,9 +6636,8 @@
return $.add(nodes.form, flag); return $.add(nodes.form, flag);
} }
}, },
preSubmitHooks: [],
submit: function(e) { submit: function(e) {
var challenge, err, extra, filetag, formData, hook, options, post, response, textOnly, thread, threadID, _i, _len, _ref, _ref1; var challenge, err, extra, filetag, formData, options, post, response, textOnly, thread, threadID, _ref;
if (e != null) { if (e != null) {
e.preventDefault(); e.preventDefault();
} }
@ -6704,17 +6670,9 @@
err = 'No file selected.'; err = 'No file selected.';
} else if (post.file && thread.fileLimit) { } else if (post.file && thread.fileLimit) {
err = 'Max limit of image replies has been reached.'; err = 'Max limit of image replies has been reached.';
} else {
_ref = QR.preSubmitHooks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
hook = _ref[_i];
if (err = hook(post, thread)) {
break;
}
}
} }
if (QR.captcha.isEnabled && !err) { if (QR.captcha.isEnabled && !err) {
_ref1 = QR.captcha.getOne(), challenge = _ref1.challenge, response = _ref1.response; _ref = QR.captcha.getOne(), challenge = _ref.challenge, response = _ref.response;
if (!response) { if (!response) {
err = 'No valid captcha.'; err = 'No valid captcha.';
} }
@ -6849,11 +6807,12 @@
}); });
ThreadUpdater.postID = postID; ThreadUpdater.postID = postID;
$.event('QRPostSuccessful', { $.event('QRPostSuccessful', {
board: g.BOARD, boardID: g.BOARD.ID,
threadID: threadID, threadID: threadID,
postID: postID postID: postID
}); });
$.event('QRPostSuccessful_', { $.event('QRPostSuccessful_', {
boardID: g.BOARD.ID,
threadID: threadID, threadID: threadID,
postID: postID postID: postID
}); });
@ -7025,7 +6984,7 @@
} }
if (response) { if (response) {
response = response.trim(); response = response.trim();
if (!/\s/.test(response)) { if (!/\s|^\d+$/.test(response)) {
response = "" + response + " " + response; response = "" + response + " " + response;
} }
} }
@ -7464,8 +7423,7 @@
rectEl = this.nodes.el.getBoundingClientRect(); rectEl = this.nodes.el.getBoundingClientRect();
rectList = this.nodes.el.parentNode.getBoundingClientRect(); rectList = this.nodes.el.parentNode.getBoundingClientRect();
this.nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width / 2 - rectList.left - rectList.width / 2; this.nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width / 2 - rectList.left - rectList.width / 2;
this.load(); return this.load();
return $.event('QRPostSelection', this);
}; };
_Class.prototype.load = function() { _Class.prototype.load = function() {
@ -7757,8 +7715,7 @@
}); });
FappeTyme[lc] = input = el.firstElementChild; FappeTyme[lc] = input = el.firstElementChild;
$.on(input, 'change', FappeTyme.cb.toggle.bind(input)); $.on(input, 'change', FappeTyme.cb.toggle.bind(input));
$.event('AddMenuEntry', { Header.menu.addEntry({
type: 'header',
el: el, el: el,
order: 97 order: 97
}); });
@ -7844,7 +7801,7 @@
nodes[key] = $(value, dialog); nodes[key] = $(value, dialog);
} }
menuButton = $('.menu-button', dialog); menuButton = $('.menu-button', dialog);
nodes.menu = new UI.Menu('gallery'); nodes.menu = new UI.Menu();
cb = Gallery.cb; cb = Gallery.cb;
$.on(nodes.frame, 'click', cb.blank); $.on(nodes.frame, 'click', cb.blank);
$.on(nodes.next, 'click', cb.advance); $.on(nodes.next, 'click', cb.advance);
@ -7857,8 +7814,7 @@
createSubEntry = Gallery.menu.createSubEntry; createSubEntry = Gallery.menu.createSubEntry;
for (name in Config.gallery) { for (name in Config.gallery) {
el = createSubEntry(name).el; el = createSubEntry(name).el;
$.event('AddMenuEntry', { nodes.menu.addEntry({
type: 'gallery',
el: el, el: el,
order: 0 order: 0
}); });
@ -8087,8 +8043,7 @@
for (name in Config.gallery) { for (name in Config.gallery) {
subEntries.push(createSubEntry(name)); subEntries.push(createSubEntry(name));
} }
return $.event('AddMenuEntry', { return Header.menu.addEntry({
type: 'header',
el: el, el: el,
order: 105, order: 105,
subEntries: subEntries subEntries: subEntries
@ -8450,8 +8405,7 @@
conf = _ref[name]; conf = _ref[name];
subEntries.push(createSubEntry(name, conf[1])); subEntries.push(createSubEntry(name, conf[1]));
} }
return $.event('AddMenuEntry', { return Header.menu.addEntry({
type: 'header',
el: el, el: el,
order: 105, order: 105,
subEntries: subEntries subEntries: subEntries
@ -8618,8 +8572,7 @@
}); });
this.el = prefetch.firstElementChild; this.el = prefetch.firstElementChild;
$.on(this.el, 'change', this.toggle); $.on(this.el, 'change', this.toggle);
return $.event('AddMenuEntry', { return Header.menu.addEntry({
type: 'header',
el: prefetch, el: prefetch,
order: 104 order: 104
}); });
@ -9379,7 +9332,6 @@
textContent: 'Archive' textContent: 'Archive'
}); });
entry = { entry = {
type: 'post',
el: div, el: div,
order: 90, order: 90,
open: function(_arg) { open: function(_arg) {
@ -9398,7 +9350,7 @@
type = _ref[_i]; type = _ref[_i];
entry.subEntries.push(this.createSubEntry(type[0], type[1])); entry.subEntries.push(this.createSubEntry(type[0], type[1]));
} }
return $.event('AddMenuEntry', entry); return Menu.menu.addEntry(entry);
}, },
createSubEntry: function(text, type) { createSubEntry: function(text, type) {
var el, open; var el, open;
@ -9475,8 +9427,7 @@
return true; return true;
} }
}; };
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: div, el: div,
order: 40, order: 40,
open: function(post) { open: function(post) {
@ -9584,8 +9535,7 @@
className: 'download-link', className: 'download-link',
textContent: 'Download file' textContent: 'Download file'
}); });
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: a, el: a,
order: 100, order: 100,
open: function(_arg) { open: function(_arg) {
@ -9607,8 +9557,7 @@
if (!Conf['Menu']) { if (!Conf['Menu']) {
return; return;
} }
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: $.el('div', { el: $.el('div', {
textContent: 'Labels' textContent: 'Labels'
}), }),
@ -9646,7 +9595,7 @@
innerHTML: '<i class="fa fa-bars"></i>', innerHTML: '<i class="fa fa-bars"></i>',
href: 'javascript:;' href: 'javascript:;'
}); });
this.menu = new UI.Menu('post'); this.menu = new UI.Menu();
Post.callbacks.push({ Post.callbacks.push({
name: 'Menu', name: 'Menu',
cb: this.node cb: this.node
@ -9699,8 +9648,7 @@
textContent: 'Report this post' textContent: 'Report this post'
}); });
$.on(a, 'click', ReportLink.report); $.on(a, 'click', ReportLink.report);
return $.event('AddMenuEntry', { return Menu.menu.addEntry({
type: 'post',
el: a, el: a,
order: 10, order: 10,
open: function(post) { open: function(post) {
@ -9961,8 +9909,7 @@
subEntries.push({ subEntries.push({
el: this.settings el: this.settings
}); });
$.event('AddMenuEntry', this.entry = { Header.menu.addEntry(this.entry = {
type: 'header',
el: $.el('span', { el: $.el('span', {
textContent: 'Updater' textContent: 'Updater'
}), }),
@ -10113,7 +10060,7 @@
ThreadUpdater.thread.kill(); ThreadUpdater.thread.kill();
$.event('ThreadUpdate', { $.event('ThreadUpdate', {
404: true, 404: true,
thread: ThreadUpdater.thread threadID: ThreadUpdater.thread.fullID
}); });
break; break;
default: default:
@ -10212,7 +10159,7 @@
return new Notice('info', "The thread is " + change + ".", 30); return new Notice('info', "The thread is " + change + ".", 30);
}, },
parse: function(postObjects) { parse: function(postObjects) {
var OP, count, deletedFiles, deletedPosts, files, index, node, num, post, postObject, posts, root, scroll, sendEvent, _i, _j, _len, _len1; var OP, count, files, index, node, num, post, postObject, posts, root, scroll, sendEvent, _i, _j, _len, _len1;
OP = postObjects[0]; OP = postObjects[0];
Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler;
ThreadUpdater.updateThreadStatus('Sticky', !!OP.sticky); ThreadUpdater.updateThreadStatus('Sticky', !!OP.sticky);
@ -10237,19 +10184,15 @@
node = Build.postFromObject(postObject, ThreadUpdater.thread.board.ID); node = Build.postFromObject(postObject, ThreadUpdater.thread.board.ID);
posts.push(new Post(node, ThreadUpdater.thread, ThreadUpdater.thread.board)); posts.push(new Post(node, ThreadUpdater.thread, ThreadUpdater.thread.board));
} }
deletedPosts = [];
deletedFiles = [];
ThreadUpdater.thread.posts.forEach(function(post) { ThreadUpdater.thread.posts.forEach(function(post) {
var ID; var ID;
ID = +post.ID; ID = +post.ID;
if (__indexOf.call(index, ID) < 0) { if (__indexOf.call(index, ID) < 0) {
post.kill(); post.kill();
deletedPosts.push(post);
} else if (post.isDead) { } else if (post.isDead) {
post.resurrect(); post.resurrect();
} else if (post.file && !(post.file.isDead || __indexOf.call(files, ID) >= 0)) { } else if (post.file && !post.file.isDead && __indexOf.call(files, ID) < 0) {
post.kill(true); post.kill(true);
deletedFiles.push(post);
} }
if (ThreadUpdater.postID && ThreadUpdater.postID === ID) { if (ThreadUpdater.postID && ThreadUpdater.postID === ID) {
return ThreadUpdater.foundPost = true; return ThreadUpdater.foundPost = true;
@ -10258,10 +10201,10 @@
sendEvent = function() { sendEvent = function() {
return $.event('ThreadUpdate', { return $.event('ThreadUpdate', {
404: false, 404: false,
thread: ThreadUpdater.thread, threadID: ThreadUpdater.thread.fullID,
newPosts: posts, newPosts: posts.map(function(post) {
deletedPosts: deletedPosts, return post.fullID;
deletedFiles: deletedFiles, }),
postCount: OP.replies + 1, postCount: OP.replies + 1,
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead) fileCount: OP.images + (!!ThreadUpdater.thread.OP.file && !ThreadUpdater.thread.OP.file.isDead)
}); });
@ -10436,14 +10379,14 @@
return ThreadWatcher.rm(boardID, +threadID); return ThreadWatcher.rm(boardID, +threadID);
}, },
post: function(e) { post: function(e) {
var board, postID, threadID, _ref; var boardID, postID, threadID, _ref;
_ref = e.detail, board = _ref.board, postID = _ref.postID, threadID = _ref.threadID; _ref = e.detail, boardID = _ref.boardID, threadID = _ref.threadID, postID = _ref.postID;
if (postID === threadID) { if (postID === threadID) {
if (Conf['Auto Watch']) { if (Conf['Auto Watch']) {
return $.set('AutoWatch', threadID); return $.set('AutoWatch', threadID);
} }
} else if (Conf['Auto Watch Reply']) { } else if (Conf['Auto Watch Reply']) {
return ThreadWatcher.add(board.threads[threadID]); return ThreadWatcher.add(g.threads[boardID + '.' + threadID]);
} }
}, },
onIndexRefresh: function() { onIndexRefresh: function() {
@ -10472,7 +10415,7 @@
}, },
onThreadRefresh: function(e) { onThreadRefresh: function(e) {
var thread; var thread;
thread = e.detail.thread; thread = g.threads[e.detail.threadID];
if (!(e.detail[404] && ThreadWatcher.db.get({ if (!(e.detail[404] && ThreadWatcher.db.get({
boardID: thread.board.ID, boardID: thread.board.ID,
threadID: thread.ID threadID: thread.ID
@ -10684,12 +10627,12 @@
if (!Conf['Thread Watcher']) { if (!Conf['Thread Watcher']) {
return; return;
} }
menu = new UI.Menu('thread watcher'); menu = new UI.Menu();
$.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) { $.on($('.menu-button', ThreadWatcher.dialog), 'click', function(e) {
return menu.toggle(e, this, ThreadWatcher); return menu.toggle(e, this, ThreadWatcher);
}); });
this.addHeaderMenuEntry(); this.addHeaderMenuEntry();
return this.addMenuEntries(); return this.addMenuEntries(menu);
}, },
addHeaderMenuEntry: function() { addHeaderMenuEntry: function() {
var entryEl; var entryEl;
@ -10699,8 +10642,7 @@
entryEl = $.el('a', { entryEl = $.el('a', {
href: 'javascript:;' href: 'javascript:;'
}); });
$.event('AddMenuEntry', { Header.menu.addEntry({
type: 'header',
el: entryEl, el: entryEl,
order: 60 order: 60
}); });
@ -10715,13 +10657,12 @@
return entryEl.textContent = text; return entryEl.textContent = text;
}); });
}, },
addMenuEntries: function() { addMenuEntries: function(menu) {
var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1; var cb, conf, entries, entry, name, refresh, subEntries, _i, _len, _ref, _ref1;
entries = []; entries = [];
entries.push({ entries.push({
cb: ThreadWatcher.cb.openAll, cb: ThreadWatcher.cb.openAll,
entry: { entry: {
type: 'thread watcher',
el: $.el('a', { el: $.el('a', {
textContent: 'Open all threads' textContent: 'Open all threads'
}) })
@ -10733,7 +10674,6 @@
entries.push({ entries.push({
cb: ThreadWatcher.cb.checkThreads, cb: ThreadWatcher.cb.checkThreads,
entry: { entry: {
type: 'thread watcher',
el: $.el('a', { el: $.el('a', {
textContent: 'Check 404\'d threads' textContent: 'Check 404\'d threads'
}) })
@ -10745,7 +10685,6 @@
entries.push({ entries.push({
cb: ThreadWatcher.cb.pruneDeads, cb: ThreadWatcher.cb.pruneDeads,
entry: { entry: {
type: 'thread watcher',
el: $.el('a', { el: $.el('a', {
textContent: 'Prune 404\'d threads' textContent: 'Prune 404\'d threads'
}) })
@ -10762,7 +10701,6 @@
} }
entries.push({ entries.push({
entry: { entry: {
type: 'thread watcher',
el: $.el('span', { el: $.el('span', {
textContent: 'Settings' textContent: 'Settings'
}), }),
@ -10780,7 +10718,7 @@
if (refresh) { if (refresh) {
this.refreshers.push(refresh.bind(entry)); this.refreshers.push(refresh.bind(entry));
} }
$.event('AddMenuEntry', entry); menu.addEntry(entry);
} }
}, },
createSubEntry: function(name, desc) { createSubEntry: function(name, desc) {
@ -10980,11 +10918,13 @@
onUpdate: function(e) { onUpdate: function(e) {
if (e.detail[404]) { if (e.detail[404]) {
return Unread.update(); return Unread.update();
} else if (!Conf['Quote Threading']) { } else if (Conf['Quote Threading']) {
return Unread.addPosts(e.detail.newPosts);
} else {
Unread.read(); Unread.read();
return Unread.update(); return Unread.update();
} else {
return Unread.addPosts(e.detail.newPosts.map(function(fullID) {
return g.posts[fullID];
}));
} }
}, },
readSinglePost: function(post) { readSinglePost: function(post) {
@ -11129,7 +11069,7 @@
} }
return Redirect.data = o; return Redirect.data = o;
}, },
archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","co","diy","gd","jp","m","sci","sp","tg","tv","vg","vp","vr","wsg"],"files":["a","biz","diy","gd","jp","m","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":2,"name":"The Dark Cave","domain":"archive.thedarkcave.org","http":true,"https":true,"software":"foolfuuka","boards":["c","int","out","po"],"files":["c","po"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":18,"name":"4plebs Flash Archive","domain":"flash.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["f"],"files":["f"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i","lgbt"],"files":["d","i","lgbt"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","mlp","r9k","toy"]},{"uid":10,"name":"warosu","domain":"fuuka.warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.eu","http":true,"https":true,"software":"foolfuuka","boards":["asp","cm","h","hc","hm","n","p","r","s","soc","y"],"files":["asp","cm","h","hc","hm","n","p","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":17,"name":"installgentoo.com","domain":"chan.installgentoo.com","http":true,"https":false,"software":"foolfuuka","boards":["g","t"],"files":["g","t"]},{"uid":19,"name":"Innovandalism Archive","domain":"boards.innovandalism.eu","http":true,"https":false,"software":"foolfuuka","boards":["v"],"files":[]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","co","d","diy","gd","jp","m","s4s","sci","sp","tg","tv","u","vg","vp","vr","wsg"],"files":["a","biz","d","diy","gd","jp","m","s4s","sci","tg","u","vg","vp","vr","wsg"]},{"uid":19,"name":"Innovandalism Archive","domain":"boards.innovandalism.eu","http":true,"https":false,"software":"foolfuuka","boards":["v"],"files":[]}], archives: [{"uid":0,"name":"Foolz","domain":"archive.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","gd","int","jp","m","out","po","sci","sp","tg","tv","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","gd","jp","m","po","sci","tg","vg","vp","vr","wsg"]},{"uid":1,"name":"NSFW Foolz","domain":"nsfw.foolz.us","http":true,"https":true,"software":"foolfuuka","boards":["u"],"files":["u"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["d","i","lgbt"],"files":["d","i","lgbt"]},{"uid":8,"name":"Rebecca Black Tech","domain":"archive.rebeccablacktech.com","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","w"],"files":["cgl","g","mu","w"]},{"uid":9,"name":"Heinessen","domain":"archive.heinessen.com","http":true,"https":false,"software":"fuuka","boards":["an","fit","k","mlp","r9k","toy"],"files":["an","fit","k","mlp","r9k","toy"]},{"uid":10,"name":"warosu","domain":"warosu.org","http":false,"https":true,"software":"fuuka","boards":["3","biz","cgl","ck","diy","fa","g","ic","jp","lit","sci","tg","vr"],"files":["3","biz","cgl","ck","diy","fa","ic","jp","lit","sci","tg","vr"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","cm","h","hc","hm","n","p","r","s","soc","y"],"files":["asp","cm","h","hc","hm","n","p","r","s","soc","y"]},{"uid":16,"name":"maware","domain":"archive.mawa.re","http":true,"https":false,"software":"foolfuuka","boards":["t"],"files":["t"]},{"uid":19,"name":"Deniable Plausibility","domain":"boards.deniableplausibility.net","http":true,"https":false,"software":"foolfuuka","boards":["v","vg"],"files":["v","vg"]},{"uid":19,"name":"Innovandalism Archive","domain":"boards.innovandalism.eu","http":true,"https":false,"software":"foolfuuka","boards":["v"],"files":[]},{"uid":13,"name":"Foolz Beta","domain":"beta.foolz.us","http":true,"https":true,"withCredentials":true,"software":"foolfuuka","boards":["a","biz","c","co","d","diy","gd","int","jp","m","out","po","s4s","sci","sp","tg","tv","u","vg","vp","vr","wsg"],"files":["a","biz","c","co","d","diy","gd","jp","m","po","s4s","sci","tg","u","vg","vp","vr","wsg"]}],
to: function(dest, data) { to: function(dest, data) {
var archive; var archive;
archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID]; archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID];
@ -11198,7 +11138,6 @@
return; return;
} }
entry = { entry = {
type: 'header',
el: $.el('a', { el: $.el('a', {
textContent: 'Show announcement', textContent: 'Show announcement',
className: 'show-announcement', className: 'show-announcement',
@ -11209,7 +11148,7 @@
return psa.hidden; return psa.hidden;
} }
}; };
$.event('AddMenuEntry', entry); Header.menu.addEntry(entry);
$.on(entry.el, 'click', PSAHiding.toggle); $.on(entry.el, 'click', PSAHiding.toggle);
PSAHiding.btn = btn = $.el('span', { PSAHiding.btn = btn = $.el('span', {
innerHTML: '[<a href=javascript:;>Dismiss</a>]', innerHTML: '[<a href=javascript:;>Dismiss</a>]',
@ -11367,8 +11306,7 @@
input = $('input', el); input = $('input', el);
$.on(input, 'change', this.toggle); $.on(input, 'change', this.toggle);
$.sync('Header catalog links', CatalogLinks.set); $.sync('Header catalog links', CatalogLinks.set);
$.event('AddMenuEntry', { Header.menu.addEntry({
type: 'header',
el: el, el: el,
order: 95 order: 95
}); });
@ -11900,7 +11838,7 @@
}); });
} }
if (board === 'sci') { if (board === 'sci') {
$.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(e.detail);\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);"); $.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(document.getElementById(e.detail));\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);");
return Post.callbacks.push({ return Post.callbacks.push({
name: 'Parse /sci/ math', name: 'Parse /sci/ math',
cb: this.math cb: this.math
@ -11927,7 +11865,7 @@
if (this.isClone || !$('.math', this.nodes.comment)) { if (this.isClone || !$('.math', this.nodes.comment)) {
return; return;
} }
return $.event('jsmath', this.nodes.post, window); return $.event('jsmath', this.nodes.post.id, window);
} }
}; };
@ -12230,31 +12168,31 @@
} }
break; break;
case Conf['Next thread']: case Conf['Next thread']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
Nav.scroll(+1); Nav.scroll(+1);
break; break;
case Conf['Previous thread']: case Conf['Previous thread']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
Nav.scroll(-1); Nav.scroll(-1);
break; break;
case Conf['Expand thread']: case Conf['Expand thread']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
ExpandThread.toggle(thread); ExpandThread.toggle(thread);
break; break;
case Conf['Open thread']: case Conf['Open thread']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
Keybinds.open(thread); Keybinds.open(thread);
break; break;
case Conf['Open thread tab']: case Conf['Open thread tab']:
if (g.VIEW !== 'index') { if (g.VIEW !== 'index' || Conf['Index Mode'] === 'catalog') {
return; return;
} }
Keybinds.open(thread, true); Keybinds.open(thread, true);
@ -12643,7 +12581,7 @@
var response; var response;
e.preventDefault(); e.preventDefault();
response = field.value.trim(); response = field.value.trim();
if (!/\s/.test(response)) { if (!/\s|^\d+$/.test(response)) {
field.value = "" + response + " " + response; field.value = "" + response + " " + response;
} }
return this.submit(); return this.submit();
@ -13194,7 +13132,6 @@
Settings.addSection('Sauce', Settings.sauce); Settings.addSection('Sauce', Settings.sauce);
Settings.addSection('Advanced', Settings.advanced); Settings.addSection('Advanced', Settings.advanced);
Settings.addSection('Keybinds', Settings.keybinds); Settings.addSection('Keybinds', Settings.keybinds);
$.on(d, 'AddSettingsSection', Settings.addSection);
$.on(d, 'OpenSettings', function(e) { $.on(d, 'OpenSettings', function(e) {
return Settings.open(e.detail); return Settings.open(e.detail);
}); });
@ -13258,10 +13195,7 @@
}, },
sections: [], sections: [],
addSection: function(title, open) { addSection: function(title, open) {
var hyphenatedTitle, _ref; var hyphenatedTitle;
if (typeof title !== 'string') {
_ref = title.detail, title = _ref.title, open = _ref.open;
}
hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-'); hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-');
return Settings.sections.push({ return Settings.sections.push({
title: title, title: title,
@ -13941,7 +13875,6 @@
init('Banner', Banner); init('Banner', Banner);
init('Navigate', Navigate); init('Navigate', Navigate);
init('Flash Features', Flash); init('Flash Features', Flash);
$.on(d, 'AddCallback', Main.addCallback);
return $.ready(Main.initReady); return $.ready(Main.initReady);
}, },
initStyle: function() { initStyle: function() {
@ -14074,25 +14007,6 @@
return $.set('previousversion', g.VERSION); return $.set('previousversion', g.VERSION);
}); });
}, },
addCallback: function(e) {
var Klass, obj;
obj = e.detail;
if (typeof obj.callback.name !== 'string') {
throw new Error("Invalid callback name: " + obj.callback.name);
}
switch (obj.type) {
case 'Post':
Klass = Post;
break;
case 'Thread':
Klass = Thread;
break;
default:
return;
}
obj.callback.isAddon = true;
return Klass.callbacks.push(obj.callback);
},
handleErrors: function(errors) { handleErrors: function(errors) {
var div, error, logs, _i, _len; var div, error, logs, _i, _len;
if (!(errors instanceof Array)) { if (!(errors instanceof Array)) {

View File

@ -8,7 +8,6 @@
.field { .field {
background-color: #FFF; background-color: #FFF;
border: 1px solid #CCC; border: 1px solid #CCC;
-moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
color: #333; color: #333;
font-family: inherit; font-family: inherit;
@ -235,7 +234,6 @@ body > .desktop:not(hr):not(.navLinks):not(#boardNavDesktop):not(#boardNavDeskto
position: absolute; position: absolute;
} }
.message { .message {
-moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
padding: 6px 20px; padding: 6px 20px;
max-height: 200px; max-height: 200px;
@ -245,7 +243,6 @@ body > .desktop:not(hr):not(.navLinks):not(#boardNavDesktop):not(#boardNavDeskto
/* Settings */ /* Settings */
:root.fourchan-x body { :root.fourchan-x body {
-moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
} }
#overlay { #overlay {
@ -258,7 +255,6 @@ body > .desktop:not(hr):not(.navLinks):not(#boardNavDesktop):not(#boardNavDeskto
width: 100%; width: 100%;
} }
#fourchanx-settings { #fourchanx-settings {
-moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
box-shadow: 0 0 15px rgba(0, 0, 0, .15); box-shadow: 0 0 15px rgba(0, 0, 0, .15);
height: 600px; height: 600px;
@ -400,16 +396,16 @@ body > .desktop:not(hr):not(.navLinks):not(#boardNavDesktop):not(#boardNavDeskto
text-decoration: none; text-decoration: none;
} }
.catalog-mode .board { .catalog-mode .board {
text-align: center; display: flex;
flex-wrap: wrap;
justify-content: center;
} }
.catalog-thread { .catalog-thread {
display: inline-flex; display: inline-flex;
text-align: left;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin: 0 2px 5px; margin: 0 2px 5px;
word-break: break-word; word-break: break-word;
vertical-align: top;
} }
.catalog-small .catalog-thread { .catalog-small .catalog-thread {
width: 165px; width: 165px;
@ -682,7 +678,6 @@ a.hide-announcement {
clear: both; clear: both;
} }
#ihover { #ihover {
-moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
max-height: 100%; max-height: 100%;
max-width: 75%; max-width: 75%;
@ -824,7 +819,6 @@ a.hide-announcement {
border: 1px solid #808080; border: 1px solid #808080;
color: #FFF !important; color: #FFF !important;
font-size: 12px; font-size: 12px;
-moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
cursor: move; cursor: move;
display: inline-block; display: inline-block;

View File

@ -27,18 +27,18 @@
} }
}, },
"devDependencies": { "devDependencies": {
"font-awesome": "~4.0.3", "font-awesome": "~4.1.0",
"grunt": "~0.4.4", "grunt": "~0.4.5",
"grunt-bump": "~0.0.13", "grunt-bump": "~0.0.14",
"grunt-concurrent": "~0.5.0", "grunt-concurrent": "~0.5.0",
"grunt-contrib-clean": "~0.5.0", "grunt-contrib-clean": "~0.5.0",
"grunt-contrib-coffee": "~0.10.1", "grunt-contrib-coffee": "~0.10.1",
"grunt-contrib-compress": "~0.8.0", "grunt-contrib-compress": "~0.9.1",
"grunt-contrib-concat": "~0.4.0", "grunt-contrib-concat": "~0.4.0",
"grunt-contrib-copy": "~0.5.0", "grunt-contrib-copy": "~0.5.0",
"grunt-contrib-watch": "~0.6.1", "grunt-contrib-watch": "~0.6.1",
"grunt-shell": "~0.7.0", "grunt-shell": "~0.7.0",
"load-grunt-tasks": "~0.4.0" "load-grunt-tasks": "~0.5.0"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -5,8 +5,8 @@
"http": true, "http": true,
"https": true, "https": true,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["a", "biz", "co", "diy", "gd", "jp", "m", "sci", "sp", "tg", "tv", "vg", "vp", "vr", "wsg"], "boards": ["a", "biz", "c", "co", "diy", "gd", "int", "jp", "m", "out", "po", "sci", "sp", "tg", "tv", "vg", "vp", "vr", "wsg"],
"files": ["a", "biz", "diy", "gd", "jp", "m", "sci", "tg", "vg", "vp", "vr", "wsg"] "files": ["a", "biz", "c", "co", "diy", "gd", "jp", "m", "po", "sci", "tg", "vg", "vp", "vr", "wsg"]
}, { }, {
"uid": 1, "uid": 1,
"name": "NSFW Foolz", "name": "NSFW Foolz",
@ -16,15 +16,6 @@
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["u"], "boards": ["u"],
"files": ["u"] "files": ["u"]
}, {
"uid": 2,
"name": "The Dark Cave",
"domain": "archive.thedarkcave.org",
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["c", "int", "out", "po"],
"files": ["c", "po"]
}, { }, {
"uid": 3, "uid": 3,
"name": "4plebs Archive", "name": "4plebs Archive",
@ -32,17 +23,8 @@
"http": true, "http": true,
"https": true, "https": true,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["adv", "hr", "o", "pol", "s4s", "tg", "trv", "tv", "x"], "boards": ["adv", "f", "hr", "o", "pol", "s4s", "tg", "trv", "tv", "x"],
"files": ["adv", "hr", "o", "pol", "s4s", "tg", "trv", "tv", "x"] "files": ["adv", "f", "hr", "o", "pol", "s4s", "tg", "trv", "tv", "x"]
}, {
"uid": 18,
"name": "4plebs Flash Archive",
"domain": "flash.4plebs.org",
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["f"],
"files": ["f"]
}, { }, {
"uid": 4, "uid": 4,
"name": "Nyafuu", "name": "Nyafuu",
@ -64,7 +46,7 @@
}, { }, {
"uid": 8, "uid": 8,
"name": "Rebecca Black Tech", "name": "Rebecca Black Tech",
"domain": "rbt.asia", "domain": "archive.rebeccablacktech.com",
"http": false, "http": false,
"https": true, "https": true,
"software": "fuuka", "software": "fuuka",
@ -82,7 +64,7 @@
}, { }, {
"uid": 10, "uid": 10,
"name": "warosu", "name": "warosu",
"domain": "fuuka.warosu.org", "domain": "warosu.org",
"http": false, "http": false,
"https": true, "https": true,
"software": "fuuka", "software": "fuuka",
@ -91,7 +73,7 @@
}, { }, {
"uid": 15, "uid": 15,
"name": "fgts", "name": "fgts",
"domain": "fgts.eu", "domain": "fgts.jp",
"http": true, "http": true,
"https": true, "https": true,
"software": "foolfuuka", "software": "foolfuuka",
@ -107,14 +89,14 @@
"boards": ["t"], "boards": ["t"],
"files": ["t"] "files": ["t"]
}, { }, {
"uid": 17, "uid": 19,
"name": "installgentoo.com", "name": "Deniable Plausibility",
"domain": "chan.installgentoo.com", "domain": "boards.deniableplausibility.net",
"http": true, "http": true,
"https": false, "https": false,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["g", "t"], "boards": ["v", "vg"],
"files": ["g", "t"] "files": ["v", "vg"]
}, { }, {
"uid": 19, "uid": 19,
"name": "Innovandalism Archive", "name": "Innovandalism Archive",
@ -132,15 +114,6 @@
"https": true, "https": true,
"withCredentials": true, "withCredentials": true,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["a", "biz", "co", "d", "diy", "gd", "jp", "m", "s4s", "sci", "sp", "tg", "tv", "u", "vg", "vp", "vr", "wsg"], "boards": ["a", "biz", "c", "co", "d", "diy", "gd", "int", "jp", "m", "out", "po", "s4s", "sci", "sp", "tg", "tv", "u", "vg", "vp", "vr", "wsg"],
"files": ["a", "biz", "d", "diy", "gd", "jp", "m", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"] "files": ["a", "biz", "c", "co", "d", "diy", "gd","jp", "m", "po", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"]
}, {
"uid": 19,
"name": "Innovandalism Archive",
"domain": "boards.innovandalism.eu",
"http": true,
"https": false,
"software": "foolfuuka",
"boards": ["v"],
"files": []
}] }]

View File

@ -170,7 +170,6 @@ Filter =
textContent: 'Filter' textContent: 'Filter'
entry = entry =
type: 'post'
el: div el: div
order: 50 order: 50
open: (post) -> open: (post) ->
@ -195,7 +194,7 @@ Filter =
# Add a sub entry for each filter type. # Add a sub entry for each filter type.
entry.subEntries.push Filter.menu.createSubEntry type[0], type[1] entry.subEntries.push Filter.menu.createSubEntry type[0], type[1]
$.event 'AddMenuEntry', entry Menu.menu.addEntry entry
createSubEntry: (text, type) -> createSubEntry: (text, type) ->
el = $.el 'a', el = $.el 'a',

View File

@ -88,8 +88,7 @@ PostHiding =
makeStub = makeStub =
el: $.el 'label', innerHTML: "<input type=checkbox name=makeStub checked=#{Conf['Stubs']}> Make stub" el: $.el 'label', innerHTML: "<input type=checkbox name=makeStub checked=#{Conf['Stubs']}> Make stub"
$.event 'AddMenuEntry', Menu.menu.addEntry
type: 'post'
el: $.el 'div', el: $.el 'div',
textContent: 'Hide post' textContent: 'Hide post'
className: 'hide-post-link' className: 'hide-post-link'
@ -117,8 +116,7 @@ PostHiding =
@el.firstChild.checked = if 'hideRecursively' of data then data.hideRecursively else Conf['Recursive Hiding'] @el.firstChild.checked = if 'hideRecursively' of data then data.hideRecursively else Conf['Recursive Hiding']
true true
$.event 'AddMenuEntry', Menu.menu.addEntry
type: 'post'
el: $.el 'div', el: $.el 'div',
textContent: 'Unhide post' textContent: 'Unhide post'
className: 'show-post-link' className: 'show-post-link'
@ -132,8 +130,7 @@ PostHiding =
subEntries: [apply, thisPost, replies] subEntries: [apply, thisPost, replies]
return if g.VIEW isnt 'index' return if g.VIEW isnt 'index'
$.event 'AddMenuEntry', Menu.menu.addEntry
type: 'post'
el: $.el 'a', href: 'javascript:;' el: $.el 'a', href: 'javascript:;'
order: 20 order: 20
open: (post) -> open: (post) ->

View File

@ -229,12 +229,6 @@ Config =
true true
'Add post and image deletion links to the menu.' 'Add post and image deletion links to the menu.'
] ]
<% if (type === 'crx') { %>
'Download Link': [
true
'Add a download with original filename link to the menu. Chrome-only currently.'
]
<% } %>
'Archive Link': [ 'Archive Link': [
true true
'Add an archive link to the menu.' 'Add an archive link to the menu.'
@ -307,10 +301,6 @@ Config =
true true
'Open new threads or replies to a thread from the index in a new tab.' 'Open new threads or replies to a thread from the index in a new tab.'
] ]
'Remember Subject': [
false
'Remember the subject field, instead of resetting after posting.'
]
<% if (type === 'userscript') { %> <% if (type === 'userscript') { %>
'Remember QR Size': [ 'Remember QR Size': [
false false

View File

@ -1,8 +1,8 @@
Header = Header =
init: -> init: ->
@menu = new UI.Menu 'header' @menu = new UI.Menu()
menuButton = $.el 'span', menuButton = $.el 'a',
className: 'menu-button' className: 'menu-button'
innerHTML: '<i></i>' innerHTML: '<i></i>'
@ -62,10 +62,8 @@ Header =
@addShortcut menuButton @addShortcut menuButton
$.event 'AddMenuEntry', @menu.addEntry
type: 'header' el: $.el 'span', textContent: 'Header'
el: $.el 'span',
textContent: 'Header'
order: 107 order: 107
subEntries: [ subEntries: [
el: barFixedToggler el: barFixedToggler
@ -436,7 +434,7 @@ Header =
shortcut = $.el 'span', shortcut = $.el 'span',
className: 'shortcut brackets-wrap' className: 'shortcut brackets-wrap'
$.add shortcut, el $.add shortcut, el
$.prepend Header.shortcuts, shortcut $.add Header.shortcuts, shortcut
rmShortcut: (el) -> rmShortcut: (el) ->
$.rm el.parentElement $.rm el.parentElement
@ -445,9 +443,8 @@ Header =
Header.menu.toggle e, @, g Header.menu.toggle e, @, g
createNotification: (e) -> createNotification: (e) ->
{type, content, lifetime, cb} = e.detail {type, content, lifetime} = e.detail
notice = new Notice type, content, lifetime notice = new Notice type, content, lifetime
cb notice if cb
areNotificationsEnabled: false areNotificationsEnabled: false
enableDesktopNotifications: -> enableDesktopNotifications: ->

View File

@ -73,8 +73,7 @@ Index =
when 'Show Replies' when 'Show Replies'
$.on input, 'change', @cb.replies $.on input, 'change', @cb.replies
$.event 'AddMenuEntry', Header.menu.addEntry
type: 'header'
el: $.el 'span', el: $.el 'span',
textContent: 'Index Navigation' textContent: 'Index Navigation'
order: 98 order: 98
@ -171,8 +170,7 @@ Index =
init: -> init: ->
return if g.VIEW isnt 'index' or !Conf['Menu'] or g.BOARD.ID is 'f' return if g.VIEW isnt 'index' or !Conf['Menu'] or g.BOARD.ID is 'f'
$.event 'AddMenuEntry', Menu.menu.addEntry
type: 'post'
el: $.el 'a', href: 'javascript:;' el: $.el 'a', href: 'javascript:;'
order: 19 order: 19
open: ({thread}) -> open: ({thread}) ->
@ -372,7 +370,7 @@ Index =
onSameIndex = g.VIEW is 'index' and a.pathname.split('/')[1] is g.BOARD.ID onSameIndex = g.VIEW is 'index' and a.pathname.split('/')[1] is g.BOARD.ID
needChange = Index.cb.indexNav a, onSameIndex needChange = Index.cb.indexNav a, onSameIndex
# Do nav if this isn't a simple click, or different board. # Do nav if this isn't a simple click, or different board.
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or !onSameIndex return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or !onSameIndex or g.BOARD.ID is 'f'
e.preventDefault() e.preventDefault()
Index.update() unless needChange Index.update() unless needChange
@ -781,7 +779,6 @@ Index =
$.rmAll Index.root unless infinite $.rmAll Index.root unless infinite
$.add Index.root, nodes $.add Index.root, nodes
$.event 'IndexBuild', nodes
isSearching: false isSearching: false

View File

@ -142,7 +142,6 @@ Main =
init 'Navigate', Navigate init 'Navigate', Navigate
init 'Flash Features', Flash init 'Flash Features', Flash
$.on d, 'AddCallback', Main.addCallback
$.ready Main.initReady $.ready Main.initReady
initStyle: -> initStyle: ->
@ -260,20 +259,6 @@ Main =
Settings.open() Settings.open()
$.set 'previousversion', g.VERSION $.set 'previousversion', g.VERSION
addCallback: (e) ->
obj = e.detail
unless typeof obj.callback.name is 'string'
throw new Error "Invalid callback name: #{obj.callback.name}"
switch obj.type
when 'Post'
Klass = Post
when 'Thread'
Klass = Thread
else
return
obj.callback.isAddon = true
Klass.callbacks.push obj.callback
handleErrors: (errors) -> handleErrors: (errors) ->
unless errors instanceof Array unless errors instanceof Array
error = errors error = errors

View File

@ -16,8 +16,7 @@ Settings =
Settings.addSection 'Advanced', Settings.advanced Settings.addSection 'Advanced', Settings.advanced
Settings.addSection 'Keybinds', Settings.keybinds Settings.addSection 'Keybinds', Settings.keybinds
$.on d, 'AddSettingsSection', Settings.addSection $.on d, 'OpenSettings', (e) -> Settings.open e.detail
$.on d, 'OpenSettings', (e) -> Settings.open e.detail
settings = JSON.parse(localStorage.getItem '4chan-settings') or {} settings = JSON.parse(localStorage.getItem '4chan-settings') or {}
return if settings.disableAll return if settings.disableAll
@ -72,8 +71,6 @@ Settings =
sections: [] sections: []
addSection: (title, open) -> addSection: (title, open) ->
if typeof title isnt 'string'
{title, open} = title.detail
hyphenatedTitle = title.toLowerCase().replace /\s+/g, '-' hyphenatedTitle = title.toLowerCase().replace /\s+/g, '-'
Settings.sections.push {title, hyphenatedTitle, open} Settings.sections.push {title, hyphenatedTitle, open}
openSection: -> openSection: ->

View File

@ -21,10 +21,7 @@ UI = do ->
currentMenu = null currentMenu = null
lastToggledButton = null lastToggledButton = null
constructor: (@type) -> constructor: ->
# Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API
$.on d, 'AddMenuEntry', @addEntry
$.on d, 'rmMenuEntry', @rmEntry
@entries = [] @entries = []
makeMenu: -> makeMenu: ->
@ -190,9 +187,7 @@ UI = do ->
style.left = left style.left = left
style.right = right style.right = right
addEntry: (e) => addEntry: (entry) ->
entry = e.detail
return if entry.type isnt @type
@parseEntry entry @parseEntry entry
@entries.push entry @entries.push entry

View File

@ -179,6 +179,10 @@ $.off = (el, events, handler) ->
return return
$.event = (event, detail, root=d) -> $.event = (event, detail, root=d) ->
<% if (type === 'userscript') { %>
if detail? and typeof cloneInto is 'function'
detail = cloneInto detail, d.defaultView
<% } %>
root.dispatchEvent new CustomEvent event, {bubbles: true, detail} root.dispatchEvent new CustomEvent event, {bubbles: true, detail}
$.open = $.open =

View File

@ -11,8 +11,7 @@ FappeTyme =
FappeTyme[lc] = input = el.firstElementChild FappeTyme[lc] = input = el.firstElementChild
$.on input, 'change', FappeTyme.cb.toggle.bind input $.on input, 'change', FappeTyme.cb.toggle.bind input
$.event 'AddMenuEntry', Header.menu.addEntry
type: 'header'
el: el el: el
order: 97 order: 97

View File

@ -60,7 +60,7 @@ Gallery =
} }
menuButton = $ '.menu-button', dialog menuButton = $ '.menu-button', dialog
nodes.menu = new UI.Menu 'gallery' nodes.menu = new UI.Menu()
{cb} = Gallery {cb} = Gallery
$.on nodes.frame, 'click', cb.blank $.on nodes.frame, 'click', cb.blank
@ -76,8 +76,7 @@ Gallery =
for name of Config.gallery for name of Config.gallery
{el} = createSubEntry name {el} = createSubEntry name
$.event 'AddMenuEntry', nodes.menu.addEntry
type: 'gallery'
el: el el: el
order: 0 order: 0
@ -251,8 +250,7 @@ Gallery =
for name of Config.gallery for name of Config.gallery
subEntries.push createSubEntry name subEntries.push createSubEntry name
$.event 'AddMenuEntry', Header.menu.addEntry
type: 'header'
el: el el: el
order: 105 order: 105
subEntries: subEntries subEntries: subEntries

View File

@ -266,8 +266,7 @@ ImageExpand =
for name, conf of Config.imageExpansion for name, conf of Config.imageExpansion
subEntries.push createSubEntry name, conf[1] subEntries.push createSubEntry name, conf[1]
$.event 'AddMenuEntry', Header.menu.addEntry
type: 'header'
el: el el: el
order: 105 order: 105
subEntries: subEntries subEntries: subEntries

View File

@ -16,8 +16,7 @@ ImageLoader =
@el = prefetch.firstElementChild @el = prefetch.firstElementChild
$.on @el, 'change', @toggle $.on @el, 'change', @toggle
$.event 'AddMenuEntry', Header.menu.addEntry
type: 'header'
el: prefetch el: prefetch
order: 104 order: 104

View File

@ -6,7 +6,6 @@ ArchiveLink =
textContent: 'Archive' textContent: 'Archive'
entry = entry =
type: 'post'
el: div el: div
order: 90 order: 90
open: ({ID, thread, board}) -> open: ({ID, thread, board}) ->
@ -25,7 +24,7 @@ ArchiveLink =
# Add a sub entry for each type. # Add a sub entry for each type.
entry.subEntries.push @createSubEntry type[0], type[1] entry.subEntries.push @createSubEntry type[0], type[1]
$.event 'AddMenuEntry', entry Menu.menu.addEntry entry
createSubEntry: (text, type) -> createSubEntry: (text, type) ->
el = $.el 'a', el = $.el 'a',

View File

@ -26,8 +26,7 @@ DeleteLink =
$.on fileEl, 'click', DeleteLink.delete $.on fileEl, 'click', DeleteLink.delete
true true
$.event 'AddMenuEntry', Menu.menu.addEntry
type: 'post'
el: div el: div
order: 40 order: 40
open: (post) -> open: (post) ->

View File

@ -5,8 +5,7 @@ DownloadLink =
a = $.el 'a', a = $.el 'a',
className: 'download-link' className: 'download-link'
textContent: 'Download file' textContent: 'Download file'
$.event 'AddMenuEntry', Menu.menu.addEntry
type: 'post'
el: a el: a
order: 100 order: 100
open: ({file}) -> open: ({file}) ->

View File

@ -2,8 +2,7 @@ Labels =
init: -> init: ->
return if !Conf['Menu'] return if !Conf['Menu']
$.event 'AddMenuEntry', Menu.menu.addEntry
type: 'post'
el: $.el 'div', textContent: 'Labels' el: $.el 'div', textContent: 'Labels'
order: 60 order: 60
open: (post, addSubEntry) -> open: (post, addSubEntry) ->

View File

@ -7,7 +7,7 @@ Menu =
innerHTML: '<i class="fa fa-bars"></i>' innerHTML: '<i class="fa fa-bars"></i>'
href: 'javascript:;' href: 'javascript:;'
@menu = new UI.Menu 'post' @menu = new UI.Menu()
Post.callbacks.push Post.callbacks.push
name: 'Menu' name: 'Menu'
cb: @node cb: @node

View File

@ -7,8 +7,7 @@ ReportLink =
href: 'javascript:;' href: 'javascript:;'
textContent: 'Report this post' textContent: 'Report this post'
$.on a, 'click', ReportLink.report $.on a, 'click', ReportLink.report
$.event 'AddMenuEntry', Menu.menu.addEntry
type: 'post'
el: a el: a
order: 10 order: 10
open: (post) -> open: (post) ->

View File

@ -11,14 +11,13 @@ PSAHiding =
return return
entry = entry =
type: 'header'
el: $.el 'a', el: $.el 'a',
textContent: 'Show announcement' textContent: 'Show announcement'
className: 'show-announcement' className: 'show-announcement'
href: 'javascript:;' href: 'javascript:;'
order: 50 order: 50
open: -> psa.hidden open: -> psa.hidden
$.event 'AddMenuEntry', entry Header.menu.addEntry entry
$.on entry.el, 'click', PSAHiding.toggle $.on entry.el, 'click', PSAHiding.toggle
PSAHiding.btn = btn = $.el 'span', PSAHiding.btn = btn = $.el 'span',

View File

@ -10,8 +10,7 @@ CatalogLinks =
$.on input, 'change', @toggle $.on input, 'change', @toggle
$.sync 'Header catalog links', CatalogLinks.set $.sync 'Header catalog links', CatalogLinks.set
$.event 'AddMenuEntry', Header.menu.addEntry
type: 'header'
el: el el: el
order: 95 order: 95

View File

@ -18,7 +18,7 @@ Fourchan =
window.addEventListener('jsmath', function(e) { window.addEventListener('jsmath', function(e) {
if (jsMath.loaded) { if (jsMath.loaded) {
// process one post // process one post
jsMath.ProcessBeforeShowing(e.detail); jsMath.ProcessBeforeShowing(document.getElementById(e.detail));
} else { } else {
// load jsMath and process whole document // load jsMath and process whole document
jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]); jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);
@ -39,4 +39,4 @@ Fourchan =
return return
math: -> math: ->
return if @isClone or !$ '.math', @nodes.comment return if @isClone or !$ '.math', @nodes.comment
$.event 'jsmath', @nodes.post, window $.event 'jsmath', @nodes.post.id, window

View File

@ -144,19 +144,19 @@ Keybinds =
Index.setIndexMode 'catalog' Index.setIndexMode 'catalog'
# Thread Navigation # Thread Navigation
when Conf['Next thread'] when Conf['Next thread']
return if g.VIEW isnt 'index' return if g.VIEW isnt 'index' or Conf['Index Mode'] is 'catalog'
Nav.scroll +1 Nav.scroll +1
when Conf['Previous thread'] when Conf['Previous thread']
return if g.VIEW isnt 'index' return if g.VIEW isnt 'index' or Conf['Index Mode'] is 'catalog'
Nav.scroll -1 Nav.scroll -1
when Conf['Expand thread'] when Conf['Expand thread']
return if g.VIEW isnt 'index' return if g.VIEW isnt 'index' or Conf['Index Mode'] is 'catalog'
ExpandThread.toggle thread ExpandThread.toggle thread
when Conf['Open thread'] when Conf['Open thread']
return if g.VIEW isnt 'index' return if g.VIEW isnt 'index' or Conf['Index Mode'] is 'catalog'
Keybinds.open thread Keybinds.open thread
when Conf['Open thread tab'] when Conf['Open thread tab']
return if g.VIEW isnt 'index' return if g.VIEW isnt 'index' or Conf['Index Mode'] is 'catalog'
Keybinds.open thread, true Keybinds.open thread, true
# Reply Navigation # Reply Navigation
when Conf['Next reply'] when Conf['Next reply']

View File

@ -9,5 +9,5 @@ Report =
$.on $('form'), 'submit', (e) -> $.on $('form'), 'submit', (e) ->
e.preventDefault() e.preventDefault()
response = field.value.trim() response = field.value.trim()
field.value = "#{response} #{response}" unless /\s/.test response field.value = "#{response} #{response}" unless /\s|^\d+$/.test response
@submit() @submit()

View File

@ -47,8 +47,7 @@ ThreadUpdater =
subEntries.push el: @settings subEntries.push el: @settings
$.event 'AddMenuEntry', @entry = Header.menu.addEntry @entry =
type: 'header'
el: $.el 'span', el: $.el 'span',
textContent: 'Updater' textContent: 'Updater'
order: 110 order: 110
@ -169,7 +168,7 @@ ThreadUpdater =
ThreadUpdater.thread.kill() ThreadUpdater.thread.kill()
$.event 'ThreadUpdate', $.event 'ThreadUpdate',
404: true 404: true
thread: ThreadUpdater.thread threadID: ThreadUpdater.thread.fullID
else else
ThreadUpdater.outdateCount++ ThreadUpdater.outdateCount++
ThreadUpdater.setInterval() ThreadUpdater.setInterval()
@ -292,9 +291,6 @@ ThreadUpdater =
node = Build.postFromObject postObject, ThreadUpdater.thread.board.ID node = Build.postFromObject postObject, ThreadUpdater.thread.board.ID
posts.push new Post node, ThreadUpdater.thread, ThreadUpdater.thread.board posts.push new Post node, ThreadUpdater.thread, ThreadUpdater.thread.board
deletedPosts = []
deletedFiles = []
# Check for deleted posts/files. # Check for deleted posts/files.
ThreadUpdater.thread.posts.forEach (post) -> ThreadUpdater.thread.posts.forEach (post) ->
# XXX tmp fix for 4chan's racing condition # XXX tmp fix for 4chan's racing condition
@ -304,12 +300,10 @@ ThreadUpdater =
unless ID in index unless ID in index
post.kill() post.kill()
deletedPosts.push post
else if post.isDead else if post.isDead
post.resurrect() post.resurrect()
else if post.file and not (post.file.isDead or ID in files) else if post.file and !post.file.isDead and ID not in files
post.kill true post.kill true
deletedFiles.push post
# Fetching your own posts after posting # Fetching your own posts after posting
if ThreadUpdater.postID and ThreadUpdater.postID is ID if ThreadUpdater.postID and ThreadUpdater.postID is ID
@ -318,10 +312,8 @@ ThreadUpdater =
sendEvent = -> sendEvent = ->
$.event 'ThreadUpdate', $.event 'ThreadUpdate',
404: false 404: false
thread: ThreadUpdater.thread threadID: ThreadUpdater.thread.fullID
newPosts: posts newPosts: posts.map (post) -> post.fullID
deletedPosts: deletedPosts
deletedFiles: deletedFiles
postCount: OP.replies + 1 postCount: OP.replies + 1
fileCount: OP.images + (!!ThreadUpdater.thread.OP.file and !ThreadUpdater.thread.OP.file.isDead) fileCount: OP.images + (!!ThreadUpdater.thread.OP.file and !ThreadUpdater.thread.OP.file.isDead)

View File

@ -88,12 +88,12 @@ ThreadWatcher =
[boardID, threadID] = @parentNode.dataset.fullID.split '.' [boardID, threadID] = @parentNode.dataset.fullID.split '.'
ThreadWatcher.rm boardID, +threadID ThreadWatcher.rm boardID, +threadID
post: (e) -> post: (e) ->
{board, postID, threadID} = e.detail {boardID, threadID, postID} = e.detail
if postID is threadID if postID is threadID
if Conf['Auto Watch'] if Conf['Auto Watch']
$.set 'AutoWatch', threadID $.set 'AutoWatch', threadID
else if Conf['Auto Watch Reply'] else if Conf['Auto Watch Reply']
ThreadWatcher.add board.threads[threadID] ThreadWatcher.add g.threads[boardID + '.' + threadID]
onIndexRefresh: -> onIndexRefresh: ->
boardID = g.BOARD.ID boardID = g.BOARD.ID
for threadID, data of ThreadWatcher.db.data.boards[boardID] when not data.isDead and threadID not of g.BOARD.threads for threadID, data of ThreadWatcher.db.data.boards[boardID] when not data.isDead and threadID not of g.BOARD.threads
@ -104,7 +104,7 @@ ThreadWatcher =
ThreadWatcher.db.set {boardID, threadID, val: data} ThreadWatcher.db.set {boardID, threadID, val: data}
ThreadWatcher.refresh() ThreadWatcher.refresh()
onThreadRefresh: (e) -> onThreadRefresh: (e) ->
{thread} = e.detail thread = g.threads[e.detail.threadID]
return unless e.detail[404] and ThreadWatcher.db.get {boardID: thread.board.ID, threadID: thread.ID} return unless e.detail[404] and ThreadWatcher.db.get {boardID: thread.board.ID, threadID: thread.ID}
# Update 404 status. # Update 404 status.
ThreadWatcher.add thread ThreadWatcher.add thread
@ -227,18 +227,17 @@ ThreadWatcher =
refreshers: [] refreshers: []
init: -> init: ->
return if !Conf['Thread Watcher'] return if !Conf['Thread Watcher']
menu = new UI.Menu 'thread watcher' menu = new UI.Menu()
$.on $('.menu-button', ThreadWatcher.dialog), 'click', (e) -> $.on $('.menu-button', ThreadWatcher.dialog), 'click', (e) ->
menu.toggle e, @, ThreadWatcher menu.toggle e, @, ThreadWatcher
@addHeaderMenuEntry() @addHeaderMenuEntry()
@addMenuEntries() @addMenuEntries menu
addHeaderMenuEntry: -> addHeaderMenuEntry: ->
return if g.VIEW isnt 'thread' return if g.VIEW isnt 'thread'
entryEl = $.el 'a', entryEl = $.el 'a',
href: 'javascript:;' href: 'javascript:;'
$.event 'AddMenuEntry', Header.menu.addEntry
type: 'header'
el: entryEl el: entryEl
order: 60 order: 60
$.on entryEl, 'click', -> ThreadWatcher.toggle g.threads["#{g.BOARD}.#{g.THREADID}"] $.on entryEl, 'click', -> ThreadWatcher.toggle g.threads["#{g.BOARD}.#{g.THREADID}"]
@ -251,14 +250,13 @@ ThreadWatcher =
$.rmClass entryEl, rmClass $.rmClass entryEl, rmClass
entryEl.textContent = text entryEl.textContent = text
addMenuEntries: -> addMenuEntries: (menu) ->
entries = [] entries = []
# `Open all` entry # `Open all` entry
entries.push entries.push
cb: ThreadWatcher.cb.openAll cb: ThreadWatcher.cb.openAll
entry: entry:
type: 'thread watcher'
el: $.el 'a', el: $.el 'a',
textContent: 'Open all threads' textContent: 'Open all threads'
refresh: -> (if ThreadWatcher.list.firstElementChild then $.rmClass else $.addClass) @el, 'disabled' refresh: -> (if ThreadWatcher.list.firstElementChild then $.rmClass else $.addClass) @el, 'disabled'
@ -267,7 +265,6 @@ ThreadWatcher =
entries.push entries.push
cb: ThreadWatcher.cb.checkThreads cb: ThreadWatcher.cb.checkThreads
entry: entry:
type: 'thread watcher'
el: $.el 'a', el: $.el 'a',
textContent: 'Check 404\'d threads' textContent: 'Check 404\'d threads'
refresh: -> (if $('div:not(.dead-thread)', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled' refresh: -> (if $('div:not(.dead-thread)', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled'
@ -276,7 +273,6 @@ ThreadWatcher =
entries.push entries.push
cb: ThreadWatcher.cb.pruneDeads cb: ThreadWatcher.cb.pruneDeads
entry: entry:
type: 'thread watcher'
el: $.el 'a', el: $.el 'a',
textContent: 'Prune 404\'d threads' textContent: 'Prune 404\'d threads'
refresh: -> (if $('.dead-thread', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled' refresh: -> (if $('.dead-thread', ThreadWatcher.list) then $.rmClass else $.addClass) @el, 'disabled'
@ -287,7 +283,6 @@ ThreadWatcher =
subEntries.push @createSubEntry name, conf[1] subEntries.push @createSubEntry name, conf[1]
entries.push entries.push
entry: entry:
type: 'thread watcher'
el: $.el 'span', el: $.el 'span',
textContent: 'Settings' textContent: 'Settings'
subEntries: subEntries subEntries: subEntries
@ -296,7 +291,7 @@ ThreadWatcher =
entry.el.href = 'javascript:;' if entry.el.nodeName is 'A' entry.el.href = 'javascript:;' if entry.el.nodeName is 'A'
$.on entry.el, 'click', cb if cb $.on entry.el, 'click', cb if cb
@refreshers.push refresh.bind entry if refresh @refreshers.push refresh.bind entry if refresh
$.event 'AddMenuEntry', entry menu.addEntry entry
return return
createSubEntry: (name, desc) -> createSubEntry: (name, desc) ->
entry = entry =

View File

@ -122,11 +122,11 @@ Unread =
onUpdate: (e) -> onUpdate: (e) ->
if e.detail[404] if e.detail[404]
Unread.update() Unread.update()
else if !Conf['Quote Threading'] else if Conf['Quote Threading']
Unread.addPosts e.detail.newPosts
else
Unread.read() Unread.read()
Unread.update() Unread.update()
else
Unread.addPosts e.detail.newPosts.map (fullID) -> g.posts[fullID]
readSinglePost: (post) -> readSinglePost: (post) ->
{ID} = post {ID} = post

View File

@ -93,7 +93,7 @@ QR.captcha =
response = response.trim() response = response.trim()
# one-word-captcha: # one-word-captcha:
# If there's only one word, duplicate it. # If there's only one word, duplicate it.
response = "#{response} #{response}" unless /\s/.test response response = "#{response} #{response}" unless /\s|^\d+$/.test response
{challenge, response} {challenge, response}
save: -> save: ->

View File

@ -53,11 +53,6 @@ QR =
$.before $.id('togglePostFormLink'), link $.before $.id('togglePostFormLink'), link
$.on d, 'QRGetSelectedPost', ({detail: cb}) ->
cb QR.selected
$.on d, 'QRAddPreSubmitHook', ({detail: cb}) ->
QR.preSubmitHooks.push cb
<% if (type === 'crx') { %> <% if (type === 'crx') { %>
$.on d, 'paste', QR.paste $.on d, 'paste', QR.paste
<% } %> <% } %>
@ -580,8 +575,6 @@ QR =
nodes.flag = flag nodes.flag = flag
$.add nodes.form, flag $.add nodes.form, flag
preSubmitHooks: []
submit: (e) -> submit: (e) ->
e?.preventDefault() e?.preventDefault()
@ -614,9 +607,6 @@ QR =
err = 'No file selected.' err = 'No file selected.'
else if post.file and thread.fileLimit else if post.file and thread.fileLimit
err = 'Max limit of image replies has been reached.' err = 'Max limit of image replies has been reached.'
else for hook in QR.preSubmitHooks
if err = hook post, thread
break
if QR.captcha.isEnabled and !err if QR.captcha.isEnabled and !err
{challenge, response} = QR.captcha.getOne() {challenge, response} = QR.captcha.getOne()
@ -775,11 +765,11 @@ QR =
# Post/upload confirmed as successful. # Post/upload confirmed as successful.
$.event 'QRPostSuccessful', { $.event 'QRPostSuccessful', {
board: g.BOARD boardID: g.BOARD.ID
threadID threadID
postID postID
} }
$.event 'QRPostSuccessful_', {threadID, postID} $.event 'QRPostSuccessful_', {boardID: g.BOARD.ID, threadID, postID}
# Enable auto-posting if we have stuff left to post, disable it otherwise. # Enable auto-posting if we have stuff left to post, disable it otherwise.
postsCount = QR.posts.length - 1 postsCount = QR.posts.length - 1

View File

@ -112,7 +112,6 @@ QR.post = class
rectList = @nodes.el.parentNode.getBoundingClientRect() rectList = @nodes.el.parentNode.getBoundingClientRect()
@nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width/2 - rectList.left - rectList.width/2 @nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width/2 - rectList.left - rectList.width/2
@load() @load()
$.event 'QRPostSelection', @
load: -> load: ->
# Load this post's values. # Load this post's values.

View File

@ -13,8 +13,7 @@ QuoteThreading =
input = $ 'input', @controls input = $ 'input', @controls
$.on input, 'change', @toggle $.on input, 'change', @toggle
$.event 'AddMenuEntry', @entry = Header.menu.addEntry @entry =
type: 'header'
el: @controls el: @controls
order: 98 order: 98