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() { (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);

View File

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

View File

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

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} 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 = []

View File

@ -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.']

View File

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

View File

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