이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greatest.deepsurf.us/scripts/432003/1381253/UserscriptAPIWeb.js
을(를) 사용하여 포함하는 라이브러리입니다.
줄 바꿈
/** * UserscriptAPIWeb * * 依赖于 `UserscriptAPI`。 * * 需要通过 `@grant` 引入 `GM_xmlhttpRequest` 或 `GM_download`。 * @version 1.4.0.20240522 * @author Laster2800 * @see {@link https://gitee.com/liangjiancang/userscript/tree/master/lib/UserscriptAPI UserscriptAPI} */ class UserscriptAPIWeb { /** * @param {UserscriptAPI} api `UserscriptAPI` * @param {(xhr: GM_XHR) => void} [api.options.web.preproc] 请求预处理 */ constructor ( api ) { this . api = api api . options . web ??= { preproc : null } } /** * @typedef {XMLHttpRequest} GM_XHR GM 定义的类 `XMLHttpRequest` 对象 */ /** * 发起网络请求,获取 `GM_XHR` * @param {Object} details 定义及细节类似于 `GM_xmlhttpRequest` `details` * @param {'GET' | 'HEAD' | 'POST'} [details.method='GET'] `METHOD` * @param {string} [details.url] `URL` * @param {number} [details.timeout] 超时时间 * @param {(xhr: GM_XHR) => void} [details.ontimeout] 超时回调 * @param {(xhr: GM_XHR) => void} [details.onerror] 错误回调 * @param {(xhr: GM_XHR) => void} [details.onload] 加载回调 * @param {string | URLSearchParams | FormData} [details.data] `DATA` * @param {Object} [options] 选项 * @param {(xhr: GM_XHR) => boolean} [options.check] 检查 `GM_XHR` 是否符合条件 * @param {boolean} [options.throwOnFailed = true] 失败时是否抛出异常,否则打印错误信息 * @returns {Promise<GM_XHR>} `GM_XHR` * @throws 等待超时、达成终止条件、等待错误时抛出 * @see {@link https://www.tampermonkey.net/documentation.php#GM_xmlhttpRequest GM_xmlhttpRequest} */ requestXHR ( details , options ) { if ( details ) { const { api } = this const { check , throwOnFailed = true } = options ?? {} return new Promise (( resolve , reject ) => { if ( details . data && details . data instanceof URLSearchParams ) { details . data = details . data . toString () details . headers = { 'content-type' : 'application/x-www-form-urlencoded' , ... details . headers , } if ( GM_info . scriptHandler === 'Violentmonkey' && ! details . headers . origin ) { details . headers . origin = '' } } details . ontimeout ??= xhr => fail ( 'request: TIMEOUT' , details , xhr ) details . onerror ??= xhr => fail ( 'request: ERROR' , details , xhr ) details . onload ??= xhr => { if ( check && ! check ( xhr )) { fail ( 'request: CHECK-FAIL' , details , check , xhr ) if ( throwOnFailed ) return } resolve ( xhr ) } GM_xmlhttpRequest ( details ) function fail ( msg , ... cause ) { if ( throwOnFailed ) { reject ( new Error ( msg , cause . length > 0 ? { cause } : undefined )) } else { api . logger . error ( msg , ... cause ) } } }) } } /** * 发起网络请求,获取解析结果 * @param {Object} details 定义及细节类似于 `GM_xmlhttpRequest` `details` * @param {'GET' | 'HEAD' | 'POST'} [details.method='GET'] `METHOD` * @param {string} [details.url] `URL` * @param {number} [details.timeout] 超时时间 * @param {(xhr: GM_XHR) => void} [details.ontimeout] 超时回调 * @param {(xhr: GM_XHR) => void} [details.onerror] 错误回调 * @param {(xhr: GM_XHR) => void} [details.onload] 加载回调 * @param {string | URLSearchParams | FormData} [details.data] `DATA` * @param {Object} [options] 选项 * @param {'json' | 'check' | 'silentCheck'} [options.parser='json'] ```text * json: 返回 JSON.parse(resp) * check: 返回 check(resp, xhr),检查失败时打印信息 * silentCheck: 返回 check(resp, xhr),检查失败时不打印信息 * ``` * @param {(resp: Object, xhr: GM_XHR) => boolean} [options.check] 检查 `GM_XHR` 是否符合条件 * @param {boolean} [options.throwOnFailed=true] 失败时是否抛出异常,否则打印错误信息 * @returns {Promise<Object>} 解析结果 * @see {@link https://www.tampermonkey.net/documentation.php#GM_xmlhttpRequest GM_xmlhttpRequest} */ async request ( details , options ) { const { api } = this const { parser = 'json' , check , throwOnFailed = true } = options ?? {} try { try { await api . options . web . preproc ?.( details ) } catch { fail ( 'request: PREPROC' , api . options . web . preproc , details ) } const xhr = await this . requestXHR ( details ) let resp = null try { resp = JSON . parse ( xhr . response ) } catch { fail ( 'request: PARSE' , details , xhr ) return null } const checkResult = ! check || check ( resp , xhr ) if ( parser === 'silentCheck' ) { return checkResult } else if ( parser === 'check' ) { if (! checkResult ) { api . logger . error ( 'request: CHECK-FAIL' , details , check , resp , xhr ) } return checkResult } else { if (! checkResult ) { fail ( 'request: CHECK-FAIL' , details , check , resp , xhr ) } return resp } } catch ( e ) { if ( throwOnFailed ) { throw e } else { api . logger . error ( e ) } } function fail ( msg , ... cause ) { if ( throwOnFailed ) { throw new Error ( msg , cause . length > 0 ? { cause } : undefined ) } else { api . logger . error ( msg , ... cause ) } } } /** * 下载资源 * @param {Object} details 定义及细节同 `GM_download` `details` * @returns {() => void} 用于终止下载的方法 * @see {@link https://www.tampermonkey.net/documentation.php#GM_download GM_download} */ download ( details ) { if ( details ) { const { api } = this try { let { name } = details if ( name . includes ( '.' )) { // name「.」后内容会被误认为后缀导致一系列问题,从 URL 找出真正的后缀名以修复之 let parts = details . url . split ( '/' ) const last = parts . at (- 1 ). split ( '?' )[ 0 ] if ( last . includes ( '.' )) { parts = last . split ( '.' ) name = ` $ { name }. $ { parts . at (- 1 )}` } else { name = name . replaceAll ( '.' , '_' ) // 实在找不到后缀时才用这种消极的方案 } details . name = name } details . onerror ??= ( error , details ) => api . logger . error ( 'download: ERROR' , error , details ) details . ontimeout ??= () => api . logger . error ( 'download: TIMEOUT' ) GM_download ( details ) } catch ( e ) { api . logger . error ( 'download: ERROR' , e ) } } return () => {} } } /* global UserscriptAPI */ // eslint-disable-next-line no-lone-blocks { UserscriptAPI . registerModule ( 'web' , UserscriptAPIWeb ) }