Release 4chan X v1.7.32.
This commit is contained in:
parent
93628fb502
commit
368baebfc1
@ -1,3 +1,6 @@
|
||||
### v1.7.32
|
||||
*2014-05-10*
|
||||
|
||||
**Zixaphir, ccd0**
|
||||
- Bug fixes in linkification/embedding.
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 4chan X - Version 1.7.31 - 2014-05-08
|
||||
* 4chan X - Version 1.7.32 - 2014-05-10
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/ccd0/4chan-x/blob/master/LICENSE
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// ==UserScript==
|
||||
// @name 4chan X
|
||||
// @version 1.7.31
|
||||
// @version 1.7.32
|
||||
// @minGMVer 1.14
|
||||
// @minFFVer 26
|
||||
// @namespace 4chan-X
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Generated by CoffeeScript
|
||||
// ==UserScript==
|
||||
// @name 4chan X
|
||||
// @version 1.7.31
|
||||
// @version 1.7.32
|
||||
// @minGMVer 1.14
|
||||
// @minFFVer 26
|
||||
// @namespace 4chan-X
|
||||
@ -24,7 +24,7 @@
|
||||
// ==/UserScript==
|
||||
|
||||
/*
|
||||
* 4chan X - Version 1.7.31 - 2014-05-08
|
||||
* 4chan X - Version 1.7.32 - 2014-05-10
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/ccd0/4chan-x/blob/master/LICENSE
|
||||
@ -372,7 +372,7 @@
|
||||
doc = d.documentElement;
|
||||
|
||||
g = {
|
||||
VERSION: '1.7.31',
|
||||
VERSION: '1.7.32',
|
||||
NAMESPACE: '4chan X.',
|
||||
boards: {}
|
||||
};
|
||||
@ -8488,7 +8488,6 @@
|
||||
if (g.VIEW === 'catalog' || !Conf['Linkify']) {
|
||||
return;
|
||||
}
|
||||
this.regString = /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})(\/|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i;
|
||||
this.types = {};
|
||||
_ref = this.ordered_types;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
@ -8498,27 +8497,28 @@
|
||||
if (Conf['Comment Expansion']) {
|
||||
ExpandComment.callbacks.push(this.node);
|
||||
}
|
||||
if (Conf['Title Link']) {
|
||||
$.sync('CachedTitles', Linkify.titleSync);
|
||||
}
|
||||
return Post.callbacks.push({
|
||||
name: 'Linkify',
|
||||
cb: this.node
|
||||
});
|
||||
},
|
||||
node: function() {
|
||||
var data, el, end, endNode, i, index, items, length, link, links, node, result, saved, snapshot, space, test, word, _i, _len, _ref;
|
||||
if (this.isClone) {
|
||||
if (Conf['Embedding']) {
|
||||
i = 0;
|
||||
items = $$('.embed', this.nodes.comment);
|
||||
while (el = items[i++]) {
|
||||
$.on(el, 'click', Linkify.cb.toggle);
|
||||
if ($.hasClass(el, 'embedded')) {
|
||||
Linkify.cb.toggle.call(el);
|
||||
}
|
||||
}
|
||||
events: function(post) {
|
||||
var el, i, items;
|
||||
i = 0;
|
||||
items = $$('.embedder', post.nodes.comment);
|
||||
while (el = items[i++]) {
|
||||
$.on(el, 'click', Linkify.cb.toggle);
|
||||
if ($.hasClass(el, 'embedded')) {
|
||||
Linkify.cb.toggle.call(el);
|
||||
}
|
||||
}
|
||||
},
|
||||
node: function() {
|
||||
var data, end, endNode, i, index, length, link, links, node, result, saved, snapshot, space, test, word;
|
||||
if (this.isClone) {
|
||||
return (Conf['Embedding'] ? Linkify.events(this) : null);
|
||||
}
|
||||
if (!Linkify.regString.test(this.info.comment)) {
|
||||
return;
|
||||
}
|
||||
test = /[^\s'"]+/g;
|
||||
@ -8528,7 +8528,7 @@
|
||||
links = [];
|
||||
while (node = snapshot.snapshotItem(i++)) {
|
||||
data = node.data;
|
||||
if (node.parentElement.nodeName === "A" || !data) {
|
||||
if (!data || node.parentElement.nodeName === "A") {
|
||||
continue;
|
||||
}
|
||||
while (result = test.exec(data)) {
|
||||
@ -8560,28 +8560,25 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
_ref = links.reverse();
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
link = _ref[_i];
|
||||
this.nodes.links.push(Linkify.makeLink(link, this));
|
||||
link.detach();
|
||||
i = links.length;
|
||||
while (i--) {
|
||||
link = links[i];
|
||||
Linkify.embedProcess(Linkify.makeLink(link, this), this);
|
||||
}
|
||||
if (!(Conf['Embedding'] || Conf['Link Title'])) {
|
||||
return;
|
||||
}
|
||||
links = this.nodes.links;
|
||||
i = 0;
|
||||
while (link = links[i++]) {
|
||||
if (data = Linkify.services(link)) {
|
||||
if (Conf['Embedding']) {
|
||||
Linkify.embed(data);
|
||||
}
|
||||
if (Conf['Link Title']) {
|
||||
Linkify.title(data);
|
||||
}
|
||||
},
|
||||
embedProcess: function(link, post) {
|
||||
var data;
|
||||
if (data = Linkify.services(link)) {
|
||||
data.push(post);
|
||||
if (Conf['Embedding']) {
|
||||
Linkify.embed(data);
|
||||
}
|
||||
if (Conf['Link Title']) {
|
||||
return Linkify.title(data);
|
||||
}
|
||||
}
|
||||
},
|
||||
regString: /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})([:\/]|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i,
|
||||
makeRange: function(startNode, endNode, startOffset, endOffset) {
|
||||
var range;
|
||||
range = document.createRange();
|
||||
@ -8590,7 +8587,7 @@
|
||||
return range;
|
||||
},
|
||||
makeLink: function(range) {
|
||||
var a, char, i, text;
|
||||
var a, i, t, text;
|
||||
text = range.toString();
|
||||
i = 0;
|
||||
while (/[(\[{<>]/.test(text.charAt(i))) {
|
||||
@ -8606,8 +8603,8 @@
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
while (/[)\]}>.,]/.test(char = text.charAt(text.length - (1 + i)))) {
|
||||
if (!(/[.,]/.test(char) || (text.match(/[()\[\]{}<>]/g)).length % 2)) {
|
||||
while (/[)\]}>.,]/.test(t = text.charAt(text.length - (1 + i)))) {
|
||||
if (!(/[.,]/.test(t) || (text.match(/[()\[\]{}<>]/g)).length % 2)) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
@ -8621,7 +8618,7 @@
|
||||
range.setEnd(range.endContainer, range.endOffset - i);
|
||||
}
|
||||
}
|
||||
if (!/(https?|mailto|git|magnet|ftp|irc):/.test(text)) {
|
||||
if (!/(mailto:|.+:\/\/)/.test(text)) {
|
||||
text = (/@/.test(text) ? 'mailto:' : 'http://') + text;
|
||||
}
|
||||
a = $.el('a', {
|
||||
@ -8632,6 +8629,7 @@
|
||||
});
|
||||
$.add(a, range.extractContents());
|
||||
range.insertNode(a);
|
||||
range.detach();
|
||||
return a;
|
||||
},
|
||||
services: function(link) {
|
||||
@ -8644,14 +8642,14 @@
|
||||
continue;
|
||||
}
|
||||
if (type.dummy) {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
return [type.key, match[1], match[2], link];
|
||||
}
|
||||
},
|
||||
embed: function(data) {
|
||||
var embed, href, key, link, name, options, uid, value, _ref;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3];
|
||||
var embed, href, key, link, name, options, post, uid, value, _ref;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], post = data[4];
|
||||
href = link.href;
|
||||
embed = $.el('a', {
|
||||
className: 'embedder',
|
||||
@ -8668,57 +8666,47 @@
|
||||
value = _ref[name];
|
||||
embed.dataset[name] = value;
|
||||
}
|
||||
embed.dataset.nodedata = link.innerHTML;
|
||||
$.addClass(link, "" + embed.dataset.key);
|
||||
$.on(embed, 'click', Linkify.cb.toggle);
|
||||
$.after(link, [$.tn(' '), embed]);
|
||||
if (Conf['Auto-embed']) {
|
||||
Linkify.cb.toggle.call(embed);
|
||||
return Linkify.cb.toggle.call(embed);
|
||||
}
|
||||
data.push(embed);
|
||||
},
|
||||
title: function(data) {
|
||||
var embed, err, key, link, options, service, title, titles, uid;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4];
|
||||
var err, key, link, options, post, service, title, titles, uid;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], post = data[4];
|
||||
if (!(service = Linkify.types[key].title)) {
|
||||
return;
|
||||
}
|
||||
titles = Conf['CachedTitles'];
|
||||
if (title = titles[uid]) {
|
||||
if (link) {
|
||||
link.textContent = title[0];
|
||||
}
|
||||
if (Conf['Embedding']) {
|
||||
return embed.dataset.title = title[0];
|
||||
}
|
||||
return link.textContent = title[0];
|
||||
} else {
|
||||
try {
|
||||
$.cache(service.api(uid), function() {
|
||||
return title = Linkify.cb.title(this, data);
|
||||
}, {
|
||||
return $.cache(service.api(uid), (function() {
|
||||
return Linkify.cb.title(this, data);
|
||||
}), {
|
||||
responseType: 'json'
|
||||
});
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (link) {
|
||||
link.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>";
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (title) {
|
||||
titles[uid] = [title, Date.now()];
|
||||
return $.set('CachedTitles', titles);
|
||||
link.innerHTML = '<span class="warning">Title Link Blocked</span> (are you using NoScript?)</a>';
|
||||
$.prepend(link, $.tn("[" + key + "] "));
|
||||
}
|
||||
}
|
||||
},
|
||||
titleSync: function(value) {
|
||||
return Conf['CachedTitles'] = value;
|
||||
},
|
||||
cb: {
|
||||
toggle: function() {
|
||||
var string, _ref;
|
||||
_ref = $.hasClass(this, "embedded") ? ['unembed', '(embed)'] : ['embed', '(unembed)'], string = _ref[0], this.textContent = _ref[1];
|
||||
$.replace(this.previousElementSibling, Linkify.cb[string](this));
|
||||
if ($.hasClass(this, "embedded")) {
|
||||
$.rm(this.previousElementSibling);
|
||||
this.previousElementSibling.hidden = false;
|
||||
this.textContent = '(embed)';
|
||||
} else {
|
||||
this.previousElementSibling.hidden = true;
|
||||
$.before(this, Linkify.cb.embed(this));
|
||||
this.textContent = '(unembed)';
|
||||
}
|
||||
return $.toggleClass(this, 'embedded');
|
||||
},
|
||||
embed: function(a) {
|
||||
@ -8727,45 +8715,35 @@
|
||||
el.style.cssText = type.style != null ? type.style : "border: 0; width: 640px; height: 390px";
|
||||
return el;
|
||||
},
|
||||
unembed: function(a) {
|
||||
var el;
|
||||
el = $.el('a', {
|
||||
rel: 'nofollow noreferrer',
|
||||
target: 'blank',
|
||||
className: 'linkify',
|
||||
href: a.dataset.href
|
||||
});
|
||||
if (a.dataset.title) {
|
||||
el.textContent = a.dataset.title;
|
||||
} else {
|
||||
el.innerHTML = a.dataset.nodedata;
|
||||
}
|
||||
$.addClass(el, a.dataset.key);
|
||||
return el;
|
||||
},
|
||||
title: function(response, data) {
|
||||
var embed, key, link, options, service, text, uid;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4];
|
||||
title: function(req, data) {
|
||||
var key, link, link2, options, post, post2, service, status, text, uid, _i, _j, _len, _len1, _ref, _ref1;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], post = data[4];
|
||||
status = req.status;
|
||||
service = Linkify.types[key].title;
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
case 304:
|
||||
text = "" + (service.text(response.response));
|
||||
if (Conf['Embedding']) {
|
||||
embed.dataset.title = text;
|
||||
text = "[" + key + "] " + ((function() {
|
||||
switch (status) {
|
||||
case 200:
|
||||
case 304:
|
||||
return service.text(req.response);
|
||||
case 404:
|
||||
return "Not Found";
|
||||
case 403:
|
||||
return "Forbidden or Private";
|
||||
default:
|
||||
return "" + status + "'d";
|
||||
}
|
||||
})());
|
||||
link.textContent = text;
|
||||
_ref = post.clones;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
post2 = _ref[_i];
|
||||
_ref1 = $$('a', post2.nodes.comment);
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
link2 = _ref1[_j];
|
||||
if (link2.href === link.href) {
|
||||
link2.textContent = text;
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
text = "[" + key + "] Not Found";
|
||||
break;
|
||||
case 403:
|
||||
text = "[" + key + "] Forbidden or Private";
|
||||
break;
|
||||
default:
|
||||
text = "[" + key + "] " + this.status + "'d";
|
||||
}
|
||||
if (link) {
|
||||
return link.textContent = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -8809,18 +8787,11 @@
|
||||
regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/,
|
||||
style: 'border: 0; width: auto; height: auto;',
|
||||
el: function(a) {
|
||||
var div, img, link;
|
||||
img = $.el('img', {
|
||||
src: a.dataset.href
|
||||
});
|
||||
link = $.el('a', {
|
||||
target: '_blank',
|
||||
href: a.dataset.href
|
||||
});
|
||||
div = $.el('div');
|
||||
$.add(link, img);
|
||||
$.add(div, link);
|
||||
return div;
|
||||
var el;
|
||||
el = $.el('div');
|
||||
el.innerHTML = '<a target="_blank"><img></a>';
|
||||
el.firstChild.href = el.firstChild.firstChild.src = a.dataset.href;
|
||||
return el;
|
||||
}
|
||||
}, {
|
||||
key: 'InstallGentoo',
|
||||
@ -8860,13 +8831,13 @@
|
||||
var el;
|
||||
el = $.el('div');
|
||||
$.cache("https://mediacru.sh/" + a.dataset.uid + ".json", function() {
|
||||
var embed, file, files, status, type, _i, _j, _len, _len1, _ref;
|
||||
var embed, ext, file, files, i, status, type, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _results, _results1;
|
||||
status = this.status;
|
||||
if (status !== 200 && status !== 304) {
|
||||
return el.textContent = "ERROR " + status;
|
||||
}
|
||||
files = this.response.files;
|
||||
_ref = ['video/mp4', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'image/svg', 'audio/mpeg'];
|
||||
_ref = ['video/mp4', 'video/webm', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'audio/mpeg', 'audio/ogg'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
type = _ref[_i];
|
||||
for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
|
||||
@ -8885,20 +8856,35 @@
|
||||
}
|
||||
switch (embed.type) {
|
||||
case 'video/mp4':
|
||||
case 'video/webm':
|
||||
case 'video/ogv':
|
||||
return el.innerHTML = "<video autoplay loop>\n <source src=\"https://mediacru.sh/" + a.dataset.uid + ".mp4\" type=\"video/mp4;\">\n <source src=\"https://mediacru.sh/" + a.dataset.uid + ".ogv\" type=\"video/ogg; codecs='theora, vorbis'\">\n</video>";
|
||||
el.innerHTML = '<video autoplay loop><source type="video/mp4"><source type="video/webm"><source type="video/ogg"></video>';
|
||||
_ref1 = ['mp4', 'webm', 'ogv'];
|
||||
_results = [];
|
||||
for (i = _k = 0, _len2 = _ref1.length; _k < _len2; i = ++_k) {
|
||||
ext = _ref1[i];
|
||||
_results.push(el.firstChild.children[i].src = "https://mediacru.sh/" + a.dataset.uid + "." + ext);
|
||||
}
|
||||
return _results;
|
||||
break;
|
||||
case 'image/svg+xml':
|
||||
case 'image/png':
|
||||
case 'image/gif':
|
||||
case 'image/jpeg':
|
||||
case 'image/svg':
|
||||
case 'image/svg+xml':
|
||||
return $.add(el, $.el('a', {
|
||||
target: '_blank',
|
||||
href: a.dataset.href,
|
||||
innerHTML: "<img src='https://mediacru.sh/" + file.file + "'>"
|
||||
}));
|
||||
el.innerHTML = '<a target="_blank"><img></a>';
|
||||
el.firstChild.href = a.dataset.href;
|
||||
return el.firstChild.firstChild.src = "https://mediacru.sh/" + file.file;
|
||||
case 'audio/mpeg':
|
||||
return el.innerHTML = "<audio controls><source src='https://mediacru.sh/" + file.file + "'></audio>";
|
||||
case 'audio/ogg':
|
||||
el.innerHTML = '<audio controls><source type="audio/ogg"><source type="audio/mpeg"></audio>';
|
||||
_ref2 = ['ogg', 'mp3'];
|
||||
_results1 = [];
|
||||
for (i = _l = 0, _len3 = _ref2.length; _l < _len3; i = ++_l) {
|
||||
ext = _ref2[i];
|
||||
_results1.push(el.firstChild.children[i].src = "https://mediacru.sh/" + a.dataset.uid + "." + ext);
|
||||
}
|
||||
return _results1;
|
||||
break;
|
||||
default:
|
||||
return el.textContent = "ERROR: No valid filetype.";
|
||||
}
|
||||
@ -8926,23 +8912,15 @@
|
||||
}, {
|
||||
key: 'SoundCloud',
|
||||
regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/,
|
||||
style: 'height: auto; width: 500px; display: inline-block;',
|
||||
style: 'border: 0; width: 500px; height: 400px;',
|
||||
el: function(a) {
|
||||
var div;
|
||||
div = $.el('div', {
|
||||
className: "soundcloud",
|
||||
name: "soundcloud"
|
||||
return $.el('iframe', {
|
||||
src: "//w.soundcloud.com/player/?visual=true&show_comments=false&url=https%3A%2F%2Fsoundcloud.com%2F" + (encodeURIComponent(a.dataset.uid))
|
||||
});
|
||||
$.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + a.dataset.uid, {
|
||||
onloadend: function() {
|
||||
return div.innerHTML = JSON.parse(this.responseText).html;
|
||||
}
|
||||
}, false);
|
||||
return div;
|
||||
},
|
||||
title: {
|
||||
api: function(uid) {
|
||||
return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid;
|
||||
return "//soundcloud.com/oembed?format=json&url=https%3A%2F%2Fsoundcloud.com%2F" + (encodeURIComponent(uid));
|
||||
},
|
||||
text: function(_) {
|
||||
return _.title;
|
||||
@ -8962,19 +8940,27 @@
|
||||
regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/,
|
||||
style: "border: none; width: 640px; height: 360px;",
|
||||
el: function(a) {
|
||||
var channel, chapter, result, _;
|
||||
if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(a.dataset.uid)) {
|
||||
_ = result[0], channel = result[1], chapter = result[2];
|
||||
return $.el('object', {
|
||||
data: 'http://www.twitch.tv/widgets/archive_embed_player.swf',
|
||||
innerHTML: "<param name='allowFullScreen' value='true' />\n<param name='flashvars' value='channel=" + channel + "&start_volume=25&auto_play=false" + (chapter ? "&chapter_id=" + chapter : "") + "' />"
|
||||
var channel, id, idparam, obj, result, type, _;
|
||||
if (result = /(\w+)\/([bc])\/(\d+)/i.exec(a.dataset.uid)) {
|
||||
_ = result[0], channel = result[1], type = result[2], id = result[3];
|
||||
idparam = {
|
||||
'b': 'archive_id',
|
||||
'c': 'chapter_id'
|
||||
};
|
||||
obj = $.el('object', {
|
||||
data: 'http://www.twitch.tv/widgets/archive_embed_player.swf'
|
||||
});
|
||||
obj.innerHTML = '<param name="allowFullScreen" value="true"><param name="flashvars">';
|
||||
obj.children[1].value = "channel=" + channel + "&start_volume=25&auto_play=false&" + idparam[type] + "=" + id;
|
||||
return obj;
|
||||
} else {
|
||||
channel = (/(\w+)/.exec(a.dataset.uid))[0];
|
||||
return $.el('object', {
|
||||
data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel,
|
||||
innerHTML: "<param name=\"allowFullScreen\" value=\"true\" />\n<param name=\"movie\" value=\"http://www.twitch.tv/widgets/live_embed_player.swf\" />\n<param name=\"flashvars\" value=\"hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25\" />"
|
||||
obj = $.el('object', {
|
||||
data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel
|
||||
});
|
||||
obj.innerHTML = '<param name="allowFullScreen" value="true"><param name="flashvars">';
|
||||
obj.children[1].value = "hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25";
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
|
||||
BIN
builds/crx.crx
BIN
builds/crx.crx
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "4chan X",
|
||||
"version": "1.7.31",
|
||||
"version": "1.7.32",
|
||||
"manifest_version": 2,
|
||||
"description": "Cross-browser userscript for maximum lurking on 4chan.",
|
||||
"icons": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript
|
||||
/*
|
||||
* 4chan X - Version 1.7.31 - 2014-05-08
|
||||
* 4chan X - Version 1.7.32 - 2014-05-10
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/ccd0/4chan-x/blob/master/LICENSE
|
||||
@ -348,7 +348,7 @@
|
||||
doc = d.documentElement;
|
||||
|
||||
g = {
|
||||
VERSION: '1.7.31',
|
||||
VERSION: '1.7.32',
|
||||
NAMESPACE: '4chan X.',
|
||||
boards: {}
|
||||
};
|
||||
@ -8485,7 +8485,6 @@
|
||||
if (g.VIEW === 'catalog' || !Conf['Linkify']) {
|
||||
return;
|
||||
}
|
||||
this.regString = /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})(\/|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i;
|
||||
this.types = {};
|
||||
_ref = this.ordered_types;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
@ -8495,27 +8494,28 @@
|
||||
if (Conf['Comment Expansion']) {
|
||||
ExpandComment.callbacks.push(this.node);
|
||||
}
|
||||
if (Conf['Title Link']) {
|
||||
$.sync('CachedTitles', Linkify.titleSync);
|
||||
}
|
||||
return Post.callbacks.push({
|
||||
name: 'Linkify',
|
||||
cb: this.node
|
||||
});
|
||||
},
|
||||
node: function() {
|
||||
var data, el, end, endNode, i, index, items, length, link, links, node, result, saved, snapshot, space, test, word, _i, _len, _ref;
|
||||
if (this.isClone) {
|
||||
if (Conf['Embedding']) {
|
||||
i = 0;
|
||||
items = $$('.embed', this.nodes.comment);
|
||||
while (el = items[i++]) {
|
||||
$.on(el, 'click', Linkify.cb.toggle);
|
||||
if ($.hasClass(el, 'embedded')) {
|
||||
Linkify.cb.toggle.call(el);
|
||||
}
|
||||
}
|
||||
events: function(post) {
|
||||
var el, i, items;
|
||||
i = 0;
|
||||
items = $$('.embedder', post.nodes.comment);
|
||||
while (el = items[i++]) {
|
||||
$.on(el, 'click', Linkify.cb.toggle);
|
||||
if ($.hasClass(el, 'embedded')) {
|
||||
Linkify.cb.toggle.call(el);
|
||||
}
|
||||
}
|
||||
},
|
||||
node: function() {
|
||||
var data, end, endNode, i, index, length, link, links, node, result, saved, snapshot, space, test, word;
|
||||
if (this.isClone) {
|
||||
return (Conf['Embedding'] ? Linkify.events(this) : null);
|
||||
}
|
||||
if (!Linkify.regString.test(this.info.comment)) {
|
||||
return;
|
||||
}
|
||||
test = /[^\s'"]+/g;
|
||||
@ -8525,7 +8525,7 @@
|
||||
links = [];
|
||||
while (node = snapshot.snapshotItem(i++)) {
|
||||
data = node.data;
|
||||
if (node.parentElement.nodeName === "A" || !data) {
|
||||
if (!data || node.parentElement.nodeName === "A") {
|
||||
continue;
|
||||
}
|
||||
while (result = test.exec(data)) {
|
||||
@ -8557,28 +8557,25 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
_ref = links.reverse();
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
link = _ref[_i];
|
||||
this.nodes.links.push(Linkify.makeLink(link, this));
|
||||
link.detach();
|
||||
i = links.length;
|
||||
while (i--) {
|
||||
link = links[i];
|
||||
Linkify.embedProcess(Linkify.makeLink(link, this), this);
|
||||
}
|
||||
if (!(Conf['Embedding'] || Conf['Link Title'])) {
|
||||
return;
|
||||
}
|
||||
links = this.nodes.links;
|
||||
i = 0;
|
||||
while (link = links[i++]) {
|
||||
if (data = Linkify.services(link)) {
|
||||
if (Conf['Embedding']) {
|
||||
Linkify.embed(data);
|
||||
}
|
||||
if (Conf['Link Title']) {
|
||||
Linkify.title(data);
|
||||
}
|
||||
},
|
||||
embedProcess: function(link, post) {
|
||||
var data;
|
||||
if (data = Linkify.services(link)) {
|
||||
data.push(post);
|
||||
if (Conf['Embedding']) {
|
||||
Linkify.embed(data);
|
||||
}
|
||||
if (Conf['Link Title']) {
|
||||
return Linkify.title(data);
|
||||
}
|
||||
}
|
||||
},
|
||||
regString: /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})([:\/]|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i,
|
||||
makeRange: function(startNode, endNode, startOffset, endOffset) {
|
||||
var range;
|
||||
range = document.createRange();
|
||||
@ -8587,7 +8584,7 @@
|
||||
return range;
|
||||
},
|
||||
makeLink: function(range) {
|
||||
var a, char, i, text;
|
||||
var a, i, t, text;
|
||||
text = range.toString();
|
||||
i = 0;
|
||||
while (/[(\[{<>]/.test(text.charAt(i))) {
|
||||
@ -8603,8 +8600,8 @@
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
while (/[)\]}>.,]/.test(char = text.charAt(text.length - (1 + i)))) {
|
||||
if (!(/[.,]/.test(char) || (text.match(/[()\[\]{}<>]/g)).length % 2)) {
|
||||
while (/[)\]}>.,]/.test(t = text.charAt(text.length - (1 + i)))) {
|
||||
if (!(/[.,]/.test(t) || (text.match(/[()\[\]{}<>]/g)).length % 2)) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
@ -8618,7 +8615,7 @@
|
||||
range.setEnd(range.endContainer, range.endOffset - i);
|
||||
}
|
||||
}
|
||||
if (!/(https?|mailto|git|magnet|ftp|irc):/.test(text)) {
|
||||
if (!/(mailto:|.+:\/\/)/.test(text)) {
|
||||
text = (/@/.test(text) ? 'mailto:' : 'http://') + text;
|
||||
}
|
||||
a = $.el('a', {
|
||||
@ -8629,6 +8626,7 @@
|
||||
});
|
||||
$.add(a, range.extractContents());
|
||||
range.insertNode(a);
|
||||
range.detach();
|
||||
return a;
|
||||
},
|
||||
services: function(link) {
|
||||
@ -8641,14 +8639,14 @@
|
||||
continue;
|
||||
}
|
||||
if (type.dummy) {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
return [type.key, match[1], match[2], link];
|
||||
}
|
||||
},
|
||||
embed: function(data) {
|
||||
var embed, href, key, link, name, options, uid, value, _ref;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3];
|
||||
var embed, href, key, link, name, options, post, uid, value, _ref;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], post = data[4];
|
||||
href = link.href;
|
||||
embed = $.el('a', {
|
||||
className: 'embedder',
|
||||
@ -8665,57 +8663,47 @@
|
||||
value = _ref[name];
|
||||
embed.dataset[name] = value;
|
||||
}
|
||||
embed.dataset.nodedata = link.innerHTML;
|
||||
$.addClass(link, "" + embed.dataset.key);
|
||||
$.on(embed, 'click', Linkify.cb.toggle);
|
||||
$.after(link, [$.tn(' '), embed]);
|
||||
if (Conf['Auto-embed']) {
|
||||
Linkify.cb.toggle.call(embed);
|
||||
return Linkify.cb.toggle.call(embed);
|
||||
}
|
||||
data.push(embed);
|
||||
},
|
||||
title: function(data) {
|
||||
var embed, err, key, link, options, service, title, titles, uid;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4];
|
||||
var err, key, link, options, post, service, title, titles, uid;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], post = data[4];
|
||||
if (!(service = Linkify.types[key].title)) {
|
||||
return;
|
||||
}
|
||||
titles = Conf['CachedTitles'];
|
||||
if (title = titles[uid]) {
|
||||
if (link) {
|
||||
link.textContent = title[0];
|
||||
}
|
||||
if (Conf['Embedding']) {
|
||||
return embed.dataset.title = title[0];
|
||||
}
|
||||
return link.textContent = title[0];
|
||||
} else {
|
||||
try {
|
||||
$.cache(service.api(uid), function() {
|
||||
return title = Linkify.cb.title(this, data);
|
||||
}, {
|
||||
return $.cache(service.api(uid), (function() {
|
||||
return Linkify.cb.title(this, data);
|
||||
}), {
|
||||
responseType: 'json'
|
||||
});
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (link) {
|
||||
link.innerHTML = "[" + key + "] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>";
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (title) {
|
||||
titles[uid] = [title, Date.now()];
|
||||
return $.set('CachedTitles', titles);
|
||||
link.innerHTML = '<span class="warning">Title Link Blocked</span> (are you using NoScript?)</a>';
|
||||
$.prepend(link, $.tn("[" + key + "] "));
|
||||
}
|
||||
}
|
||||
},
|
||||
titleSync: function(value) {
|
||||
return Conf['CachedTitles'] = value;
|
||||
},
|
||||
cb: {
|
||||
toggle: function() {
|
||||
var string, _ref;
|
||||
_ref = $.hasClass(this, "embedded") ? ['unembed', '(embed)'] : ['embed', '(unembed)'], string = _ref[0], this.textContent = _ref[1];
|
||||
$.replace(this.previousElementSibling, Linkify.cb[string](this));
|
||||
if ($.hasClass(this, "embedded")) {
|
||||
$.rm(this.previousElementSibling);
|
||||
this.previousElementSibling.hidden = false;
|
||||
this.textContent = '(embed)';
|
||||
} else {
|
||||
this.previousElementSibling.hidden = true;
|
||||
$.before(this, Linkify.cb.embed(this));
|
||||
this.textContent = '(unembed)';
|
||||
}
|
||||
return $.toggleClass(this, 'embedded');
|
||||
},
|
||||
embed: function(a) {
|
||||
@ -8724,45 +8712,35 @@
|
||||
el.style.cssText = type.style != null ? type.style : "border: 0; width: 640px; height: 390px";
|
||||
return el;
|
||||
},
|
||||
unembed: function(a) {
|
||||
var el;
|
||||
el = $.el('a', {
|
||||
rel: 'nofollow noreferrer',
|
||||
target: 'blank',
|
||||
className: 'linkify',
|
||||
href: a.dataset.href
|
||||
});
|
||||
if (a.dataset.title) {
|
||||
el.textContent = a.dataset.title;
|
||||
} else {
|
||||
el.innerHTML = a.dataset.nodedata;
|
||||
}
|
||||
$.addClass(el, a.dataset.key);
|
||||
return el;
|
||||
},
|
||||
title: function(response, data) {
|
||||
var embed, key, link, options, service, text, uid;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], embed = data[4];
|
||||
title: function(req, data) {
|
||||
var key, link, link2, options, post, post2, service, status, text, uid, _i, _j, _len, _len1, _ref, _ref1;
|
||||
key = data[0], uid = data[1], options = data[2], link = data[3], post = data[4];
|
||||
status = req.status;
|
||||
service = Linkify.types[key].title;
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
case 304:
|
||||
text = "" + (service.text(response.response));
|
||||
if (Conf['Embedding']) {
|
||||
embed.dataset.title = text;
|
||||
text = "[" + key + "] " + ((function() {
|
||||
switch (status) {
|
||||
case 200:
|
||||
case 304:
|
||||
return service.text(req.response);
|
||||
case 404:
|
||||
return "Not Found";
|
||||
case 403:
|
||||
return "Forbidden or Private";
|
||||
default:
|
||||
return "" + status + "'d";
|
||||
}
|
||||
})());
|
||||
link.textContent = text;
|
||||
_ref = post.clones;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
post2 = _ref[_i];
|
||||
_ref1 = $$('a', post2.nodes.comment);
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
link2 = _ref1[_j];
|
||||
if (link2.href === link.href) {
|
||||
link2.textContent = text;
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
text = "[" + key + "] Not Found";
|
||||
break;
|
||||
case 403:
|
||||
text = "[" + key + "] Forbidden or Private";
|
||||
break;
|
||||
default:
|
||||
text = "[" + key + "] " + this.status + "'d";
|
||||
}
|
||||
if (link) {
|
||||
return link.textContent = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -8806,18 +8784,11 @@
|
||||
regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/,
|
||||
style: 'border: 0; width: auto; height: auto;',
|
||||
el: function(a) {
|
||||
var div, img, link;
|
||||
img = $.el('img', {
|
||||
src: a.dataset.href
|
||||
});
|
||||
link = $.el('a', {
|
||||
target: '_blank',
|
||||
href: a.dataset.href
|
||||
});
|
||||
div = $.el('div');
|
||||
$.add(link, img);
|
||||
$.add(div, link);
|
||||
return div;
|
||||
var el;
|
||||
el = $.el('div');
|
||||
el.innerHTML = '<a target="_blank"><img></a>';
|
||||
el.firstChild.href = el.firstChild.firstChild.src = a.dataset.href;
|
||||
return el;
|
||||
}
|
||||
}, {
|
||||
key: 'InstallGentoo',
|
||||
@ -8857,13 +8828,13 @@
|
||||
var el;
|
||||
el = $.el('div');
|
||||
$.cache("https://mediacru.sh/" + a.dataset.uid + ".json", function() {
|
||||
var embed, file, files, status, type, _i, _j, _len, _len1, _ref;
|
||||
var embed, ext, file, files, i, status, type, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _results, _results1;
|
||||
status = this.status;
|
||||
if (status !== 200 && status !== 304) {
|
||||
return el.textContent = "ERROR " + status;
|
||||
}
|
||||
files = this.response.files;
|
||||
_ref = ['video/mp4', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'image/svg', 'audio/mpeg'];
|
||||
_ref = ['video/mp4', 'video/webm', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'audio/mpeg', 'audio/ogg'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
type = _ref[_i];
|
||||
for (_j = 0, _len1 = files.length; _j < _len1; _j++) {
|
||||
@ -8882,20 +8853,35 @@
|
||||
}
|
||||
switch (embed.type) {
|
||||
case 'video/mp4':
|
||||
case 'video/webm':
|
||||
case 'video/ogv':
|
||||
return el.innerHTML = "<video autoplay loop>\n <source src=\"https://mediacru.sh/" + a.dataset.uid + ".mp4\" type=\"video/mp4;\">\n <source src=\"https://mediacru.sh/" + a.dataset.uid + ".ogv\" type=\"video/ogg; codecs='theora, vorbis'\">\n</video>";
|
||||
el.innerHTML = '<video autoplay loop><source type="video/mp4"><source type="video/webm"><source type="video/ogg"></video>';
|
||||
_ref1 = ['mp4', 'webm', 'ogv'];
|
||||
_results = [];
|
||||
for (i = _k = 0, _len2 = _ref1.length; _k < _len2; i = ++_k) {
|
||||
ext = _ref1[i];
|
||||
_results.push(el.firstChild.children[i].src = "https://mediacru.sh/" + a.dataset.uid + "." + ext);
|
||||
}
|
||||
return _results;
|
||||
break;
|
||||
case 'image/svg+xml':
|
||||
case 'image/png':
|
||||
case 'image/gif':
|
||||
case 'image/jpeg':
|
||||
case 'image/svg':
|
||||
case 'image/svg+xml':
|
||||
return $.add(el, $.el('a', {
|
||||
target: '_blank',
|
||||
href: a.dataset.href,
|
||||
innerHTML: "<img src='https://mediacru.sh/" + file.file + "'>"
|
||||
}));
|
||||
el.innerHTML = '<a target="_blank"><img></a>';
|
||||
el.firstChild.href = a.dataset.href;
|
||||
return el.firstChild.firstChild.src = "https://mediacru.sh/" + file.file;
|
||||
case 'audio/mpeg':
|
||||
return el.innerHTML = "<audio controls><source src='https://mediacru.sh/" + file.file + "'></audio>";
|
||||
case 'audio/ogg':
|
||||
el.innerHTML = '<audio controls><source type="audio/ogg"><source type="audio/mpeg"></audio>';
|
||||
_ref2 = ['ogg', 'mp3'];
|
||||
_results1 = [];
|
||||
for (i = _l = 0, _len3 = _ref2.length; _l < _len3; i = ++_l) {
|
||||
ext = _ref2[i];
|
||||
_results1.push(el.firstChild.children[i].src = "https://mediacru.sh/" + a.dataset.uid + "." + ext);
|
||||
}
|
||||
return _results1;
|
||||
break;
|
||||
default:
|
||||
return el.textContent = "ERROR: No valid filetype.";
|
||||
}
|
||||
@ -8923,23 +8909,15 @@
|
||||
}, {
|
||||
key: 'SoundCloud',
|
||||
regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/,
|
||||
style: 'height: auto; width: 500px; display: inline-block;',
|
||||
style: 'border: 0; width: 500px; height: 400px;',
|
||||
el: function(a) {
|
||||
var div;
|
||||
div = $.el('div', {
|
||||
className: "soundcloud",
|
||||
name: "soundcloud"
|
||||
return $.el('iframe', {
|
||||
src: "//w.soundcloud.com/player/?visual=true&show_comments=false&url=https%3A%2F%2Fsoundcloud.com%2F" + (encodeURIComponent(a.dataset.uid))
|
||||
});
|
||||
$.ajax("//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + a.dataset.uid, {
|
||||
onloadend: function() {
|
||||
return div.innerHTML = JSON.parse(this.responseText).html;
|
||||
}
|
||||
}, false);
|
||||
return div;
|
||||
},
|
||||
title: {
|
||||
api: function(uid) {
|
||||
return "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/" + uid;
|
||||
return "//soundcloud.com/oembed?format=json&url=https%3A%2F%2Fsoundcloud.com%2F" + (encodeURIComponent(uid));
|
||||
},
|
||||
text: function(_) {
|
||||
return _.title;
|
||||
@ -8959,19 +8937,27 @@
|
||||
regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/,
|
||||
style: "border: none; width: 640px; height: 360px;",
|
||||
el: function(a) {
|
||||
var channel, chapter, result, _;
|
||||
if (result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec(a.dataset.uid)) {
|
||||
_ = result[0], channel = result[1], chapter = result[2];
|
||||
return $.el('object', {
|
||||
data: 'http://www.twitch.tv/widgets/archive_embed_player.swf',
|
||||
innerHTML: "<param name='allowFullScreen' value='true' />\n<param name='flashvars' value='channel=" + channel + "&start_volume=25&auto_play=false" + (chapter ? "&chapter_id=" + chapter : "") + "' />"
|
||||
var channel, id, idparam, obj, result, type, _;
|
||||
if (result = /(\w+)\/([bc])\/(\d+)/i.exec(a.dataset.uid)) {
|
||||
_ = result[0], channel = result[1], type = result[2], id = result[3];
|
||||
idparam = {
|
||||
'b': 'archive_id',
|
||||
'c': 'chapter_id'
|
||||
};
|
||||
obj = $.el('object', {
|
||||
data: 'http://www.twitch.tv/widgets/archive_embed_player.swf'
|
||||
});
|
||||
obj.innerHTML = '<param name="allowFullScreen" value="true"><param name="flashvars">';
|
||||
obj.children[1].value = "channel=" + channel + "&start_volume=25&auto_play=false&" + idparam[type] + "=" + id;
|
||||
return obj;
|
||||
} else {
|
||||
channel = (/(\w+)/.exec(a.dataset.uid))[0];
|
||||
return $.el('object', {
|
||||
data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel,
|
||||
innerHTML: "<param name=\"allowFullScreen\" value=\"true\" />\n<param name=\"movie\" value=\"http://www.twitch.tv/widgets/live_embed_player.swf\" />\n<param name=\"flashvars\" value=\"hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25\" />"
|
||||
obj = $.el('object', {
|
||||
data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=" + channel
|
||||
});
|
||||
obj.innerHTML = '<param name="allowFullScreen" value="true"><param name="flashvars">';
|
||||
obj.children[1].value = "hostname=www.twitch.tv&channel=" + channel + "&auto_play=true&start_volume=25";
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
|
||||
<app appid='lacclbnghgdicfifcamcmcnilckjamag'>
|
||||
<updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.7.31' />
|
||||
<updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.7.32' />
|
||||
</app>
|
||||
</gupdate>
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "4chan-X",
|
||||
"version": "1.7.31",
|
||||
"version": "1.7.32",
|
||||
"description": "Cross-browser userscript for maximum lurking on 4chan.",
|
||||
"meta": {
|
||||
"name": "4chan X",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user