Infinite Scrolling

Conflicts:
	LICENSE
	builds/appchan-x.user.js
	builds/crx/script.js
	src/General/Main.coffee
This commit is contained in:
Zixaphir 2013-10-19 19:41:49 -07:00
parent 9fb8ab50e1
commit 4f50fd300d
7 changed files with 16780 additions and 13 deletions

View File

@ -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

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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);

View File

@ -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': [

View File

@ -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

View 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