Release 4chan X v1.7.52.

This commit is contained in:
ccd0 2014-06-02 04:17:08 -07:00
parent c8d29b6741
commit f67675ec11
17 changed files with 261 additions and 162 deletions

View File

@ -1,3 +1,9 @@
### v1.7.52
*2014-06-02*
**ccd0**
- Add workaround for downloading with the original filename in Firefox.
### v1.7.51
*2014-06-02*

View File

@ -27,6 +27,7 @@ module.exports = (grunt) ->
'src/General/Get.coffee'
'src/General/UI.coffee'
'src/General/Notice.coffee'
'src/General/CrossOrigin.coffee'
'src/Filtering/**/*.coffee'
'src/Quotelinks/**/*.coffee'
'src/Posting/QR.coffee'

View File

@ -1,5 +1,5 @@
/*
* 4chan X - Version 1.7.51 - 2014-06-02
* 4chan X - Version 1.7.52 - 2014-06-02
*
* Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE

View File

@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
// @version 1.7.51
// @version 1.7.52
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X

View File

@ -1,7 +1,7 @@
// Generated by CoffeeScript
// ==UserScript==
// @name 4chan X
// @version 1.7.51
// @version 1.7.52
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@ -24,7 +24,7 @@
// ==/UserScript==
/*
* 4chan X - Version 1.7.51 - 2014-06-02
* 4chan X - Version 1.7.52 - 2014-06-02
*
* Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE
@ -106,7 +106,7 @@
'use strict';
(function() {
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, TrashQueue, UI, Unread, Video, c, d, doc, g,
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Conf, Config, CrossOrigin, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, TrashQueue, UI, Unread, Video, c, d, doc, g,
__slice = [].slice,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__hasProp = {}.hasOwnProperty,
@ -194,6 +194,7 @@
'Thread Hiding Link': [true, 'Add a link to hide entire threads.'],
'Reply Hiding Link': [true, 'Add a link to hide single replies.'],
'Delete Link': [true, 'Add post and image deletion links to the menu.'],
'Download Link': [true, 'Add a download with original filename link to the menu.'],
'Archive Link': [true, 'Add an archive link to the menu.']
},
'Monitoring': {
@ -372,7 +373,7 @@
doc = d.documentElement;
g = {
VERSION: '1.7.51',
VERSION: '1.7.52',
NAMESPACE: '4chan X.',
boards: {}
};
@ -4016,6 +4017,50 @@
};
})();
CrossOrigin = (function() {
var handleBlob, handleUrl;
handleBlob = function(urlBlob, contentType, contentDisposition, url, cb) {
var blob, match, mime, name, _ref, _ref1, _ref2;
name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0;
mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0);
if (match) {
name = match.replace(/\\"/g, '"');
}
blob = new Blob([urlBlob], {
type: mime
});
blob.name = name;
return cb(blob);
};
handleUrl = function(url, cb) {
return GM_xmlhttpRequest({
method: "GET",
url: url,
overrideMimeType: "text/plain; charset=x-user-defined",
onload: function(xhr) {
var contentDisposition, contentType, data, i, r, _ref, _ref1;
r = xhr.responseText;
data = new Uint8Array(r.length);
i = 0;
while (i < r.length) {
data[i] = r.charCodeAt(i);
i++;
}
contentType = (_ref = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)) != null ? _ref[1] : void 0;
contentDisposition = (_ref1 = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)) != null ? _ref1[1] : void 0;
return handleBlob(data, contentType, contentDisposition, url, cb);
},
onerror: function(xhr) {
return cb(null);
}
});
};
return {
request: handleUrl
};
})();
Anonymize = {
init: function() {
if (g.VIEW === 'catalog' || !Conf['Anonymize']) {
@ -6052,44 +6097,16 @@
QR.handleFiles(files);
return $.addClass(QR.nodes.el, 'dump');
},
handleBlob: function(urlBlob, contentType, contentDisposition, url) {
var blob, match, mime, name, _ref, _ref1, _ref2;
name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0;
mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0);
if (match) {
name = match.replace(/\\"/g, '"');
}
blob = new Blob([urlBlob], {
type: mime
});
blob.name = name;
return QR.handleFiles([blob]);
},
handleUrl: function() {
var url;
url = prompt("Insert an url:");
if (url === null) {
return;
}
return GM_xmlhttpRequest({
method: "GET",
url: url,
overrideMimeType: "text/plain; charset=x-user-defined",
onload: function(xhr) {
var contentDisposition, contentType, data, i, r, _ref, _ref1;
r = xhr.responseText;
data = new Uint8Array(r.length);
i = 0;
while (i < r.length) {
data[i] = r.charCodeAt(i);
i++;
}
contentType = (_ref = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)) != null ? _ref[1] : void 0;
contentDisposition = (_ref1 = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)) != null ? _ref1[1] : void 0;
return QR.handleBlob(data, contentType, contentDisposition, url);
},
onerror: function(xhr) {
return CrossOrigin.request(url, function(blob) {
if (blob) {
return QR.handleFiles([blob]);
} else {
return QR.error("Can't load image.");
}
});
@ -9320,6 +9337,24 @@
className: 'download-link',
textContent: 'Download file'
});
if (typeof chrome === "undefined" || chrome === null) {
$.on(a, 'click', function(e) {
if (this.protocol === 'blob:') {
return true;
}
e.preventDefault();
return CrossOrigin.request(this.href, (function(_this) {
return function(blob) {
if (blob) {
_this.href = URL.createObjectURL(blob);
return _this.click();
} else {
return new Notice('error', "Could not download " + file.URL, 30);
}
};
})(this));
});
}
return $.event('AddMenuEntry', {
type: 'post',
el: a,
@ -12444,7 +12479,7 @@
Settings.dialog = dialog = $.el('div', {
id: 'fourchanx-settings',
className: 'dialog',
innerHTML: '<nav><div class=sections-list></div><p class=\'imp-exp-result warning\'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href=\'https://github.com/ccd0/4chan-x\' target=_blank>4chan X</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md\' target=_blank>1.7.51</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/issues\' target=_blank>Issues</a>&nbsp|&nbsp<a href=javascript:; class=\'close fa fa-times\' title=Close></a></div></nav><div class=section-container><section></section></div>'
innerHTML: '<nav><div class=sections-list></div><p class=\'imp-exp-result warning\'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href=\'https://github.com/ccd0/4chan-x\' target=_blank>4chan X</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md\' target=_blank>1.7.52</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/issues\' target=_blank>Issues</a>&nbsp|&nbsp<a href=javascript:; class=\'close fa fa-times\' title=Close></a></div></nav><div class=section-container><section></section></div>'
});
$.on($('.export', Settings.dialog), 'click', Settings["export"]);
$.on($('.import', Settings.dialog), 'click', Settings["import"]);
@ -13289,7 +13324,7 @@
}
if (previousversion) {
el = $.el('span', {
innerHTML: '4chan X has been updated to <a href="https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md" target="_blank">version 1.7.51</a>.'
innerHTML: '4chan X has been updated to <a href="https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md" target="_blank">version 1.7.52</a>.'
});
new Notice('info', el, 15);
} else {

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"name": "4chan X",
"version": "1.7.51",
"version": "1.7.52",
"manifest_version": 2,
"description": "Cross-browser userscript for maximum lurking on 4chan.",
"icons": {

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript
/*
* 4chan X - Version 1.7.51 - 2014-06-02
* 4chan X - Version 1.7.52 - 2014-06-02
*
* Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE
@ -82,7 +82,7 @@
'use strict';
(function() {
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, TrashQueue, UI, Unread, Video, c, d, doc, g,
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Conf, Config, CrossOrigin, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, TrashQueue, UI, Unread, Video, c, d, doc, g,
__slice = [].slice,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__hasProp = {}.hasOwnProperty,
@ -170,7 +170,7 @@
'Thread Hiding Link': [true, 'Add a link to hide entire threads.'],
'Reply Hiding Link': [true, 'Add a link to hide single replies.'],
'Delete Link': [true, 'Add post and image deletion links to the menu.'],
'Download Link': [true, 'Add a download with original filename link to the menu. Chrome-only currently.'],
'Download Link': [true, 'Add a download with original filename link to the menu.'],
'Archive Link': [true, 'Add an archive link to the menu.']
},
'Monitoring': {
@ -348,7 +348,7 @@
doc = d.documentElement;
g = {
VERSION: '1.7.51',
VERSION: '1.7.52',
NAMESPACE: '4chan X.',
boards: {}
};
@ -4045,6 +4045,47 @@
};
})();
CrossOrigin = (function() {
var handleBlob, handleUrl;
handleBlob = function(urlBlob, contentType, contentDisposition, url, cb) {
var blob, match, mime, name, _ref, _ref1, _ref2;
name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0;
mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0);
if (match) {
name = match.replace(/\\"/g, '"');
}
blob = new Blob([urlBlob], {
type: mime
});
blob.name = name;
return cb(blob);
};
handleUrl = function(url, cb) {
var xhr;
xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
var contentDisposition, contentType;
if (this.readyState === this.DONE && xhr.status === 200) {
contentType = this.getResponseHeader('Content-Type');
contentDisposition = this.getResponseHeader('Content-Disposition');
return handleBlob(this.response, contentType, contentDisposition, url, cb);
} else {
return cb(null);
}
};
xhr.onerror = function(e) {
return cb(null);
};
return xhr.send();
};
return {
request: handleUrl
};
})();
Anonymize = {
init: function() {
if (g.VIEW === 'catalog' || !Conf['Anonymize']) {
@ -6090,43 +6131,19 @@
QR.handleFiles(files);
return $.addClass(QR.nodes.el, 'dump');
},
handleBlob: function(urlBlob, contentType, contentDisposition, url) {
var blob, match, mime, name, _ref, _ref1, _ref2;
name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0;
mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0);
if (match) {
name = match.replace(/\\"/g, '"');
}
blob = new Blob([urlBlob], {
type: mime
});
blob.name = name;
return QR.handleFiles([blob]);
},
handleUrl: function() {
var url, xhr;
var url;
url = prompt("Insert an url:");
if (url === null) {
return;
}
xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
var contentDisposition, contentType;
if (this.readyState === this.DONE && xhr.status === 200) {
contentType = this.getResponseHeader('Content-Type');
contentDisposition = this.getResponseHeader('Content-Disposition');
return QR.handleBlob(this.response, contentType, contentDisposition, url);
return CrossOrigin.request(url, function(blob) {
if (blob) {
return QR.handleFiles([blob]);
} else {
return QR.error("Can't load image.");
}
};
xhr.onerror = function(e) {
return QR.error("Can't load image.");
};
return xhr.send();
});
},
handleFiles: function(files) {
var file, i, _i, _len;
@ -12438,7 +12455,7 @@
Settings.dialog = dialog = $.el('div', {
id: 'fourchanx-settings',
className: 'dialog',
innerHTML: '<nav><div class=sections-list></div><p class=\'imp-exp-result warning\'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href=\'https://github.com/ccd0/4chan-x\' target=_blank>4chan X</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md\' target=_blank>1.7.51</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/issues\' target=_blank>Issues</a>&nbsp|&nbsp<a href=javascript:; class=\'close fa fa-times\' title=Close></a></div></nav><div class=section-container><section></section></div>'
innerHTML: '<nav><div class=sections-list></div><p class=\'imp-exp-result warning\'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href=\'https://github.com/ccd0/4chan-x\' target=_blank>4chan X</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md\' target=_blank>1.7.52</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/issues\' target=_blank>Issues</a>&nbsp|&nbsp<a href=javascript:; class=\'close fa fa-times\' title=Close></a></div></nav><div class=section-container><section></section></div>'
});
$.on($('.export', Settings.dialog), 'click', Settings["export"]);
$.on($('.import', Settings.dialog), 'click', Settings["import"]);
@ -13270,7 +13287,7 @@
}
if (previousversion) {
el = $.el('span', {
innerHTML: '4chan X has been updated to <a href="https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md" target="_blank">version 1.7.51</a>.'
innerHTML: '4chan X has been updated to <a href="https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md" target="_blank">version 1.7.52</a>.'
});
new Notice('info', el, 15);
} else {

View File

@ -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.51' />
<updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.7.52' />
</app>
</gupdate>

View File

@ -1,6 +1,6 @@
{
"name": "4chan X",
"version": "1.7.51",
"version": "1.7.52",
"manifest_version": 2,
"description": "Cross-browser userscript for maximum lurking on 4chan.",
"icons": {

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript
/*
* 4chan X - Version 1.7.51 - 2014-06-02
* 4chan X - Version 1.7.52 - 2014-06-02
*
* Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE
@ -82,7 +82,7 @@
'use strict';
(function() {
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, TrashQueue, UI, Unread, Video, c, d, doc, g,
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Conf, Config, CrossOrigin, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, TrashQueue, UI, Unread, Video, c, d, doc, g,
__slice = [].slice,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__hasProp = {}.hasOwnProperty,
@ -170,7 +170,7 @@
'Thread Hiding Link': [true, 'Add a link to hide entire threads.'],
'Reply Hiding Link': [true, 'Add a link to hide single replies.'],
'Delete Link': [true, 'Add post and image deletion links to the menu.'],
'Download Link': [true, 'Add a download with original filename link to the menu. Chrome-only currently.'],
'Download Link': [true, 'Add a download with original filename link to the menu.'],
'Archive Link': [true, 'Add an archive link to the menu.']
},
'Monitoring': {
@ -348,7 +348,7 @@
doc = d.documentElement;
g = {
VERSION: '1.7.51',
VERSION: '1.7.52',
NAMESPACE: '4chan X.',
boards: {}
};
@ -4045,6 +4045,47 @@
};
})();
CrossOrigin = (function() {
var handleBlob, handleUrl;
handleBlob = function(urlBlob, contentType, contentDisposition, url, cb) {
var blob, match, mime, name, _ref, _ref1, _ref2;
name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0;
mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0);
if (match) {
name = match.replace(/\\"/g, '"');
}
blob = new Blob([urlBlob], {
type: mime
});
blob.name = name;
return cb(blob);
};
handleUrl = function(url, cb) {
var xhr;
xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
var contentDisposition, contentType;
if (this.readyState === this.DONE && xhr.status === 200) {
contentType = this.getResponseHeader('Content-Type');
contentDisposition = this.getResponseHeader('Content-Disposition');
return handleBlob(this.response, contentType, contentDisposition, url, cb);
} else {
return cb(null);
}
};
xhr.onerror = function(e) {
return cb(null);
};
return xhr.send();
};
return {
request: handleUrl
};
})();
Anonymize = {
init: function() {
if (g.VIEW === 'catalog' || !Conf['Anonymize']) {
@ -6090,43 +6131,19 @@
QR.handleFiles(files);
return $.addClass(QR.nodes.el, 'dump');
},
handleBlob: function(urlBlob, contentType, contentDisposition, url) {
var blob, match, mime, name, _ref, _ref1, _ref2;
name = (_ref = url.match(/([^\/]+)\/*$/)) != null ? _ref[1] : void 0;
mime = (contentType != null ? contentType.match(/[^;]*/)[0] : void 0) || 'application/octet-stream';
match = (contentDisposition != null ? (_ref1 = contentDisposition.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref1[1] : void 0 : void 0) || (contentType != null ? (_ref2 = contentType.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)) != null ? _ref2[1] : void 0 : void 0);
if (match) {
name = match.replace(/\\"/g, '"');
}
blob = new Blob([urlBlob], {
type: mime
});
blob.name = name;
return QR.handleFiles([blob]);
},
handleUrl: function() {
var url, xhr;
var url;
url = prompt("Insert an url:");
if (url === null) {
return;
}
xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
var contentDisposition, contentType;
if (this.readyState === this.DONE && xhr.status === 200) {
contentType = this.getResponseHeader('Content-Type');
contentDisposition = this.getResponseHeader('Content-Disposition');
return QR.handleBlob(this.response, contentType, contentDisposition, url);
return CrossOrigin.request(url, function(blob) {
if (blob) {
return QR.handleFiles([blob]);
} else {
return QR.error("Can't load image.");
}
};
xhr.onerror = function(e) {
return QR.error("Can't load image.");
};
return xhr.send();
});
},
handleFiles: function(files) {
var file, i, _i, _len;
@ -12438,7 +12455,7 @@
Settings.dialog = dialog = $.el('div', {
id: 'fourchanx-settings',
className: 'dialog',
innerHTML: '<nav><div class=sections-list></div><p class=\'imp-exp-result warning\'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href=\'https://github.com/ccd0/4chan-x\' target=_blank>4chan X</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md\' target=_blank>1.7.51</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/issues\' target=_blank>Issues</a>&nbsp|&nbsp<a href=javascript:; class=\'close fa fa-times\' title=Close></a></div></nav><div class=section-container><section></section></div>'
innerHTML: '<nav><div class=sections-list></div><p class=\'imp-exp-result warning\'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href=\'https://github.com/ccd0/4chan-x\' target=_blank>4chan X</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md\' target=_blank>1.7.52</a>&nbsp|&nbsp<a href=\'https://github.com/ccd0/4chan-x/issues\' target=_blank>Issues</a>&nbsp|&nbsp<a href=javascript:; class=\'close fa fa-times\' title=Close></a></div></nav><div class=section-container><section></section></div>'
});
$.on($('.export', Settings.dialog), 'click', Settings["export"]);
$.on($('.import', Settings.dialog), 'click', Settings["import"]);
@ -13270,7 +13287,7 @@
}
if (previousversion) {
el = $.el('span', {
innerHTML: '4chan X has been updated to <a href="https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md" target="_blank">version 1.7.51</a>.'
innerHTML: '4chan X has been updated to <a href="https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md" target="_blank">version 1.7.52</a>.'
});
new Notice('info', el, 15);
} else {

View File

@ -1,6 +1,6 @@
{
"name": "4chan-X",
"version": "1.7.51",
"version": "1.7.52",
"description": "Cross-browser userscript for maximum lurking on 4chan.",
"meta": {
"name": "4chan X",

View File

@ -229,12 +229,10 @@ Config =
true
'Add post and image deletion links to the menu.'
]
<% if (type === 'crx') { %>
'Download Link': [
true
'Add a download with original filename link to the menu. Chrome-only currently.'
'Add a download with original filename link to the menu.'
]
<% } %>
'Archive Link': [
true
'Add an archive link to the menu.'

View File

@ -0,0 +1,52 @@
CrossOrigin = do ->
handleBlob = (urlBlob, contentType, contentDisposition, url, cb) ->
name = url.match(/([^\/]+)\/*$/)?[1]
mime = contentType?.match(/[^;]*/)[0] or 'application/octet-stream'
match =
contentDisposition?.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)?[1] or
contentType?.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)?[1]
if match
name = match.replace /\\"/g, '"'
blob = new Blob([urlBlob], {type: mime})
blob.name = name
cb blob
handleUrl = (url, cb) ->
<% if (type === 'crx') { %>
xhr = new XMLHttpRequest();
xhr.open('GET', url, true)
xhr.responseType = 'blob'
xhr.onload = (e) ->
if @readyState is @DONE && xhr.status is 200
contentType = @getResponseHeader('Content-Type')
contentDisposition = @getResponseHeader('Content-Disposition')
handleBlob @response, contentType, contentDisposition, url, cb
else
cb null
xhr.onerror = (e) ->
cb null
xhr.send()
<% } %>
<% if (type === 'userscript') { %>
GM_xmlhttpRequest
method: "GET"
url: url
overrideMimeType: "text/plain; charset=x-user-defined"
onload: (xhr) ->
r = xhr.responseText
data = new Uint8Array(r.length)
i = 0
while i < r.length
data[i] = r.charCodeAt(i)
i++
contentType = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)?[1]
contentDisposition = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)?[1]
handleBlob data, contentType, contentDisposition, url, cb
onerror: (xhr) ->
cb null
<% } %>
return {request: handleUrl}

