Merge branch 'v3' into Av2

Conflicts:
	LICENSE
	builds/4chan-X.meta.js
	builds/4chan-X.user.js
	builds/crx/manifest.json
	builds/crx/script.js
	builds/version
	package.json
	src/General/Main.coffee
	src/General/css/style.css
	src/Posting/QuickReply.coffee
	src/Quotelinks/QuoteInline.coffee
This commit is contained in:
Zixaphir 2013-05-06 16:54:39 -07:00
commit 93b94ec9d0
27 changed files with 10946 additions and 361 deletions

View File

@ -1,7 +1,33 @@
### 1.1.13 - 2013-05-06
seaweedchan:
- Disable settings removing scroll bar when opened, thus fixing the issue where it jumps up the page randomly
- Hide watcher by default, add [Watcher] shortcut.
### 1.1.12 - 2013-05-06
detharonil
- Support for %Y in time formatting
- More future-proof %y
MayhemYDG:
- Fix whitespaces not being preserved in code tags in /g/.
seaweedchan:
- Fix QR not being able to drag to the top with fixed header disabled
zixaphir:
- Fix custom CSS
- Fix [Deleted] showing up randomly after submitting a post
### 1.1.11 - 2013-05-04
seaweedchan:
- Add `Highlight Posts Quoting You` option
- Add 'catalog', 'index', or 'thread' classes to document depending on what's open
- Add `Filtered Backlinks` options that when disabled, hides filtered backlinks
### 1.1.10 - 2013-05-03
seaweedchan:
- Fix update checking
### 1.1.9 - 2013-05-02
seaweedchan
- Fix boards with previously deleted archives not switching to new archives
@ -25,7 +51,6 @@ seaweedchan:
- Add paste.installgentoo.com embedding
- Added `Posting Success Notifications` option to make "Post Successful!" and "_____ uploaded" notifications optional
- Added `Allow False Positives` option under Linkification, giving the user more control over what's linkified.
- Fix URL for update checking
### 1.1.6 - 2013-05-01
seaweedchan:
@ -62,18 +87,6 @@ seaweedchan:
- Toggle keybind for header auto-hiding
MayhemYDG:
=======
- Access it in the `QR` tab of the Settings window.
- Updated archive redirection for /h/, /v/ and /vg/.
### 3.2.3 - *2013-04-30*
- Update archive redirection for /c/, /d/, /v/, /vg/, /w/ and /wg/.
- Minor fixes.
### 3.2.2 - *2013-04-27*
>>>>>>> b74e0c92fdf2d755d996cb574dddb3c8d964e91a
- Fix Unread Count taking into account hidden posts.
### 1.1.2 - 2013-04-26

View File

@ -43,7 +43,7 @@ module.exports = (grunt) ->
options: concatOptions
files:
'LICENSE': 'src/General/meta/banner.js',
'builds/version': 'src/General/meta/version.js'
'latest.js': 'src/General/meta/latest.js'
crx:
options: concatOptions

File diff suppressed because one or more lines are too long

19
builds/4chan-X.meta.js Normal file
View File

@ -0,0 +1,19 @@
// ==UserScript==
// @name 4chan X
// @version 1.1.13
// @namespace 4chan-X
// @description Cross-browser userscript for maximum lurking on 4chan.
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
// @match *://api.4chan.org/*
// @match *://boards.4chan.org/*
// @match *://images.4chan.org/*
// @match *://sys.4chan.org/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_openInTab
// @run-at document-start
// @updateURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan_X.meta.js
// @downloadURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan_X.user.js
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC
// ==/UserScript==

