Merge branch 'master' into v3
This commit is contained in:
commit
20ece62290
134
4chan_x.user.js
134
4chan_x.user.js
@ -43,7 +43,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Board, Build, Clone, Conf, Config, DeleteLink, DownloadLink, FileInfo, Filter, Get, Header, ImageExpand, ImageHover, Main, Menu, Notification, Post, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Recursive, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadHiding, ThreadUpdater, Time, UI, d, doc, g,
|
var $, $$, Anonymize, ArchiveLink, AutoGIF, Board, Build, Clone, Conf, Config, DeleteLink, DownloadLink, FileInfo, Filter, Get, Header, ImageExpand, ImageHover, Main, Menu, Notification, Polyfill, Post, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Recursive, Redirect, RelativeDates, ReplyHiding, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadHiding, ThreadUpdater, Time, UI, 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; },
|
||||||
__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; };
|
||||||
@ -55,6 +55,7 @@
|
|||||||
'404 Redirect': [true, 'Redirect dead threads and images.'],
|
'404 Redirect': [true, 'Redirect dead threads and images.'],
|
||||||
'Keybinds': [true, 'Bind actions to keyboard shortcuts.'],
|
'Keybinds': [true, 'Bind actions to keyboard shortcuts.'],
|
||||||
'Time Formatting': [true, 'Localize and format timestamps arbitrarily.'],
|
'Time Formatting': [true, 'Localize and format timestamps arbitrarily.'],
|
||||||
|
'Relative Post Dates': [false, 'Display dates like "3 minutes ago". Tooltip shows the timestamp.'],
|
||||||
'File Info Formatting': [true, 'Reformat the file information.'],
|
'File Info Formatting': [true, 'Reformat the file information.'],
|
||||||
'Comment Expansion': [true, 'Can expand too long comments.'],
|
'Comment Expansion': [true, 'Can expand too long comments.'],
|
||||||
'Thread Expansion': [true, 'Can expand threads to view all replies.'],
|
'Thread Expansion': [true, 'Can expand threads to view all replies.'],
|
||||||
@ -834,8 +835,19 @@
|
|||||||
open: function(url) {
|
open: function(url) {
|
||||||
return (GM_openInTab || window.open)(url, '_blank');
|
return (GM_openInTab || window.open)(url, '_blank');
|
||||||
},
|
},
|
||||||
hidden: function() {
|
debounce: function(wait, fn) {
|
||||||
return d.hidden || d.oHidden || d.mozHidden || d.webkitHidden;
|
var timeout;
|
||||||
|
timeout = null;
|
||||||
|
return function() {
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
} else {
|
||||||
|
fn.apply(this, arguments);
|
||||||
|
}
|
||||||
|
return timeout = setTimeout((function() {
|
||||||
|
return timeout = null;
|
||||||
|
}), wait);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
queueTask: (function() {
|
queueTask: (function() {
|
||||||
var execTask, taskChannel, taskQueue;
|
var execTask, taskChannel, taskQueue;
|
||||||
@ -940,6 +952,34 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Polyfill = {
|
||||||
|
init: function() {
|
||||||
|
return Polyfill.visibility();
|
||||||
|
},
|
||||||
|
visibility: function() {
|
||||||
|
var event, prefix, property;
|
||||||
|
if ('visibilityState' in document) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ('webkitVisibilityState' in document) {
|
||||||
|
prefix = 'webkit';
|
||||||
|
} else if ('mozVisibilityState' in document) {
|
||||||
|
prefix = 'moz';
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
property = prefix + 'VisibilityState';
|
||||||
|
event = prefix + 'visibilitychange';
|
||||||
|
d.visibilityState = d[property];
|
||||||
|
d.hidden = d.visibilityState === 'hidden';
|
||||||
|
return $.on(d, event, function() {
|
||||||
|
d.visibilityState = d[property];
|
||||||
|
d.hidden = d.visibilityState === 'hidden';
|
||||||
|
return $.event('visibilitychange');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Header = {
|
Header = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var boardList, boardListButton, boardTitle, catalogToggler, headerBar, menuButton, toggleBar;
|
var boardList, boardListButton, boardTitle, catalogToggler, headerBar, menuButton, toggleBar;
|
||||||
@ -2220,7 +2260,7 @@
|
|||||||
case 'u':
|
case 'u':
|
||||||
return "//nsfw.foolz.us/" + board + "/full_image/" + filename;
|
return "//nsfw.foolz.us/" + board + "/full_image/" + filename;
|
||||||
case 'po':
|
case 'po':
|
||||||
return "http://archive.thedarkcave.org/" + board + "/full_image/" + filename;
|
return "//archive.thedarkcave.org/" + board + "/full_image/" + filename;
|
||||||
case 'ck':
|
case 'ck':
|
||||||
case 'lit':
|
case 'lit':
|
||||||
return "//fuuka.warosu.org/" + board + "/full_image/" + filename;
|
return "//fuuka.warosu.org/" + board + "/full_image/" + filename;
|
||||||
@ -2263,8 +2303,10 @@
|
|||||||
case 'u':
|
case 'u':
|
||||||
case 'kuku':
|
case 'kuku':
|
||||||
return "//nsfw.foolz.us/_/api/chan/post/?board=" + board + "&num=" + postID;
|
return "//nsfw.foolz.us/_/api/chan/post/?board=" + board + "&num=" + postID;
|
||||||
|
case 'c':
|
||||||
|
case 'int':
|
||||||
case 'po':
|
case 'po':
|
||||||
return "http://archive.thedarkcave.org/_/api/chan/post/?board=" + board + "&num=" + postID;
|
return "//archive.thedarkcave.org/_/api/chan/post/?board=" + board + "&num=" + postID;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
to: function(data) {
|
to: function(data) {
|
||||||
@ -2290,8 +2332,9 @@
|
|||||||
case 'kuku':
|
case 'kuku':
|
||||||
url = Redirect.path('//nsfw.foolz.us', 'foolfuuka', data);
|
url = Redirect.path('//nsfw.foolz.us', 'foolfuuka', data);
|
||||||
break;
|
break;
|
||||||
|
case 'int':
|
||||||
case 'po':
|
case 'po':
|
||||||
url = Redirect.path('http://archive.thedarkcave.org', 'foolfuuka', data);
|
url = Redirect.path('//archive.thedarkcave.org', 'foolfuuka', data);
|
||||||
break;
|
break;
|
||||||
case 'ck':
|
case 'ck':
|
||||||
case 'lit':
|
case 'lit':
|
||||||
@ -3284,6 +3327,73 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RelativeDates = {
|
||||||
|
INTERVAL: $.MINUTE,
|
||||||
|
init: function() {
|
||||||
|
if (g.VIEW === 'catalog' || !Conf['Relative Post Dates']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.on(d, 'visibilitychange ThreadUpdate', this.flush);
|
||||||
|
return Post.prototype.callbacks.push({
|
||||||
|
name: 'Relative Post Dates',
|
||||||
|
cb: this.node
|
||||||
|
});
|
||||||
|
},
|
||||||
|
node: function() {
|
||||||
|
var dateEl, diff;
|
||||||
|
dateEl = this.nodes.date;
|
||||||
|
dateEl.title = dateEl.textContent;
|
||||||
|
diff = Date.now() - this.info.date;
|
||||||
|
dateEl.textContent = RelativeDates.relative(diff);
|
||||||
|
return RelativeDates.setUpdate(this, diff);
|
||||||
|
},
|
||||||
|
relative: function(diff) {
|
||||||
|
var number, rounded, unit;
|
||||||
|
unit = (number = diff / $.DAY) > 1 ? 'day' : (number = diff / $.HOUR) > 1 ? 'hour' : (number = diff / $.MINUTE) > 1 ? 'minute' : (number = diff / $.SECOND, 'second');
|
||||||
|
rounded = Math.round(number);
|
||||||
|
if (rounded !== 1) {
|
||||||
|
unit += 's';
|
||||||
|
}
|
||||||
|
return "" + rounded + " " + unit + " ago";
|
||||||
|
},
|
||||||
|
stale: [],
|
||||||
|
flush: $.debounce($.SECOND, function() {
|
||||||
|
var now, update, _i, _len, _ref;
|
||||||
|
if (d.hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
now = Date.now();
|
||||||
|
_ref = RelativeDates.stale;
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
update = _ref[_i];
|
||||||
|
update(now);
|
||||||
|
}
|
||||||
|
RelativeDates.stale = [];
|
||||||
|
clearTimeout(RelativeDates.timeout);
|
||||||
|
return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL);
|
||||||
|
}),
|
||||||
|
setUpdate: function(post, diff) {
|
||||||
|
var dateEl, markStale, setOwnTimeout, update;
|
||||||
|
setOwnTimeout = function(diff) {
|
||||||
|
var delay;
|
||||||
|
delay = diff > $.HOUR ? diff % $.HOUR : diff > $.MINUTE ? diff % $.MINUTE : diff % $.SECOND;
|
||||||
|
return setTimeout(markStale, delay);
|
||||||
|
};
|
||||||
|
dateEl = post.nodes.date;
|
||||||
|
update = function(now) {
|
||||||
|
if (d.contains(dateEl)) {
|
||||||
|
diff = now - post.info.date;
|
||||||
|
dateEl.textContent = RelativeDates.relative(diff);
|
||||||
|
return setOwnTimeout(diff);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
markStale = function() {
|
||||||
|
return RelativeDates.stale.push(update);
|
||||||
|
};
|
||||||
|
return setOwnTimeout(diff);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FileInfo = {
|
FileInfo = {
|
||||||
init: function() {
|
init: function() {
|
||||||
if (g.VIEW === 'catalog' || !Conf['File Info Formatting']) {
|
if (g.VIEW === 'catalog' || !Conf['File Info Formatting']) {
|
||||||
@ -3839,7 +3949,7 @@
|
|||||||
}
|
}
|
||||||
$.on(window, 'online offline', ThreadUpdater.cb.online);
|
$.on(window, 'online offline', ThreadUpdater.cb.online);
|
||||||
$.on(d, 'QRPostSuccessful', ThreadUpdater.cb.post);
|
$.on(d, 'QRPostSuccessful', ThreadUpdater.cb.post);
|
||||||
$.on(d, 'visibilitychange ovisibilitychange mozvisibilitychange webkitvisibilitychange', ThreadUpdater.cb.visibility);
|
$.on(d, 'visibilitychange', ThreadUpdater.cb.visibility);
|
||||||
ThreadUpdater.cb.online();
|
ThreadUpdater.cb.online();
|
||||||
return $.add(d.body, ThreadUpdater.dialog);
|
return $.add(d.body, ThreadUpdater.dialog);
|
||||||
},
|
},
|
||||||
@ -3874,7 +3984,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
visibility: function() {
|
visibility: function() {
|
||||||
if ($.hidden()) {
|
if (d.hidden) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ThreadUpdater.outdateCount = 0;
|
ThreadUpdater.outdateCount = 0;
|
||||||
@ -3886,7 +3996,7 @@
|
|||||||
return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() {
|
return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() {
|
||||||
return true;
|
return true;
|
||||||
} : function() {
|
} : function() {
|
||||||
return !$.hidden();
|
return !d.hidden;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
autoUpdate: function() {
|
autoUpdate: function() {
|
||||||
@ -3942,7 +4052,7 @@
|
|||||||
var i, j;
|
var i, j;
|
||||||
i = ThreadUpdater.interval;
|
i = ThreadUpdater.interval;
|
||||||
j = Math.min(ThreadUpdater.outdateCount, 10);
|
j = Math.min(ThreadUpdater.outdateCount, 10);
|
||||||
if (!$.hidden()) {
|
if (!d.hidden) {
|
||||||
j = Math.min(j, 7);
|
j = Math.min(j, 7);
|
||||||
}
|
}
|
||||||
return ThreadUpdater.seconds = Math.max(i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]);
|
return ThreadUpdater.seconds = Math.max(i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]);
|
||||||
@ -4037,7 +4147,7 @@
|
|||||||
} else {
|
} else {
|
||||||
ThreadUpdater.set('status', "+" + count, 'new');
|
ThreadUpdater.set('status', "+" + count, 'new');
|
||||||
ThreadUpdater.outdateCount = 0;
|
ThreadUpdater.outdateCount = 0;
|
||||||
if (Conf['Beep'] && $.hidden()) {
|
if (Conf['Beep'] && d.hidden) {
|
||||||
if (!ThreadUpdater.audio) {
|
if (!ThreadUpdater.audio) {
|
||||||
ThreadUpdater.audio = $.el('audio', {
|
ThreadUpdater.audio = $.el('audio', {
|
||||||
src: ThreadUpdater.beep
|
src: ThreadUpdater.beep
|
||||||
@ -5381,6 +5491,7 @@
|
|||||||
return console.timeEnd("" + name + " initialization");
|
return console.timeEnd("" + name + " initialization");
|
||||||
};
|
};
|
||||||
console.time('All initializations');
|
console.time('All initializations');
|
||||||
|
initFeature('Polyfill', Polyfill);
|
||||||
initFeature('Header', Header);
|
initFeature('Header', Header);
|
||||||
initFeature('Settings', Settings);
|
initFeature('Settings', Settings);
|
||||||
initFeature('Resurrect Quotes', Quotify);
|
initFeature('Resurrect Quotes', Quotify);
|
||||||
@ -5404,6 +5515,7 @@
|
|||||||
initFeature('Mark Cross-thread Quotes', QuoteCT);
|
initFeature('Mark Cross-thread Quotes', QuoteCT);
|
||||||
initFeature('Anonymize', Anonymize);
|
initFeature('Anonymize', Anonymize);
|
||||||
initFeature('Time Formatting', Time);
|
initFeature('Time Formatting', Time);
|
||||||
|
initFeature('Relative Post Dates', RelativeDates);
|
||||||
initFeature('File Info Formatting', FileInfo);
|
initFeature('File Info Formatting', FileInfo);
|
||||||
initFeature('Sauce', Sauce);
|
initFeature('Sauce', Sauce);
|
||||||
initFeature('Image Expansion', ImageExpand);
|
initFeature('Image Expansion', ImageExpand);
|
||||||
|
|||||||
23
changelog
23
changelog
@ -26,8 +26,31 @@ alpha
|
|||||||
Fix unreadable inlined posts with the Tomorrow theme.
|
Fix unreadable inlined posts with the Tomorrow theme.
|
||||||
|
|
||||||
master
|
master
|
||||||
|
|
||||||
|
2.38.1
|
||||||
|
- Mayhem
|
||||||
|
Fix a little regression introduced in 2.38.0 for webkit browsers.
|
||||||
|
|
||||||
|
2.38.0
|
||||||
|
- Queue
|
||||||
|
Add Relative Post Dates ("35 seconds ago"), disabled by default.
|
||||||
|
- Mayhem
|
||||||
|
Add /int/ archive redirection for threads, and post resurrection.
|
||||||
|
|
||||||
|
2.37.6
|
||||||
|
- Mayhem
|
||||||
|
Fix image expanding.
|
||||||
|
|
||||||
|
2.37.5
|
||||||
|
- Mayhem
|
||||||
|
Fix quoting inside inlined backlinks.
|
||||||
|
|
||||||
|
2.37.4
|
||||||
|
- James Campos
|
||||||
|
Don't expand pdfs
|
||||||
- Mayhem
|
- Mayhem
|
||||||
Add /po/ archive redirection for threads, images and post resurrection.
|
Add /po/ archive redirection for threads, images and post resurrection.
|
||||||
|
Fix quoting.
|
||||||
|
|
||||||
2.37.3
|
2.37.3
|
||||||
- Mayhem
|
- Mayhem
|
||||||
|
|||||||
1
grunt.js
1
grunt.js
@ -19,6 +19,7 @@ module.exports = function(grunt) {
|
|||||||
'<file_template:src/globals.coffee>',
|
'<file_template:src/globals.coffee>',
|
||||||
'<file_template:lib/ui.coffee>',
|
'<file_template:lib/ui.coffee>',
|
||||||
'<file_template:lib/$.coffee>',
|
'<file_template:lib/$.coffee>',
|
||||||
|
'<file_template:lib/polyfill.coffee>',
|
||||||
'<file_template:src/features.coffee>',
|
'<file_template:src/features.coffee>',
|
||||||
'<file_template:src/qr.coffee>',
|
'<file_template:src/qr.coffee>',
|
||||||
'<file_template:src/main.coffee>'
|
'<file_template:src/main.coffee>'
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
postMessage({version:'2.37.3'},'*')
|
postMessage({version:'2.38.1'},'*')
|
||||||
13
lib/$.coffee
13
lib/$.coffee
@ -140,8 +140,17 @@ $.extend $,
|
|||||||
root.dispatchEvent new CustomEvent event, {bubbles: true, detail}
|
root.dispatchEvent new CustomEvent event, {bubbles: true, detail}
|
||||||
open: (url) ->
|
open: (url) ->
|
||||||
(GM_openInTab or window.open) url, '_blank'
|
(GM_openInTab or window.open) url, '_blank'
|
||||||
hidden: ->
|
debounce: (wait, fn) ->
|
||||||
d.hidden or d.oHidden or d.mozHidden or d.webkitHidden
|
timeout = null
|
||||||
|
return ->
|
||||||
|
if timeout
|
||||||
|
# stop current reset
|
||||||
|
clearTimeout timeout
|
||||||
|
else
|
||||||
|
fn.apply this, arguments
|
||||||
|
|
||||||
|
# after wait, let next invocation execute immediately
|
||||||
|
timeout = setTimeout (-> timeout = null), wait
|
||||||
queueTask: (->
|
queueTask: (->
|
||||||
# inspired by https://www.w3.org/Bugs/Public/show_bug.cgi?id=15007
|
# inspired by https://www.w3.org/Bugs/Public/show_bug.cgi?id=15007
|
||||||
taskQueue = []
|
taskQueue = []
|
||||||
|
|||||||
@ -5,6 +5,7 @@ Config =
|
|||||||
'404 Redirect': [true, 'Redirect dead threads and images.']
|
'404 Redirect': [true, 'Redirect dead threads and images.']
|
||||||
'Keybinds': [true, 'Bind actions to keyboard shortcuts.']
|
'Keybinds': [true, 'Bind actions to keyboard shortcuts.']
|
||||||
'Time Formatting': [true, 'Localize and format timestamps arbitrarily.']
|
'Time Formatting': [true, 'Localize and format timestamps arbitrarily.']
|
||||||
|
'Relative Post Dates': [false, 'Display dates like "3 minutes ago". Tooltip shows the timestamp.']
|
||||||
'File Info Formatting': [true, 'Reformat the file information.']
|
'File Info Formatting': [true, 'Reformat the file information.']
|
||||||
'Comment Expansion': [true, 'Can expand too long comments.']
|
'Comment Expansion': [true, 'Can expand too long comments.']
|
||||||
'Thread Expansion': [true, 'Can expand threads to view all replies.']
|
'Thread Expansion': [true, 'Can expand threads to view all replies.']
|
||||||
|
|||||||
@ -1032,7 +1032,7 @@ Redirect =
|
|||||||
when 'u'
|
when 'u'
|
||||||
"//nsfw.foolz.us/#{board}/full_image/#{filename}"
|
"//nsfw.foolz.us/#{board}/full_image/#{filename}"
|
||||||
when 'po'
|
when 'po'
|
||||||
"http://archive.thedarkcave.org/#{board}/full_image/#{filename}"
|
"//archive.thedarkcave.org/#{board}/full_image/#{filename}"
|
||||||
when 'ck', 'lit'
|
when 'ck', 'lit'
|
||||||
"//fuuka.warosu.org/#{board}/full_image/#{filename}"
|
"//fuuka.warosu.org/#{board}/full_image/#{filename}"
|
||||||
when 'diy', 'sci'
|
when 'diy', 'sci'
|
||||||
@ -1049,8 +1049,8 @@ Redirect =
|
|||||||
"//archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
|
"//archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
|
||||||
when 'u', 'kuku'
|
when 'u', 'kuku'
|
||||||
"//nsfw.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
|
"//nsfw.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
|
||||||
when 'po'
|
when 'c', 'int', 'po'
|
||||||
"http://archive.thedarkcave.org/_/api/chan/post/?board=#{board}&num=#{postID}"
|
"//archive.thedarkcave.org/_/api/chan/post/?board=#{board}&num=#{postID}"
|
||||||
# for fuuka-based archives:
|
# for fuuka-based archives:
|
||||||
# https://github.com/eksopl/fuuka/issues/27
|
# https://github.com/eksopl/fuuka/issues/27
|
||||||
to: (data) ->
|
to: (data) ->
|
||||||
@ -1060,8 +1060,8 @@ Redirect =
|
|||||||
url = Redirect.path '//archive.foolz.us', 'foolfuuka', data
|
url = Redirect.path '//archive.foolz.us', 'foolfuuka', data
|
||||||
when 'u', 'kuku'
|
when 'u', 'kuku'
|
||||||
url = Redirect.path '//nsfw.foolz.us', 'foolfuuka', data
|
url = Redirect.path '//nsfw.foolz.us', 'foolfuuka', data
|
||||||
when 'po'
|
when 'int', 'po'
|
||||||
url = Redirect.path 'http://archive.thedarkcave.org', 'foolfuuka', data
|
url = Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data
|
||||||
when 'ck', 'lit'
|
when 'ck', 'lit'
|
||||||
url = Redirect.path '//fuuka.warosu.org', 'fuuka', data
|
url = Redirect.path '//fuuka.warosu.org', 'fuuka', data
|
||||||
when 'diy', 'sci'
|
when 'diy', 'sci'
|
||||||
@ -1998,6 +1998,93 @@ Time =
|
|||||||
S: -> Time.zeroPad @getSeconds()
|
S: -> Time.zeroPad @getSeconds()
|
||||||
y: -> @getFullYear() - 2000
|
y: -> @getFullYear() - 2000
|
||||||
|
|
||||||
|
RelativeDates =
|
||||||
|
INTERVAL: $.MINUTE
|
||||||
|
init: ->
|
||||||
|
return if g.VIEW is 'catalog' or !Conf['Relative Post Dates']
|
||||||
|
|
||||||
|
# flush when page becomes visible again
|
||||||
|
$.on d, 'visibilitychange ThreadUpdate', @flush
|
||||||
|
|
||||||
|
Post::callbacks.push
|
||||||
|
name: 'Relative Post Dates'
|
||||||
|
cb: @node
|
||||||
|
node: ->
|
||||||
|
dateEl = @nodes.date
|
||||||
|
|
||||||
|
# Show original absolute time as tooltip so users can still know exact times
|
||||||
|
# Since "Time Formatting" runs `node` before us, the title tooltip will
|
||||||
|
# pick up the user-formatted time instead of 4chan time when enabled.
|
||||||
|
dateEl.title = dateEl.textContent
|
||||||
|
|
||||||
|
diff = Date.now() - @info.date
|
||||||
|
|
||||||
|
dateEl.textContent = RelativeDates.relative diff
|
||||||
|
RelativeDates.setUpdate @, diff
|
||||||
|
|
||||||
|
# diff is milliseconds from now
|
||||||
|
relative: (diff) ->
|
||||||
|
unit = if (number = (diff / $.DAY)) > 1
|
||||||
|
'day'
|
||||||
|
else if (number = (diff / $.HOUR)) > 1
|
||||||
|
'hour'
|
||||||
|
else if (number = (diff / $.MINUTE)) > 1
|
||||||
|
'minute'
|
||||||
|
else
|
||||||
|
number = diff / $.SECOND
|
||||||
|
'second'
|
||||||
|
|
||||||
|
rounded = Math.round number
|
||||||
|
unit += 's' if rounded isnt 1 # pluralize
|
||||||
|
|
||||||
|
"#{rounded} #{unit} ago"
|
||||||
|
|
||||||
|
# changing all relative dates as soon as possible incurs many annoying
|
||||||
|
# redraws and scroll stuttering. Thus, sacrifice accuracy for UX/CPU economy,
|
||||||
|
# and perform redraws when the DOM is otherwise being manipulated (and scroll
|
||||||
|
# stuttering won't be noticed), falling back to INTERVAL while the page
|
||||||
|
# is visible.
|
||||||
|
#
|
||||||
|
# each individual dateTime element will add its update() function to the stale list
|
||||||
|
# when it is to be called.
|
||||||
|
stale: []
|
||||||
|
flush: $.debounce($.SECOND, ->
|
||||||
|
# no point in changing the dates until the user sees them
|
||||||
|
return if d.hidden
|
||||||
|
|
||||||
|
now = Date.now()
|
||||||
|
update now for update in RelativeDates.stale
|
||||||
|
RelativeDates.stale = []
|
||||||
|
|
||||||
|
# reset automatic flush
|
||||||
|
clearTimeout RelativeDates.timeout
|
||||||
|
RelativeDates.timeout = setTimeout RelativeDates.flush, RelativeDates.INTERVAL)
|
||||||
|
|
||||||
|
# create function `update()`, closed over post and diff, that, when called
|
||||||
|
# from `flush()`, updates the element, and re-calls `setOwnTimeout()` to
|
||||||
|
# re-add `update()` to the stale list later.
|
||||||
|
setUpdate: (post, diff) ->
|
||||||
|
setOwnTimeout = (diff) ->
|
||||||
|
delay = if diff > $.HOUR
|
||||||
|
diff % $.HOUR
|
||||||
|
else if diff > $.MINUTE
|
||||||
|
diff % $.MINUTE
|
||||||
|
else
|
||||||
|
diff % $.SECOND
|
||||||
|
setTimeout markStale, delay
|
||||||
|
|
||||||
|
dateEl = post.nodes.date
|
||||||
|
update = (now) ->
|
||||||
|
if d.contains dateEl # not removed from DOM
|
||||||
|
diff = now - post.info.date
|
||||||
|
dateEl.textContent = RelativeDates.relative diff
|
||||||
|
setOwnTimeout diff
|
||||||
|
|
||||||
|
markStale = -> RelativeDates.stale.push update
|
||||||
|
|
||||||
|
# kick off initial timeout with current diff
|
||||||
|
setOwnTimeout diff
|
||||||
|
|
||||||
FileInfo =
|
FileInfo =
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW is 'catalog' or !Conf['File Info Formatting']
|
return if g.VIEW is 'catalog' or !Conf['File Info Formatting']
|
||||||
@ -2360,7 +2447,7 @@ ThreadUpdater =
|
|||||||
|
|
||||||
$.on window, 'online offline', ThreadUpdater.cb.online
|
$.on window, 'online offline', ThreadUpdater.cb.online
|
||||||
$.on d, 'QRPostSuccessful', ThreadUpdater.cb.post
|
$.on d, 'QRPostSuccessful', ThreadUpdater.cb.post
|
||||||
$.on d, 'visibilitychange ovisibilitychange mozvisibilitychange webkitvisibilitychange', ThreadUpdater.cb.visibility
|
$.on d, 'visibilitychange', ThreadUpdater.cb.visibility
|
||||||
|
|
||||||
ThreadUpdater.cb.online()
|
ThreadUpdater.cb.online()
|
||||||
$.add d.body, ThreadUpdater.dialog
|
$.add d.body, ThreadUpdater.dialog
|
||||||
@ -2387,7 +2474,7 @@ ThreadUpdater =
|
|||||||
ThreadUpdater.outdateCount = 0
|
ThreadUpdater.outdateCount = 0
|
||||||
setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2
|
setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2
|
||||||
visibility: ->
|
visibility: ->
|
||||||
return if $.hidden()
|
return if d.hidden
|
||||||
# Reset the counter when we focus this tab.
|
# Reset the counter when we focus this tab.
|
||||||
ThreadUpdater.outdateCount = 0
|
ThreadUpdater.outdateCount = 0
|
||||||
if ThreadUpdater.seconds > ThreadUpdater.interval
|
if ThreadUpdater.seconds > ThreadUpdater.interval
|
||||||
@ -2396,7 +2483,7 @@ ThreadUpdater =
|
|||||||
ThreadUpdater.scrollBG = if Conf['Scroll BG']
|
ThreadUpdater.scrollBG = if Conf['Scroll BG']
|
||||||
-> true
|
-> true
|
||||||
else
|
else
|
||||||
-> not $.hidden()
|
-> not d.hidden
|
||||||
autoUpdate: ->
|
autoUpdate: ->
|
||||||
if Conf['Auto Update This'] and ThreadUpdater.online
|
if Conf['Auto Update This'] and ThreadUpdater.online
|
||||||
ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000
|
ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000
|
||||||
@ -2447,7 +2534,7 @@ ThreadUpdater =
|
|||||||
getInterval: ->
|
getInterval: ->
|
||||||
i = ThreadUpdater.interval
|
i = ThreadUpdater.interval
|
||||||
j = Math.min ThreadUpdater.outdateCount, 10
|
j = Math.min ThreadUpdater.outdateCount, 10
|
||||||
unless $.hidden()
|
unless d.hidden
|
||||||
# Lower the max refresh rate limit on visible tabs.
|
# Lower the max refresh rate limit on visible tabs.
|
||||||
j = Math.min j, 7
|
j = Math.min j, 7
|
||||||
ThreadUpdater.seconds = Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]
|
ThreadUpdater.seconds = Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]
|
||||||
@ -2523,7 +2610,7 @@ ThreadUpdater =
|
|||||||
else
|
else
|
||||||
ThreadUpdater.set 'status', "+#{count}", 'new'
|
ThreadUpdater.set 'status', "+#{count}", 'new'
|
||||||
ThreadUpdater.outdateCount = 0
|
ThreadUpdater.outdateCount = 0
|
||||||
if Conf['Beep'] and $.hidden() # XXX and !Unread.replies.length
|
if Conf['Beep'] and d.hidden # XXX and !Unread.replies.length
|
||||||
unless ThreadUpdater.audio
|
unless ThreadUpdater.audio
|
||||||
ThreadUpdater.audio = $.el 'audio', src: ThreadUpdater.beep
|
ThreadUpdater.audio = $.el 'audio', src: ThreadUpdater.beep
|
||||||
ThreadUpdater.audio.play()
|
ThreadUpdater.audio.play()
|
||||||
|
|||||||
@ -295,6 +295,7 @@ Main =
|
|||||||
console.timeEnd "#{name} initialization"
|
console.timeEnd "#{name} initialization"
|
||||||
|
|
||||||
console.time 'All initializations'
|
console.time 'All initializations'
|
||||||
|
initFeature 'Polyfill', Polyfill
|
||||||
initFeature 'Header', Header
|
initFeature 'Header', Header
|
||||||
initFeature 'Settings', Settings
|
initFeature 'Settings', Settings
|
||||||
initFeature 'Resurrect Quotes', Quotify
|
initFeature 'Resurrect Quotes', Quotify
|
||||||
@ -318,6 +319,7 @@ Main =
|
|||||||
initFeature 'Mark Cross-thread Quotes', QuoteCT
|
initFeature 'Mark Cross-thread Quotes', QuoteCT
|
||||||
initFeature 'Anonymize', Anonymize
|
initFeature 'Anonymize', Anonymize
|
||||||
initFeature 'Time Formatting', Time
|
initFeature 'Time Formatting', Time
|
||||||
|
initFeature 'Relative Post Dates', RelativeDates
|
||||||
initFeature 'File Info Formatting', FileInfo
|
initFeature 'File Info Formatting', FileInfo
|
||||||
initFeature 'Sauce', Sauce
|
initFeature 'Sauce', Sauce
|
||||||
initFeature 'Image Expansion', ImageExpand
|
initFeature 'Image Expansion', ImageExpand
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user