Merge branch 'v3' of git://github.com/MayhemYDG/4chan-x into v3

Conflicts:
	CHANGELOG.md
	html/General/Settings-section-Rice.html
	img/changelog/3.9.0/0.png
	package.json
	src/Images/AutoGIF.coffee
	src/Images/RevealSpoilers.coffee
	src/Miscellaneous/ExpandComment.coffee
	src/Miscellaneous/IDColor.coffee
	src/Monitoring/Unread.coffee
	src/Posting/QuickReply.coffee
	src/Quotelinks/QuoteInline.coffee
	src/Quotelinks/QuotePreview.coffee
	src/Quotelinks/QuoteYou.coffee
	src/Quotelinks/Quotify.coffee
This commit is contained in:
Zixaphir 2013-10-13 13:53:38 -07:00
commit 91cedb3b89
51 changed files with 499 additions and 191 deletions

View File

@ -1,28 +1,29 @@
**MayhemYDG**:
- Tiny posting cooldown adjustment:
* You can post an image reply immediately after a non-image reply.
- Update posting cooldown timers to match 4chan settings:
* Cooldown may vary between inter-thread and intra-thread replies.
* Cooldown may vary when posting a file or not.
* Cooldown does not take sageing into account anymore.
* Timers vary across boards.
- Updated post and deletion cooldown timers to match 4chan changes: they are now twice longer.
- Added support for the flag selector on /pol/.
- Tiny posting cooldown adjustment:
* You can post an image reply immediately after a non-image reply.
- Update posting cooldown timers to match 4chan settings:
* Cooldown may vary between inter-thread and intra-thread replies.
* Cooldown may vary when posting a file or not.
* Cooldown does not take sageing into account anymore.
* Timers vary across boards.
- Updated post and deletion cooldown timers to match 4chan changes: they are now twice longer.
- Added support for the flag selector on /pol/.
- Minor Chrome 30 fix.
### v1.2.39
*2013-09-19*
**seaweedchan**:
- Fix thread updater bug introduced in last version
- Fix thread updater bug introduced in last version
### v1.2.38
*2013-09-19*
**MayhemYDG**:
- Update posting cooldown timers to match 4chan settings:
- Cooldown may vary between inter-thread and intra-thread replies.
- Cooldown may vary when posting a file or not.
- Cooldown does not take sageing into account anymore.
- Timers vary across boards.
- Update posting cooldown timers to match 4chan settings:
- Cooldown may vary between inter-thread and intra-thread replies.
- Cooldown may vary when posting a file or not.
- Cooldown does not take sageing into account anymore.
- Timers vary across boards.
### v1.2.37
*2013-09-12*
@ -37,10 +38,10 @@
- New desktop notification:
* The QR will now warn you when you are running low on cached captchas while auto-posting.
**seaweedchan**:
**seaweedchan**:
- Visual overhaul for gallery mode
**Zixaphir**:
**Zixaphir**:
- Fix an issue with the file dialog randomly opening multiple times (with seaweedchan)
![Gallery](src/General/img/changelog/2.3.6.png)
- Add new feature: `Gallery`.

View File