10256
builds/4chan-X.user.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -118,7 +118,7 @@
__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; },
__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; };
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Config = {
main: {
@ -154,6 +154,7 @@
'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'],
'Thread Hiding Buttons': [true, 'Add buttons to hide entire threads.'],
'Reply Hiding Buttons': [true, 'Add buttons to hide single replies.'],
'Filtered Backlinks': [true, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.'],
'Stubs': [true, 'Show stubs of hidden threads / replies.']
},
'Images': {
@ -204,11 +205,13 @@
'Quote Backlinks': [true, 'Add quote backlinks.'],
'OP Backlinks': [true, 'Add backlinks to the OP.'],
'Quote Inlining': [true, 'Inline quoted post on click.'],
'Quote Hash Navigation': [false, 'Include an extra link after quotes for autoscrolling to quoted posts.'],
'Forward Hiding': [true, 'Hide original posts of inlined backlinks.'],
'Quote Previewing': [true, 'Show quoted post on hover.'],
'Quote Highlighting': [true, 'Highlight the previewed post.'],
'Resurrect Quotes': [true, 'Link dead quotes to the archives.'],
'Mark Quotes of You': [true, 'Add \'(You)\' to quotes linking to your posts.'],
'Highlight Posts Quoting You': [false, 'Highlights any posts that contain a quote to your post.'],
'Highlight Own Posts': [false, 'Highlights own posts if Mark Quotes of You is enabled.'],
'Mark OP Quotes': [true, 'Add \'(OP)\' to OP quotes.'],
'Mark Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes.'],
@ -3574,12 +3577,10 @@
})();
Notification = (function() {
var add, close;
function Notification(type, content, timeout) {
this.timeout = timeout;
this.add = add.bind(this);
this.close = close.bind(this);
this.close = __bind(this.close, this);
this.add = __bind(this.add, this);
this.el = $.el('div', {
innerHTML: '<a href=javascript:; class=close title=Close>×</a><div class=message></div>'
});
@ -3597,7 +3598,7 @@
return this.el.className = "notification " + type;
};
add = function() {
Notification.prototype.add = function() {
if (d.hidden) {
$.on(d, 'visibilitychange', this.add);
return;
@ -3611,7 +3612,7 @@
}
};
close = function() {
Notification.prototype.close = function() {
return $.rm(this.el);
};
@ -4653,7 +4654,7 @@
screenWidth: screenWidth,
isTouching: isTouching
};
_ref = Conf['Header auto-hide'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = _ref[0], o.bottomBorder = _ref[1];
_ref = Conf['Header auto-hide'] || !Conf['Fixed Header'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = _ref[0], o.bottomBorder = _ref[1];
if (isTouching) {
o.identifier = e.identifier;
o.move = touchmove.bind(o);
@ -4821,6 +4822,9 @@
if (g.VIEW === 'catalog' || !Conf['Filter']) {
return;
}
if (!Conf['Filtered Backlinks']) {
$.addClass(doc, 'hide-backlinks');
}
for (key in Config.filter) {
this.filters[key] = [];
_ref = Conf[key].split('\n');
@ -5685,7 +5689,7 @@
});
},
firstNode: function() {
var a, clone, container, containers, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
var a, clone, container, containers, frag, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
if (this.isClone || !this.quotes.length) {
return;
@ -5708,14 +5712,17 @@
}
for (_k = 0, _len2 = containers.length; _k < _len2; _k++) {
container = containers[_k];
link = a.cloneNode(true);
frag = [$.tn(' '), link = a.cloneNode(true)];
if (Conf['Quote Previewing']) {
$.on(link, 'mouseover', QuotePreview.mouseover);
}
if (Conf['Quote Inlining']) {
$.on(link, 'click', QuoteInline.toggle);
if (Conf['Quote Hash Navigation']) {
frag.pushArrays(QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
}
}
$.add(container, [$.tn(' '), link]);
$.add(container, frag);
}
}
},
@ -5791,19 +5798,43 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
if (Conf['Quote Hash Navigation']) {
this.node = function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
if (!this.isClone) {
$.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
}
$.on(link, 'click', QuoteInline.toggle);
}
};
} else {
this.node = function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'click', QuoteInline.toggle);
}
};
}
return Post.prototype.callbacks.push({
name: 'Quote Inlining',
cb: this.node
});
},
node: function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'click', QuoteInline.toggle);
}
qiQuote: function(link, hidden) {
return [
$.tn(' '), $.el('a', {
className: hidden ? 'hashlink filtered' : 'hashlink',
textContent: '#',
href: link.href
})
];
},
toggle: function(e) {
var boardID, context, postID, threadID, _ref;
@ -6212,6 +6243,9 @@
if (Conf['Highlight Own Posts']) {
$.addClass(doc, 'highlight-own');
}
if (Conf['Highlight Posts Quoting You']) {
$.addClass(doc, 'highlight-you');
}
if (!(quotes = this.quotes).length) {
return;
}
@ -6220,6 +6254,7 @@
quotelink = quotelinks[_i];
if (QR.db.get(Get.postDataFromLink(quotelink))) {
$.add(quotelink, $.tn(QuoteYou.text));
$.addClass(this.nodes.root, 'quotesYou');
}
}
}
@ -6854,7 +6889,7 @@
});
},
parseItem: function(item, types) {
var boards, match, type, val, _ref, _ref1, _ref2;
var boards, match, type, val, _ref, _ref1;
if (item[0] === '#') {
return;
@ -6865,7 +6900,7 @@
_ref = match, match = _ref[0], type = _ref[1], val = _ref[2];
item = item.replace(match, '');
boards = ((_ref1 = item.match(/boards:([^;]+)/i)) != null ? _ref1[1].toLowerCase() : void 0) || 'global';
if (boards !== 'global' && !(_ref2 = g.BOARD.ID, __indexOf.call(boards.split(','), _ref2) >= 0)) {
if (boards !== 'global' && !((boards.split(',')).contains(g.BOARD.ID))) {
return;
}
if (type === 'password') {
@ -6878,7 +6913,7 @@
if (/always/i.test(item)) {
QR.persona.always[type] = val;
}
if (__indexOf.call(types[type], val) < 0) {
if (!types[type].contains(val)) {
return types[type].push(val);
}
},
@ -8933,7 +8968,9 @@
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>",
id: 'thread-stats'
});
Header.addShortcut(sc);
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>");
$.ready(function() {
@ -8998,7 +9035,9 @@
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>",
id: 'updater'
});
Header.addShortcut(sc);
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', "<div class=move></div><span id=update-status></span><span id=update-timer title='Update now'></span>");
$.addClass(doc, 'float');
@ -9096,10 +9135,19 @@
return setTimeout(ThreadUpdater.update, 1000);
}
},
checkpost: function() {
checkpost: function(e) {
if (!ThreadUpdater.checkPostCount) {
if (e.detail.threadID !== ThreadUpdater.thread.ID) {
return;
}
ThreadUpdater.seconds = 0;
ThreadUpdater.outdateCount = 0;
ThreadUpdater.set('timer', '...');
}
if (!(g.DEAD || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) {
return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND);
}
ThreadUpdater.set('timer', ThreadUpdater.getInterval());
ThreadUpdater.checkPostCount = 0;
delete ThreadUpdater.foundPost;
return delete ThreadUpdater.postID;
@ -9384,13 +9432,28 @@
ThreadWatcher = {
init: function() {
var sc;
if (!Conf['Thread Watcher']) {
return;
}
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '<div class=move>Thread Watcher</div>');
this.shortcut = sc = $.el('a', {
textContent: 'Watcher',
id: 'watcher-link',
href: 'javascript:;',
className: 'disabled'
});
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '<div class=move>Thread Watcher<a class=close href=javascript:;>×</a></div>');
$.on(d, 'QRPostSuccessful', this.cb.post);
$.on(d, '4chanXInitFinished', this.ready);
$.sync('WatchedThreads', this.refresh);
$.on(sc, 'click', this.toggleWatcher);
$.on($('.move>.close', ThreadWatcher.dialog), 'click', this.toggleWatcher);
Header.addShortcut(sc);
$.ready(function() {
ThreadWatcher.refresh();
$.add(d.body, ThreadWatcher.dialog);
return ThreadWatcher.dialog.hidden = true;
});
return Thread.prototype.callbacks.push({
name: 'Thread Watcher',
cb: this.node
@ -9416,14 +9479,6 @@
return $["delete"]('AutoWatch');
});
},
ready: function() {
$.off(d, '4chanXInitFinished', ThreadWatcher.ready);
if (!Main.isThisPageLegit()) {
return;
}
ThreadWatcher.refresh();
return $.add(d.body, ThreadWatcher.dialog);
},
refresh: function(watched) {
var ID, board, div, favicon, id, link, nodes, props, thread, x, _ref, _ref1;
@ -9461,6 +9516,10 @@
favicon.src = ID in watched ? Favicon["default"] : Favicon.empty;
}
},
toggleWatcher: function() {
$.toggleClass(ThreadWatcher.shortcut, 'disabled');
return ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden;
},
cb: {
toggle: function() {
return ThreadWatcher.toggle(Get.postFromNode(this).thread);
@ -9546,7 +9605,10 @@
});
$.on(d, '4chanXInitFinished', Unread.ready);
$.on(d, 'ThreadUpdate', Unread.onUpdate);
return $.on(d, 'scroll visibilitychange', Unread.read);
$.on(d, 'scroll visibilitychange', Unread.read);
if (Conf['Unread Line']) {
return $.on(d, 'visibilitychange', Unread.setLine);
}
},
ready: function() {
var ID, post, posts, _ref;
@ -9561,9 +9623,6 @@
}
}
Unread.addPosts(posts);
if (Conf['Unread Line']) {
Unread.setLine();
}
if (Conf['Scroll to Last Read Post']) {
return Unread.scroll();
}
@ -12207,7 +12266,7 @@
}
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);");
$.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML.replace(/\\s/g, '&nbsp;'));\n}, false);");
Post.prototype.callbacks.push({
name: 'Parse /g/ code',
cb: this.code
@ -12230,7 +12289,9 @@
_ref = $$('.prettyprint', this.nodes.comment);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
pre = _ref[_i];
$.event('prettyprint', pre, window);
if (!$('.pln', pre)) {
$.event('prettyprint', pre, window);
}
}
},
math: function() {
@ -13087,16 +13148,12 @@
(sectionToOpen ? sectionToOpen : links[0]).click();
$.on($('.close', dialog), 'click', Settings.close);
$.on(overlay, 'click', Settings.close);
d.body.style.width = "" + d.body.clientWidth + "px";
$.addClass(d.body, 'unscroll');
return $.add(d.body, [overlay, dialog]);
},
close: function() {
if (!Settings.dialog) {
return;
}
d.body.style.removeProperty('width');
$.rmClass(d.body, 'unscroll');
$.rm(Settings.overlay);
$.rm(Settings.dialog);
delete Settings.overlay;
@ -13468,11 +13525,14 @@
for (key in items) {
val = items[key];
if (['usercss', 'emojiPos', 'archiver'].contains(key)) {
if (['emojiPos', 'archiver'].contains(key)) {
continue;
}
input = inputs[key];
input.value = val;
if (key === 'usercss') {
continue;
}
$.on(input, event, Settings[key]);
Settings[key].call(input);
}
@ -14416,6 +14476,17 @@
obj.callback.isAddon = true;
return Klass.prototype.callbacks.push(obj.callback);
},
message: function(e) {
var el, version;
version = e.data.version;
if (version && version !== g.VERSION) {
el = $.el('span', {
innerHTML: "Update: appchan x v" + version + " is out, get it <a href=http://zixaphir.github.com/appchan-x/ target=_blank>here</a>."
});
return new Notification('info', el, 120);
}
},
checkUpdate: function() {
var freq, items, now;
@ -14432,27 +14503,12 @@
if (items.lastupdate > now - freq || items.lastchecked > now - $.DAY) {
return;
}
return $.ajax('https://github.com/zixaphir/appchan-x/raw/Av2/builds/version', {
onload: function() {
var el, version;
if (this.status !== 200) {
return;
}
version = this.response;
if (!/^\d\.\d+\.\d+$/.test(version)) {
return;
}
if (g.VERSION === version) {
$.set('lastupdate', now);
return;
}
$.set('lastchecked', now);
el = $.el('span', {
innerHTML: "Update: appchan x v" + version + " is out, get it <a href=http://zixaphir.github.com/appchan-x/ target=_blank>here</a>."
});
return new Notification('info', el, 120);
}
return $.ready(function() {
$.on(window, 'message', Main.message);
$.set('lastUpdate', now);
return $.add(d.head, $.el('script', {
src: 'https://github.com/zixaphir/appchan-x/raw/Av2/latest.js'
}));
});
});
},

View File

@ -118,7 +118,7 @@
__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; },
__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; };
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Config = {
main: {
@ -154,6 +154,7 @@
'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'],
'Thread Hiding Buttons': [true, 'Add buttons to hide entire threads.'],
'Reply Hiding Buttons': [true, 'Add buttons to hide single replies.'],
'Filtered Backlinks': [true, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.'],
'Stubs': [true, 'Show stubs of hidden threads / replies.']
},
'Images': {
@ -205,11 +206,13 @@
'Quote Backlinks': [true, 'Add quote backlinks.'],
'OP Backlinks': [true, 'Add backlinks to the OP.'],
'Quote Inlining': [true, 'Inline quoted post on click.'],
'Quote Hash Navigation': [false, 'Include an extra link after quotes for autoscrolling to quoted posts.'],
'Forward Hiding': [true, 'Hide original posts of inlined backlinks.'],
'Quote Previewing': [true, 'Show quoted post on hover.'],
'Quote Highlighting': [true, 'Highlight the previewed post.'],
'Resurrect Quotes': [true, 'Link dead quotes to the archives.'],
'Mark Quotes of You': [true, 'Add \'(You)\' to quotes linking to your posts.'],
'Highlight Posts Quoting You': [false, 'Highlights any posts that contain a quote to your post.'],
'Highlight Own Posts': [false, 'Highlights own posts if Mark Quotes of You is enabled.'],
'Mark OP Quotes': [true, 'Add \'(OP)\' to OP quotes.'],
'Mark Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes.'],
@ -3570,12 +3573,10 @@
})();
Notification = (function() {
var add, close;
function Notification(type, content, timeout) {
this.timeout = timeout;
this.add = add.bind(this);
this.close = close.bind(this);
this.close = __bind(this.close, this);
this.add = __bind(this.add, this);
this.el = $.el('div', {
innerHTML: '<a href=javascript:; class=close title=Close>×</a><div class=message></div>'
});
@ -3593,7 +3594,7 @@
return this.el.className = "notification " + type;
};
add = function() {
Notification.prototype.add = function() {
if (d.hidden) {
$.on(d, 'visibilitychange', this.add);
return;
@ -3607,7 +3608,7 @@
}
};
close = function() {
Notification.prototype.close = function() {
return $.rm(this.el);
};
@ -4649,7 +4650,7 @@
screenWidth: screenWidth,
isTouching: isTouching
};
_ref = Conf['Header auto-hide'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = _ref[0], o.bottomBorder = _ref[1];
_ref = Conf['Header auto-hide'] || !Conf['Fixed Header'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = _ref[0], o.bottomBorder = _ref[1];
if (isTouching) {
o.identifier = e.identifier;
o.move = touchmove.bind(o);
@ -4817,6 +4818,9 @@
if (g.VIEW === 'catalog' || !Conf['Filter']) {
return;
}
if (!Conf['Filtered Backlinks']) {
$.addClass(doc, 'hide-backlinks');
}
for (key in Config.filter) {
this.filters[key] = [];
_ref = Conf[key].split('\n');
@ -5681,7 +5685,7 @@
});
},
firstNode: function() {
var a, clone, container, containers, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
var a, clone, container, containers, frag, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
if (this.isClone || !this.quotes.length) {
return;
@ -5704,14 +5708,17 @@
}
for (_k = 0, _len2 = containers.length; _k < _len2; _k++) {
container = containers[_k];
link = a.cloneNode(true);
frag = [$.tn(' '), link = a.cloneNode(true)];
if (Conf['Quote Previewing']) {
$.on(link, 'mouseover', QuotePreview.mouseover);
}
if (Conf['Quote Inlining']) {
$.on(link, 'click', QuoteInline.toggle);
if (Conf['Quote Hash Navigation']) {
frag.pushArrays(QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
}
}
$.add(container, [$.tn(' '), link]);
$.add(container, frag);
}
}
},
@ -5787,19 +5794,43 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
if (Conf['Quote Hash Navigation']) {
this.node = function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
if (!this.isClone) {
$.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
}
$.on(link, 'click', QuoteInline.toggle);
}
};
} else {
this.node = function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'click', QuoteInline.toggle);
}
};
}
return Post.prototype.callbacks.push({
name: 'Quote Inlining',
cb: this.node
});
},
node: function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'click', QuoteInline.toggle);
}
qiQuote: function(link, hidden) {
return [
$.tn(' '), $.el('a', {
className: hidden ? 'hashlink filtered' : 'hashlink',
textContent: '#',
href: link.href
})
];
},
toggle: function(e) {
var boardID, context, postID, threadID, _ref;
@ -6208,6 +6239,9 @@
if (Conf['Highlight Own Posts']) {
$.addClass(doc, 'highlight-own');
}
if (Conf['Highlight Posts Quoting You']) {
$.addClass(doc, 'highlight-you');
}
if (!(quotes = this.quotes).length) {
return;
}
@ -6216,6 +6250,7 @@
quotelink = quotelinks[_i];
if (QR.db.get(Get.postDataFromLink(quotelink))) {
$.add(quotelink, $.tn(QuoteYou.text));
$.addClass(this.nodes.root, 'quotesYou');
}
}
}
@ -6850,7 +6885,7 @@
});
},
parseItem: function(item, types) {
var boards, match, type, val, _ref, _ref1, _ref2;
var boards, match, type, val, _ref, _ref1;
if (item[0] === '#') {
return;
@ -6861,7 +6896,7 @@
_ref = match, match = _ref[0], type = _ref[1], val = _ref[2];
item = item.replace(match, '');
boards = ((_ref1 = item.match(/boards:([^;]+)/i)) != null ? _ref1[1].toLowerCase() : void 0) || 'global';
if (boards !== 'global' && !(_ref2 = g.BOARD.ID, __indexOf.call(boards.split(','), _ref2) >= 0)) {
if (boards !== 'global' && !((boards.split(',')).contains(g.BOARD.ID))) {
return;
}
if (type === 'password') {
@ -6874,7 +6909,7 @@
if (/always/i.test(item)) {
QR.persona.always[type] = val;
}
if (__indexOf.call(types[type], val) < 0) {
if (!types[type].contains(val)) {
return types[type].push(val);
}
},
@ -8954,7 +8989,9 @@
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>",
id: 'thread-stats'
});
Header.addShortcut(sc);
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>");
$.ready(function() {
@ -9019,7 +9056,9 @@
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>",
id: 'updater'
});
Header.addShortcut(sc);
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', "<div class=move></div><span id=update-status></span><span id=update-timer title='Update now'></span>");
$.addClass(doc, 'float');
@ -9117,10 +9156,19 @@
return setTimeout(ThreadUpdater.update, 1000);
}
},
checkpost: function() {
checkpost: function(e) {
if (!ThreadUpdater.checkPostCount) {
if (e.detail.threadID !== ThreadUpdater.thread.ID) {
return;
}
ThreadUpdater.seconds = 0;
ThreadUpdater.outdateCount = 0;
ThreadUpdater.set('timer', '...');
}
if (!(g.DEAD || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) {
return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND);
}
ThreadUpdater.set('timer', ThreadUpdater.getInterval());
ThreadUpdater.checkPostCount = 0;
delete ThreadUpdater.foundPost;
return delete ThreadUpdater.postID;
@ -9405,13 +9453,28 @@
ThreadWatcher = {
init: function() {
var sc;
if (!Conf['Thread Watcher']) {
return;
}
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '<div class=move>Thread Watcher</div>');
this.shortcut = sc = $.el('a', {
textContent: 'Watcher',
id: 'watcher-link',
href: 'javascript:;',
className: 'disabled'
});
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '<div class=move>Thread Watcher<a class=close href=javascript:;>×</a></div>');
$.on(d, 'QRPostSuccessful', this.cb.post);
$.on(d, '4chanXInitFinished', this.ready);
$.sync('WatchedThreads', this.refresh);
$.on(sc, 'click', this.toggleWatcher);
$.on($('.move>.close', ThreadWatcher.dialog), 'click', this.toggleWatcher);
Header.addShortcut(sc);
$.ready(function() {
ThreadWatcher.refresh();
$.add(d.body, ThreadWatcher.dialog);
return ThreadWatcher.dialog.hidden = true;
});
return Thread.prototype.callbacks.push({
name: 'Thread Watcher',
cb: this.node
@ -9437,14 +9500,6 @@
return $["delete"]('AutoWatch');
});
},
ready: function() {
$.off(d, '4chanXInitFinished', ThreadWatcher.ready);
if (!Main.isThisPageLegit()) {
return;
}
ThreadWatcher.refresh();
return $.add(d.body, ThreadWatcher.dialog);
},
refresh: function(watched) {
var ID, board, div, favicon, id, link, nodes, props, thread, x, _ref, _ref1;
@ -9482,6 +9537,10 @@
favicon.src = ID in watched ? Favicon["default"] : Favicon.empty;
}
},
toggleWatcher: function() {
$.toggleClass(ThreadWatcher.shortcut, 'disabled');
return ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden;
},
cb: {
toggle: function() {
return ThreadWatcher.toggle(Get.postFromNode(this).thread);
@ -9567,7 +9626,10 @@
});
$.on(d, '4chanXInitFinished', Unread.ready);
$.on(d, 'ThreadUpdate', Unread.onUpdate);
return $.on(d, 'scroll visibilitychange', Unread.read);
$.on(d, 'scroll visibilitychange', Unread.read);
if (Conf['Unread Line']) {
return $.on(d, 'visibilitychange', Unread.setLine);
}
},
ready: function() {
var ID, post, posts, _ref;
@ -9582,9 +9644,6 @@
}
}
Unread.addPosts(posts);
if (Conf['Unread Line']) {
Unread.setLine();
}
if (Conf['Scroll to Last Read Post']) {
return Unread.scroll();
}
@ -12228,7 +12287,7 @@
}
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);");
$.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML.replace(/\\s/g, '&nbsp;'));\n}, false);");
Post.prototype.callbacks.push({
name: 'Parse /g/ code',
cb: this.code
@ -12251,7 +12310,9 @@
_ref = $$('.prettyprint', this.nodes.comment);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
pre = _ref[_i];
$.event('prettyprint', pre, window);
if (!$('.pln', pre)) {
$.event('prettyprint', pre, window);
}
}
},
math: function() {
@ -13108,16 +13169,12 @@
(sectionToOpen ? sectionToOpen : links[0]).click();
$.on($('.close', dialog), 'click', Settings.close);
$.on(overlay, 'click', Settings.close);
d.body.style.width = "" + d.body.clientWidth + "px";
$.addClass(d.body, 'unscroll');
return $.add(d.body, [overlay, dialog]);
},
close: function() {
if (!Settings.dialog) {
return;
}
d.body.style.removeProperty('width');
$.rmClass(d.body, 'unscroll');
$.rm(Settings.overlay);
$.rm(Settings.dialog);
delete Settings.overlay;
@ -13487,11 +13544,14 @@
for (key in items) {
val = items[key];
if (['usercss', 'emojiPos', 'archiver'].contains(key)) {
if (['emojiPos', 'archiver'].contains(key)) {
continue;
}
input = inputs[key];
input.value = val;
if (key === 'usercss') {
continue;
}
$.on(input, event, Settings[key]);
Settings[key].call(input);
}
@ -14435,6 +14495,17 @@
obj.callback.isAddon = true;
return Klass.prototype.callbacks.push(obj.callback);
},
message: function(e) {
var el, version;
version = e.data.version;
if (version && version !== g.VERSION) {
el = $.el('span', {
innerHTML: "Update: appchan x v" + version + " is out, get it <a href=http://zixaphir.github.com/appchan-x/ target=_blank>here</a>."
});
return new Notification('info', el, 120);
}
},
checkUpdate: function() {
var freq, items, now;
@ -14451,27 +14522,12 @@
if (items.lastupdate > now - freq || items.lastchecked > now - $.DAY) {
return;
}
return $.ajax('https://github.com/zixaphir/appchan-x/raw/Av2/builds/version', {
onload: function() {
var el, version;
if (this.status !== 200) {
return;
}
version = this.response;
if (!/^\d\.\d+\.\d+$/.test(version)) {
return;
}
if (g.VERSION === version) {
$.set('lastupdate', now);
return;
}
$.set('lastchecked', now);
el = $.el('span', {
innerHTML: "Update: appchan x v" + version + " is out, get it <a href=http://zixaphir.github.com/appchan-x/ target=_blank>here</a>."
});
return new Notification('info', el, 120);
}
return $.ready(function() {
$.on(window, 'message', Main.message);
$.set('lastUpdate', now);
return $.add(d.head, $.el('script', {
src: 'https://github.com/zixaphir/appchan-x/raw/Av2/latest.js'
}));
});
});
},

View File

@ -98,7 +98,8 @@
__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,
__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; },
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
Config = {
main: {
@ -134,6 +135,7 @@
'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'],
'Thread Hiding Buttons': [true, 'Add buttons to hide entire threads.'],
'Reply Hiding Buttons': [true, 'Add buttons to hide single replies.'],
'Filtered Backlinks': [true, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.'],
'Stubs': [true, 'Show stubs of hidden threads / replies.']
},
'Images': {
@ -185,11 +187,13 @@
'Quote Backlinks': [true, 'Add quote backlinks.'],
'OP Backlinks': [true, 'Add backlinks to the OP.'],
'Quote Inlining': [true, 'Inline quoted post on click.'],
'Quote Hash Navigation': [false, 'Include an extra link after quotes for autoscrolling to quoted posts.'],
'Forward Hiding': [true, 'Hide original posts of inlined backlinks.'],
'Quote Previewing': [true, 'Show quoted post on hover.'],
'Quote Highlighting': [true, 'Highlight the previewed post.'],
'Resurrect Quotes': [true, 'Link dead quotes to the archives.'],
'Mark Quotes of You': [true, 'Add \'(You)\' to quotes linking to your posts.'],
'Highlight Posts Quoting You': [false, 'Highlights any posts that contain a quote to your post.'],
'Highlight Own Posts': [false, 'Highlights own posts if Mark Quotes of You is enabled.'],
'Mark OP Quotes': [true, 'Add \'(OP)\' to OP quotes.'],
'Mark Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes.'],
@ -3571,12 +3575,10 @@
})();
Notification = (function() {
var add, close;
function Notification(type, content, timeout) {
this.timeout = timeout;
this.add = add.bind(this);
this.close = close.bind(this);
this.close = __bind(this.close, this);
this.add = __bind(this.add, this);
this.el = $.el('div', {
innerHTML: '<a href=javascript:; class=close title=Close>×</a><div class=message></div>'
});
@ -3594,7 +3596,7 @@
return this.el.className = "notification " + type;
};
add = function() {
Notification.prototype.add = function() {
if (d.hidden) {
$.on(d, 'visibilitychange', this.add);
return;
@ -3608,7 +3610,7 @@
}
};
close = function() {
Notification.prototype.close = function() {
return $.rm(this.el);
};
@ -4650,7 +4652,7 @@
screenWidth: screenWidth,
isTouching: isTouching
};
_ref = Conf['Header auto-hide'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = _ref[0], o.bottomBorder = _ref[1];
_ref = Conf['Header auto-hide'] || !Conf['Fixed Header'] ? [0, 0] : Conf['Bottom Header'] ? [0, Header.bar.getBoundingClientRect().height] : [Header.bar.getBoundingClientRect().height, 0], o.topBorder = _ref[0], o.bottomBorder = _ref[1];
if (isTouching) {
o.identifier = e.identifier;
o.move = touchmove.bind(o);
@ -4818,6 +4820,9 @@
if (g.VIEW === 'catalog' || !Conf['Filter']) {
return;
}
if (!Conf['Filtered Backlinks']) {
$.addClass(doc, 'hide-backlinks');
}
for (key in Config.filter) {
this.filters[key] = [];
_ref = Conf[key].split('\n');
@ -5682,7 +5687,7 @@
});
},
firstNode: function() {
var a, clone, container, containers, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
var a, clone, container, containers, frag, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1;
if (this.isClone || !this.quotes.length) {
return;
@ -5705,14 +5710,17 @@
}
for (_k = 0, _len2 = containers.length; _k < _len2; _k++) {
container = containers[_k];
link = a.cloneNode(true);
frag = [$.tn(' '), link = a.cloneNode(true)];
if (Conf['Quote Previewing']) {
$.on(link, 'mouseover', QuotePreview.mouseover);
}
if (Conf['Quote Inlining']) {
$.on(link, 'click', QuoteInline.toggle);
if (Conf['Quote Hash Navigation']) {
frag.pushArrays(QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
}
}
$.add(container, [$.tn(' '), link]);
$.add(container, frag);
}
}
},
@ -5788,19 +5796,43 @@
if (Conf['Comment Expansion']) {
ExpandComment.callbacks.push(this.node);
}
if (Conf['Quote Hash Navigation']) {
this.node = function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
if (!this.isClone) {
$.after(link, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
}
$.on(link, 'click', QuoteInline.toggle);
}
};
} else {
this.node = function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'click', QuoteInline.toggle);
}
};
}
return Post.prototype.callbacks.push({
name: 'Quote Inlining',
cb: this.node
});
},
node: function() {
var link, _i, _len, _ref;
_ref = this.nodes.quotelinks.concat(__slice.call(this.nodes.backlinks));
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
$.on(link, 'click', QuoteInline.toggle);
}
qiQuote: function(link, hidden) {
return [
$.tn(' '), $.el('a', {
className: hidden ? 'hashlink filtered' : 'hashlink',
textContent: '#',
href: link.href
})
];
},
toggle: function(e) {
var boardID, context, postID, threadID, _ref;
@ -6209,6 +6241,9 @@
if (Conf['Highlight Own Posts']) {
$.addClass(doc, 'highlight-own');
}
if (Conf['Highlight Posts Quoting You']) {
$.addClass(doc, 'highlight-you');
}
if (!(quotes = this.quotes).length) {
return;
}
@ -6217,6 +6252,7 @@
quotelink = quotelinks[_i];
if (QR.db.get(Get.postDataFromLink(quotelink))) {
$.add(quotelink, $.tn(QuoteYou.text));
$.addClass(this.nodes.root, 'quotesYou');
}
}
}
@ -6852,7 +6888,7 @@
});
},
parseItem: function(item, types) {
var boards, match, type, val, _ref, _ref1, _ref2;
var boards, match, type, val, _ref, _ref1;
if (item[0] === '#') {
return;
@ -6863,7 +6899,7 @@
_ref = match, match = _ref[0], type = _ref[1], val = _ref[2];
item = item.replace(match, '');
boards = ((_ref1 = item.match(/boards:([^;]+)/i)) != null ? _ref1[1].toLowerCase() : void 0) || 'global';
if (boards !== 'global' && !(_ref2 = g.BOARD.ID, __indexOf.call(boards.split(','), _ref2) >= 0)) {
if (boards !== 'global' && !((boards.split(',')).contains(g.BOARD.ID))) {
return;
}
if (type === 'password') {
@ -6876,7 +6912,7 @@
if (/always/i.test(item)) {
QR.persona.always[type] = val;
}
if (__indexOf.call(types[type], val) < 0) {
if (!types[type].contains(val)) {
return types[type].push(val);
}
},
@ -8931,7 +8967,9 @@
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>",
id: 'thread-stats'
});
Header.addShortcut(sc);
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>");
$.ready(function() {
@ -8996,7 +9034,9 @@
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>",
id: 'updater'
});
Header.addShortcut(sc);
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('updater', 'bottom: 0px; left: 0px;', "<div class=move></div><span id=update-status></span><span id=update-timer title='Update now'></span>");
$.addClass(doc, 'float');
@ -9094,10 +9134,19 @@
return setTimeout(ThreadUpdater.update, 1000);
}
},
checkpost: function() {
checkpost: function(e) {
if (!ThreadUpdater.checkPostCount) {
if (e.detail.threadID !== ThreadUpdater.thread.ID) {
return;
}
ThreadUpdater.seconds = 0;
ThreadUpdater.outdateCount = 0;
ThreadUpdater.set('timer', '...');
}
if (!(g.DEAD || ThreadUpdater.foundPost || ThreadUpdater.checkPostCount >= 5)) {
return setTimeout(ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND);
}
ThreadUpdater.set('timer', ThreadUpdater.getInterval());
ThreadUpdater.checkPostCount = 0;
delete ThreadUpdater.foundPost;
return delete ThreadUpdater.postID;
@ -9382,13 +9431,28 @@
ThreadWatcher = {
init: function() {
var sc;
if (!Conf['Thread Watcher']) {
return;
}
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '<div class=move>Thread Watcher</div>');
this.shortcut = sc = $.el('a', {
textContent: 'Watcher',
id: 'watcher-link',
href: 'javascript:;',
className: 'disabled'
});
this.dialog = UI.dialog('watcher', 'top: 50px; left: 0px;', '<div class=move>Thread Watcher<a class=close href=javascript:;>×</a></div>');
$.on(d, 'QRPostSuccessful', this.cb.post);
$.on(d, '4chanXInitFinished', this.ready);
$.sync('WatchedThreads', this.refresh);
$.on(sc, 'click', this.toggleWatcher);
$.on($('.move>.close', ThreadWatcher.dialog), 'click', this.toggleWatcher);
Header.addShortcut(sc);
$.ready(function() {
ThreadWatcher.refresh();
$.add(d.body, ThreadWatcher.dialog);
return ThreadWatcher.dialog.hidden = true;
});
return Thread.prototype.callbacks.push({
name: 'Thread Watcher',
cb: this.node
@ -9414,14 +9478,6 @@
return $["delete"]('AutoWatch');
});
},
ready: function() {
$.off(d, '4chanXInitFinished', ThreadWatcher.ready);
if (!Main.isThisPageLegit()) {
return;
}
ThreadWatcher.refresh();
return $.add(d.body, ThreadWatcher.dialog);
},
refresh: function(watched) {
var ID, board, div, favicon, id, link, nodes, props, thread, x, _ref, _ref1;
@ -9459,6 +9515,10 @@
favicon.src = ID in watched ? Favicon["default"] : Favicon.empty;
}
},
toggleWatcher: function() {
$.toggleClass(ThreadWatcher.shortcut, 'disabled');
return ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden;
},
cb: {
toggle: function() {
return ThreadWatcher.toggle(Get.postFromNode(this).thread);
@ -9544,7 +9604,10 @@
});
$.on(d, '4chanXInitFinished', Unread.ready);
$.on(d, 'ThreadUpdate', Unread.onUpdate);
return $.on(d, 'scroll visibilitychange', Unread.read);
$.on(d, 'scroll visibilitychange', Unread.read);
if (Conf['Unread Line']) {
return $.on(d, 'visibilitychange', Unread.setLine);
}
},
ready: function() {
var ID, post, posts, _ref;
@ -9559,9 +9622,6 @@
}
}
Unread.addPosts(posts);
if (Conf['Unread Line']) {
Unread.setLine();
}
if (Conf['Scroll to Last Read Post']) {
return Unread.scroll();
}
@ -12210,7 +12270,7 @@
}
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);");
$.globalEval("window.addEventListener('prettyprint', function(e) {\n var pre = e.detail;\n pre.innerHTML = prettyPrintOne(pre.innerHTML.replace(/\\s/g, '&nbsp;'));\n}, false);");
Post.prototype.callbacks.push({
name: 'Parse /g/ code',
cb: this.code
@ -12233,7 +12293,9 @@
_ref = $$('.prettyprint', this.nodes.comment);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
pre = _ref[_i];
$.event('prettyprint', pre, window);
if (!$('.pln', pre)) {
$.event('prettyprint', pre, window);
}
}
},
math: function() {
@ -13090,16 +13152,12 @@
(sectionToOpen ? sectionToOpen : links[0]).click();
$.on($('.close', dialog), 'click', Settings.close);
$.on(overlay, 'click', Settings.close);
d.body.style.width = "" + d.body.clientWidth + "px";
$.addClass(d.body, 'unscroll');
return $.add(d.body, [overlay, dialog]);
},
close: function() {
if (!Settings.dialog) {
return;
}
d.body.style.removeProperty('width');
$.rmClass(d.body, 'unscroll');
$.rm(Settings.overlay);
$.rm(Settings.dialog);
delete Settings.overlay;
@ -13471,11 +13529,14 @@
for (key in items) {
val = items[key];
if (['usercss', 'emojiPos', 'archiver'].contains(key)) {
if (['emojiPos', 'archiver'].contains(key)) {
continue;
}
input = inputs[key];
input.value = val;
if (key === 'usercss') {
continue;
}
$.on(input, event, Settings[key]);
Settings[key].call(input);
}
@ -14419,6 +14480,17 @@
obj.callback.isAddon = true;
return Klass.prototype.callbacks.push(obj.callback);
},
message: function(e) {
var el, version;
version = e.data.version;
if (version && version !== g.VERSION) {
el = $.el('span', {
innerHTML: "Update: appchan x v" + version + " is out, get it <a href=http://zixaphir.github.com/appchan-x/ target=_blank>here</a>."
});
return new Notification('info', el, 120);
}
},
checkUpdate: function() {
var freq, items, now;
@ -14435,27 +14507,12 @@
if (items.lastupdate > now - freq || items.lastchecked > now - $.DAY) {
return;
}
return $.ajax('https://github.com/zixaphir/appchan-x/raw/Av2/builds/version', {
onload: function() {
var el, version;
if (this.status !== 200) {
return;
}
version = this.response;
if (!/^\d\.\d+\.\d+$/.test(version)) {
return;
}
if (g.VERSION === version) {
$.set('lastupdate', now);
return;
}
$.set('lastchecked', now);
el = $.el('span', {
innerHTML: "Update: appchan x v" + version + " is out, get it <a href=http://zixaphir.github.com/appchan-x/ target=_blank>here</a>."
});
return new Notification('info', el, 120);
}
return $.ready(function() {
$.on(window, 'message', Main.message);
$.set('lastUpdate', now);
return $.add(d.head, $.el('script', {
src: 'https://github.com/zixaphir/appchan-x/raw/Av2/latest.js'
}));
});
});
},

1
latest.js Normal file
View File

@ -0,0 +1 @@
postMessage({version:'2.0.0'},'*')

View File

@ -2,6 +2,7 @@
"name": "appchan-x",
"version": "2.0.0",
"description": "The most comprehensive 4chan userscript.",
"meta": {
"name": "appchan x",
"namespace": "zixaphir",
@ -29,7 +30,7 @@
"grunt-contrib-compress": "~0.5.0",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-watch": "~0.3.1",
"grunt-contrib-watch": "~0.4.0",
"grunt-shell": "~0.2.2"
},
"repository": {

View File

@ -3,6 +3,9 @@ Filter =
init: ->
return if g.VIEW is 'catalog' or !Conf['Filter']
unless Conf['Filtered Backlinks']
$.addClass doc, 'hide-backlinks'
for key of Config.filter
@filters[key] = []
for filter in Conf[key].split '\n'

View File

@ -113,6 +113,10 @@ Config =
true
'Add buttons to hide single replies.'
]
'Filtered Backlinks': [
true
'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.'
]
'Stubs': [
true
'Show stubs of hidden threads / replies.'
@ -295,6 +299,10 @@ Config =
true
'Inline quoted post on click.'
]
'Quote Hash Navigation': [
false
'Include an extra link after quotes for autoscrolling to quoted posts.'
]
'Forward Hiding': [
true
'Hide original posts of inlined backlinks.'
@ -315,6 +323,10 @@ Config =
true
'Add \'(You)\' to quotes linking to your posts.'
]
'Highlight Posts Quoting You': [
false
'Highlights any posts that contain a quote to your post.'
]
'Highlight Own Posts': [
false
'Highlights own posts if Mark Quotes of You is enabled.'

View File

@ -279,6 +279,13 @@ Main =
obj.callback.isAddon = true
Klass::callbacks.push obj.callback
message: (e) ->
{version} = e.data
if version and version isnt g.VERSION
el = $.el 'span',
innerHTML: "Update: <%= meta.name %> v#{version} is out, get it <a href=<%= meta.page %> target=_blank>here</a>."
new Notification 'info', el, 120
checkUpdate: ->
return unless Conf['Check for Updates'] and Main.isThisPageLegit()
# Check for updates after:
@ -293,18 +300,11 @@ Main =
$.get items, (items) ->
if items.lastupdate > now - freq or items.lastchecked > now - $.DAY
return
$.ajax '<%= meta.repo %>raw/<%= meta.mainBranch %>/<%= meta.buildsPath %>version', onload: ->
return unless @status is 200
version = @response
return unless /^\d\.\d+\.\d+$/.test version
if g.VERSION is version
# Don't check for updates too frequently if there wasn't one in a 'long' time.
$.set 'lastupdate', now
return
$.set 'lastchecked', now
el = $.el 'span',
innerHTML: "Update: <%= meta.name %> v#{version} is out, get it <a href=<%= meta.page %> target=_blank>here</a>."
new Notification 'info', el, 120
$.ready ->
$.on window, 'message', Main.message
$.set 'lastUpdate', now
$.add d.head, $.el 'script',
src: '<%= meta.repo %>raw/<%= meta.mainBranch %>/latest.js'
handleErrors: (errors) ->
unless errors instanceof Array

View File

@ -91,14 +91,10 @@ Settings =
$.on $('.close', dialog), 'click', Settings.close
$.on overlay, 'click', Settings.close
d.body.style.width = "#{d.body.clientWidth}px"
$.addClass d.body, 'unscroll'
$.add d.body, [overlay, dialog]
close: ->
return unless Settings.dialog
d.body.style.removeProperty 'width'
$.rmClass d.body, 'unscroll'
$.rm Settings.overlay
$.rm Settings.dialog
delete Settings.overlay
@ -374,9 +370,10 @@ Settings =
$.get items, (items) ->
for key, val of items
continue if ['usercss', 'emojiPos', 'archiver'].contains key
continue if ['emojiPos', 'archiver'].contains key
input = inputs[key]
input.value = val
continue if key is 'usercss'
$.on input, event, Settings[key]
Settings[key].call input
Rice.nodes sectionreturn

View File

@ -227,7 +227,7 @@ UI = do ->
isTouching: isTouching
}
[o.topBorder, o.bottomBorder] = if Conf['Header auto-hide']
[o.topBorder, o.bottomBorder] = if Conf['Header auto-hide'] or not Conf['Fixed Header']
[0, 0]
else if Conf['Bottom Header']
[0, Header.bar.getBoundingClientRect().height]

View File

@ -1,8 +1,5 @@
class Notification
constructor: (type, content, @timeout) ->
@add = add.bind @
@close = close.bind @
@el = $.el 'div',
innerHTML: '<a href=javascript:; class=close title=Close>×</a><div class=message></div>'
@el.style.opacity = 0
@ -17,7 +14,7 @@ class Notification
setType: (type) ->
@el.className = "notification #{type}"
add = ->
add: =>
if d.hidden
$.on d, 'visibilitychange', @add
return
@ -27,5 +24,5 @@ class Notification
@el.style.opacity = 1
setTimeout @close, @timeout * $.SECOND if @timeout
close = ->
close: =>
$.rm @el

View File

@ -0,0 +1 @@
postMessage({version:'<%= version %>'},'*')

View File

@ -1 +0,0 @@
<%= version %>

View File

@ -7,7 +7,7 @@ Fourchan =
$.globalEval """
window.addEventListener('prettyprint', function(e) {
var pre = e.detail;
pre.innerHTML = prettyPrintOne(pre.innerHTML);
pre.innerHTML = prettyPrintOne(pre.innerHTML.replace(/\\s/g, '&nbsp;'));
}, false);
"""
Post::callbacks.push
@ -33,7 +33,12 @@ Fourchan =
code: ->
return if @isClone
for pre in $$ '.prettyprint', @nodes.comment
$.event 'prettyprint', pre, window
# Don't pretty print twice:
# Might need a better way to detect if a .prettyprint
# is already pretty-printed. We can't just look for spans
# since 4chan inserts its quotes and whatnot inside.
unless $ '.pln', pre
$.event 'prettyprint', pre, window
return
math: ->
return if @isClone or !$ '.math', @nodes.comment
@ -44,4 +49,4 @@ Fourchan =
$.event '4chanParsingDone',
threadId: threadID
offset: offset
limit: limit
limit: limit

View File

@ -6,7 +6,8 @@ ThreadStats =
@dialog = sc = $.el 'span',
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>"
id: 'thread-stats'
Header.addShortcut sc
$.ready ->
Header.addShortcut sc
else
@dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;',
"<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>"

View File

@ -8,7 +8,8 @@ ThreadUpdater =
@dialog = sc = $.el 'span',
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>"
id: 'updater'
Header.addShortcut sc
$.ready ->
Header.addShortcut sc
else
@dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;',
"<div class=move></div><span id=update-status></span><span id=update-timer title='Update now'></span>"
@ -97,9 +98,15 @@ ThreadUpdater =
return unless e.detail.threadID is ThreadUpdater.thread.ID
ThreadUpdater.outdateCount = 0
setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2
checkpost: ->
checkpost: (e) ->
unless ThreadUpdater.checkPostCount
return unless e.detail.threadID is ThreadUpdater.thread.ID
ThreadUpdater.seconds = 0
ThreadUpdater.outdateCount = 0
ThreadUpdater.set 'timer', '...'
unless g.DEAD or ThreadUpdater.foundPost or ThreadUpdater.checkPostCount >= 5
return setTimeout ThreadUpdater.update, ++ThreadUpdater.checkPostCount * $.SECOND
ThreadUpdater.set 'timer', ThreadUpdater.getInterval()
ThreadUpdater.checkPostCount = 0
delete ThreadUpdater.foundPost
delete ThreadUpdater.postID

View File

@ -1,12 +1,26 @@
ThreadWatcher =
init: ->
return unless Conf['Thread Watcher']
@shortcut = sc = $.el 'a',
textContent: 'Watcher'
id: 'watcher-link'
href: 'javascript:;'
className: 'disabled'
@dialog = UI.dialog 'watcher', 'top: 50px; left: 0px;',
'<div class=move>Thread Watcher</div>'
'<div class=move>Thread Watcher<a class=close href=javascript:;>×</a></div>'
$.on d, 'QRPostSuccessful', @cb.post
$.on d, '4chanXInitFinished', @ready
$.sync 'WatchedThreads', @refresh
$.on sc, 'click', @toggleWatcher
$.on $('.move>.close', ThreadWatcher.dialog), 'click', @toggleWatcher
Header.addShortcut sc
$.ready ->
ThreadWatcher.refresh()
$.add d.body, ThreadWatcher.dialog
ThreadWatcher.dialog.hidden = true
Thread::callbacks.push
name: 'Thread Watcher'
@ -23,12 +37,6 @@ ThreadWatcher =
ThreadWatcher.watch @
$.delete 'AutoWatch'
ready: ->
$.off d, '4chanXInitFinished', ThreadWatcher.ready
return unless Main.isThisPageLegit()
ThreadWatcher.refresh()
$.add d.body, ThreadWatcher.dialog
refresh: (watched) ->
unless watched
$.get 'WatchedThreads', {}, (item) ->
@ -61,6 +69,10 @@ ThreadWatcher =
Favicon.empty
return
toggleWatcher: ->
$.toggleClass ThreadWatcher.shortcut, 'disabled'
ThreadWatcher.dialog.hidden = !ThreadWatcher.dialog.hidden
cb:
toggle: ->
ThreadWatcher.toggle Get.postFromNode(@).thread

View File

@ -22,6 +22,7 @@ Unread =
$.on d, '4chanXInitFinished', Unread.ready
$.on d, 'ThreadUpdate', Unread.onUpdate
$.on d, 'scroll visibilitychange', Unread.read
$.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line']
ready: ->
$.off d, '4chanXInitFinished', Unread.ready
@ -29,8 +30,7 @@ Unread =
for ID, post of Unread.thread.posts
posts.push post if post.isReply
Unread.addPosts posts
Unread.setLine() if Conf['Unread Line']
Unread.scroll() if Conf['Scroll to Last Read Post']
Unread.scroll() if Conf['Scroll to Last Read Post']
scroll: ->
# Let the header's onload callback handle it.

View File

@ -161,6 +161,7 @@ QR =
for type, arr of types
QR.persona.loadPersonas type, arr
return
parseItem: (item, types) ->
return if item[0] is '#'
return unless match = item.match /(name|email|subject|password):"(.*)"/i
@ -170,7 +171,7 @@ QR =
item = item.replace match, ''
boards = item.match(/boards:([^;]+)/i)?[1].toLowerCase() or 'global'
if boards isnt 'global' and not (g.BOARD.ID in boards.split ',')
if boards isnt 'global' and not ((boards.split ',').contains g.BOARD.ID)
return
if type is 'password'
@ -182,14 +183,16 @@ QR =
if /always/i.test item
QR.persona.always[type] = val
unless val in types[type]
unless types[type].contains val
types[type].push val
loadPersonas: (type, arr) ->
list = $ "#list-#{type}", QR.nodes.el
for val in arr
$.add list, $.el 'option',
textContent: val
return
getPassword: ->
unless QR.persona.pwd
QR.persona.pwd = if m = d.cookie.match /4chan_pass=([^;]+)/
@ -202,9 +205,11 @@ QR =
# we'd rather use #postPassword when we can.
$.id('delPassword').value
return QR.persona.pwd
get: (cb) ->
$.get 'QR.persona', {}, ({'QR.persona': persona}) ->
cb persona
set: (post) ->
$.get 'QR.persona', {}, ({'QR.persona': persona}) ->
persona =
@ -231,17 +236,20 @@ QR =
QR.cooldown.cooldowns = item["cooldown.#{board}"]
QR.cooldown.start()
$.sync "cooldown.#{board}", QR.cooldown.sync
start: ->
return unless Conf['Cooldown']
return if QR.cooldown.isCounting
QR.cooldown.isCounting = true
QR.cooldown.count()
sync: (cooldowns) ->
# Add each cooldowns, don't overwrite everything in case we
# still need to prune one in the current tab to auto-post.
for id of cooldowns
QR.cooldown.cooldowns[id] = cooldowns[id]
QR.cooldown.start()
set: (data) ->
return unless Conf['Cooldown']
{req, post, isReply, delay} = data
@ -271,12 +279,14 @@ QR =
QR.cooldown.cooldowns[start] = cooldown
$.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns
QR.cooldown.start()
unset: (id) ->
delete QR.cooldown.cooldowns[id]
if Object.keys(QR.cooldown.cooldowns).length
$.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns
else
$.delete "cooldown.#{g.BOARD}"
count: ->
unless Object.keys(QR.cooldown.cooldowns).length
$.delete "#{g.BOARD}.cooldown"
@ -382,11 +392,11 @@ QR =
toggle = if e.type is 'dragstart' then $.off else $.on
toggle d, 'dragover', QR.dragOver
toggle d, 'drop', QR.dropFile
dragOver: (e) ->
e.preventDefault()
e.dataTransfer.dropEffect = 'copy' # cursor feedback
dropFile: (e) ->
# Let it only handle files from the desktop.
return unless e.dataTransfer.files.length
@ -394,7 +404,7 @@ QR =
QR.open()
QR.fileInput e.dataTransfer.files
$.addClass QR.nodes.el, 'dump'
paste: (e) ->
files = []
for item in e.clipboardData.items
@ -410,7 +420,7 @@ QR =
openFileInput: (e) ->
return if e.keyCode and e.keyCode isnt 32
QR.nodes.fileInput.click()
fileInput: (files) ->
if @ instanceof Element #or files instanceof Event # file input
files = [@files...]
@ -448,7 +458,7 @@ QR =
$.addClass QR.nodes.el, 'dump'
posts: []
post: class
constructor: (select) ->
el = $.el 'a',
@ -516,7 +526,7 @@ QR =
@load() if QR.selected is @ # load persona
@select() if select
@unlock()
rm: ->
$.rm @nodes.el
index = QR.posts.indexOf @
@ -527,7 +537,7 @@ QR =
QR.posts.splice index, 1
return unless window.URL
URL.revokeObjectURL @URL
lock: (lock=true) ->
@isLocked = lock
return unless @ is QR.selected
@ -538,10 +548,10 @@ QR =
(if lock then $.off else $.on) QR.nodes.filename.parentNode, 'click', QR.openFileInput
@nodes.spoiler.disabled = lock
@nodes.el.draggable = !lock
unlock: ->
@lock false
select: ->
if QR.selected
QR.selected.nodes.el.id = null
@ -556,6 +566,7 @@ QR =
@load()
$.event 'QRPostSelection', @
load: ->
# Load this post's values.
for name in ['thread', 'name', 'email', 'sub', 'com']
@ -564,7 +575,7 @@ QR =
QR.tripcodeHider.call QR.nodes['name']
@showFileData()
QR.characterCount()
save: (input) ->
if input.type is 'checkbox'
@spoiler = input.checked
@ -578,7 +589,7 @@ QR =
# during the last 5 seconds of the cooldown.
if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5
QR.cooldown.auto = false
forceSave: ->
return unless @ is QR.selected
# Do this in case people use extensions
@ -586,7 +597,7 @@ QR =
for name in ['thread', 'name', 'email', 'sub', 'com', 'spoiler']
@save QR.nodes[name]
return
setFile: (@file) ->
@filename = "#{file.name} (#{$.bytesToString file.size})"
@nodes.el.title = @filename
@ -597,7 +608,7 @@ QR =
@nodes.el.style.backgroundImage = null
return
@setThumbnail()
setThumbnail: (fileURL) ->
# XXX Opera does not support blob URL
# Create a redimensioned thumbnail.
@ -657,7 +668,7 @@ QR =
applyBlob new Blob [ui8a], type: 'image/png'
img.src = fileURL
rmFile: ->
delete @file
delete @filename
@ -676,7 +687,7 @@ QR =
$.addClass QR.nodes.fileSubmit, 'has-file'
else
$.rmClass QR.nodes.fileSubmit, 'has-file'
pasteText: (file) ->
reader = new FileReader()
reader.onload = (e) =>
@ -689,7 +700,7 @@ QR =
QR.nodes.com.value = @com
@nodes.span.textContent = @com
reader.readAsText file
dragStart: ->
$.addClass @, 'drag'
@ -705,7 +716,7 @@ QR =
dragOver: (e) ->
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
drop: ->
el = $ '.drag', @parentNode
$.rmClass el, 'drag' # Opera doesn't fire dragEnd if we drop it on something else
@ -723,7 +734,7 @@ QR =
return if d.cookie.indexOf('pass_enabled=1') >= 0
return unless @isEnabled = !!$.id 'captchaFormPart'
$.asap (-> $.id 'recaptcha_challenge_field_holder'), @ready.bind @
ready: ->
setLifetime = (e) => @lifetime = e.detail
$.on window, 'captcha:timeout', setLifetime
@ -774,7 +785,7 @@ QR =
sync: (@captchas) ->
QR.captcha.count()
getOne: ->
@clear()
if captcha = @captchas.shift()
@ -790,7 +801,7 @@ QR =
# If there's only one word, duplicate it.
response = "#{response} #{response}" unless /\s/.test response
{challenge, response}
save: ->
return unless response = @nodes.input.value.trim()
@captchas.push
@ -800,7 +811,7 @@ QR =
@count()
@reload()
$.set 'captchas', @captchas
clear: ->
now = Date.now()
for captcha, i in @captchas
@ -809,7 +820,7 @@ QR =
@captchas = @captchas[i..]
@count()
$.set 'captchas', @captchas
load: ->
return unless @nodes.challenge.firstChild
# -1 minute to give upload some time.
@ -819,7 +830,7 @@ QR =
@nodes.img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}"
@nodes.input.value = null
@clear()
count: ->
count = @captchas.length
@nodes.input.placeholder = switch count
@ -829,14 +840,15 @@ QR =
'Verification (1 cached captcha)'
else
"Verification (#{count} cached captchas)"
@nodes.input.alt = count # For XTRM RICE.
@nodes.input.alt = count
reload: (focus) ->
# the 't' argument prevents the input from being focused
$.globalEval 'Recaptcha.reload("t")'
# Focus if we meant to.
@nodes.input.focus() if focus
keydown: (e) ->
if e.keyCode is 8 and not @nodes.input.value
@reload()
@ -975,6 +987,7 @@ QR =
else if !check and @.className.match "\\btripped\\b" then $.rmClass @, 'tripped'
preSubmitHooks: []
submit: (e) ->
e?.preventDefault()