View File

@ -5,6 +5,21 @@ DownloadLink =
a = $.el 'a',
className: 'download-link'
textContent: 'Download file'
<% if (type === 'userscript') { %>
unless chrome?
# Firefox places same-origin restrictions on links with the download attribute.
$.on a, 'click', (e) ->
return true if @protocol is 'blob:'
e.preventDefault()
CrossOrigin.request @href, (blob) =>
if blob
@href = URL.createObjectURL blob
@click()
else
new Notice 'error', "Could not download #{file.URL}", 30
<% } %>
$.event 'AddMenuEntry',
type: 'post'
el: a

View File

@ -293,56 +293,14 @@ QR =
QR.handleFiles files
$.addClass QR.nodes.el, 'dump'
handleBlob: (urlBlob, contentType, contentDisposition, url) ->
name = url.match(/([^\/]+)\/*$/)?[1]
mime = contentType?.match(/[^;]*/)[0] or 'application/octet-stream'
match =
contentDisposition?.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)?[1] or
contentType?.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)?[1]
if match
name = match.replace /\\"/g, '"'
blob = new Blob([urlBlob], {type: mime})
blob.name = name
QR.handleFiles([blob])
handleUrl: ->
url = prompt("Insert an url:")
return if url is null
<% if (type === 'crx') { %>
xhr = new XMLHttpRequest();
xhr.open('GET', url, true)
xhr.responseType = 'blob'
xhr.onload = (e) ->
if @readyState is @DONE && xhr.status is 200
contentType = @getResponseHeader('Content-Type')
contentDisposition = @getResponseHeader('Content-Disposition')
QR.handleBlob @response, contentType, contentDisposition, url
CrossOrigin.request url, (blob) ->
if blob
QR.handleFiles([blob])
else
QR.error "Can't load image."
xhr.onerror = (e) ->
QR.error "Can't load image."
xhr.send()
<% } %>
<% if (type === 'userscript') { %>
GM_xmlhttpRequest
method: "GET"
url: url
overrideMimeType: "text/plain; charset=x-user-defined"
onload: (xhr) ->
r = xhr.responseText
data = new Uint8Array(r.length)
i = 0
while i < r.length
data[i] = r.charCodeAt(i)
i++
contentType = xhr.responseHeaders.match(/Content-Type:\s*(.*)/i)?[1]
contentDisposition = xhr.responseHeaders.match(/Content-Disposition:\s*(.*)/i)?[1]
QR.handleBlob data, contentType, contentDisposition, url
onerror: (xhr) ->
QR.error "Can't load image."
<% } %>
handleFiles: (files) ->
if @ isnt QR # file input