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() {
|
(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,
|
__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; };
|
||||||
|
|
||||||
@ -1030,6 +1030,13 @@
|
|||||||
$.log(err, 'Image Hover');
|
$.log(err, 'Image Hover');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Conf['Thread Updater']) {
|
||||||
|
try {
|
||||||
|
ThreadUpdater.init();
|
||||||
|
} catch (err) {
|
||||||
|
$.log(err, 'Thread Updater');
|
||||||
|
}
|
||||||
|
}
|
||||||
return $.ready(Main.initFeaturesReady);
|
return $.ready(Main.initFeaturesReady);
|
||||||
},
|
},
|
||||||
initFeaturesReady: function() {
|
initFeaturesReady: function() {
|
||||||
@ -1087,7 +1094,7 @@
|
|||||||
settings: function() {
|
settings: function() {
|
||||||
return alert('Here be settings');
|
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 = {
|
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();
|
Main.init();
|
||||||
|
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|||||||
219
script.coffee
219
script.coffee
@ -830,6 +830,13 @@ Main =
|
|||||||
# XXX handle error
|
# XXX handle error
|
||||||
$.log err, 'Image Hover'
|
$.log err, 'Image Hover'
|
||||||
|
|
||||||
|
if Conf['Thread Updater']
|
||||||
|
try
|
||||||
|
ThreadUpdater.init()
|
||||||
|
catch err
|
||||||
|
# XXX handle error
|
||||||
|
$.log err, 'Thread Updater'
|
||||||
|
|
||||||
$.ready Main.initFeaturesReady
|
$.ready Main.initFeaturesReady
|
||||||
initFeaturesReady: ->
|
initFeaturesReady: ->
|
||||||
if d.title is '4chan - 404 Not Found'
|
if d.title is '4chan - 404 Not Found'
|
||||||
@ -932,6 +939,25 @@ body.fourchan_x {
|
|||||||
float: right;
|
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 */
|
/* quote */
|
||||||
.quotelink.deadlink {
|
.quotelink.deadlink {
|
||||||
text-decoration: underline !important;
|
text-decoration: underline !important;
|
||||||
@ -2087,6 +2113,199 @@ ImageHover =
|
|||||||
$.off @, 'mousemove', UI.hover
|
$.off @, 'mousemove', UI.hover
|
||||||
$.off @, 'mouseout', ImageHover.mouseout
|
$.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()
|
Main.init()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user