View File

@ -36,12 +36,13 @@ QuoteBacklink =
for clone in post.clones
containers.push clone.nodes.backlinkContainer
for container in containers
link = a.cloneNode true
frag = [$.tn(' '), link = a.cloneNode true]
if Conf['Quote Previewing']
$.on link, 'mouseover', QuotePreview.mouseover
if Conf['Quote Inlining']
$.on link, 'click', QuoteInline.toggle
$.add container, [$.tn(' '), link]
frag.pushArrays QuoteInline.qiQuote link, $.hasClass link, 'filtered' if Conf['Quote Hash Navigation']
$.add container, frag
return
secondNode: ->
if @isClone and (@origin.isReply or Conf['OP Backlinks'])

View File

@ -5,14 +5,31 @@ QuoteInline =
if Conf['Comment Expansion']
ExpandComment.callbacks.push @node
if Conf['Quote Hash Navigation']
@node = ->
for link in @nodes.quotelinks.concat [@nodes.backlinks...]
$.after link, QuoteInline.qiQuote link, $.hasClass link, 'filtered' unless @isClone
$.on link, 'click', QuoteInline.toggle
return
else
@node = ->
for link in @nodes.quotelinks.concat [@nodes.backlinks...]
$.on link, 'click', QuoteInline.toggle
return
Post::callbacks.push
name: 'Quote Inlining'
cb: @node
node: ->
for link in @nodes.quotelinks.concat [@nodes.backlinks...]
$.on link, 'click', QuoteInline.toggle
return
qiQuote: (link, hidden) ->
[
$.tn(' ')
$.el 'a',
className: if hidden then 'hashlink filtered' else 'hashlink'
textContent: '#'
href: link.href
]
toggle: (e) ->
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0

View File

@ -17,6 +17,9 @@ QuoteYou =
if Conf['Highlight Own Posts']
$.addClass doc, 'highlight-own'
if Conf['Highlight Posts Quoting You']
$.addClass doc, 'highlight-you'
# Stop there if there's no quotes in that post.
return unless (quotes = @quotes).length
{quotelinks} = @nodes
@ -24,4 +27,5 @@ QuoteYou =
for quotelink in quotelinks
if QR.db.get Get.postDataFromLink quotelink
$.add quotelink, $.tn QuoteYou.text
$.addClass @nodes.root, 'quotesYou'
return