mirror of
https://github.com/LalleSX/4chan-XZ.git
synced 2025-10-07 07:22:37 +02:00
234 lines
7.6 KiB
TypeScript
234 lines
7.6 KiB
TypeScript
import QR from "../Posting/QR";
|
|
import $ from "./$";
|
|
import { dict, platform } from "./helpers";
|
|
|
|
/*
|
|
* decaffeinate suggestions:
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
* DS205: Consider reworking code to avoid use of IIFEs
|
|
* DS206: Consider reworking classes to avoid initClass
|
|
* DS207: Consider shorter variations of null checks
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
|
*/
|
|
let eventPageRequest;
|
|
if (platform === 'crx') {
|
|
eventPageRequest = (function () {
|
|
const callbacks = [];
|
|
chrome.runtime.onMessage.addListener(function(response) {
|
|
callbacks[response.id](response.data);
|
|
return delete callbacks[response.id];});
|
|
return (params, cb) => chrome.runtime.sendMessage(params, id => callbacks[id] = cb);
|
|
})();
|
|
}
|
|
var CrossOrigin = {
|
|
binary(url, cb, headers = dict()) {
|
|
// XXX https://forums.lanik.us/viewtopic.php?f=64&t=24173&p=78310
|
|
url = url.replace(/^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/');
|
|
if (platform === 'crx') {
|
|
eventPageRequest({type: 'ajax', url, headers, responseType: 'arraybuffer'}, function({response, responseHeaderString}) {
|
|
if (response) { response = new Uint8Array(response); }
|
|
return cb(response, responseHeaderString);
|
|
});
|
|
} else {
|
|
const fallback = function() {
|
|
return $.ajax(url, {
|
|
headers,
|
|
responseType: 'arraybuffer',
|
|
onloadend() {
|
|
if (this.status && this.response) {
|
|
return cb(new Uint8Array(this.response), this.getAllResponseHeaders());
|
|
} else {
|
|
return cb(null);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
if ((typeof window.GM_xmlhttpRequest === 'undefined' || window.GM_xmlhttpRequest === null)) {
|
|
fallback();
|
|
return;
|
|
}
|
|
const gmOptions = {
|
|
method: "GET",
|
|
url,
|
|
headers,
|
|
responseType: 'arraybuffer',
|
|
overrideMimeType: 'text/plain; charset=x-user-defined',
|
|
onload(xhr) {
|
|
let data;
|
|
if (xhr.response instanceof ArrayBuffer) {
|
|
data = new Uint8Array(xhr.response);
|
|
} else {
|
|
const r = xhr.responseText;
|
|
data = new Uint8Array(r.length);
|
|
let i = 0;
|
|
while (i < r.length) {
|
|
data[i] = r.charCodeAt(i);
|
|
i++;
|
|
}
|
|
}
|
|
return cb(data, xhr.responseHeaders);
|
|
},
|
|
onerror() {
|
|
return cb(null);
|
|
},
|
|
onabort() {
|
|
return cb(null);
|
|
}
|
|
};
|
|
try {
|
|
return (GM?.xmlHttpRequest || GM_xmlhttpRequest)(gmOptions);
|
|
} catch (error) {
|
|
return fallback();
|
|
}
|
|
}
|
|
},
|
|
|
|
file(url, cb) {
|
|
return CrossOrigin.binary(url, function(data, headers) {
|
|
if (data == null) { return cb(null); }
|
|
let name = url.match(/([^\/?#]+)\/*(?:$|[?#])/)?.[1];
|
|
const contentType = headers.match(/Content-Type:\s*(.*)/i)?.[1];
|
|
const contentDisposition = headers.match(/Content-Disposition:\s*(.*)/i)?.[1];
|
|
let mime = contentType?.match(/[^;]*/)[0] || 'application/octet-stream';
|
|
const match =
|
|
contentDisposition?.match(/\bfilename\s*=\s*"((\\"|[^"])+)"/i)?.[1] ||
|
|
contentType?.match(/\bname\s*=\s*"((\\"|[^"])+)"/i)?.[1];
|
|
if (match) {
|
|
name = match.replace(/\\"/g, '"');
|
|
}
|
|
if (/^text\/plain;\s*charset=x-user-defined$/i.test(mime)) {
|
|
// In JS Blocker (Safari) content type comes back as 'text/plain; charset=x-user-defined'; guess from filename instead.
|
|
mime = $.getOwn(QR.typeFromExtension, name.match(/[^.]*$/)[0].toLowerCase()) || 'application/octet-stream';
|
|
}
|
|
const blob = new Blob([data], {type: mime});
|
|
blob.name = name;
|
|
return cb(blob);
|
|
});
|
|
},
|
|
|
|
Request: (function() {
|
|
const Request = class Request {
|
|
static initClass() {
|
|
this.prototype.status = 0;
|
|
this.prototype.statusText = '';
|
|
this.prototype.response = null;
|
|
this.prototype.responseHeaderString = null;
|
|
}
|
|
getResponseHeader(headerName) {
|
|
if ((this.responseHeaders == null) && (this.responseHeaderString != null)) {
|
|
this.responseHeaders = dict();
|
|
for (var header of this.responseHeaderString.split('\r\n')) {
|
|
var i;
|
|
if ((i = header.indexOf(':')) >= 0) {
|
|
var key = header.slice(0, i).trim().toLowerCase();
|
|
var val = header.slice(i+1).trim();
|
|
this.responseHeaders[key] = val;
|
|
}
|
|
}
|
|
}
|
|
return this.responseHeaders?.[headerName.toLowerCase()] ?? null;
|
|
}
|
|
abort() {}
|
|
onloadend() {}
|
|
};
|
|
Request.initClass();
|
|
return Request;
|
|
})(),
|
|
|
|
// Attempts to fetch `url` using cross-origin privileges, if available.
|
|
// Interface is a subset of that of $.ajax.
|
|
// Options:
|
|
// `onloadend` - called with the returned object as `this` on success or error/abort/timeout.
|
|
// `timeout` - time limit for request
|
|
// `responseType` - expected response type, 'json' by default; 'json' and 'text' supported
|
|
// `headers` - request headers
|
|
// Returned object properties:
|
|
// `status` - HTTP status (0 if connection not successful)
|
|
// `statusText` - HTTP status text
|
|
// `response` - decoded response body
|
|
// `abort` - function for aborting the request (silently fails on some platforms)
|
|
// `getResponseHeader` - function for reading response headers
|
|
ajax(url, options={}) {
|
|
let gmReq;
|
|
let {onloadend, timeout, responseType, headers} = options;
|
|
if (responseType == null) { responseType = 'json'; }
|
|
|
|
if ((window.GM?.xmlHttpRequest == null) && (typeof window.GM_xmlhttpRequest === 'undefined' || window.GM_xmlhttpRequest === null)) {
|
|
return $.ajax(url, options);
|
|
}
|
|
|
|
const req = new CrossOrigin.Request();
|
|
req.onloadend = onloadend;
|
|
|
|
if (platform === 'userscript') {
|
|
const gmOptions = {
|
|
method: 'GET',
|
|
url,
|
|
headers,
|
|
timeout,
|
|
onload(xhr) {
|
|
try {
|
|
const response = (() => { switch (responseType) {
|
|
case 'json':
|
|
if (xhr.responseText) { return JSON.parse(xhr.responseText); } else { return null; }
|
|
default:
|
|
return xhr.responseText;
|
|
} })();
|
|
$.extend(req, {
|
|
response,
|
|
status: xhr.status,
|
|
statusText: xhr.statusText,
|
|
responseHeaderString: xhr.responseHeaders
|
|
});
|
|
} catch (error) {}
|
|
return req.onloadend();
|
|
},
|
|
onerror() { return req.onloadend(); },
|
|
onabort() { return req.onloadend(); },
|
|
ontimeout() { return req.onloadend(); }
|
|
};
|
|
try {
|
|
gmReq = (GM?.xmlHttpRequest || GM_xmlhttpRequest)(gmOptions);
|
|
} catch (error) {
|
|
return $.ajax(url, options);
|
|
}
|
|
|
|
if (gmReq && (typeof gmReq.abort === 'function')) {
|
|
req.abort = function() {
|
|
try {
|
|
return gmReq.abort();
|
|
} catch (error1) {}
|
|
};
|
|
}
|
|
} else {
|
|
eventPageRequest({type: 'ajax', url, responseType, headers, timeout}, function(result) {
|
|
if (result.status) {
|
|
$.extend(req, result);
|
|
}
|
|
return req.onloadend();
|
|
});
|
|
}
|
|
|
|
return req;
|
|
},
|
|
|
|
cache(url, cb) {
|
|
return $.cache(url, cb,
|
|
{ajax: CrossOrigin.ajax});
|
|
},
|
|
|
|
permission(cb, cbFail, origins) {
|
|
if (platform === 'crx') {
|
|
return eventPageRequest({type: 'permission', origins}, function(result) {
|
|
if (result) {
|
|
return cb();
|
|
} else {
|
|
return cbFail();
|
|
}
|
|
});
|
|
}
|
|
return cb();
|
|
},
|
|
};
|
|
export default CrossOrigin;
|