Merge branch 'master' into v3

This commit is contained in:
Nicolas Stepien 2013-02-14 23:30:44 +01:00
commit 20ece62290
8 changed files with 259 additions and 24 deletions

View File

@ -43,7 +43,7 @@
*/
(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; },
__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; };
@ -55,6 +55,7 @@
'404 Redirect': [true, 'Redirect dead threads and images.'],
'Keybinds': [true, 'Bind actions to keyboard shortcuts.'],
'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.'],
'Comment Expansion': [true, 'Can expand too long comments.'],
'Thread Expansion': [true, 'Can expand threads to view all replies.'],
@ -834,8 +835,19 @@
open: function(url) {
return (GM_openInTab || window.open)(url, '_blank');
},
hidden: function() {
return d.hidden || d.oHidden || d.mozHidden || d.webkitHidden;
debounce: function(wait, fn) {
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() {
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 = {
init: function() {
var boardList, boardListButton, boardTitle, catalogToggler, headerBar, menuButton, toggleBar;
@ -2220,7 +2260,7 @@
case 'u':
return "//nsfw.foolz.us/" + board + "/full_image/" + filename;
case 'po':
return "http://archive.thedarkcave.org/" + board + "/full_image/" + filename;
return "//archive.thedarkcave.org/" + board + "/full_image/" + filename;
case 'ck':
case 'lit':
return "//fuuka.warosu.org/" + board + "/full_image/" + filename;
@ -2263,8 +2303,10 @@
case 'u':
case 'kuku':
return "//nsfw.foolz.us/_/api/chan/post/?board=" + board + "&num=" + postID;
case 'c':
case 'int':
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) {
@ -2290,8 +2332,9 @@
case 'kuku':
url = Redirect.path('//nsfw.foolz.us', 'foolfuuka', data);
break;
case 'int':
case 'po':
url = Redirect.path('http://archive.thedarkcave.org', 'foolfuuka', data);
url = Redirect.path('//archive.thedarkcave.org', 'foolfuuka', data);
break;
case 'ck':
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 = {
init: function() {
if (g.VIEW === 'catalog' || !Conf['File Info Formatting']) {
@ -3839,7 +3949,7 @@
}
$.on(window, 'online offline', ThreadUpdater.cb.online);
$.on(d, 'QRPostSuccessful', ThreadUpdater.cb.post);
$.on(d, 'visibilitychange ovisibilitychange mozvisibilitychange webkitvisibilitychange', ThreadUpdater.cb.visibility);
$.on(d, 'visibilitychange', ThreadUpdater.cb.visibility);
ThreadUpdater.cb.online();
return $.add(d.body, ThreadUpdater.dialog);
},
@ -3874,7 +3984,7 @@
}
},
visibility: function() {
if ($.hidden()) {
if (d.hidden) {
return;
}
ThreadUpdater.outdateCount = 0;
@ -3886,7 +3996,7 @@
return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() {
return true;
} : function() {
return !$.hidden();
return !d.hidden;
};
},
autoUpdate: function() {
@ -3942,7 +4052,7 @@
var i, j;
i = ThreadUpdater.interval;
j = Math.min(ThreadUpdater.outdateCount, 10);
if (!$.hidden()) {
if (!d.hidden) {
j = Math.min(j, 7);
}
return ThreadUpdater.seconds = Math.max(i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]);
@ -4037,7 +4147,7 @@
} else {
ThreadUpdater.set('status', "+" + count, 'new');
ThreadUpdater.outdateCount = 0;
if (Conf['Beep'] && $.hidden()) {
if (Conf['Beep'] && d.hidden) {
if (!ThreadUpdater.audio) {
ThreadUpdater.audio = $.el('audio', {
src: ThreadUpdater.beep
@ -5381,6 +5491,7 @@
return console.timeEnd("" + name + " initialization");
};
console.time('All initializations');
initFeature('Polyfill', Polyfill);
initFeature('Header', Header);
initFeature('Settings', Settings);
initFeature('Resurrect Quotes', Quotify);
@ -5404,6 +5515,7 @@
initFeature('Mark Cross-thread Quotes', QuoteCT);
initFeature('Anonymize', Anonymize);
initFeature('Time Formatting', Time);
initFeature('Relative Post Dates', RelativeDates);
initFeature('File Info Formatting', FileInfo);
initFeature('Sauce', Sauce);
initFeature('Image Expansion', ImageExpand);

View File

@ -26,8 +26,31 @@ alpha
Fix unreadable inlined posts with the Tomorrow theme.
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
Add /po/ archive redirection for threads, images and post resurrection.
Fix quoting.
2.37.3
- Mayhem

View File

@ -19,6 +19,7 @@ module.exports = function(grunt) {
'<file_template:src/globals.coffee>',
'<file_template:lib/ui.coffee>',
'<file_template:lib/$.coffee>',
'<file_template:lib/polyfill.coffee>',
'<file_template:src/features.coffee>',
'<file_template:src/qr.coffee>',
'<file_template:src/main.coffee>'

View File

@ -1 +1 @@
postMessage({version:'2.37.3'},'*')
postMessage({version:'2.38.1'},'*')

View File

@ -140,8 +140,17 @@ $.extend $,
root.dispatchEvent new CustomEvent event, {bubbles: true, detail}
open: (url) ->
(GM_openInTab or window.open) url, '_blank'
hidden: ->
d.hidden or d.oHidden or d.mozHidden or d.webkitHidden
debounce: (wait, fn) ->
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: (->
# inspired by https://www.w3.org/Bugs/Public/show_bug.cgi?id=15007
taskQueue = []

View File

@ -5,6 +5,7 @@ Config =
'404 Redirect': [true, 'Redirect dead threads and images.']
'Keybinds': [true, 'Bind actions to keyboard shortcuts.']
'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.']
'Comment Expansion': [true, 'Can expand too long comments.']
'Thread Expansion': [true, 'Can expand threads to view all replies.']

View File

@ -1032,7 +1032,7 @@ Redirect =
when 'u'
"//nsfw.foolz.us/#{board}/full_image/#{filename}"
when 'po'
"http://archive.thedarkcave.org/#{board}/full_image/#{filename}"
"//archive.thedarkcave.org/#{board}/full_image/#{filename}"
when 'ck', 'lit'
"//fuuka.warosu.org/#{board}/full_image/#{filename}"
when 'diy', 'sci'
@ -1049,8 +1049,8 @@ Redirect =
"//archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
when 'u', 'kuku'
"//nsfw.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
when 'po'
"http://archive.thedarkcave.org/_/api/chan/post/?board=#{board}&num=#{postID}"
when 'c', 'int', 'po'
"//archive.thedarkcave.org/_/api/chan/post/?board=#{board}&num=#{postID}"
# for fuuka-based archives:
# https://github.com/eksopl/fuuka/issues/27
to: (data) ->
@ -1060,8 +1060,8 @@ Redirect =
url = Redirect.path '//archive.foolz.us', 'foolfuuka', data
when 'u', 'kuku'
url = Redirect.path '//nsfw.foolz.us', 'foolfuuka', data
when 'po'
url = Redirect.path 'http://archive.thedarkcave.org', 'foolfuuka', data
when 'int', 'po'
url = Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data
when 'ck', 'lit'
url = Redirect.path '//fuuka.warosu.org', 'fuuka', data
when 'diy', 'sci'
@ -1998,6 +1998,93 @@ Time =
S: -> Time.zeroPad @getSeconds()
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 =
init: ->
return if g.VIEW is 'catalog' or !Conf['File Info Formatting']
@ -2360,7 +2447,7 @@ ThreadUpdater =
$.on window, 'online offline', ThreadUpdater.cb.online
$.on d, 'QRPostSuccessful', ThreadUpdater.cb.post
$.on d, 'visibilitychange ovisibilitychange mozvisibilitychange webkitvisibilitychange', ThreadUpdater.cb.visibility
$.on d, 'visibilitychange', ThreadUpdater.cb.visibility
ThreadUpdater.cb.online()
$.add d.body, ThreadUpdater.dialog
@ -2387,7 +2474,7 @@ ThreadUpdater =
ThreadUpdater.outdateCount = 0
setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2
visibility: ->
return if $.hidden()
return if d.hidden
# Reset the counter when we focus this tab.
ThreadUpdater.outdateCount = 0
if ThreadUpdater.seconds > ThreadUpdater.interval
@ -2396,7 +2483,7 @@ ThreadUpdater =
ThreadUpdater.scrollBG = if Conf['Scroll BG']
-> true
else
-> not $.hidden()
-> not d.hidden
autoUpdate: ->
if Conf['Auto Update This'] and ThreadUpdater.online
ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000
@ -2447,7 +2534,7 @@ ThreadUpdater =
getInterval: ->
i = ThreadUpdater.interval
j = Math.min ThreadUpdater.outdateCount, 10
unless $.hidden()
unless d.hidden
# Lower the max refresh rate limit on visible tabs.
j = Math.min j, 7
ThreadUpdater.seconds = Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]
@ -2523,7 +2610,7 @@ ThreadUpdater =
else
ThreadUpdater.set 'status', "+#{count}", 'new'
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
ThreadUpdater.audio = $.el 'audio', src: ThreadUpdater.beep
ThreadUpdater.audio.play()

View File

@ -295,6 +295,7 @@ Main =
console.timeEnd "#{name} initialization"
console.time 'All initializations'
initFeature 'Polyfill', Polyfill
initFeature 'Header', Header
initFeature 'Settings', Settings
initFeature 'Resurrect Quotes', Quotify
@ -318,6 +319,7 @@ Main =
initFeature 'Mark Cross-thread Quotes', QuoteCT
initFeature 'Anonymize', Anonymize
initFeature 'Time Formatting', Time
initFeature 'Relative Post Dates', RelativeDates
initFeature 'File Info Formatting', FileInfo
initFeature 'Sauce', Sauce
initFeature 'Image Expansion', ImageExpand