Add Thread Updater.
This commit is contained in:
parent
f769714eeb
commit
659bf231b4
249
4chan_x.user.js
249
4chan_x.user.js
@ -79,7 +79,7 @@
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var $, $$, AutoGIF, Board, Build, Clone, Conf, Config, FileInfo, Get, ImageHover, Main, Post, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, RevealSpoilers, Sauce, Thread, Time, UI, d, g,
|
||||
var $, $$, AutoGIF, Board, Build, Clone, Conf, Config, FileInfo, Get, ImageHover, Main, Post, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, RevealSpoilers, Sauce, Thread, ThreadUpdater, Time, UI, d, g,
|
||||
__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; };
|
||||
|
||||
@ -1030,6 +1030,13 @@
|
||||
$.log(err, 'Image Hover');
|
||||
}
|
||||
}
|
||||
if (Conf['Thread Updater']) {
|
||||
try {
|
||||
ThreadUpdater.init();
|
||||
} catch (err) {
|
||||
$.log(err, 'Thread Updater');
|
||||
}
|
||||
}
|
||||
return $.ready(Main.initFeaturesReady);
|
||||
},
|
||||
initFeaturesReady: function() {
|
||||
@ -1087,7 +1094,7 @@
|
||||
settings: function() {
|
||||
return alert('Here be settings');
|
||||
},
|
||||
css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\n}\n.warning {\n color: red;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\n display: block !important;\n}\n.post {\n overflow: visible !important;\n}\n\n/* header */\nbody.fourchan_x {\n margin-top: 2.5em;\n}\n#boardNavDesktop.reply {\n border-width: 0 0 1px;\n padding: 4px;\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n transition: opacity .1s ease-in-out;\n -o-transition: opacity .1s ease-in-out;\n -moz-transition: opacity .1s ease-in-out;\n -webkit-transition: opacity .1s ease-in-out;\n z-index: 1;\n}\n#boardNavDesktop.reply:not(:hover) {\n opacity: .4;\n transition: opacity 1.5s .5s ease-in-out;\n -o-transition: opacity 1.5s .5s ease-in-out;\n -moz-transition: opacity 1.5s .5s ease-in-out;\n -webkit-transition: opacity 1.5s .5s ease-in-out;\n}\n#boardNavDesktop.reply a {\n margin: -1px;\n}\n#settings {\n float: right;\n}\n\n/* quote */\n.quotelink.deadlink {\n text-decoration: underline !important;\n}\n.deadlink:not(.quotelink) {\n text-decoration: none !important;\n}\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n position: fixed;\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n outline: 2px solid rgba(216, 94, 49, .7);\n}\n\n/* file */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull {\n display: none;\n}\n#ihover {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n max-height: 100%;\n max-width: 75%;\n position: fixed;\n padding-bottom: 16px;\n}"
|
||||
css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\n}\n.warning {\n color: red;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\n display: block !important;\n}\n.post {\n overflow: visible !important;\n}\n\n/* header */\nbody.fourchan_x {\n margin-top: 2.5em;\n}\n#boardNavDesktop.reply {\n border-width: 0 0 1px;\n padding: 4px;\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n transition: opacity .1s ease-in-out;\n -o-transition: opacity .1s ease-in-out;\n -moz-transition: opacity .1s ease-in-out;\n -webkit-transition: opacity .1s ease-in-out;\n z-index: 1;\n}\n#boardNavDesktop.reply:not(:hover) {\n opacity: .4;\n transition: opacity 1.5s .5s ease-in-out;\n -o-transition: opacity 1.5s .5s ease-in-out;\n -moz-transition: opacity 1.5s .5s ease-in-out;\n -webkit-transition: opacity 1.5s .5s ease-in-out;\n}\n#boardNavDesktop.reply a {\n margin: -1px;\n}\n#settings {\n float: right;\n}\n\n/* thread updater */\n#updater {\n position: fixed;\n text-align: right;\n}\n#updater:not(:hover) {\n background: none;\n border: none;\n}\n#updater input[type=number] {\n width: 4em;\n}\n#updater:not(:hover) > div:not(.move) {\n display: none;\n}\n.new {\n color: limegreen;\n}\n\n/* quote */\n.quotelink.deadlink {\n text-decoration: underline !important;\n}\n.deadlink:not(.quotelink) {\n text-decoration: none !important;\n}\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n position: fixed;\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n outline: 2px solid rgba(216, 94, 49, .7);\n}\n\n/* file */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull {\n display: none;\n}\n#ihover {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n max-height: 100%;\n max-width: 75%;\n position: fixed;\n padding-bottom: 16px;\n}"
|
||||
};
|
||||
|
||||
Redirect = {
|
||||
@ -2362,6 +2369,244 @@
|
||||
}
|
||||
};
|
||||
|
||||
ThreadUpdater = {
|
||||
init: function() {
|
||||
if (!g.REPLY) {
|
||||
return;
|
||||
}
|
||||
return Thread.prototype.callbacks.push({
|
||||
name: 'Thread Updater',
|
||||
cb: this.node
|
||||
});
|
||||
},
|
||||
node: function() {
|
||||
return new ThreadUpdater.Updater(this);
|
||||
},
|
||||
Updater: (function() {
|
||||
|
||||
function _Class(thread) {
|
||||
var checked, dialog, html, input, name, title, val, _i, _len, _ref, _ref1;
|
||||
this.thread = thread;
|
||||
html = '<div class=move><span id=status></span> <span id=timer></span></div>';
|
||||
_ref = Config.updater.checkbox;
|
||||
for (name in _ref) {
|
||||
val = _ref[name];
|
||||
title = val[1];
|
||||
checked = Conf[name] ? 'checked' : '';
|
||||
html += "<div><label title='" + title + "'>" + name + "<input name='" + name + "' type=checkbox " + checked + "></label></div>";
|
||||
}
|
||||
checked = Conf['Auto Update'] ? 'checked' : '';
|
||||
html += "<div><label title='Controls whether *this* thread automatically updates or not'>Auto Update This<input name='Auto Update This' type=checkbox " + checked + "></label></div>\n<div><label>Interval (s)<input type=number name=Interval class=field min=5 value=" + Conf['Interval'] + "></label></div>\n<div><input value='Update Now' type=button name='Update Now'></div>";
|
||||
dialog = UI.dialog('updater', 'bottom: 0; right: 0;', html);
|
||||
this.timer = $('#timer', dialog);
|
||||
this.status = $('#status', dialog);
|
||||
this.unsuccessfulFetchCount = 0;
|
||||
this.lastModified = '0';
|
||||
this.threadRoot = thread.posts[thread].nodes.root.parentNode;
|
||||
this.lastPost = +this.threadRoot.lastElementChild.id.slice(2);
|
||||
_ref1 = $$('input', dialog);
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
input = _ref1[_i];
|
||||
switch (input.type) {
|
||||
case 'checkbox':
|
||||
$.on(input, 'click', this.cb.checkbox.bind(this));
|
||||
input.dispatchEvent(new Event('click'));
|
||||
$.on(input, 'click', $.cb.checked);
|
||||
break;
|
||||
case 'number':
|
||||
$.on(input, 'change', this.cb.interval.bind(this));
|
||||
input.dispatchEvent(new Event('change'));
|
||||
break;
|
||||
case 'button':
|
||||
$.on(input, 'click', this.update.bind(this));
|
||||
}
|
||||
}
|
||||
$.on(d, 'QRPostSuccessful', this.cb.post.bind(this));
|
||||
$.on(d, 'visibilitychange ovisibilitychange mozvisibilitychange webkitvisibilitychange', this.cb.visibility.bind(this));
|
||||
this.set('timer', this.getInterval());
|
||||
$.add(d.body, dialog);
|
||||
}
|
||||
|
||||
_Class.prototype.cb = {
|
||||
post: function(e) {
|
||||
if (!(this['Auto Update This'] && +e.detail.threadID === this.thread.ID)) {
|
||||
return;
|
||||
}
|
||||
this.unsuccessfulFetchCount = 0;
|
||||
if (this.seconds > 2) {
|
||||
return setTimeout(this.update.bind(this), 1000);
|
||||
}
|
||||
},
|
||||
visibility: function() {
|
||||
var state;
|
||||
state = d.visibilityState || d.oVisibilityState || d.mozVisibilityState || d.webkitVisibilityState;
|
||||
if (state !== 'visible') {
|
||||
return;
|
||||
}
|
||||
this.unsuccessfulFetchCount = 0;
|
||||
if (this.seconds > this.interval) {
|
||||
return this.set('timer', this.getInterval());
|
||||
}
|
||||
},
|
||||
checkbox: function(e) {
|
||||
var checked, input, name;
|
||||
input = e.target;
|
||||
checked = input.checked, name = input.name;
|
||||
this[name] = checked;
|
||||
switch (name) {
|
||||
case 'Scroll BG':
|
||||
return this.scrollBG = checked ? function() {
|
||||
return true;
|
||||
} : function() {
|
||||
return !(d.hidden || d.oHidden || d.mozHidden || d.webkitHidden);
|
||||
};
|
||||
case 'Auto Update This':
|
||||
if (checked) {
|
||||
return this.timeoutID = setTimeout(this.timeout.bind(this), 1000);
|
||||
} else {
|
||||
return clearTimeout(this.timeoutID);
|
||||
}
|
||||
}
|
||||
},
|
||||
interval: function(e) {
|
||||
var input, val;
|
||||
input = e.target;
|
||||
val = Math.max(5, parseInt(input.value, 10));
|
||||
this.interval = input.value = val;
|
||||
return $.cb.value.call(input);
|
||||
},
|
||||
load: function() {
|
||||
switch (this.req.status) {
|
||||
case 404:
|
||||
this.set('timer', null);
|
||||
this.set('status', '404');
|
||||
this.status.className = 'warning';
|
||||
clearTimeout(this.timeoutID);
|
||||
this.thread.isDead = true;
|
||||
break;
|
||||
case 0:
|
||||
case 304:
|
||||
/*
|
||||
Status Code 304: Not modified
|
||||
By sending the `If-Modified-Since` header we get a proper status code, and no response.
|
||||
This saves bandwidth for both the user and the servers and avoid unnecessary computation.
|
||||
*/
|
||||
|
||||
this.unsuccessfulFetchCount++;
|
||||
this.set('timer', this.getInterval());
|
||||
this.set('status', null);
|
||||
this.status.className = null;
|
||||
break;
|
||||
case 200:
|
||||
this.lastModified = this.req.getResponseHeader('Last-Modified');
|
||||
this.parse(JSON.parse(this.req.response).posts);
|
||||
this.set('timer', this.getInterval());
|
||||
break;
|
||||
default:
|
||||
this.unsuccessfulFetchCount++;
|
||||
this.set('timer', this.getInterval());
|
||||
this.set('status', this.req.statusText);
|
||||
this.status.className = 'warning';
|
||||
}
|
||||
return delete this.req;
|
||||
}
|
||||
};
|
||||
|
||||
_Class.prototype.getInterval = function() {
|
||||
var i, j;
|
||||
i = this.interval;
|
||||
j = Math.min(this.unsuccessfulFetchCount, 10);
|
||||
if (!(d.hidden || d.oHidden || d.mozHidden || d.webkitHidden)) {
|
||||
j = Math.min(j, 7);
|
||||
}
|
||||
return this.seconds = Math.max(i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]);
|
||||
};
|
||||
|
||||
_Class.prototype.set = function(name, text) {
|
||||
var el, node;
|
||||
el = this[name];
|
||||
if (node = el.firstChild) {
|
||||
return node.data = text;
|
||||
} else {
|
||||
return el.textContent = text;
|
||||
}
|
||||
};
|
||||
|
||||
_Class.prototype.timeout = function() {
|
||||
var n;
|
||||
this.timeoutID = setTimeout(this.timeout.bind(this), 1000);
|
||||
if (!(n = --this.seconds)) {
|
||||
return this.update();
|
||||
} else if (n <= -60) {
|
||||
this.set('status', 'Retrying');
|
||||
this.status.className = null;
|
||||
return this.update();
|
||||
} else if (n > 0) {
|
||||
return this.set('timer', n);
|
||||
}
|
||||
};
|
||||
|
||||
_Class.prototype.update = function() {
|
||||
var url;
|
||||
this.seconds = 0;
|
||||
this.set('timer', '...');
|
||||
if (this.req) {
|
||||
this.req.onloadend = null;
|
||||
this.req.abort();
|
||||
}
|
||||
url = "//api.4chan.org/" + this.thread.board + "/res/" + this.thread + ".json";
|
||||
return this.req = $.ajax(url, {
|
||||
onloadend: this.cb.load.bind(this)
|
||||
}, {
|
||||
headers: {
|
||||
'If-Modified-Since': this.lastModified
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_Class.prototype.parse = function(postObjects) {
|
||||
var count, node, nodes, postObject, posts, scroll, spoilerRange, _i, _len, _ref;
|
||||
if (spoilerRange = postObjects[0].custom_spoiler) {
|
||||
Build.spoilerRange[this.thread.board] = spoilerRange;
|
||||
}
|
||||
nodes = [];
|
||||
posts = [];
|
||||
count = 0;
|
||||
_ref = postObjects.reverse();
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
postObject = _ref[_i];
|
||||
if (postObject.no <= this.lastPost) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
node = Build.postFromObject(postObject, this.thread.board.ID);
|
||||
nodes.unshift(node);
|
||||
posts.unshift(new Post(node, this.thread, this.thread.board));
|
||||
}
|
||||
if (count) {
|
||||
this.set('status', "+" + count);
|
||||
this.status.className = 'new';
|
||||
this.unsuccessfulFetchCount = 0;
|
||||
} else {
|
||||
this.set('status', null);
|
||||
this.status.className = null;
|
||||
this.unsuccessfulFetchCount++;
|
||||
return;
|
||||
}
|
||||
this.lastPost = posts[count - 1].ID;
|
||||
Main.callbackNodes(Post, posts);
|
||||
scroll = this['Auto Scroll'] && this.scrollBG() && this.threadRoot.getBoundingClientRect().bottom - d.documentElement.clientHeight < 25;
|
||||
$.add(this.threadRoot, nodes);
|
||||
if (scroll) {
|
||||
return nodes[0].scrollIntoView();
|
||||
}
|
||||
};
|
||||
|
||||
return _Class;
|
||||
|
||||
})()
|
||||
};
|
||||
|
||||
Main.init();
|
||||
|
||||
}).call(this);
|
||||
|
||||
219
script.coffee
219
script.coffee
@ -830,6 +830,13 @@ Main =
|
||||
# XXX handle error
|
||||
$.log err, 'Image Hover'
|
||||
|
||||
if Conf['Thread Updater']
|
||||
try
|
||||
ThreadUpdater.init()
|
||||
catch err
|
||||
# XXX handle error
|
||||
$.log err, 'Thread Updater'
|
||||
|
||||
$.ready Main.initFeaturesReady
|
||||
initFeaturesReady: ->
|
||||
if d.title is '4chan - 404 Not Found'
|
||||
@ -932,6 +939,25 @@ body.fourchan_x {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* thread updater */
|
||||
#updater {
|
||||
position: fixed;
|
||||
text-align: right;
|
||||
}
|
||||
#updater:not(:hover) {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
#updater input[type=number] {
|
||||
width: 4em;
|
||||
}
|
||||
#updater:not(:hover) > div:not(.move) {
|
||||
display: none;
|
||||
}
|
||||
.new {
|
||||
color: limegreen;
|
||||
}
|
||||
|
||||
/* quote */
|
||||
.quotelink.deadlink {
|
||||
text-decoration: underline !important;
|
||||
@ -2087,6 +2113,199 @@ ImageHover =
|
||||
$.off @, 'mousemove', UI.hover
|
||||
$.off @, 'mouseout', ImageHover.mouseout
|
||||
|
||||
ThreadUpdater =
|
||||
init: ->
|
||||
return unless g.REPLY
|
||||
Thread::callbacks.push
|
||||
name: 'Thread Updater'
|
||||
cb: @node
|
||||
node: ->
|
||||
new ThreadUpdater.Updater @
|
||||
|
||||
Updater: class
|
||||
constructor: (@thread) ->
|
||||
html = '<div class=move><span id=status></span> <span id=timer></span></div>'
|
||||
for name, val of Config.updater.checkbox
|
||||
title = val[1]
|
||||
checked = if Conf[name] then 'checked' else ''
|
||||
html += "<div><label title='#{title}'>#{name}<input name='#{name}' type=checkbox #{checked}></label></div>"
|
||||
|
||||
checked = if Conf['Auto Update'] then 'checked' else ''
|
||||
html += """
|
||||
<div><label title='Controls whether *this* thread automatically updates or not'>Auto Update This<input name='Auto Update This' type=checkbox #{checked}></label></div>
|
||||
<div><label>Interval (s)<input type=number name=Interval class=field min=5 value=#{Conf['Interval']}></label></div>
|
||||
<div><input value='Update Now' type=button name='Update Now'></div>
|
||||
"""
|
||||
|
||||
dialog = UI.dialog 'updater', 'bottom: 0; right: 0;', html
|
||||
|
||||
@timer = $ '#timer', dialog
|
||||
@status = $ '#status', dialog
|
||||
|
||||
@unsuccessfulFetchCount = 0
|
||||
@lastModified = '0'
|
||||
@threadRoot = thread.posts[thread].nodes.root.parentNode
|
||||
@lastPost = +@threadRoot.lastElementChild.id[2..]
|
||||
|
||||
for input in $$ 'input', dialog
|
||||
switch input.type
|
||||
when 'checkbox'
|
||||
$.on input, 'click', @cb.checkbox.bind @
|
||||
input.dispatchEvent new Event 'click'
|
||||
$.on input, 'click', $.cb.checked
|
||||
when 'number'
|
||||
$.on input, 'change', @cb.interval.bind @
|
||||
input.dispatchEvent new Event 'change'
|
||||
when 'button'
|
||||
$.on input, 'click', @update.bind @
|
||||
|
||||
$.on d, 'QRPostSuccessful', @cb.post.bind @
|
||||
$.on d, 'visibilitychange ovisibilitychange mozvisibilitychange webkitvisibilitychange', @cb.visibility.bind @
|
||||
|
||||
@set 'timer', @getInterval()
|
||||
$.add d.body, dialog
|
||||
|
||||
cb:
|
||||
post: (e) ->
|
||||
return unless @['Auto Update This'] and +e.detail.threadID is @thread.ID
|
||||
@unsuccessfulFetchCount = 0
|
||||
setTimeout @update.bind(@), 1000 if @seconds > 2
|
||||
visibility: ->
|
||||
state = d.visibilityState or d.oVisibilityState or d.mozVisibilityState or d.webkitVisibilityState
|
||||
return if state isnt 'visible'
|
||||
# Reset the counter when we focus this tab.
|
||||
@unsuccessfulFetchCount = 0
|
||||
if @seconds > @interval
|
||||
@set 'timer', @getInterval()
|
||||
checkbox: (e) ->
|
||||
input = e.target
|
||||
{checked, name} = input
|
||||
@[name] = checked
|
||||
switch name
|
||||
when 'Scroll BG'
|
||||
@scrollBG =
|
||||
if checked
|
||||
-> true
|
||||
else
|
||||
-> !(d.hidden or d.oHidden or d.mozHidden or d.webkitHidden)
|
||||
when 'Auto Update This'
|
||||
if checked
|
||||
@timeoutID = setTimeout @timeout.bind(@), 1000
|
||||
else
|
||||
clearTimeout @timeoutID
|
||||
interval: (e) ->
|
||||
input = e.target
|
||||
val = Math.max 5, parseInt input.value, 10
|
||||
@interval = input.value = val
|
||||
$.cb.value.call input
|
||||
load: ->
|
||||
switch @req.status
|
||||
when 404
|
||||
@set 'timer', null
|
||||
@set 'status', '404'
|
||||
@status.className = 'warning'
|
||||
clearTimeout @timeoutID
|
||||
@thread.isDead = true
|
||||
# if Conf['Unread Count']
|
||||
# Unread.title = Unread.title.match(/^.+-/)[0] + ' 404'
|
||||
# else
|
||||
# d.title = d.title.match(/^.+-/)[0] + ' 404'
|
||||
# Unread.update true
|
||||
# QR.abort()
|
||||
# XXX 304 -> 0 in Opera
|
||||
when 0, 304
|
||||
###
|
||||
Status Code 304: Not modified
|
||||
By sending the `If-Modified-Since` header we get a proper status code, and no response.
|
||||
This saves bandwidth for both the user and the servers and avoid unnecessary computation.
|
||||
###
|
||||
@unsuccessfulFetchCount++
|
||||
@set 'timer', @getInterval()
|
||||
@set 'status', null
|
||||
@status.className = null
|
||||
when 200
|
||||
@lastModified = @req.getResponseHeader 'Last-Modified'
|
||||
@parse JSON.parse(@req.response).posts
|
||||
@set 'timer', @getInterval()
|
||||
else
|
||||
@unsuccessfulFetchCount++
|
||||
@set 'timer', @getInterval()
|
||||
@set 'status', @req.statusText
|
||||
@status.className = 'warning'
|
||||
delete @req
|
||||
|
||||
getInterval: ->
|
||||
i = @interval
|
||||
j = Math.min @unsuccessfulFetchCount, 10
|
||||
unless d.hidden or d.oHidden or d.mozHidden or d.webkitHidden
|
||||
# Lower the max refresh rate limit on visible tabs.
|
||||
j = Math.min j, 7
|
||||
@seconds = Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]
|
||||
|
||||
set: (name, text) ->
|
||||
el = @[name]
|
||||
if node = el.firstChild
|
||||
# Prevent the creation of a new DOM Node
|
||||
# by setting the text node's data.
|
||||
node.data = text
|
||||
else
|
||||
el.textContent = text
|
||||
|
||||
timeout: ->
|
||||
@timeoutID = setTimeout @timeout.bind(@), 1000
|
||||
unless n = --@seconds
|
||||
@update()
|
||||
else if n <= -60
|
||||
@set 'status', 'Retrying'
|
||||
@status.className = null
|
||||
@update()
|
||||
else if n > 0
|
||||
@set 'timer', n
|
||||
|
||||
update: ->
|
||||
@seconds = 0
|
||||
@set 'timer', '...'
|
||||
if @req
|
||||
# abort() triggers onloadend, we don't want that.
|
||||
@req.onloadend = null
|
||||
@req.abort()
|
||||
url = "//api.4chan.org/#{@thread.board}/res/#{@thread}.json"
|
||||
@req = $.ajax url, onloadend: @cb.load.bind @,
|
||||
headers: 'If-Modified-Since': @lastModified
|
||||
|
||||
parse: (postObjects) ->
|
||||
if spoilerRange = postObjects[0].custom_spoiler
|
||||
Build.spoilerRange[@thread.board] = spoilerRange
|
||||
|
||||
nodes = []
|
||||
posts = []
|
||||
count = 0
|
||||
for postObject in postObjects.reverse()
|
||||
break if postObject.no <= @lastPost # Make sure to not insert older posts.
|
||||
count++
|
||||
node = Build.postFromObject postObject, @thread.board.ID
|
||||
nodes.unshift node
|
||||
posts.unshift new Post node, @thread, @thread.board
|
||||
|
||||
if count
|
||||
@set 'status', "+#{count}"
|
||||
@status.className = 'new'
|
||||
@unsuccessfulFetchCount = 0
|
||||
else
|
||||
@set 'status', null
|
||||
@status.className = null
|
||||
@unsuccessfulFetchCount++
|
||||
return
|
||||
|
||||
@lastPost = posts[count - 1].ID
|
||||
Main.callbackNodes Post, posts
|
||||
|
||||
scroll = @['Auto Scroll'] and @scrollBG() and
|
||||
@threadRoot.getBoundingClientRect().bottom - d.documentElement.clientHeight < 25
|
||||
$.add @threadRoot, nodes
|
||||
if scroll
|
||||
nodes[0].scrollIntoView()
|
||||
|
||||
|
||||
|
||||
Main.init()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user