mirror of
https://github.com/LalleSX/4chan-XZ.git
synced 2025-10-07 07:22:37 +02:00
290 lines
8.3 KiB
JavaScript
290 lines
8.3 KiB
JavaScript
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) {
|
|
if (platform === 'userscript') {
|
|
return CrossOrigin.file(url, cb)
|
|
}
|
|
return eventPageRequest({ type: 'cache', url }, function (result) {
|
|
if (result) {
|
|
return cb(result)
|
|
} else {
|
|
return cb(null)
|
|
}
|
|
})
|
|
},
|
|
|
|
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
|