@ -1,5 +1,5 @@
/*
* 4chan X - Version 1.2.39 - 2013-09-24
* 4chan X - Version 1.2.39 - 2013-10-13
*
* Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE

View File

@ -22,7 +22,7 @@
// ==/UserScript==
/*
* 4chan X - Version 1.2.39 - 2013-09-24
* 4chan X - Version 1.2.39 - 2013-10-13
*
* Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -104,7 +104,7 @@
'use strict';
(function() {
var $, $$, Anonymize, ArchiveLink, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
__slice = [].slice,
__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; },
@ -845,7 +845,7 @@
})();
Thread = (function() {
Thread.prototype.callbacks = [];
Thread.callbacks = [];
Thread.prototype.toString = function() {
return this.ID;
@ -869,7 +869,7 @@
})();
Post = (function() {
Post.prototype.callbacks = [];
Post.callbacks = [];
Post.prototype.toString = function() {
return this.ID;
@ -2763,7 +2763,7 @@
if (g.VIEW === 'catalog' || !Conf['Anonymize']) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Anonymize',
cb: this.node
});
@ -2854,7 +2854,7 @@
if (!Object.keys(this.filters).length) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Filter',
cb: this.node
});
@ -3084,7 +3084,7 @@
$.addClass(doc, "reply-hide");
}
this.db = new DataBoard('hiddenPosts');
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Reply Hiding',
cb: this.node
});
@ -3387,7 +3387,7 @@
if (g.VIEW === 'catalog') {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Recursive',
cb: this.node
});
@ -3458,7 +3458,7 @@
}
this.db = new DataBoard('hiddenThreads');
this.syncCatalog();
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Hiding',
cb: this.node
});
@ -3727,11 +3727,11 @@
format = Conf['backlink'].replace(/%id/g, "' + id + '");
this.funk = Function('id', "return '" + format + "'");
this.containers = {};
Post.prototype.callbacks.push({
Post.callbacks.push({
name: 'Quote Backlinking Part 1',
cb: this.firstNode
});
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quote Backlinking Part 2',
cb: this.secondNode
});
@ -3806,7 +3806,7 @@
ExpandComment.callbacks.push(this.node);
}
this.text = '\u00A0(Cross-thread)';
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Mark Cross-thread Quotes',
cb: this.node
});
@ -3867,7 +3867,7 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quote Inlining',
cb: this.node
});
@ -3969,7 +3969,7 @@
ExpandComment.callbacks.push(this.node);
}
this.text = '\u00A0(OP)';
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Mark OP Quotes',
cb: this.node
});
@ -4012,7 +4012,7 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quote Previewing',
cb: this.node
});
@ -4095,7 +4095,7 @@
if (g.VIEW === 'catalog' || !Conf['Reply Hiding Buttons'] && !Conf['Reply Hiding Link'] && !Conf['Filter']) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Strike-through Quotes',
cb: this.node
});
@ -4141,7 +4141,7 @@
order: 98
});
$.on(d, '4chanXInitFinished', this.setup);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quote Threading',
cb: this.node
});
@ -4278,7 +4278,8 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
return Post.prototype.callbacks.push({
this.text = '\u00A0(You)';
return Post.callbacks.push({
name: 'Mark Quotes of You',
cb: this.node
});
@ -4301,7 +4302,7 @@
if (!(QR.db.get(Get.postDataFromLink(quotelink)))) {
continue;
}
$.add(quotelink, $.tn('\u00A0(You)'));
$.add(quotelink, $.tn(QuoteYou.text));
$.addClass(quotelink, 'you');
$.addClass(this.nodes.root, 'quotesYou');
}
@ -4358,7 +4359,7 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Resurrect Quotes',
cb: this.node
});
@ -4450,6 +4451,15 @@
}
},
fixDeadlink: function(deadlink) {
var el, green;
if (!(el = deadlink.previousSibling) || el.nodeName === 'BR') {
green = $.el('span', {
className: 'quote'
});
$.before(deadlink, green);
$.add(green, deadlink);
}
return $.replace(deadlink, __slice.call(deadlink.childNodes));
}
};
@ -4466,7 +4476,7 @@
if (Conf['Title Link']) {
$.sync('CachedTitles', Linkify.titleSync);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Linkify',
cb: this.node
});
@ -4958,7 +4968,7 @@
$.ready(this.persist);
}
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quick Reply',
cb: this.node
});
@ -5479,8 +5489,7 @@
}
e.preventDefault();
QR.open();
QR.handleFiles(e.dataTransfer.files);
return $.addClass(QR.nodes.el, 'dump');
return QR.handleFiles(e.dataTransfer.files);
},
paste: function(e) {
var blob, files, item, _i, _len, _ref;
@ -6409,6 +6418,10 @@
threadID: threadID,
postID: postID
});
$.event('QRPostSuccessful_', {
threadID: threadID,
postID: postID
});
postsCount = QR.posts.length - 1;
QR.cooldown.auto = postsCount && isReply;
if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) {
@ -6460,6 +6473,40 @@
}
};
AutoGIF = {
init: function() {
var _ref;
if (g.VIEW === 'catalog' || !Conf['Auto-GIF'] || ((_ref = g.BOARD.ID) === 'gif' || _ref === 'wsg')) {
return;
}
return Post.callbacks.push({
name: 'Auto-GIF',
cb: this.node
});
},
node: function() {
var URL, gif, style, thumb, _ref, _ref1;
if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) {
return;
}
_ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL;
if (!(/gif$/.test(URL) && !/spoiler/.test(thumb.src))) {
return;
}
if (this.file.isSpoiler) {
style = thumb.style;
style.maxHeight = style.maxWidth = this.isReply ? '125px' : '250px';
}
gif = $.el('img');
$.on(gif, 'load', function() {
return thumb.src = URL;
});
return gif.src = URL;
}
};
FappeTyme = {
init: function() {
var el, input;
@ -6493,7 +6540,7 @@
order: 98
});
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Fappe Tyme',
cb: this.node
});
@ -6532,7 +6579,7 @@
});
$.on(el, 'click', this.cb.toggle);
Header.addShortcut(el);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Gallery',
cb: this.node
});
@ -6832,7 +6879,7 @@
});
$.on(this.EAI, 'click', ImageExpand.cb.toggleAll);
Header.addShortcut(this.EAI, 2);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Image Expansion',
cb: this.node
});
@ -7091,7 +7138,7 @@
if (g.VIEW === 'catalog' || !Conf['Image Hover']) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Image Hover',
cb: this.node
});
@ -7186,7 +7233,7 @@
if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"])) {
return;
}
Post.prototype.callbacks.push({
Post.callbacks.push({
name: 'Image Replace',
cb: this.node
});
@ -7245,8 +7292,7 @@
if (g.VIEW === 'catalog' || !Conf['Reveal Spoiler Thumbnails']) {
return;
}
return Post.prototype.callbacks.push({
name: 'Reveal Spoiler Thumbnails',
return Post.callbacks.push({
cb: this.node
});
},
@ -7288,7 +7334,7 @@
this.link = $.el('a', {
target: '_blank'
});
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Sauce',
cb: this.node
});
@ -7582,7 +7628,7 @@
return;
}
this.menu = new UI.Menu('post');
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Menu',
cb: this.node
});
@ -7716,7 +7762,7 @@
if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) {
return;
}
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Excerpt',
cb: this.node
});
@ -7752,7 +7798,7 @@
this.postCountEl = $('#post-count', sc);
this.fileCountEl = $('#file-count', sc);
this.pageCountEl = $('#page-count', sc);
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Stats',
cb: this.node
});
@ -7897,7 +7943,7 @@
order: 110,
subEntries: subEntries
});
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Updater',
cb: this.node
});
@ -8262,7 +8308,7 @@
ThreadWatcher.fetchAllStatus();
this.db.save();
}
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Watcher',
cb: this.node
});
@ -8722,7 +8768,7 @@
});
this.posts = [];
this.postsQuotingYou = [];
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Unread',
cb: this.node
});
@ -8855,6 +8901,7 @@
}
Unread.postsQuotingYou.push(post);
Unread.openNotification(post);
return;
}
},
openNotification: function(post) {
@ -9432,7 +9479,7 @@
return;
}
this.ids = {};
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Color User IDs',
cb: this.node
});
@ -9506,7 +9553,7 @@
if (g.BOARD.ID !== 'tg' || g.VIEW === 'catalog' || !Conf['Show Dice Roll']) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Show Dice Roll',
cb: this.node
});
@ -9588,7 +9635,7 @@
if (g.BOARD.ID === 'sci') {
this.callbacks.push(Fourchan.math);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Comment Expansion',
cb: this.node
});
@ -9684,7 +9731,7 @@
if (g.VIEW !== 'index' || !Conf['Thread Expansion']) {
return;
}
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Expansion',
cb: this.node
});
@ -9840,7 +9887,7 @@
return;
}
this.funk = this.createFunc(Conf['fileInfo']);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'File Info Formatting',
cb: this.node
});
@ -9947,14 +9994,14 @@
board = g.BOARD.ID;
if (board === 'g') {
$.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML);\n}, false);");
Post.prototype.callbacks.push({
Post.callbacks.push({
name: 'Parse /g/ code',
cb: this.code
});
}
if (board === 'sci') {
$.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(e.detail);\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);");
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Parse /sci/ math',
cb: this.math
});
@ -9988,6 +10035,54 @@
}
};
IDColor = {
init: function() {
if (g.VIEW === 'catalog' || !Conf['Color User IDs']) {
return;
}
this.ids = {};
return Post.callbacks.push({
name: 'Color User IDs',
cb: this.node
});
},
node: function() {
var rgb, span, style, uid;
if (this.isClone || !(uid = this.info.uniqueID)) {
return;
}
rgb = IDColor.compute(uid);
span = this.nodes.uniqueID;
style = span.style;
style.color = rgb[3];
style.backgroundColor = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
$.addClass(span, 'painted');
span.textContent = uid;
return span.title = 'Highlight posts by this ID';
},
compute: function(uniqueID) {
var hash, rgb;
if (uniqueID in IDColor.ids) {
return IDColor.ids[uniqueID];
}
hash = this.hash(uniqueID);
rgb = [(hash >> 24) & 0xFF, (hash >> 16) & 0xFF, (hash >> 8) & 0xFF];
rgb.push((rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 170 ? 'black' : 'white');
return this.ids[uniqueID] = rgb;
},
hash: function(uniqueID) {
var i, msg, _i, _ref;
msg = 0;
for (i = _i = 0, _ref = uniqueID.length; _i < _ref; i = _i += 1) {
msg = (msg << 5) - msg + uniqueID.charCodeAt(i);
}
return msg;
}
};
Keybinds = {
init: function() {
var init;
@ -10442,7 +10537,7 @@
}
$.on(d, 'visibilitychange ThreadUpdate', this.flush);
this.flush();
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Relative Post Dates',
cb: this.node
});
@ -10526,7 +10621,7 @@
return "[spoiler]" + text + "[/spoiler]";
};
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Reveal Spoilers',
cb: this.node
});
@ -10582,7 +10677,7 @@
return;
}
this.funk = this.createFunc(Conf['time']);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Time Formatting',
cb: this.node
});
@ -11616,7 +11711,7 @@
var callback, err, errors, i, len, node, _i, _len, _ref;
len = nodes.length;
_ref = klass.prototype.callbacks;
_ref = klass.callbacks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
callback = _ref[_i];
i = 0;
@ -11715,7 +11810,7 @@
return;
}
obj.callback.isAddon = true;
return Klass.prototype.callbacks.push(obj.callback);
return Klass.callbacks.push(obj.callback);
},
handleErrors: function(errors) {
var div, error, logs, _i, _len;

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript
/*
* 4chan X - Version 1.2.39 - 2013-09-24
* 4chan X - Version 1.2.39 - 2013-10-13
*
* Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -82,7 +82,7 @@
'use strict';
(function() {
var $, $$, Anonymize, ArchiveLink, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, 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; },
__slice = [].slice,
__hasProp = {}.hasOwnProperty,
@ -854,7 +854,7 @@
})();
Thread = (function() {
Thread.prototype.callbacks = [];
Thread.callbacks = [];
Thread.prototype.toString = function() {
return this.ID;
@ -878,7 +878,7 @@
})();
Post = (function() {
Post.prototype.callbacks = [];
Post.callbacks = [];
Post.prototype.toString = function() {
return this.ID;
@ -2770,7 +2770,7 @@
if (g.VIEW === 'catalog' || !Conf['Anonymize']) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Anonymize',
cb: this.node
});
@ -2861,7 +2861,7 @@
if (!Object.keys(this.filters).length) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Filter',
cb: this.node
});
@ -3091,7 +3091,7 @@
$.addClass(doc, "reply-hide");
}
this.db = new DataBoard('hiddenPosts');
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Reply Hiding',
cb: this.node
});
@ -3394,7 +3394,7 @@
if (g.VIEW === 'catalog') {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Recursive',
cb: this.node
});
@ -3465,7 +3465,7 @@
}
this.db = new DataBoard('hiddenThreads');
this.syncCatalog();
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Hiding',
cb: this.node
});
@ -3734,11 +3734,11 @@
format = Conf['backlink'].replace(/%id/g, "' + id + '");
this.funk = Function('id', "return '" + format + "'");
this.containers = {};
Post.prototype.callbacks.push({
Post.callbacks.push({
name: 'Quote Backlinking Part 1',
cb: this.firstNode
});
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quote Backlinking Part 2',
cb: this.secondNode
});
@ -3813,7 +3813,7 @@
ExpandComment.callbacks.push(this.node);
}
this.text = '\u00A0(Cross-thread)';
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Mark Cross-thread Quotes',
cb: this.node
});
@ -3874,7 +3874,7 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quote Inlining',
cb: this.node
});
@ -3976,7 +3976,7 @@
ExpandComment.callbacks.push(this.node);
}
this.text = '\u00A0(OP)';
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Mark OP Quotes',
cb: this.node
});
@ -4019,7 +4019,7 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quote Previewing',
cb: this.node
});
@ -4102,7 +4102,7 @@
if (g.VIEW === 'catalog' || !Conf['Reply Hiding Buttons'] && !Conf['Reply Hiding Link'] && !Conf['Filter']) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Strike-through Quotes',
cb: this.node
});
@ -4148,7 +4148,7 @@
order: 98
});
$.on(d, '4chanXInitFinished', this.setup);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quote Threading',
cb: this.node
});
@ -4285,7 +4285,8 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
return Post.prototype.callbacks.push({
this.text = '\u00A0(You)';
return Post.callbacks.push({
name: 'Mark Quotes of You',
cb: this.node
});
@ -4308,7 +4309,7 @@
if (!(QR.db.get(Get.postDataFromLink(quotelink)))) {
continue;
}
$.add(quotelink, $.tn('\u00A0(You)'));
$.add(quotelink, $.tn(QuoteYou.text));
$.addClass(quotelink, 'you');
$.addClass(this.nodes.root, 'quotesYou');
}
@ -4365,7 +4366,7 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Resurrect Quotes',
cb: this.node
});
@ -4457,6 +4458,15 @@
}
},
fixDeadlink: function(deadlink) {
var el, green;
if (!(el = deadlink.previousSibling) || el.nodeName === 'BR') {
green = $.el('span', {
className: 'quote'
});
$.before(deadlink, green);
$.add(green, deadlink);
}
return $.replace(deadlink, __slice.call(deadlink.childNodes));
}
};
@ -4473,7 +4483,7 @@
if (Conf['Title Link']) {
$.sync('CachedTitles', Linkify.titleSync);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Linkify',
cb: this.node
});
@ -4965,7 +4975,7 @@
$.ready(this.persist);
}
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Quick Reply',
cb: this.node
});
@ -5491,8 +5501,7 @@
}
e.preventDefault();
QR.open();
QR.handleFiles(e.dataTransfer.files);
return $.addClass(QR.nodes.el, 'dump');
return QR.handleFiles(e.dataTransfer.files);
},
paste: function(e) {
var blob, files, item, _i, _len, _ref;
@ -6396,6 +6405,10 @@
threadID: threadID,
postID: postID
});
$.event('QRPostSuccessful_', {
threadID: threadID,
postID: postID
});
postsCount = QR.posts.length - 1;
QR.cooldown.auto = postsCount && isReply;
if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) {
@ -6447,6 +6460,40 @@
}
};
AutoGIF = {
init: function() {
var _ref;
if (g.VIEW === 'catalog' || !Conf['Auto-GIF'] || ((_ref = g.BOARD.ID) === 'gif' || _ref === 'wsg')) {
return;
}
return Post.callbacks.push({
name: 'Auto-GIF',
cb: this.node
});
},
node: function() {
var URL, gif, style, thumb, _ref, _ref1;
if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) {
return;
}
_ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL;
if (!(/gif$/.test(URL) && !/spoiler/.test(thumb.src))) {
return;
}
if (this.file.isSpoiler) {
style = thumb.style;
style.maxHeight = style.maxWidth = this.isReply ? '125px' : '250px';
}
gif = $.el('img');
$.on(gif, 'load', function() {
return thumb.src = URL;
});
return gif.src = URL;
}
};
FappeTyme = {
init: function() {
var el, input;
@ -6480,7 +6527,7 @@
order: 98
});
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Fappe Tyme',
cb: this.node
});
@ -6519,7 +6566,7 @@
});
$.on(el, 'click', this.cb.toggle);
Header.addShortcut(el);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Gallery',
cb: this.node
});
@ -6819,7 +6866,7 @@
});
$.on(this.EAI, 'click', ImageExpand.cb.toggleAll);
Header.addShortcut(this.EAI, 2);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Image Expansion',
cb: this.node
});
@ -7078,7 +7125,7 @@
if (g.VIEW === 'catalog' || !Conf['Image Hover']) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Image Hover',
cb: this.node
});
@ -7173,7 +7220,7 @@
if (!(Conf["Image Prefetching"] || Conf["Replace JPG"] || Conf["Replace PNG"] || Conf["Replace GIF"])) {
return;
}
Post.prototype.callbacks.push({
Post.callbacks.push({
name: 'Image Replace',
cb: this.node
});
@ -7232,8 +7279,7 @@
if (g.VIEW === 'catalog' || !Conf['Reveal Spoiler Thumbnails']) {
return;
}
return Post.prototype.callbacks.push({
name: 'Reveal Spoiler Thumbnails',
return Post.callbacks.push({
cb: this.node
});
},
@ -7275,7 +7321,7 @@
this.link = $.el('a', {
target: '_blank'
});
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Sauce',
cb: this.node
});
@ -7569,7 +7615,7 @@
return;
}
this.menu = new UI.Menu('post');
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Menu',
cb: this.node
});
@ -7703,7 +7749,7 @@
if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) {
return;
}
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Excerpt',
cb: this.node
});
@ -7739,7 +7785,7 @@
this.postCountEl = $('#post-count', sc);
this.fileCountEl = $('#file-count', sc);
this.pageCountEl = $('#page-count', sc);
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Stats',
cb: this.node
});
@ -7884,7 +7930,7 @@
order: 110,
subEntries: subEntries
});
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Updater',
cb: this.node
});
@ -8249,7 +8295,7 @@
ThreadWatcher.fetchAllStatus();
this.db.save();
}
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Watcher',
cb: this.node
});
@ -8709,7 +8755,7 @@
});
this.posts = [];
this.postsQuotingYou = [];
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Unread',
cb: this.node
});
@ -8842,6 +8888,7 @@
}
Unread.postsQuotingYou.push(post);
Unread.openNotification(post);
return;
}
},
openNotification: function(post) {
@ -9425,7 +9472,7 @@
return;
}
this.ids = {};
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Color User IDs',
cb: this.node
});
@ -9499,7 +9546,7 @@
if (g.BOARD.ID !== 'tg' || g.VIEW === 'catalog' || !Conf['Show Dice Roll']) {
return;
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Show Dice Roll',
cb: this.node
});
@ -9581,7 +9628,7 @@
if (g.BOARD.ID === 'sci') {
this.callbacks.push(Fourchan.math);
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Comment Expansion',
cb: this.node
});
@ -9677,7 +9724,7 @@
if (g.VIEW !== 'index' || !Conf['Thread Expansion']) {
return;
}
return Thread.prototype.callbacks.push({
return Thread.callbacks.push({
name: 'Thread Expansion',
cb: this.node
});
@ -9833,7 +9880,7 @@
return;
}
this.funk = this.createFunc(Conf['fileInfo']);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'File Info Formatting',
cb: this.node
});
@ -9940,14 +9987,14 @@
board = g.BOARD.ID;
if (board === 'g') {
$.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML);\n}, false);");
Post.prototype.callbacks.push({
Post.callbacks.push({
name: 'Parse /g/ code',
cb: this.code
});
}
if (board === 'sci') {
$.globalEval("window.addEventListener('jsmath', function(e) {\n if (jsMath.loaded) {\n // process one post\n jsMath.ProcessBeforeShowing(e.detail);\n } else {\n // load jsMath and process whole document\n jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);\n jsMath.Autoload.LoadJsMath();\n }\n}, false);");
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Parse /sci/ math',
cb: this.math
});
@ -9981,6 +10028,54 @@
}
};
IDColor = {
init: function() {
if (g.VIEW === 'catalog' || !Conf['Color User IDs']) {
return;
}
this.ids = {};
return Post.callbacks.push({
name: 'Color User IDs',
cb: this.node
});
},
node: function() {
var rgb, span, style, uid;
if (this.isClone || !(uid = this.info.uniqueID)) {
return;
}
rgb = IDColor.compute(uid);
span = this.nodes.uniqueID;
style = span.style;
style.color = rgb[3];
style.backgroundColor = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
$.addClass(span, 'painted');
span.textContent = uid;
return span.title = 'Highlight posts by this ID';
},
compute: function(uniqueID) {
var hash, rgb;
if (uniqueID in IDColor.ids) {
return IDColor.ids[uniqueID];
}
hash = this.hash(uniqueID);
rgb = [(hash >> 24) & 0xFF, (hash >> 16) & 0xFF, (hash >> 8) & 0xFF];
rgb.push((rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 170 ? 'black' : 'white');
return this.ids[uniqueID] = rgb;
},
hash: function(uniqueID) {
var i, msg, _i, _ref;
msg = 0;
for (i = _i = 0, _ref = uniqueID.length; _i < _ref; i = _i += 1) {
msg = (msg << 5) - msg + uniqueID.charCodeAt(i);
}
return msg;
}
};
Keybinds = {
init: function() {
var init;
@ -10435,7 +10530,7 @@
}
$.on(d, 'visibilitychange ThreadUpdate', this.flush);
this.flush();
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Relative Post Dates',
cb: this.node
});
@ -10519,7 +10614,7 @@
return "[spoiler]" + text + "[/spoiler]";
};
}
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Reveal Spoilers',
cb: this.node
});
@ -10575,7 +10670,7 @@
return;
}
this.funk = this.createFunc(Conf['time']);
return Post.prototype.callbacks.push({
return Post.callbacks.push({
name: 'Time Formatting',
cb: this.node
});
@ -11607,7 +11702,7 @@
var callback, err, errors, i, len, node, _i, _len, _ref;
len = nodes.length;
_ref = klass.prototype.callbacks;
_ref = klass.callbacks;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
callback = _ref[_i];
i = 0;
@ -11706,7 +11801,7 @@
return;
}
obj.callback.isAddon = true;
return Klass.prototype.callbacks.push(obj.callback);
return Klass.callbacks.push(obj.callback);
},
handleErrors: function(errors) {
var div, error, logs, _i, _len;

View File

@ -0,0 +1,60 @@
<fieldset>
<legend>Custom Board Navigation <span class="warning" #{if Conf['Custom Board Navigation'] then 'hidden' else ''}>is disabled.</span></legend>
<div><input name="boardnav" class="field" spellcheck="false"></div>
<div>In the following, <code>board</code> can translate to a board ID (<code>a</code>, <code>b</code>, etc...), the current board (<code>current</code>), or the Twitter link (<code>@</code>).</div>
<div>Board link: <code>board</code></div>
<div>Title link: <code>board-title</code></div>
<div>Board link (Replace with title when on that board): <code>board-replace</code></div>
<div>Full text link: <code>board-full</code></div>
<div>Custom text link: <code>board-text:"VIP Board"</code></div>
<div>Index-only link: <code>board-index</code></div>
<div>Catalog-only link: <code>board-catalog</code></div>
<div>Combinations are possible: <code>board-index-text:"VIP Index"</code></div>
<div>Full board list toggle: <code>toggle-all</code></div>
</fieldset>
<fieldset>
<legend>Time Formatting <span class="warning" #{if Conf['Time Formatting'] then 'hidden' else ''}>is disabled.</span></legend>
<div><input name="time" class="field" spellcheck="false">: <span class="time-preview"></span></div>
<div>Supported <a href="//en.wikipedia.org/wiki/Date_%28Unix%29#Formatting">format specifiers</a>:</div>
<div>Day: <code>%a</code>, <code>%A</code>, <code>%d</code>, <code>%e</code></div>
<div>Month: <code>%m</code>, <code>%b</code>, <code>%B</code></div>
<div>Year: <code>%y</code></div>
<div>Hour: <code>%k</code>, <code>%H</code>, <code>%l</code>, <code>%I</code>, <code>%p</code>, <code>%P</code></div>
<div>Minute: <code>%M</code></div>
<div>Second: <code>%S</code></div>
</fieldset>
<fieldset>
<legend>Quote Backlinks formatting <span class="warning" #{if Conf['Quote Backlinks'] then 'hidden' else ''}>is disabled.</span></legend>
<div><input name="backlink" class="field" spellcheck="false">: <span class="backlink-preview"></span></div>
</fieldset>
<fieldset>
<legend>File Info Formatting <span class="warning" #{if Conf['File Info Formatting'] then 'hidden' else ''}>is disabled.</span></legend>
<div><input name="fileInfo" class="field" spellcheck="false">: <span class="fileText file-info-preview"></span></div>
<div>Link: <code>%l</code> (truncated), <code>%L</code> (untruncated), <code>%T</code> (Unix timestamp)</div>
<div>Original file name: <code>%n</code> (truncated), <code>%N</code> (untruncated), <code>%t</code> (Unix timestamp)</div>
<div>Spoiler indicator: <code>%p</code></div>
<div>Size: <code>%B</code> (Bytes), <code>%K</code> (KB), <code>%M</code> (MB), <code>%s</code> (4chan default)</div>
<div>Resolution: <code>%r</code> (Displays 'PDF' for PDF files)</div>
</fieldset>
<fieldset>
<legend>Unread Tab Icon <span class="warning" #{if Conf['Unread Tab Icon'] then 'hidden' else ''}>is disabled.</span></legend>
<select name="favicon">
<option value="ferongr">ferongr</option>
<option value="xat-">xat-</option>
<option value="Mayhem">Mayhem</option>
<option value="Original">Original</option>
</select>
<span class="favicon-preview"></span>
</fieldset>
<fieldset>
<legend>
<label><input type="checkbox" name="Custom CSS" #{if Conf['Custom CSS'] then 'checked' else ''}> Custom CSS</label>
</legend>
<button id="apply-css">Apply CSS</button>
<textarea name="usercss" class="field" spellcheck="false" #{if Conf['Custom CSS'] then '' else 'disabled'}></textarea>
</fieldset>

BIN
img/changelog/3.9.0/0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -43,15 +43,6 @@
"software": "foolfuuka",
"boards": ["c", "w", "wg"],
"files": ["c", "w", "wg"]
}, {
"uid": 11,
"name": "Foolz a Shit",
"domain": "archive.foolzashit.com",
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["adv", "asp", "cm", "d", "e", "i", "lgbt", "n", "o", "p", "pol", "s", "s4s", "t", "trv", "y"],
"files": ["cm", "d", "e", "i", "n", "o", "p", "s", "trv", "y"]
}, {
"uid": 12,
"name": "FapArchive",
@ -59,8 +50,8 @@
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"],
"files": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"]
"boards": ["b", "con", "e", "h", "hc", "p", "s", "soc", "sp", "u"],
"files": ["b", "con", "e", "h", "hc", "p", "s", "soc", "sp", "u"]
}, {
"uid": 7,
"name": "Install Gentoo",
@ -77,8 +68,8 @@
"http": true,
"https": true,
"software": "fuuka",
"boards": ["cgl", "g", "mu", "w"],
"files": ["cgl", "g", "mu", "w"]
"boards": ["cgl", "con", "g", "mu", "w"],
"files": ["cgl", "con", "g", "mu", "w"]
}, {
"uid": 9,
"name": "Heinessen",

View File

@ -23,7 +23,7 @@
"font-awesome": "git://github.com/MayhemYDG/Font-Awesome.git#df4285951124f9ca1f3907438462e5ba9e464bcb",
"grunt": "~0.4.1",
"grunt-bump": "~0.0.11",
"grunt-concurrent": "~0.3.1",
"grunt-concurrent": "~0.4.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-compress": "~0.5.2",

View File

@ -2,7 +2,7 @@ Anonymize =
init: ->
return if g.VIEW is 'catalog' or !Conf['Anonymize']
Post::callbacks.push
Post.callbacks.push
name: 'Anonymize'
cb: @node
node: ->

View File

@ -67,7 +67,7 @@ Filter =
delete @filters[key]
return unless Object.keys(@filters).length
Post::callbacks.push
Post.callbacks.push
name: 'Filter'
cb: @node

View File

@ -6,7 +6,7 @@ PostHiding =
$.addClass doc, "reply-hide"
@db = new DataBoard 'hiddenPosts'
Post::callbacks.push
Post.callbacks.push
name: 'Reply Hiding'
cb: @node

View File

@ -3,7 +3,7 @@ Recursive =
init: ->
return if g.VIEW is 'catalog'
Post::callbacks.push
Post.callbacks.push
name: 'Recursive'
cb: @node
@ -35,4 +35,4 @@ Recursive =
for ID, post of g.posts
if post.quotes.contains fullID
recursive post, args...
return
return

View File

@ -4,7 +4,7 @@ ThreadHiding =
@db = new DataBoard 'hiddenThreads'
@syncCatalog()
Thread::callbacks.push
Thread.callbacks.push
name: 'Thread Hiding'
cb: @node

View File

@ -234,7 +234,7 @@ Main =
callbackNodes: (klass, nodes) ->
# get the nodes' length only once
len = nodes.length
for callback in klass::callbacks
for callback in klass.callbacks
# c.profile callback.name
i = 0
while i < len
@ -301,7 +301,7 @@ Main =
else
return
obj.callback.isAddon = true
Klass::callbacks.push obj.callback
Klass.callbacks.push obj.callback
handleErrors: (errors) ->
unless errors instanceof Array

View File

@ -1,5 +1,5 @@
class Post
callbacks: []
@callbacks = []
toString: -> @ID
constructor: (root, @thread, @board, that={}) ->

View File

@ -1,5 +1,5 @@
class Thread
callbacks: []
@callbacks = []
toString: -> @ID
constructor: (@ID, @board) ->

20
src/Images/AutoGIF.coffee Normal file
View File

@ -0,0 +1,20 @@
AutoGIF =
init: ->
return if g.VIEW is 'catalog' or !Conf['Auto-GIF'] or g.BOARD.ID in ['gif', 'wsg']
Post.callbacks.push
name: 'Auto-GIF'
cb: @node
node: ->
return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage
{thumb, URL} = @file
return unless /gif$/.test(URL) and !/spoiler/.test thumb.src
if @file.isSpoiler
# Revealed spoilers do not have height/width set, this fixes auto-gifs dimensions.
{style} = thumb
style.maxHeight = style.maxWidth = if @isReply then '125px' else '250px'
gif = $.el 'img'
$.on gif, 'load', ->
# Replace the thumbnail once the GIF has finished loading.
thumb.src = URL
gif.src = URL

View File

@ -30,7 +30,7 @@ FappeTyme =
el: el
order: 98
Post::callbacks.push
Post.callbacks.push
name: 'Fappe Tyme'
cb: @node

View File

@ -13,7 +13,7 @@ Gallery =
Header.addShortcut el
Post::callbacks.push
Post.callbacks.push
name: 'Gallery'
cb: @node

View File

@ -10,7 +10,7 @@ ImageExpand =
$.on @EAI, 'click', ImageExpand.cb.toggleAll
Header.addShortcut @EAI, 2
Post::callbacks.push
Post.callbacks.push
name: 'Image Expansion'
cb: @node
node: ->

View File

@ -2,7 +2,7 @@ ImageHover =
init: ->
return if g.VIEW is 'catalog' or !Conf['Image Hover']
Post::callbacks.push
Post.callbacks.push
name: 'Image Hover'
cb: @node
node: ->

View File

@ -3,7 +3,7 @@ ImageLoader =
return if g.VIEW is 'catalog'
return unless Conf["Image Prefetching"] or Conf["Replace JPG"] or Conf["Replace PNG"] or Conf["Replace GIF"]
Post::callbacks.push
Post.callbacks.push
name: 'Image Replace'
cb: @node

View File

@ -2,11 +2,11 @@ RevealSpoilers =
init: ->
return if g.VIEW is 'catalog' or !Conf['Reveal Spoiler Thumbnails']
Post::callbacks.push
name: 'Reveal Spoiler Thumbnails'
Post.callbacks.push
cb: @node
node: ->
return if @isClone or !@file?.isSpoiler
{thumb} = @file
thumb.removeAttribute 'style'
thumb.src = @file.thumbURL
thumb.src = @file.thumbURL

View File

@ -11,7 +11,7 @@ Sauce =
return unless links.length
@links = links
@link = $.el 'a', target: '_blank'
Post::callbacks.push
Post.callbacks.push
name: 'Sauce'
cb: @node
createSauceLink: (link) ->

View File

@ -26,7 +26,7 @@ Linkify =
if Conf['Title Link']
$.sync 'CachedTitles', Linkify.titleSync
Post::callbacks.push
Post.callbacks.push
name: 'Linkify'
cb: @node

View File

@ -3,7 +3,7 @@ Menu =
return if g.VIEW is 'catalog' or !Conf['Menu']
@menu = new UI.Menu 'post'
Post::callbacks.push
Post.callbacks.push
name: 'Menu'
cb: @node

View File

@ -3,7 +3,7 @@ IDColor =
return if g.VIEW is 'catalog' or not Conf['Color User IDs']
@ids = {}
Post::callbacks.push
Post.callbacks.push
name: 'Color User IDs'
cb: @node

View File

@ -1,7 +1,7 @@
Dice =
init: ->
return if g.BOARD.ID isnt 'tg' or g.VIEW is 'catalog' or !Conf['Show Dice Roll']
Post::callbacks.push
Post.callbacks.push
name: 'Show Dice Roll'
cb: @node
node: ->

View File

@ -2,14 +2,13 @@ ExpandComment =
init: ->
return if g.VIEW isnt 'index' or !Conf['Comment Expansion']
if g.BOARD.ID is 'g'
@callbacks.push Fourchan.code
if g.BOARD.ID is 'sci'
@callbacks.push Fourchan.math
@callbacks.push Fourchan.code if g.BOARD.ID is 'g'
@callbacks.push Fourchan.math if g.BOARD.ID is 'sci'
Post::callbacks.push
Post.callbacks.push
name: 'Comment Expansion'
cb: @node
node: ->
if a = $ '.abbr > a:not([onclick])', @nodes.comment
$.on a, 'click', ExpandComment.cb

View File

@ -2,7 +2,7 @@ ExpandThread =
init: ->
return if g.VIEW isnt 'index' or !Conf['Thread Expansion']
Thread::callbacks.push
Thread.callbacks.push
name: 'Thread Expansion'
cb: @node

View File

@ -3,7 +3,7 @@ FileInfo =
return if g.VIEW is 'catalog' or !Conf['File Info Formatting']
@funk = @createFunc Conf['fileInfo']
Post::callbacks.push
Post.callbacks.push
name: 'File Info Formatting'
cb: @node
node: ->
@ -48,4 +48,4 @@ FileInfo =
B: -> FileInfo.convertUnit @file.sizeInBytes, 'B'
K: -> FileInfo.convertUnit @file.sizeInBytes, 'KB'
M: -> FileInfo.convertUnit @file.sizeInBytes, 'MB'
r: -> if @file.isImage then @file.dimensions else 'PDF'
r: -> if @file.isImage then @file.dimensions else 'PDF'

View File

@ -10,7 +10,7 @@ Fourchan =
pre.innerHTML = prettyPrintOne(pre.innerHTML);
}, false);
"""
Post::callbacks.push
Post.callbacks.push
name: 'Parse /g/ code'
cb: @code
if board is 'sci'
@ -27,7 +27,7 @@ Fourchan =
}
}, false);
"""
Post::callbacks.push
Post.callbacks.push
name: 'Parse /sci/ math'
cb: @math
code: ->

View File

@ -0,0 +1,40 @@
IDColor =
init: ->
return if g.VIEW is 'catalog' or !Conf['Color User IDs']
@ids = {}
Post.callbacks.push
name: 'Color User IDs'
cb: @node
node: ->
return if @isClone or !(uid = @info.uniqueID)
rgb = IDColor.compute uid
span = @nodes.uniqueID
{style} = span
style.color = rgb[3]
style.backgroundColor = "rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]})"
$.addClass span, 'painted'
span.textContent = uid
span.title = 'Highlight posts by this ID'
compute: (uniqueID) ->
if uniqueID of IDColor.ids
return IDColor.ids[uniqueID]
hash = @hash uniqueID
rgb = [
(hash >> 24) & 0xFF
(hash >> 16) & 0xFF
(hash >> 8) & 0xFF
]
rgb.push if (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 170
'black'
else
'white'
@ids[uniqueID] = rgb
hash: (uniqueID) ->
msg = 0
for i in [0...uniqueID.length] by 1
msg = (msg << 5) - msg + uniqueID.charCodeAt i
msg

View File

@ -9,7 +9,7 @@ RelativeDates =
# Start the timeout.
@flush()
Post::callbacks.push
Post.callbacks.push
name: 'Relative Post Dates'
cb: @node
node: ->
@ -104,4 +104,4 @@ RelativeDates =
markStale = -> RelativeDates.stale.push update
# Kick off initial timeout.
update new Date()
update new Date()

View File

@ -9,7 +9,7 @@ RemoveSpoilers =
@wrapper = (text) ->
"[spoiler]#{text}[/spoiler]"
Post::callbacks.push
Post.callbacks.push
name: 'Reveal Spoilers'
cb: @node

View File

@ -3,7 +3,7 @@ Time =
return if g.VIEW is 'catalog' or !Conf['Time Formatting']
@funk = @createFunc Conf['time']
Post::callbacks.push
Post.callbacks.push
name: 'Time Formatting'
cb: @node
node: ->

View File

@ -2,8 +2,8 @@ ThreadExcerpt =
init: ->
return if g.VIEW isnt 'thread' or !Conf['Thread Excerpt']
Thread::callbacks.push
Thread.callbacks.push
name: 'Thread Excerpt'
cb: @node
node: ->
d.title = Get.threadExcerpt @
d.title = Get.threadExcerpt @

View File

@ -19,7 +19,7 @@ ThreadStats =
@fileCountEl = $ '#file-count', sc
@pageCountEl = $ '#page-count', sc
Thread::callbacks.push
Thread.callbacks.push
name: 'Thread Stats'
cb: @node

View File

@ -54,7 +54,7 @@ ThreadUpdater =
order: 110
subEntries: subEntries
Thread::callbacks.push
Thread.callbacks.push
name: 'Thread Updater'
cb: @node

View File

@ -31,7 +31,7 @@ ThreadWatcher =
ThreadWatcher.fetchAllStatus()
@db.save()
Thread::callbacks.push
Thread.callbacks.push
name: 'Thread Watcher'
cb: @node

View File

@ -8,7 +8,7 @@ Unread =
@posts = []
@postsQuotingYou = []
Thread::callbacks.push
Thread.callbacks.push
name: 'Unread'
cb: @node
@ -90,7 +90,7 @@ Unread =
for quotelink in post.nodes.quotelinks when QR.db.get Get.postDataFromLink quotelink
Unread.postsQuotingYou.push post
Unread.openNotification post
return
return
openNotification: (post) ->
return unless Header.areNotificationsEnabled

View File

@ -33,7 +33,7 @@ QR =
else
$.ready @persist
Post::callbacks.push
Post.callbacks.push
name: 'Quick Reply'
cb: @node
@ -445,7 +445,6 @@ QR =
e.preventDefault()
QR.open()
QR.handleFiles e.dataTransfer.files
$.addClass QR.nodes.el, 'dump'
paste: (e) ->
files = []
@ -1223,6 +1222,7 @@ QR =
threadID
postID
}
$.event 'QRPostSuccessful_', {threadID, postID}
# Enable auto-posting if we have stuff left to post, disable it otherwise.
postsCount = QR.posts.length - 1

View File

@ -16,10 +16,10 @@ QuoteBacklink =
format = Conf['backlink'].replace /%id/g, "' + id + '"
@funk = Function 'id', "return '#{format}'"
@containers = {}
Post::callbacks.push
Post.callbacks.push
name: 'Quote Backlinking Part 1'
cb: @firstNode
Post::callbacks.push
Post.callbacks.push
name: 'Quote Backlinking Part 2'
cb: @secondNode
firstNode: ->
@ -55,4 +55,4 @@ QuoteBacklink =
$.add @nodes.info, container
getContainer: (id) ->
@containers[id] or=
$.el 'span', className: 'container'
$.el 'span', className: 'container'

View File

@ -7,7 +7,7 @@ QuoteCT =
# \u00A0 is nbsp
@text = '\u00A0(Cross-thread)'
Post::callbacks.push
Post.callbacks.push
name: 'Mark Cross-thread Quotes'
cb: @node
node: ->

View File

@ -18,7 +18,7 @@ QuoteInline =
if Conf['Comment Expansion']
ExpandComment.callbacks.push @node
Post::callbacks.push
Post.callbacks.push
name: 'Quote Inlining'
cb: @node

View File

@ -7,7 +7,7 @@ QuoteOP =
# \u00A0 is nbsp
@text = '\u00A0(OP)'
Post::callbacks.push
Post.callbacks.push
name: 'Mark OP Quotes'
cb: @node

View File

@ -5,7 +5,7 @@ QuotePreview =
if Conf['Comment Expansion']
ExpandComment.callbacks.push @node
Post::callbacks.push
Post.callbacks.push
name: 'Quote Previewing'
cb: @node

View File

@ -2,7 +2,7 @@ QuoteStrikeThrough =
init: ->
return if g.VIEW is 'catalog' or !Conf['Reply Hiding Buttons'] and !Conf['Reply Hiding Link'] and !Conf['Filter']
Post::callbacks.push
Post.callbacks.push
name: 'Strike-through Quotes'
cb: @node
@ -12,4 +12,4 @@ QuoteStrikeThrough =
{boardID, postID} = Get.postDataFromLink quotelink
if g.posts["#{boardID}.#{postID}"]?.isHidden
$.addClass quotelink, 'filtered'
return
return

View File

@ -20,7 +20,7 @@ QuoteThreading =
$.on d, '4chanXInitFinished', @setup
Post::callbacks.push
Post.callbacks.push
name: 'Quote Threading'
cb: @node

View File

@ -11,7 +11,9 @@ QuoteYou =
if Conf['Comment Expansion']
ExpandComment.callbacks.push @node
Post::callbacks.push
# \u00A0 is nbsp
@text = '\u00A0(You)'
Post.callbacks.push
name: 'Mark Quotes of You'
cb: @node
@ -25,7 +27,7 @@ QuoteYou =
return unless @quotes.length
for quotelink in @nodes.quotelinks when QR.db.get Get.postDataFromLink quotelink
$.add quotelink, $.tn '\u00A0(You)'
$.add quotelink, $.tn QuoteYou.text
$.addClass quotelink, 'you'
$.addClass @nodes.root, 'quotesYou'
return

View File

@ -5,7 +5,7 @@ Quotify =
if Conf['Comment Expansion']
ExpandComment.callbacks.push @node
Post::callbacks.push
Post.callbacks.push
name: 'Resurrect Quotes'
cb: @node
node: ->
@ -81,4 +81,9 @@ Quotify =
@nodes.quotelinks.push a
fixDeadlink: (deadlink) ->
if !(el = deadlink.previousSibling) or el.nodeName is 'BR'
green = $.el 'span',
className: 'quote'
$.before deadlink, green
$.add green, deadlink
$.replace deadlink, [deadlink.childNodes...]