Infinite Scrolling
Conflicts: LICENSE builds/appchan-x.user.js builds/crx/script.js src/General/Main.coffee
This commit is contained in:
parent
9fb8ab50e1
commit
4f50fd300d
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 4chan X - Version 1.2.41 - 2013-10-16
|
* 4chan X - Version 1.2.41 - 2013-10-19
|
||||||
*
|
*
|
||||||
* Licensed under the MIT license.
|
* Licensed under the MIT license.
|
||||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 4chan X - Version 1.2.41 - 2013-10-16
|
* 4chan X - Version 1.2.41 - 2013-10-19
|
||||||
*
|
*
|
||||||
* Licensed under the MIT license.
|
* Licensed under the MIT license.
|
||||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||||
@ -104,7 +104,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
|
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, InfiniScroll, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
|
||||||
__slice = [].slice,
|
__slice = [].slice,
|
||||||
__hasProp = {}.hasOwnProperty,
|
__hasProp = {}.hasOwnProperty,
|
||||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||||
@ -134,7 +134,8 @@
|
|||||||
'Emoji': [false, 'Adds icons next to names for different emails'],
|
'Emoji': [false, 'Adds icons next to names for different emails'],
|
||||||
'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'],
|
'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'],
|
||||||
'Remove Spoilers': [false, 'Remove all spoilers in text.'],
|
'Remove Spoilers': [false, 'Remove all spoilers in text.'],
|
||||||
'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.']
|
'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'],
|
||||||
|
'Infinite Scrolling': [false, 'Add new posts to the board index upon reaching the bottom of the board.']
|
||||||
},
|
},
|
||||||
'Linkification': {
|
'Linkification': {
|
||||||
'Linkify': [true, 'Convert text into links where applicable.'],
|
'Linkify': [true, 'Convert text into links where applicable.'],
|
||||||
@ -10066,6 +10067,184 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
InfiniScroll = {
|
||||||
|
init: function() {
|
||||||
|
if (!(Conf['Infinite Scrolling'] && g.VIEW === 'index' && g.BOARD !== 'f')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.threads = g.threads;
|
||||||
|
return $.on(d, '4chanXInitFinished', this.ready);
|
||||||
|
},
|
||||||
|
ready: function() {
|
||||||
|
$.off(d, '4chanXInitFinished', InfiniScroll.ready);
|
||||||
|
$.on(d, 'scroll', InfiniScroll.scroll);
|
||||||
|
return InfiniScroll.scroll();
|
||||||
|
},
|
||||||
|
scroll: $.debounce(100, function() {
|
||||||
|
var url;
|
||||||
|
|
||||||
|
if (InfiniScroll.isFetching || (doc.scrollTop <= doc.scrollHeight - (300 + window.innerHeight))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (InfiniScroll.isDead) {
|
||||||
|
return InfiniScroll.notice();
|
||||||
|
}
|
||||||
|
if (InfiniScroll.cache && InfiniScroll.cache.time > Date.now() - $.MINUTE) {
|
||||||
|
return InfiniScroll.parse(InfiniScroll.cache);
|
||||||
|
}
|
||||||
|
new Notice('info', "Fetching next page.", 2);
|
||||||
|
InfiniScroll.isFetching = true;
|
||||||
|
url = "//api.4chan.org/" + g.BOARD + "/catalog.json";
|
||||||
|
return $.ajax(url, {
|
||||||
|
onloadend: InfiniScroll.cb.load
|
||||||
|
}, {
|
||||||
|
whenModified: true
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
parse: function(response) {
|
||||||
|
var botPostForm, el, nodes, omitted_images, omitted_posts, op, post, postlink, posts, replylink, thread, threadID, threadNodes, threads, _i, _j, _len, _len1, _ref;
|
||||||
|
|
||||||
|
threads = InfiniScroll.parsePages(response);
|
||||||
|
threadNodes = [];
|
||||||
|
nodes = [];
|
||||||
|
if (!threads.length) {
|
||||||
|
InfiniScroll.notice();
|
||||||
|
return InfiniScroll.isDead = true;
|
||||||
|
}
|
||||||
|
for (_i = 0, _len = threads.length; _i < _len; _i++) {
|
||||||
|
thread = threads[_i];
|
||||||
|
posts = [];
|
||||||
|
omitted_posts = thread.omitted_posts, omitted_images = thread.omitted_images;
|
||||||
|
threadID = thread.no;
|
||||||
|
el = $.el('div', {
|
||||||
|
className: 'thread',
|
||||||
|
id: "t" + threadID
|
||||||
|
});
|
||||||
|
op = Build.postFromObject(thread, g.BOARD);
|
||||||
|
posts.push(op);
|
||||||
|
replylink = $.el('a', {
|
||||||
|
href: "res/" + threadID,
|
||||||
|
className: 'replylink',
|
||||||
|
textContent: 'Reply'
|
||||||
|
});
|
||||||
|
postlink = $.el('div', {
|
||||||
|
className: "postLink mobile",
|
||||||
|
innerHTML: "<a href=\"res/" + threadID + "\" class=\"button\">View Thread</a>"
|
||||||
|
});
|
||||||
|
if (omitted_posts) {
|
||||||
|
posts.push($.el('span', {
|
||||||
|
className: 'summary desktop',
|
||||||
|
innerHTML: "" + omitted_posts + " posts " + (omitted_images ? "and " + omitted_images + " image replies" : void 0) + " omitted. Click <a class=\"replylink\" href=\"res/" + threadID + "\">here</a> to view."
|
||||||
|
}));
|
||||||
|
$.prepend(postlink, $.el('span', {
|
||||||
|
className: 'info',
|
||||||
|
innerHTML: "<strong>" + omitted_posts + " posts omitted</strong>" + (omitted_images ? "<br><em>(" + omitted_images + " have images)</em>" : "")
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
$.add($('.postInfo', op), [$.tn('\u00A0\u00A0\u00A0['), replylink, $.tn(']\u00A0')]);
|
||||||
|
$.add(op, postlink);
|
||||||
|
if (thread.last_replies) {
|
||||||
|
_ref = thread.last_replies;
|
||||||
|
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
||||||
|
post = _ref[_j];
|
||||||
|
posts.push(Build.postFromObject(post, g.BOARD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.add(el, posts);
|
||||||
|
threadNodes.push(el);
|
||||||
|
nodes.push(el);
|
||||||
|
nodes.push($.el('hr'));
|
||||||
|
}
|
||||||
|
InfiniScroll.features(threadNodes);
|
||||||
|
if (botPostForm = $('.board > .mobile.center')) {
|
||||||
|
return $.before(botPostForm, nodes);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parsePages: function(response) {
|
||||||
|
var newThreads, number, page, pages, thread, threads, _i, _len;
|
||||||
|
|
||||||
|
pages = JSON.parse(response);
|
||||||
|
newThreads = [];
|
||||||
|
for (number in pages) {
|
||||||
|
page = pages[number];
|
||||||
|
if (!(pages.hasOwnProperty(number))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
threads = page.threads;
|
||||||
|
for (_i = 0, _len = threads.length; _i < _len; _i++) {
|
||||||
|
thread = threads[_i];
|
||||||
|
if (g.threads["" + g.BOARD + "." + thread.no]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newThreads.push(thread);
|
||||||
|
if (newThreads.length === 15) {
|
||||||
|
return newThreads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newThreads;
|
||||||
|
},
|
||||||
|
features: function(threadNodes) {
|
||||||
|
var err, errors, post, posts, thread, threadRoot, threads, _i, _j, _len, _len1, _ref;
|
||||||
|
|
||||||
|
posts = [];
|
||||||
|
threads = [];
|
||||||
|
for (_i = 0, _len = threadNodes.length; _i < _len; _i++) {
|
||||||
|
threadRoot = threadNodes[_i];
|
||||||
|
thread = new Thread(+threadRoot.id.slice(1), g.BOARD);
|
||||||
|
threads.push(thread);
|
||||||
|
_ref = $$('.thread > .postContainer', threadRoot);
|
||||||
|
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
||||||
|
post = _ref[_j];
|
||||||
|
try {
|
||||||
|
posts.push(new Post(post, thread, g.BOARD));
|
||||||
|
} catch (_error) {
|
||||||
|
err = _error;
|
||||||
|
if (!errors) {
|
||||||
|
errors = [];
|
||||||
|
}
|
||||||
|
errors.push({
|
||||||
|
message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.",
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errors) {
|
||||||
|
Main.handleErrors(errors);
|
||||||
|
}
|
||||||
|
Main.callbackNodes(Thread, threads);
|
||||||
|
return Main.callbackNodes(Post, posts);
|
||||||
|
},
|
||||||
|
notice: (function() {
|
||||||
|
var notify, reset;
|
||||||
|
|
||||||
|
notify = false;
|
||||||
|
reset = function() {
|
||||||
|
return notify = false;
|
||||||
|
};
|
||||||
|
return function() {
|
||||||
|
if (notify) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notify = true;
|
||||||
|
new Notice('info', "Last page reached.", 2);
|
||||||
|
return setTimeout(reset, 3 * $.SECOND);
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
cb: {
|
||||||
|
load: function() {
|
||||||
|
InfiniScroll.isFetching = false;
|
||||||
|
if (this.status !== 200) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InfiniScroll.cache = new String(this.response);
|
||||||
|
InfiniScroll.cache.time = Date.now();
|
||||||
|
return InfiniScroll.parse(this.response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Keybinds = {
|
Keybinds = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var init;
|
var init;
|
||||||
@ -11573,7 +11752,8 @@
|
|||||||
'Index Navigation': Nav,
|
'Index Navigation': Nav,
|
||||||
'Keybinds': Keybinds,
|
'Keybinds': Keybinds,
|
||||||
'Show Dice Roll': Dice,
|
'Show Dice Roll': Dice,
|
||||||
'Banner': Banner
|
'Banner': Banner,
|
||||||
|
'Infinite Scrolling': InfiniScroll
|
||||||
});
|
});
|
||||||
$.on(d, 'AddCallback', Main.addCallback);
|
$.on(d, 'AddCallback', Main.addCallback);
|
||||||
return $.ready(Main.initReady);
|
return $.ready(Main.initReady);
|
||||||
|
|||||||
16265
builds/appchan-x.user.js
Normal file
16265
builds/appchan-x.user.js
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
// Generated by CoffeeScript
|
// Generated by CoffeeScript
|
||||||
/*
|
/*
|
||||||
* 4chan X - Version 1.2.41 - 2013-10-16
|
* 4chan X - Version 1.2.41 - 2013-10-19
|
||||||
*
|
*
|
||||||
* Licensed under the MIT license.
|
* Licensed under the MIT license.
|
||||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||||
@ -82,7 +82,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
|
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, InfiniScroll, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
|
||||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||||
__slice = [].slice,
|
__slice = [].slice,
|
||||||
__hasProp = {}.hasOwnProperty,
|
__hasProp = {}.hasOwnProperty,
|
||||||
@ -113,7 +113,8 @@
|
|||||||
'Emoji': [false, 'Adds icons next to names for different emails'],
|
'Emoji': [false, 'Adds icons next to names for different emails'],
|
||||||
'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'],
|
'Color User IDs': [false, 'Assign unique colors to user IDs on boards that use them'],
|
||||||
'Remove Spoilers': [false, 'Remove all spoilers in text.'],
|
'Remove Spoilers': [false, 'Remove all spoilers in text.'],
|
||||||
'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.']
|
'Reveal Spoilers': [false, 'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'],
|
||||||
|
'Infinite Scrolling': [false, 'Add new posts to the board index upon reaching the bottom of the board.']
|
||||||
},
|
},
|
||||||
'Linkification': {
|
'Linkification': {
|
||||||
'Linkify': [true, 'Convert text into links where applicable.'],
|
'Linkify': [true, 'Convert text into links where applicable.'],
|
||||||
@ -10059,6 +10060,184 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
InfiniScroll = {
|
||||||
|
init: function() {
|
||||||
|
if (!(Conf['Infinite Scrolling'] && g.VIEW === 'index' && g.BOARD !== 'f')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.threads = g.threads;
|
||||||
|
return $.on(d, '4chanXInitFinished', this.ready);
|
||||||
|
},
|
||||||
|
ready: function() {
|
||||||
|
$.off(d, '4chanXInitFinished', InfiniScroll.ready);
|
||||||
|
$.on(d, 'scroll', InfiniScroll.scroll);
|
||||||
|
return InfiniScroll.scroll();
|
||||||
|
},
|
||||||
|
scroll: $.debounce(100, function() {
|
||||||
|
var url;
|
||||||
|
|
||||||
|
if (InfiniScroll.isFetching || (doc.scrollTop <= doc.scrollHeight - (300 + window.innerHeight))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (InfiniScroll.isDead) {
|
||||||
|
return InfiniScroll.notice();
|
||||||
|
}
|
||||||
|
if (InfiniScroll.cache && InfiniScroll.cache.time > Date.now() - $.MINUTE) {
|
||||||
|
return InfiniScroll.parse(InfiniScroll.cache);
|
||||||
|
}
|
||||||
|
new Notice('info', "Fetching next page.", 2);
|
||||||
|
InfiniScroll.isFetching = true;
|
||||||
|
url = "//api.4chan.org/" + g.BOARD + "/catalog.json";
|
||||||
|
return $.ajax(url, {
|
||||||
|
onloadend: InfiniScroll.cb.load
|
||||||
|
}, {
|
||||||
|
whenModified: true
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
parse: function(response) {
|
||||||
|
var botPostForm, el, nodes, omitted_images, omitted_posts, op, post, postlink, posts, replylink, thread, threadID, threadNodes, threads, _i, _j, _len, _len1, _ref;
|
||||||
|
|
||||||
|
threads = InfiniScroll.parsePages(response);
|
||||||
|
threadNodes = [];
|
||||||
|
nodes = [];
|
||||||
|
if (!threads.length) {
|
||||||
|
InfiniScroll.notice();
|
||||||
|
return InfiniScroll.isDead = true;
|
||||||
|
}
|
||||||
|
for (_i = 0, _len = threads.length; _i < _len; _i++) {
|
||||||
|
thread = threads[_i];
|
||||||
|
posts = [];
|
||||||
|
omitted_posts = thread.omitted_posts, omitted_images = thread.omitted_images;
|
||||||
|
threadID = thread.no;
|
||||||
|
el = $.el('div', {
|
||||||
|
className: 'thread',
|
||||||
|
id: "t" + threadID
|
||||||
|
});
|
||||||
|
op = Build.postFromObject(thread, g.BOARD);
|
||||||
|
posts.push(op);
|
||||||
|
replylink = $.el('a', {
|
||||||
|
href: "res/" + threadID,
|
||||||
|
className: 'replylink',
|
||||||
|
textContent: 'Reply'
|
||||||
|
});
|
||||||
|
postlink = $.el('div', {
|
||||||
|
className: "postLink mobile",
|
||||||
|
innerHTML: "<a href=\"res/" + threadID + "\" class=\"button\">View Thread</a>"
|
||||||
|
});
|
||||||
|
if (omitted_posts) {
|
||||||
|
posts.push($.el('span', {
|
||||||
|
className: 'summary desktop',
|
||||||
|
innerHTML: "" + omitted_posts + " posts " + (omitted_images ? "and " + omitted_images + " image replies" : void 0) + " omitted. Click <a class=\"replylink\" href=\"res/" + threadID + "\">here</a> to view."
|
||||||
|
}));
|
||||||
|
$.prepend(postlink, $.el('span', {
|
||||||
|
className: 'info',
|
||||||
|
innerHTML: "<strong>" + omitted_posts + " posts omitted</strong>" + (omitted_images ? "<br><em>(" + omitted_images + " have images)</em>" : "")
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
$.add($('.postInfo', op), [$.tn('\u00A0\u00A0\u00A0['), replylink, $.tn(']\u00A0')]);
|
||||||
|
$.add(op, postlink);
|
||||||
|
if (thread.last_replies) {
|
||||||
|
_ref = thread.last_replies;
|
||||||
|
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
||||||
|
post = _ref[_j];
|
||||||
|
posts.push(Build.postFromObject(post, g.BOARD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.add(el, posts);
|
||||||
|
threadNodes.push(el);
|
||||||
|
nodes.push(el);
|
||||||
|
nodes.push($.el('hr'));
|
||||||
|
}
|
||||||
|
InfiniScroll.features(threadNodes);
|
||||||
|
if (botPostForm = $('.board > .mobile.center')) {
|
||||||
|
return $.before(botPostForm, nodes);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parsePages: function(response) {
|
||||||
|
var newThreads, number, page, pages, thread, threads, _i, _len;
|
||||||
|
|
||||||
|
pages = JSON.parse(response);
|
||||||
|
newThreads = [];
|
||||||
|
for (number in pages) {
|
||||||
|
page = pages[number];
|
||||||
|
if (!(pages.hasOwnProperty(number))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
threads = page.threads;
|
||||||
|
for (_i = 0, _len = threads.length; _i < _len; _i++) {
|
||||||
|
thread = threads[_i];
|
||||||
|
if (g.threads["" + g.BOARD + "." + thread.no]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newThreads.push(thread);
|
||||||
|
if (newThreads.length === 15) {
|
||||||
|
return newThreads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newThreads;
|
||||||
|
},
|
||||||
|
features: function(threadNodes) {
|
||||||
|
var err, errors, post, posts, thread, threadRoot, threads, _i, _j, _len, _len1, _ref;
|
||||||
|
|
||||||
|
posts = [];
|
||||||
|
threads = [];
|
||||||
|
for (_i = 0, _len = threadNodes.length; _i < _len; _i++) {
|
||||||
|
threadRoot = threadNodes[_i];
|
||||||
|
thread = new Thread(+threadRoot.id.slice(1), g.BOARD);
|
||||||
|
threads.push(thread);
|
||||||
|
_ref = $$('.thread > .postContainer', threadRoot);
|
||||||
|
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
||||||
|
post = _ref[_j];
|
||||||
|
try {
|
||||||
|
posts.push(new Post(post, thread, g.BOARD));
|
||||||
|
} catch (_error) {
|
||||||
|
err = _error;
|
||||||
|
if (!errors) {
|
||||||
|
errors = [];
|
||||||
|
}
|
||||||
|
errors.push({
|
||||||
|
message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.",
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errors) {
|
||||||
|
Main.handleErrors(errors);
|
||||||
|
}
|
||||||
|
Main.callbackNodes(Thread, threads);
|
||||||
|
return Main.callbackNodes(Post, posts);
|
||||||
|
},
|
||||||
|
notice: (function() {
|
||||||
|
var notify, reset;
|
||||||
|
|
||||||
|
notify = false;
|
||||||
|
reset = function() {
|
||||||
|
return notify = false;
|
||||||
|
};
|
||||||
|
return function() {
|
||||||
|
if (notify) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notify = true;
|
||||||
|
new Notice('info', "Last page reached.", 2);
|
||||||
|
return setTimeout(reset, 3 * $.SECOND);
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
cb: {
|
||||||
|
load: function() {
|
||||||
|
InfiniScroll.isFetching = false;
|
||||||
|
if (this.status !== 200) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InfiniScroll.cache = new String(this.response);
|
||||||
|
InfiniScroll.cache.time = Date.now();
|
||||||
|
return InfiniScroll.parse(this.response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Keybinds = {
|
Keybinds = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var init;
|
var init;
|
||||||
@ -11564,7 +11743,8 @@
|
|||||||
'Index Navigation': Nav,
|
'Index Navigation': Nav,
|
||||||
'Keybinds': Keybinds,
|
'Keybinds': Keybinds,
|
||||||
'Show Dice Roll': Dice,
|
'Show Dice Roll': Dice,
|
||||||
'Banner': Banner
|
'Banner': Banner,
|
||||||
|
'Infinite Scrolling': InfiniScroll
|
||||||
});
|
});
|
||||||
$.on(d, 'AddCallback', Main.addCallback);
|
$.on(d, 'AddCallback', Main.addCallback);
|
||||||
return $.ready(Main.initReady);
|
return $.ready(Main.initReady);
|
||||||
|
|||||||
@ -89,6 +89,10 @@ Config =
|
|||||||
false
|
false
|
||||||
'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'
|
'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'
|
||||||
]
|
]
|
||||||
|
'Infinite Scrolling': [
|
||||||
|
false
|
||||||
|
'Add new posts to the board index upon reaching the bottom of the board.'
|
||||||
|
]
|
||||||
|
|
||||||
'Linkification':
|
'Linkification':
|
||||||
'Linkify': [
|
'Linkify': [
|
||||||
|
|||||||
@ -134,6 +134,7 @@ Main =
|
|||||||
'Keybinds': Keybinds
|
'Keybinds': Keybinds
|
||||||
'Show Dice Roll': Dice
|
'Show Dice Roll': Dice
|
||||||
'Banner': Banner
|
'Banner': Banner
|
||||||
|
'Infinite Scrolling': InfiniScroll
|
||||||
|
|
||||||
# c.timeEnd 'All initializations'
|
# c.timeEnd 'All initializations'
|
||||||
|
|
||||||
@ -242,8 +243,7 @@ Main =
|
|||||||
try
|
try
|
||||||
callback.cb.call node
|
callback.cb.call node
|
||||||
catch err
|
catch err
|
||||||
unless errors
|
errors = [] unless errors
|
||||||
errors = []
|
|
||||||
errors.push
|
errors.push
|
||||||
message: "\"#{callback.name}\" crashed on #{klass.name} No.#{node} (/#{node.board}/)."
|
message: "\"#{callback.name}\" crashed on #{klass.name} No.#{node} (/#{node.board}/)."
|
||||||
error: err
|
error: err
|
||||||
@ -273,8 +273,7 @@ Main =
|
|||||||
try
|
try
|
||||||
callback.cb.call node
|
callback.cb.call node
|
||||||
catch err
|
catch err
|
||||||
unless errors
|
errors = [] unless errors
|
||||||
errors = []
|
|
||||||
errors.push
|
errors.push
|
||||||
message: "\"#{callback.name}\" crashed on #{klass.name} No.#{node} (/#{node.board}/)."
|
message: "\"#{callback.name}\" crashed on #{klass.name} No.#{node} (/#{node.board}/)."
|
||||||
error: err
|
error: err
|
||||||
|
|||||||
139
src/Miscellaneous/InfiniScroll.coffee
Normal file
139
src/Miscellaneous/InfiniScroll.coffee
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
InfiniScroll =
|
||||||
|
init: ->
|
||||||
|
return unless Conf['Infinite Scrolling'] and g.VIEW is 'index' and g.BOARD isnt 'f'
|
||||||
|
|
||||||
|
@threads = g.threads
|
||||||
|
|
||||||
|
$.on d, '4chanXInitFinished', @ready
|
||||||
|
|
||||||
|
ready: ->
|
||||||
|
$.off d, '4chanXInitFinished', InfiniScroll.ready
|
||||||
|
$.on d, 'scroll', InfiniScroll.scroll
|
||||||
|
InfiniScroll.scroll()
|
||||||
|
|
||||||
|
scroll: $.debounce 100, ->
|
||||||
|
return if InfiniScroll.isFetching or (doc.scrollTop <= doc.scrollHeight - (300 + window.innerHeight))
|
||||||
|
return InfiniScroll.notice() if InfiniScroll.isDead
|
||||||
|
|
||||||
|
# For once, lets respect 4chan's API rules.
|
||||||
|
if InfiniScroll.cache and InfiniScroll.cache.time > Date.now() - $.MINUTE
|
||||||
|
return InfiniScroll.parse InfiniScroll.cache
|
||||||
|
|
||||||
|
new Notice 'info', "Fetching next page.", 2
|
||||||
|
|
||||||
|
InfiniScroll.isFetching = true
|
||||||
|
url = "//api.4chan.org/#{g.BOARD}/catalog.json"
|
||||||
|
$.ajax url, onloadend: InfiniScroll.cb.load,
|
||||||
|
whenModified: true
|
||||||
|
|
||||||
|
parse: (response) ->
|
||||||
|
threads = InfiniScroll.parsePages response
|
||||||
|
threadNodes = []
|
||||||
|
nodes = []
|
||||||
|
|
||||||
|
return unless threads.length
|
||||||
|
InfiniScroll.notice()
|
||||||
|
InfiniScroll.isDead = true
|
||||||
|
|
||||||
|
for thread in threads
|
||||||
|
posts = []
|
||||||
|
{omitted_posts, omitted_images} = thread
|
||||||
|
threadID = thread.no
|
||||||
|
|
||||||
|
el = $.el 'div',
|
||||||
|
className: 'thread'
|
||||||
|
id: "t#{threadID}"
|
||||||
|
|
||||||
|
op = Build.postFromObject thread, g.BOARD
|
||||||
|
posts.push op
|
||||||
|
|
||||||
|
replylink = $.el 'a',
|
||||||
|
href: "res/#{threadID}"
|
||||||
|
className: 'replylink'
|
||||||
|
textContent: 'Reply'
|
||||||
|
|
||||||
|
postlink = $.el 'div',
|
||||||
|
className: "postLink mobile"
|
||||||
|
innerHTML: """<a href="res/#{threadID}" class="button">View Thread</a>"""
|
||||||
|
|
||||||
|
if omitted_posts
|
||||||
|
posts.push $.el 'span',
|
||||||
|
className: 'summary desktop'
|
||||||
|
innerHTML: """
|
||||||
|
#{omitted_posts} posts #{if omitted_images then "and " + omitted_images + " image replies"} omitted. Click <a class="replylink" href="res/#{threadID}">here</a> to view.
|
||||||
|
"""
|
||||||
|
|
||||||
|
$.prepend postlink, $.el 'span',
|
||||||
|
className: 'info'
|
||||||
|
innerHTML: """
|
||||||
|
<strong>#{omitted_posts} posts omitted</strong>#{if omitted_images then "<br><em>(#{omitted_images} have images)</em>" else ""}
|
||||||
|
"""
|
||||||
|
|
||||||
|
$.add $('.postInfo', op), [$.tn('\u00A0\u00A0\u00A0['), replylink, $.tn(']\u00A0')]
|
||||||
|
$.add op, postlink
|
||||||
|
|
||||||
|
if thread.last_replies then posts.push Build.postFromObject post, g.BOARD for post in thread.last_replies
|
||||||
|
|
||||||
|
$.add el, posts
|
||||||
|
|
||||||
|
threadNodes.push el
|
||||||
|
nodes.push el
|
||||||
|
nodes.push $.el 'hr'
|
||||||
|
|
||||||
|
InfiniScroll.features threadNodes
|
||||||
|
|
||||||
|
$.before botPostForm, nodes if botPostForm = $ '.board > .mobile.center'
|
||||||
|
|
||||||
|
parsePages: (response) ->
|
||||||
|
pages = JSON.parse response
|
||||||
|
newThreads = []
|
||||||
|
|
||||||
|
for number, page of pages when pages.hasOwnProperty number
|
||||||
|
{threads} = page
|
||||||
|
for thread in threads
|
||||||
|
continue if g.threads["#{g.BOARD}.#{thread.no}"]
|
||||||
|
|
||||||
|
newThreads.push thread
|
||||||
|
|
||||||
|
return newThreads if newThreads.length is 15
|
||||||
|
|
||||||
|
return newThreads
|
||||||
|
|
||||||
|
features: (threadNodes) ->
|
||||||
|
posts = []
|
||||||
|
threads = []
|
||||||
|
for threadRoot in threadNodes
|
||||||
|
thread = new Thread +threadRoot.id[1..], g.BOARD
|
||||||
|
threads.push thread
|
||||||
|
for post in $$ '.thread > .postContainer', threadRoot
|
||||||
|
try
|
||||||
|
posts.push new Post post, thread, g.BOARD
|
||||||
|
catch err
|
||||||
|
# Skip posts that we failed to parse.
|
||||||
|
unless errors
|
||||||
|
errors = []
|
||||||
|
errors.push
|
||||||
|
message: "Parsing of Post No.#{postRoot.id.match(/\d+/)} failed. Post will be skipped."
|
||||||
|
error: err
|
||||||
|
Main.handleErrors errors if errors
|
||||||
|
|
||||||
|
Main.callbackNodes Thread, threads
|
||||||
|
Main.callbackNodes Post, posts
|
||||||
|
|
||||||
|
notice: do ->
|
||||||
|
notify = false
|
||||||
|
reset = -> notify = false
|
||||||
|
return ->
|
||||||
|
return if notify
|
||||||
|
notify = true
|
||||||
|
new Notice 'info', "Last page reached.", 2
|
||||||
|
setTimeout reset, 3 * $.SECOND
|
||||||
|
|
||||||
|
cb:
|
||||||
|
load: ->
|
||||||
|
InfiniScroll.isFetching = false
|
||||||
|
return unless @status is 200
|
||||||
|
|
||||||
|
InfiniScroll.cache = new String @response
|
||||||
|
InfiniScroll.cache.time = Date.now()
|
||||||
|
InfiniScroll.parse @response
|
||||||
Loading…
x
Reference in New Issue
Block a user