@whitesev/utils 2.9.4 → 2.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -3,7 +3,7 @@
3
3
  class ColorConversion {
4
4
  /**
5
5
  * 判断是否是16进制颜色
6
- * @param str
6
+ * @param str 十六进制颜色,如`#000000`
7
7
  */
8
8
  isHex(str) {
9
9
  if (typeof str !== "string") {
@@ -18,8 +18,8 @@ class ColorConversion {
18
18
  * 16进制颜色转rgba
19
19
  *
20
20
  * 例如:`#ff0000` 转为 `rgba(123,123,123, 0.4)`
21
- * @param hex
22
- * @param opacity
21
+ * @param hex 十六进制颜色,如`#000000`
22
+ * @param opacity 透明度,0~1
23
23
  */
24
24
  hexToRgba(hex, opacity) {
25
25
  if (!this.isHex(hex)) {
@@ -31,16 +31,16 @@ class ColorConversion {
31
31
  }
32
32
  /**
33
33
  * hex转rgb
34
- * @param str
34
+ * @param hex 十六进制颜色,如`#000000`
35
35
  */
36
- hexToRgb(str) {
37
- if (!this.isHex(str)) {
38
- throw new TypeError(`输入错误的hex:${str}`);
36
+ hexToRgb(hex) {
37
+ if (!this.isHex(hex)) {
38
+ throw new TypeError(`输入错误的hex:${hex}`);
39
39
  }
40
40
  /* replace替换查找的到的字符串 */
41
- str = str.replace("#", "");
41
+ hex = hex.replace("#", "");
42
42
  /* match得到查询数组 */
43
- const hxs = str.match(/../g);
43
+ const hxs = hex.match(/../g);
44
44
  for (let index = 0; index < 3; index++) {
45
45
  const value = parseInt(hxs[index], 16);
46
46
  Reflect.set(hxs, index, value);
@@ -49,9 +49,10 @@ class ColorConversion {
49
49
  }
50
50
  /**
51
51
  * rgb转hex
52
- * @param redValue
53
- * @param greenValue
54
- * @param blueValue
52
+ * @param redValue 红色值
53
+ * @param greenValue 绿色值
54
+ * @param blueValue 蓝色值
55
+ * @returns hex
55
56
  */
56
57
  rgbToHex(redValue, greenValue, blueValue) {
57
58
  /* 验证输入的rgb值是否合法 */
@@ -68,30 +69,42 @@ class ColorConversion {
68
69
  }
69
70
  /**
70
71
  * 获取颜色变暗或亮
71
- * @param color 颜色
72
- * @param level 0~1.0
72
+ * @param color hex颜色,如`#000000`
73
+ * @param level 0~1.0 系数越大,颜色越变暗
73
74
  */
74
75
  getDarkColor(color, level) {
75
76
  if (!this.isHex(color)) {
76
77
  throw new TypeError(`输入错误的hex:${color}`);
77
78
  }
79
+ if (typeof level !== "number") {
80
+ level = Number(level);
81
+ }
82
+ if (isNaN(level)) {
83
+ throw new TypeError(`输入错误的level:${level}`);
84
+ }
78
85
  const rgbc = this.hexToRgb(color);
79
86
  for (let index = 0; index < 3; index++) {
80
87
  const rgbcItemValue = rgbc[index];
81
- const value = Math.floor(Number(rgbcItemValue) * (1 - Number(level)));
88
+ const value = Math.floor(Number(rgbcItemValue) * (1 - level));
82
89
  Reflect.set(rgbc, index, value);
83
90
  }
84
91
  return this.rgbToHex(rgbc[0], rgbc[1], rgbc[2]);
85
92
  }
86
93
  /**
87
94
  * 获取颜色变亮
88
- * @param color 颜色
89
- * @param level 0~1.0
95
+ * @param color hex颜色,如`#000000`
96
+ * @param level 0~1.0 系数越大,颜色越变亮
90
97
  */
91
98
  getLightColor(color, level) {
92
99
  if (!this.isHex(color)) {
93
100
  throw new TypeError(`输入错误的hex:${color}`);
94
101
  }
102
+ if (typeof level !== "number") {
103
+ level = Number(level);
104
+ }
105
+ if (isNaN(level)) {
106
+ throw new TypeError(`输入错误的level:${level}`);
107
+ }
95
108
  const rgbc = this.hexToRgb(color);
96
109
  for (let index = 0; index < 3; index++) {
97
110
  const rgbcItemValue = Number(rgbc[index]);
@@ -575,7 +588,7 @@ class UtilsGMCookie {
575
588
  }
576
589
  }
577
590
  /**
578
- * 获取多组Cookie
591
+ * 获取多组Cookie
579
592
  * @param option 配置
580
593
  **/
581
594
  getList(option) {
@@ -688,7 +701,8 @@ class UtilsGMCookie {
688
701
  }
689
702
  }
690
703
  /**
691
- * 解析cookie字符串
704
+ * 解析cookie字符串,按`;`分割
705
+ *
692
706
  * 例如:document.cookie
693
707
  * @param cookieStr
694
708
  */
@@ -713,1001 +727,981 @@ class UtilsGMCookie {
713
727
  }
714
728
  }
715
729
 
716
- /* eslint-disable */
717
- // ==UserScript==
718
- // @name ajaxHooker
719
- // @author cxxjackie
720
- // @version 1.4.8
721
- // @supportURL https://bbs.tampermonkey.net.cn/thread-3284-1-1.html
722
- // @license GNU LGPL-3.0
723
- // ==/UserScript==
724
-
725
- const ajaxHooker = function () {
726
- const version = "1.4.8";
727
- const hookInst = {
728
- hookFns: [],
729
- filters: [],
730
- };
731
- const win = window.unsafeWindow || document.defaultView || window;
732
- let winAh = win.__ajaxHooker;
733
- const resProto = win.Response.prototype;
734
- const xhrResponses = ["response", "responseText", "responseXML"];
735
- const fetchResponses = ["arrayBuffer", "blob", "formData", "json", "text"];
736
- const xhrExtraProps = ["responseType", "timeout", "withCredentials"];
737
- const fetchExtraProps = [
738
- "cache",
739
- "credentials",
740
- "integrity",
741
- "keepalive",
742
- "mode",
743
- "priority",
744
- "redirect",
745
- "referrer",
746
- "referrerPolicy",
747
- "signal",
748
- ];
749
- const xhrAsyncEvents = ["readystatechange", "load", "loadend"];
750
- const getType = {}.toString.call.bind({}.toString);
751
- const getDescriptor = Object.getOwnPropertyDescriptor.bind(Object);
752
- const emptyFn = () => {};
753
- const errorFn = (e) => console.error(e);
754
- function isThenable(obj) {
755
- return obj && ["object", "function"].includes(typeof obj) && typeof obj.then === "function";
756
- }
757
- function catchError(fn, ...args) {
758
- try {
759
- const result = fn(...args);
760
- if (isThenable(result)) return result.then(null, errorFn);
761
- return result;
762
- } catch (err) {
763
- console.error(err);
764
- }
765
- }
766
- function defineProp(obj, prop, getter, setter) {
767
- Object.defineProperty(obj, prop, {
768
- configurable: true,
769
- enumerable: true,
770
- get: getter,
771
- set: setter,
772
- });
773
- }
774
- function readonly(obj, prop, value = obj[prop]) {
775
- defineProp(obj, prop, () => value, emptyFn);
776
- }
777
- function writable(obj, prop, value = obj[prop]) {
778
- Object.defineProperty(obj, prop, {
779
- configurable: true,
780
- enumerable: true,
781
- writable: true,
782
- value: value,
783
- });
784
- }
785
- function parseHeaders(obj) {
786
- const headers = {};
787
- switch (getType(obj)) {
788
- case "[object String]":
789
- for (const line of obj.trim().split(/[\r\n]+/)) {
790
- const [header, value] = line.split(/(?<=^[^:]+)\s*:\s*/);
791
- if (!value) continue;
792
- const lheader = header.toLowerCase();
793
- headers[lheader] = lheader in headers ? `${headers[lheader]}, ${value}` : value;
794
- }
795
- break;
796
- case "[object Headers]":
797
- for (const [key, val] of obj) {
798
- headers[key] = val;
799
- }
800
- break;
801
- case "[object Object]":
802
- return { ...obj };
803
- }
804
- return headers;
805
- }
806
- function stopImmediatePropagation() {
807
- this.ajaxHooker_isStopped = true;
808
- }
809
- class SyncThenable {
810
- then(fn) {
811
- fn && fn();
812
- return new SyncThenable();
813
- }
814
- }
815
- class AHRequest {
816
- constructor(request) {
817
- this.request = request;
818
- this.requestClone = { ...this.request };
819
- }
820
- _recoverRequestKey(key) {
821
- if (key in this.requestClone) this.request[key] = this.requestClone[key];
822
- else delete this.request[key];
823
- }
824
- shouldFilter(filters) {
825
- const { type, url, method, async } = this.request;
826
- return (
827
- filters.length &&
828
- !filters.find((obj) => {
829
- switch (true) {
830
- case obj.type && obj.type !== type:
831
- case getType(obj.url) === "[object String]" && !url.includes(obj.url):
832
- case getType(obj.url) === "[object RegExp]" && !obj.url.test(url):
833
- case obj.method && obj.method.toUpperCase() !== method.toUpperCase():
834
- case "async" in obj && obj.async !== async:
835
- return false;
836
- }
837
- return true;
838
- })
839
- );
840
- }
841
- waitForRequestKeys() {
842
- if (!this.request.async) {
843
- win.__ajaxHooker.hookInsts.forEach(({ hookFns, filters }) => {
844
- if (this.shouldFilter(filters)) return;
845
- hookFns.forEach((fn) => {
846
- if (getType(fn) === "[object Function]") catchError(fn, this.request);
847
- });
848
- for (const key in this.request) {
849
- if (isThenable(this.request[key])) this._recoverRequestKey(key);
850
- }
851
- });
852
- return new SyncThenable();
853
- }
854
- const promises = [];
855
- const ignoreKeys = new Set(["type", "async", "response"]);
856
- win.__ajaxHooker.hookInsts.forEach(({ hookFns, filters }) => {
857
- if (this.shouldFilter(filters)) return;
858
- promises.push(
859
- Promise.all(hookFns.map((fn) => catchError(fn, this.request))).then(() => {
860
- const requestKeys = [];
861
- for (const key in this.request) !ignoreKeys.has(key) && requestKeys.push(key);
862
- return Promise.all(
863
- requestKeys.map((key) =>
864
- Promise.resolve(this.request[key]).then(
865
- (val) => (this.request[key] = val),
866
- () => this._recoverRequestKey(key)
867
- )
868
- )
869
- );
870
- })
871
- );
872
- });
873
- return Promise.all(promises);
874
- }
875
- waitForResponseKeys(response) {
876
- const responseKeys = this.request.type === "xhr" ? xhrResponses : fetchResponses;
877
- if (!this.request.async) {
878
- if (getType(this.request.response) === "[object Function]") {
879
- catchError(this.request.response, response);
880
- responseKeys.forEach((key) => {
881
- if ("get" in getDescriptor(response, key) || isThenable(response[key])) {
882
- delete response[key];
883
- }
884
- });
885
- }
886
- return new SyncThenable();
887
- }
888
- return Promise.resolve(catchError(this.request.response, response)).then(() =>
889
- Promise.all(
890
- responseKeys.map((key) => {
891
- const descriptor = getDescriptor(response, key);
892
- if (descriptor && "value" in descriptor) {
893
- return Promise.resolve(descriptor.value).then(
894
- (val) => (response[key] = val),
895
- () => delete response[key]
896
- );
897
- } else {
898
- delete response[key];
899
- }
900
- })
901
- )
902
- );
903
- }
904
- }
905
- const proxyHandler = {
906
- get(target, prop) {
907
- const descriptor = getDescriptor(target, prop);
908
- if (descriptor && !descriptor.configurable && !descriptor.writable && !descriptor.get)
909
- return target[prop];
910
- const ah = target.__ajaxHooker;
911
- if (ah && ah.proxyProps) {
912
- if (prop in ah.proxyProps) {
913
- const pDescriptor = ah.proxyProps[prop];
914
- if ("get" in pDescriptor) return pDescriptor.get();
915
- if (typeof pDescriptor.value === "function") return pDescriptor.value.bind(ah);
916
- return pDescriptor.value;
917
- }
918
- if (typeof target[prop] === "function") return target[prop].bind(target);
919
- }
920
- return target[prop];
921
- },
922
- set(target, prop, value) {
923
- const descriptor = getDescriptor(target, prop);
924
- if (descriptor && !descriptor.configurable && !descriptor.writable && !descriptor.set) return true;
925
- const ah = target.__ajaxHooker;
926
- if (ah && ah.proxyProps && prop in ah.proxyProps) {
927
- const pDescriptor = ah.proxyProps[prop];
928
- pDescriptor.set ? pDescriptor.set(value) : (pDescriptor.value = value);
929
- } else {
930
- target[prop] = value;
931
- }
932
- return true;
933
- },
934
- };
935
- class XhrHooker {
936
- constructor(xhr) {
937
- const ah = this;
938
- Object.assign(ah, {
939
- originalXhr: xhr,
940
- proxyXhr: new Proxy(xhr, proxyHandler),
941
- resThenable: new SyncThenable(),
942
- proxyProps: {},
943
- proxyEvents: {},
944
- });
945
- xhr.addEventListener("readystatechange", (e) => {
946
- if (ah.proxyXhr.readyState === 4 && ah.request && typeof ah.request.response === "function") {
947
- const response = {
948
- finalUrl: ah.proxyXhr.responseURL,
949
- status: ah.proxyXhr.status,
950
- responseHeaders: parseHeaders(ah.proxyXhr.getAllResponseHeaders()),
951
- };
952
- const tempValues = {};
953
- for (const key of xhrResponses) {
954
- try {
955
- tempValues[key] = ah.originalXhr[key];
956
- } catch (err) {}
957
- defineProp(
958
- response,
959
- key,
960
- () => {
961
- return (response[key] = tempValues[key]);
962
- },
963
- (val) => {
964
- delete response[key];
965
- response[key] = val;
966
- }
967
- );
968
- }
969
- ah.resThenable = new AHRequest(ah.request).waitForResponseKeys(response).then(() => {
970
- for (const key of xhrResponses) {
971
- ah.proxyProps[key] = {
972
- get: () => {
973
- if (!(key in response)) response[key] = tempValues[key];
974
- return response[key];
975
- },
976
- };
977
- }
978
- });
979
- }
980
- ah.dispatchEvent(e);
981
- });
982
- xhr.addEventListener("load", (e) => ah.dispatchEvent(e));
983
- xhr.addEventListener("loadend", (e) => ah.dispatchEvent(e));
984
- for (const evt of xhrAsyncEvents) {
985
- const onEvt = "on" + evt;
986
- ah.proxyProps[onEvt] = {
987
- get: () => ah.proxyEvents[onEvt] || null,
988
- set: (val) => ah.addEvent(onEvt, val),
989
- };
990
- }
991
- for (const method of ["setRequestHeader", "addEventListener", "removeEventListener", "open", "send"]) {
992
- ah.proxyProps[method] = { value: ah[method] };
993
- }
994
- }
995
- toJSON() {} // Converting circular structure to JSON
996
- addEvent(type, event) {
997
- if (type.startsWith("on")) {
998
- this.proxyEvents[type] = typeof event === "function" ? event : null;
999
- } else {
1000
- if (typeof event === "object" && event !== null) event = event.handleEvent;
1001
- if (typeof event !== "function") return;
1002
- this.proxyEvents[type] = this.proxyEvents[type] || new Set();
1003
- this.proxyEvents[type].add(event);
1004
- }
1005
- }
1006
- removeEvent(type, event) {
1007
- if (type.startsWith("on")) {
1008
- this.proxyEvents[type] = null;
1009
- } else {
1010
- if (typeof event === "object" && event !== null) event = event.handleEvent;
1011
- this.proxyEvents[type] && this.proxyEvents[type].delete(event);
1012
- }
1013
- }
1014
- dispatchEvent(e) {
1015
- e.stopImmediatePropagation = stopImmediatePropagation;
1016
- defineProp(e, "target", () => this.proxyXhr);
1017
- defineProp(e, "currentTarget", () => this.proxyXhr);
1018
- defineProp(e, "srcElement", () => this.proxyXhr);
1019
- this.proxyEvents[e.type] &&
1020
- this.proxyEvents[e.type].forEach((fn) => {
1021
- this.resThenable.then(() => !e.ajaxHooker_isStopped && fn.call(this.proxyXhr, e));
1022
- });
1023
- if (e.ajaxHooker_isStopped) return;
1024
- const onEvent = this.proxyEvents["on" + e.type];
1025
- onEvent && this.resThenable.then(onEvent.bind(this.proxyXhr, e));
1026
- }
1027
- setRequestHeader(header, value) {
1028
- this.originalXhr.setRequestHeader(header, value);
1029
- if (!this.request) return;
1030
- const headers = this.request.headers;
1031
- headers[header] = header in headers ? `${headers[header]}, ${value}` : value;
1032
- }
1033
- addEventListener(...args) {
1034
- if (xhrAsyncEvents.includes(args[0])) {
1035
- this.addEvent(args[0], args[1]);
1036
- } else {
1037
- this.originalXhr.addEventListener(...args);
1038
- }
1039
- }
1040
- removeEventListener(...args) {
1041
- if (xhrAsyncEvents.includes(args[0])) {
1042
- this.removeEvent(args[0], args[1]);
1043
- } else {
1044
- this.originalXhr.removeEventListener(...args);
1045
- }
1046
- }
1047
- open(method, url, async = true, ...args) {
1048
- this.request = {
1049
- type: "xhr",
1050
- url: url.toString(),
1051
- method: method.toUpperCase(),
1052
- abort: false,
1053
- headers: {},
1054
- data: null,
1055
- response: null,
1056
- async: !!async,
1057
- };
1058
- this.openArgs = args;
1059
- this.resThenable = new SyncThenable();
1060
- ["responseURL", "readyState", "status", "statusText", ...xhrResponses].forEach((key) => {
1061
- delete this.proxyProps[key];
1062
- });
1063
- return this.originalXhr.open(method, url, async, ...args);
1064
- }
1065
- send(data) {
1066
- const ah = this;
1067
- const xhr = ah.originalXhr;
1068
- const request = ah.request;
1069
- if (!request) return xhr.send(data);
1070
- request.data = data;
1071
- new AHRequest(request).waitForRequestKeys().then(() => {
1072
- if (request.abort) {
1073
- if (typeof request.response === "function") {
1074
- Object.assign(ah.proxyProps, {
1075
- responseURL: { value: request.url },
1076
- readyState: { value: 4 },
1077
- status: { value: 200 },
1078
- statusText: { value: "OK" },
1079
- });
1080
- xhrAsyncEvents.forEach((evt) => xhr.dispatchEvent(new Event(evt)));
1081
- }
1082
- } else {
1083
- xhr.open(request.method, request.url, request.async, ...ah.openArgs);
1084
- for (const header in request.headers) {
1085
- xhr.setRequestHeader(header, request.headers[header]);
1086
- }
1087
- for (const prop of xhrExtraProps) {
1088
- if (prop in request) xhr[prop] = request[prop];
1089
- }
1090
- xhr.send(request.data);
1091
- }
1092
- });
1093
- }
1094
- }
1095
- function fakeXHR() {
1096
- const xhr = new winAh.realXHR();
1097
- if ("__ajaxHooker" in xhr) console.warn("检测到不同版本的ajaxHooker,可能发生冲突!");
1098
- xhr.__ajaxHooker = new XhrHooker(xhr);
1099
- return xhr.__ajaxHooker.proxyXhr;
1100
- }
1101
- fakeXHR.prototype = win.XMLHttpRequest.prototype;
1102
- Object.keys(win.XMLHttpRequest).forEach((key) => (fakeXHR[key] = win.XMLHttpRequest[key]));
1103
- function fakeFetch(url, options = {}) {
1104
- if (!url) return winAh.realFetch.call(win, url, options);
1105
- return new Promise(async (resolve, reject) => {
1106
- const init = {};
1107
- if (getType(url) === "[object Request]") {
1108
- init.method = url.method;
1109
- init.headers = url.headers;
1110
- if (url.body) init.body = await url.arrayBuffer();
1111
- for (const prop of fetchExtraProps) init[prop] = url[prop];
1112
- url = url.url;
1113
- }
1114
- url = url.toString();
1115
- Object.assign(init, options);
1116
- init.method = init.method || "GET";
1117
- init.headers = init.headers || {};
1118
- const request = {
1119
- type: "fetch",
1120
- url: url,
1121
- method: init.method.toUpperCase(),
1122
- abort: false,
1123
- headers: parseHeaders(init.headers),
1124
- data: init.body,
1125
- response: null,
1126
- async: true,
1127
- };
1128
- const req = new AHRequest(request);
1129
- await req.waitForRequestKeys();
1130
- if (request.abort) {
1131
- if (typeof request.response === "function") {
1132
- const response = {
1133
- finalUrl: request.url,
1134
- status: 200,
1135
- responseHeaders: {},
1136
- };
1137
- await req.waitForResponseKeys(response);
1138
- const key = fetchResponses.find((k) => k in response);
1139
- let val = response[key];
1140
- if (key === "json" && typeof val === "object") {
1141
- val = catchError(JSON.stringify.bind(JSON), val);
1142
- }
1143
- const res = new Response(val, {
1144
- status: 200,
1145
- statusText: "OK",
1146
- });
1147
- defineProp(res, "type", () => "basic");
1148
- defineProp(res, "url", () => request.url);
1149
- resolve(res);
1150
- } else {
1151
- reject(new DOMException("aborted", "AbortError"));
1152
- }
1153
- return;
1154
- }
1155
- init.method = request.method;
1156
- init.headers = request.headers;
1157
- init.body = request.data;
1158
- for (const prop of fetchExtraProps) {
1159
- if (prop in request) init[prop] = request[prop];
1160
- }
1161
- winAh.realFetch.call(win, request.url, init).then((res) => {
1162
- if (typeof request.response === "function") {
1163
- const response = {
1164
- finalUrl: res.url,
1165
- status: res.status,
1166
- responseHeaders: parseHeaders(res.headers),
1167
- };
1168
- if (res.ok) {
1169
- fetchResponses.forEach(
1170
- (key) =>
1171
- (res[key] = function () {
1172
- if (key in response) return Promise.resolve(response[key]);
1173
- return resProto[key].call(this).then((val) => {
1174
- response[key] = val;
1175
- return req
1176
- .waitForResponseKeys(response)
1177
- .then(() => (key in response ? response[key] : val));
1178
- });
1179
- })
1180
- );
1181
- } else {
1182
- catchError(request.response, response);
1183
- }
1184
- }
1185
- resolve(res);
1186
- }, reject);
1187
- });
1188
- }
1189
- function fakeFetchClone() {
1190
- const descriptors = Object.getOwnPropertyDescriptors(this);
1191
- const res = winAh.realFetchClone.call(this);
1192
- Object.defineProperties(res, descriptors);
1193
- return res;
1194
- }
1195
- winAh = win.__ajaxHooker = winAh || {
1196
- version,
1197
- fakeXHR,
1198
- fakeFetch,
1199
- fakeFetchClone,
1200
- realXHR: win.XMLHttpRequest,
1201
- realFetch: win.fetch,
1202
- realFetchClone: resProto.clone,
1203
- hookInsts: new Set(),
1204
- };
1205
- if (winAh.version !== version) console.warn("检测到不同版本的ajaxHooker,可能发生冲突!");
1206
- win.XMLHttpRequest = winAh.fakeXHR;
1207
- win.fetch = winAh.fakeFetch;
1208
- resProto.clone = winAh.fakeFetchClone;
1209
- winAh.hookInsts.add(hookInst);
1210
- // 针对头条、抖音 secsdk.umd.js 的兼容性处理
1211
- class AHFunction extends Function {
1212
- call(thisArg, ...args) {
1213
- if (thisArg && thisArg.__ajaxHooker && thisArg.__ajaxHooker.proxyXhr === thisArg) {
1214
- thisArg = thisArg.__ajaxHooker.originalXhr;
1215
- }
1216
- return Reflect.apply(this, thisArg, args);
1217
- }
1218
- apply(thisArg, args) {
1219
- if (thisArg && thisArg.__ajaxHooker && thisArg.__ajaxHooker.proxyXhr === thisArg) {
1220
- thisArg = thisArg.__ajaxHooker.originalXhr;
1221
- }
1222
- return Reflect.apply(this, thisArg, args || []);
1223
- }
1224
- }
1225
- function hookSecsdk(csrf) {
1226
- Object.setPrototypeOf(csrf.nativeXMLHttpRequestSetRequestHeader, AHFunction.prototype);
1227
- Object.setPrototypeOf(csrf.nativeXMLHttpRequestOpen, AHFunction.prototype);
1228
- Object.setPrototypeOf(csrf.nativeXMLHttpRequestSend, AHFunction.prototype);
1229
- }
1230
- if (win.secsdk) {
1231
- if (win.secsdk.csrf && win.secsdk.csrf.nativeXMLHttpRequestOpen) hookSecsdk(win.secsdk.csrf);
1232
- } else {
1233
- defineProp(win, "secsdk", emptyFn, (secsdk) => {
1234
- delete win.secsdk;
1235
- win.secsdk = secsdk;
1236
- defineProp(secsdk, "csrf", emptyFn, (csrf) => {
1237
- delete secsdk.csrf;
1238
- secsdk.csrf = csrf;
1239
- if (csrf.nativeXMLHttpRequestOpen) hookSecsdk(csrf);
1240
- });
1241
- });
1242
- }
1243
- return {
1244
- hook: (fn) => hookInst.hookFns.push(fn),
1245
- filter: (arr) => {
1246
- if (Array.isArray(arr)) hookInst.filters = arr;
1247
- },
1248
- protect: () => {
1249
- readonly(win, "XMLHttpRequest", winAh.fakeXHR);
1250
- readonly(win, "fetch", winAh.fakeFetch);
1251
- readonly(resProto, "clone", winAh.fakeFetchClone);
1252
- },
1253
- unhook: () => {
1254
- winAh.hookInsts.delete(hookInst);
1255
- if (!winAh.hookInsts.size) {
1256
- writable(win, "XMLHttpRequest", winAh.realXHR);
1257
- writable(win, "fetch", winAh.realFetch);
1258
- writable(resProto, "clone", winAh.realFetchClone);
1259
- delete win.__ajaxHooker;
1260
- }
1261
- },
1262
- };
730
+ /* eslint-disable */
731
+ // ==UserScript==
732
+ // @name ajaxHooker
733
+ // @author cxxjackie
734
+ // @version 1.4.8
735
+ // @supportURL https://bbs.tampermonkey.net.cn/thread-3284-1-1.html
736
+ // @license GNU LGPL-3.0
737
+ // ==/UserScript==
738
+ const ajaxHooker = function () {
739
+ const version = "1.4.8";
740
+ const hookInst = {
741
+ hookFns: [],
742
+ filters: [],
743
+ };
744
+ const win = window.unsafeWindow || document.defaultView || window;
745
+ let winAh = win.__ajaxHooker;
746
+ const resProto = win.Response.prototype;
747
+ const xhrResponses = ["response", "responseText", "responseXML"];
748
+ const fetchResponses = ["arrayBuffer", "blob", "formData", "json", "text"];
749
+ const xhrExtraProps = ["responseType", "timeout", "withCredentials"];
750
+ const fetchExtraProps = [
751
+ "cache",
752
+ "credentials",
753
+ "integrity",
754
+ "keepalive",
755
+ "mode",
756
+ "priority",
757
+ "redirect",
758
+ "referrer",
759
+ "referrerPolicy",
760
+ "signal",
761
+ ];
762
+ const xhrAsyncEvents = ["readystatechange", "load", "loadend"];
763
+ const getType = {}.toString.call.bind({}.toString);
764
+ const getDescriptor = Object.getOwnPropertyDescriptor.bind(Object);
765
+ const emptyFn = () => { };
766
+ const errorFn = (e) => console.error(e);
767
+ function isThenable(obj) {
768
+ return obj && ["object", "function"].includes(typeof obj) && typeof obj.then === "function";
769
+ }
770
+ function catchError(fn, ...args) {
771
+ try {
772
+ const result = fn(...args);
773
+ if (isThenable(result))
774
+ return result.then(null, errorFn);
775
+ return result;
776
+ }
777
+ catch (err) {
778
+ console.error(err);
779
+ }
780
+ }
781
+ function defineProp(obj, prop, getter, setter) {
782
+ Object.defineProperty(obj, prop, {
783
+ configurable: true,
784
+ enumerable: true,
785
+ get: getter,
786
+ set: setter,
787
+ });
788
+ }
789
+ function readonly(obj, prop, value = obj[prop]) {
790
+ defineProp(obj, prop, () => value, emptyFn);
791
+ }
792
+ function writable(obj, prop, value = obj[prop]) {
793
+ Object.defineProperty(obj, prop, {
794
+ configurable: true,
795
+ enumerable: true,
796
+ writable: true,
797
+ value: value,
798
+ });
799
+ }
800
+ function parseHeaders(obj) {
801
+ const headers = {};
802
+ switch (getType(obj)) {
803
+ case "[object String]":
804
+ for (const line of obj.trim().split(/[\r\n]+/)) {
805
+ const [header, value] = line.split(/(?<=^[^:]+)\s*:\s*/);
806
+ if (!value)
807
+ continue;
808
+ const lheader = header.toLowerCase();
809
+ headers[lheader] = lheader in headers ? `${headers[lheader]}, ${value}` : value;
810
+ }
811
+ break;
812
+ case "[object Headers]":
813
+ for (const [key, val] of obj) {
814
+ headers[key] = val;
815
+ }
816
+ break;
817
+ case "[object Object]":
818
+ return { ...obj };
819
+ }
820
+ return headers;
821
+ }
822
+ function stopImmediatePropagation() {
823
+ this.ajaxHooker_isStopped = true;
824
+ }
825
+ class SyncThenable {
826
+ then(fn) {
827
+ fn && fn();
828
+ return new SyncThenable();
829
+ }
830
+ }
831
+ class AHRequest {
832
+ constructor(request) {
833
+ this.request = request;
834
+ this.requestClone = { ...this.request };
835
+ }
836
+ _recoverRequestKey(key) {
837
+ if (key in this.requestClone)
838
+ this.request[key] = this.requestClone[key];
839
+ else
840
+ delete this.request[key];
841
+ }
842
+ shouldFilter(filters) {
843
+ const { type, url, method, async } = this.request;
844
+ return (filters.length &&
845
+ !filters.find((obj) => {
846
+ switch (true) {
847
+ case obj.type && obj.type !== type:
848
+ case getType(obj.url) === "[object String]" && !url.includes(obj.url):
849
+ case getType(obj.url) === "[object RegExp]" && !obj.url.test(url):
850
+ case obj.method && obj.method.toUpperCase() !== method.toUpperCase():
851
+ case "async" in obj && obj.async !== async:
852
+ return false;
853
+ }
854
+ return true;
855
+ }));
856
+ }
857
+ waitForRequestKeys() {
858
+ if (!this.request.async) {
859
+ win.__ajaxHooker.hookInsts.forEach(({ hookFns, filters }) => {
860
+ if (this.shouldFilter(filters))
861
+ return;
862
+ hookFns.forEach((fn) => {
863
+ if (getType(fn) === "[object Function]")
864
+ catchError(fn, this.request);
865
+ });
866
+ for (const key in this.request) {
867
+ if (isThenable(this.request[key]))
868
+ this._recoverRequestKey(key);
869
+ }
870
+ });
871
+ return new SyncThenable();
872
+ }
873
+ const promises = [];
874
+ const ignoreKeys = new Set(["type", "async", "response"]);
875
+ win.__ajaxHooker.hookInsts.forEach(({ hookFns, filters }) => {
876
+ if (this.shouldFilter(filters))
877
+ return;
878
+ promises.push(Promise.all(hookFns.map((fn) => catchError(fn, this.request))).then(() => {
879
+ const requestKeys = [];
880
+ for (const key in this.request)
881
+ !ignoreKeys.has(key) && requestKeys.push(key);
882
+ return Promise.all(requestKeys.map((key) => Promise.resolve(this.request[key]).then((val) => (this.request[key] = val), () => this._recoverRequestKey(key))));
883
+ }));
884
+ });
885
+ return Promise.all(promises);
886
+ }
887
+ waitForResponseKeys(response) {
888
+ const responseKeys = this.request.type === "xhr" ? xhrResponses : fetchResponses;
889
+ if (!this.request.async) {
890
+ if (getType(this.request.response) === "[object Function]") {
891
+ catchError(this.request.response, response);
892
+ responseKeys.forEach((key) => {
893
+ if ("get" in getDescriptor(response, key) || isThenable(response[key])) {
894
+ delete response[key];
895
+ }
896
+ });
897
+ }
898
+ return new SyncThenable();
899
+ }
900
+ return Promise.resolve(catchError(this.request.response, response)).then(() => Promise.all(responseKeys.map((key) => {
901
+ const descriptor = getDescriptor(response, key);
902
+ if (descriptor && "value" in descriptor) {
903
+ return Promise.resolve(descriptor.value).then((val) => (response[key] = val), () => delete response[key]);
904
+ }
905
+ else {
906
+ delete response[key];
907
+ }
908
+ })));
909
+ }
910
+ }
911
+ const proxyHandler = {
912
+ get(target, prop) {
913
+ const descriptor = getDescriptor(target, prop);
914
+ if (descriptor && !descriptor.configurable && !descriptor.writable && !descriptor.get)
915
+ return target[prop];
916
+ const ah = target.__ajaxHooker;
917
+ if (ah && ah.proxyProps) {
918
+ if (prop in ah.proxyProps) {
919
+ const pDescriptor = ah.proxyProps[prop];
920
+ if ("get" in pDescriptor)
921
+ return pDescriptor.get();
922
+ if (typeof pDescriptor.value === "function")
923
+ return pDescriptor.value.bind(ah);
924
+ return pDescriptor.value;
925
+ }
926
+ if (typeof target[prop] === "function")
927
+ return target[prop].bind(target);
928
+ }
929
+ return target[prop];
930
+ },
931
+ set(target, prop, value) {
932
+ const descriptor = getDescriptor(target, prop);
933
+ if (descriptor && !descriptor.configurable && !descriptor.writable && !descriptor.set)
934
+ return true;
935
+ const ah = target.__ajaxHooker;
936
+ if (ah && ah.proxyProps && prop in ah.proxyProps) {
937
+ const pDescriptor = ah.proxyProps[prop];
938
+ pDescriptor.set ? pDescriptor.set(value) : (pDescriptor.value = value);
939
+ }
940
+ else {
941
+ target[prop] = value;
942
+ }
943
+ return true;
944
+ },
945
+ };
946
+ class XhrHooker {
947
+ constructor(xhr) {
948
+ const ah = this;
949
+ Object.assign(ah, {
950
+ originalXhr: xhr,
951
+ proxyXhr: new Proxy(xhr, proxyHandler),
952
+ resThenable: new SyncThenable(),
953
+ proxyProps: {},
954
+ proxyEvents: {},
955
+ });
956
+ xhr.addEventListener("readystatechange", (e) => {
957
+ if (ah.proxyXhr.readyState === 4 && ah.request && typeof ah.request.response === "function") {
958
+ const response = {
959
+ finalUrl: ah.proxyXhr.responseURL,
960
+ status: ah.proxyXhr.status,
961
+ responseHeaders: parseHeaders(ah.proxyXhr.getAllResponseHeaders()),
962
+ };
963
+ const tempValues = {};
964
+ for (const key of xhrResponses) {
965
+ try {
966
+ tempValues[key] = ah.originalXhr[key];
967
+ }
968
+ catch (err) { }
969
+ defineProp(response, key, () => {
970
+ return (response[key] = tempValues[key]);
971
+ }, (val) => {
972
+ delete response[key];
973
+ response[key] = val;
974
+ });
975
+ }
976
+ ah.resThenable = new AHRequest(ah.request).waitForResponseKeys(response).then(() => {
977
+ for (const key of xhrResponses) {
978
+ ah.proxyProps[key] = {
979
+ get: () => {
980
+ if (!(key in response))
981
+ response[key] = tempValues[key];
982
+ return response[key];
983
+ },
984
+ };
985
+ }
986
+ });
987
+ }
988
+ ah.dispatchEvent(e);
989
+ });
990
+ xhr.addEventListener("load", (e) => ah.dispatchEvent(e));
991
+ xhr.addEventListener("loadend", (e) => ah.dispatchEvent(e));
992
+ for (const evt of xhrAsyncEvents) {
993
+ const onEvt = "on" + evt;
994
+ ah.proxyProps[onEvt] = {
995
+ get: () => ah.proxyEvents[onEvt] || null,
996
+ set: (val) => ah.addEvent(onEvt, val),
997
+ };
998
+ }
999
+ for (const method of ["setRequestHeader", "addEventListener", "removeEventListener", "open", "send"]) {
1000
+ ah.proxyProps[method] = { value: ah[method] };
1001
+ }
1002
+ }
1003
+ toJSON() { } // Converting circular structure to JSON
1004
+ addEvent(type, event) {
1005
+ if (type.startsWith("on")) {
1006
+ this.proxyEvents[type] = typeof event === "function" ? event : null;
1007
+ }
1008
+ else {
1009
+ if (typeof event === "object" && event !== null)
1010
+ event = event.handleEvent;
1011
+ if (typeof event !== "function")
1012
+ return;
1013
+ this.proxyEvents[type] = this.proxyEvents[type] || new Set();
1014
+ this.proxyEvents[type].add(event);
1015
+ }
1016
+ }
1017
+ removeEvent(type, event) {
1018
+ if (type.startsWith("on")) {
1019
+ this.proxyEvents[type] = null;
1020
+ }
1021
+ else {
1022
+ if (typeof event === "object" && event !== null)
1023
+ event = event.handleEvent;
1024
+ this.proxyEvents[type] && this.proxyEvents[type].delete(event);
1025
+ }
1026
+ }
1027
+ dispatchEvent(e) {
1028
+ e.stopImmediatePropagation = stopImmediatePropagation;
1029
+ defineProp(e, "target", () => this.proxyXhr);
1030
+ defineProp(e, "currentTarget", () => this.proxyXhr);
1031
+ defineProp(e, "srcElement", () => this.proxyXhr);
1032
+ this.proxyEvents[e.type] &&
1033
+ this.proxyEvents[e.type].forEach((fn) => {
1034
+ this.resThenable.then(() => !e.ajaxHooker_isStopped && fn.call(this.proxyXhr, e));
1035
+ });
1036
+ if (e.ajaxHooker_isStopped)
1037
+ return;
1038
+ const onEvent = this.proxyEvents["on" + e.type];
1039
+ onEvent && this.resThenable.then(onEvent.bind(this.proxyXhr, e));
1040
+ }
1041
+ setRequestHeader(header, value) {
1042
+ this.originalXhr.setRequestHeader(header, value);
1043
+ if (!this.request)
1044
+ return;
1045
+ const headers = this.request.headers;
1046
+ headers[header] = header in headers ? `${headers[header]}, ${value}` : value;
1047
+ }
1048
+ addEventListener(...args) {
1049
+ if (xhrAsyncEvents.includes(args[0])) {
1050
+ this.addEvent(args[0], args[1]);
1051
+ }
1052
+ else {
1053
+ this.originalXhr.addEventListener(...args);
1054
+ }
1055
+ }
1056
+ removeEventListener(...args) {
1057
+ if (xhrAsyncEvents.includes(args[0])) {
1058
+ this.removeEvent(args[0], args[1]);
1059
+ }
1060
+ else {
1061
+ this.originalXhr.removeEventListener(...args);
1062
+ }
1063
+ }
1064
+ open(method, url, async = true, ...args) {
1065
+ this.request = {
1066
+ type: "xhr",
1067
+ url: url.toString(),
1068
+ method: method.toUpperCase(),
1069
+ abort: false,
1070
+ headers: {},
1071
+ data: null,
1072
+ response: null,
1073
+ async: !!async,
1074
+ };
1075
+ this.openArgs = args;
1076
+ this.resThenable = new SyncThenable();
1077
+ ["responseURL", "readyState", "status", "statusText", ...xhrResponses].forEach((key) => {
1078
+ delete this.proxyProps[key];
1079
+ });
1080
+ return this.originalXhr.open(method, url, async, ...args);
1081
+ }
1082
+ send(data) {
1083
+ const ah = this;
1084
+ const xhr = ah.originalXhr;
1085
+ const request = ah.request;
1086
+ if (!request)
1087
+ return xhr.send(data);
1088
+ request.data = data;
1089
+ new AHRequest(request).waitForRequestKeys().then(() => {
1090
+ if (request.abort) {
1091
+ if (typeof request.response === "function") {
1092
+ Object.assign(ah.proxyProps, {
1093
+ responseURL: { value: request.url },
1094
+ readyState: { value: 4 },
1095
+ status: { value: 200 },
1096
+ statusText: { value: "OK" },
1097
+ });
1098
+ xhrAsyncEvents.forEach((evt) => xhr.dispatchEvent(new Event(evt)));
1099
+ }
1100
+ }
1101
+ else {
1102
+ xhr.open(request.method, request.url, request.async, ...ah.openArgs);
1103
+ for (const header in request.headers) {
1104
+ xhr.setRequestHeader(header, request.headers[header]);
1105
+ }
1106
+ for (const prop of xhrExtraProps) {
1107
+ if (prop in request)
1108
+ xhr[prop] = request[prop];
1109
+ }
1110
+ xhr.send(request.data);
1111
+ }
1112
+ });
1113
+ }
1114
+ }
1115
+ function fakeXHR() {
1116
+ const xhr = new winAh.realXHR();
1117
+ if ("__ajaxHooker" in xhr)
1118
+ console.warn("检测到不同版本的ajaxHooker,可能发生冲突!");
1119
+ xhr.__ajaxHooker = new XhrHooker(xhr);
1120
+ return xhr.__ajaxHooker.proxyXhr;
1121
+ }
1122
+ fakeXHR.prototype = win.XMLHttpRequest.prototype;
1123
+ Object.keys(win.XMLHttpRequest).forEach((key) => (fakeXHR[key] = win.XMLHttpRequest[key]));
1124
+ function fakeFetch(url, options = {}) {
1125
+ if (!url)
1126
+ return winAh.realFetch.call(win, url, options);
1127
+ return new Promise(async (resolve, reject) => {
1128
+ const init = {};
1129
+ if (getType(url) === "[object Request]") {
1130
+ init.method = url.method;
1131
+ init.headers = url.headers;
1132
+ if (url.body)
1133
+ init.body = await url.arrayBuffer();
1134
+ for (const prop of fetchExtraProps)
1135
+ init[prop] = url[prop];
1136
+ url = url.url;
1137
+ }
1138
+ url = url.toString();
1139
+ Object.assign(init, options);
1140
+ init.method = init.method || "GET";
1141
+ init.headers = init.headers || {};
1142
+ const request = {
1143
+ type: "fetch",
1144
+ url: url,
1145
+ method: init.method.toUpperCase(),
1146
+ abort: false,
1147
+ headers: parseHeaders(init.headers),
1148
+ data: init.body,
1149
+ response: null,
1150
+ async: true,
1151
+ };
1152
+ const req = new AHRequest(request);
1153
+ await req.waitForRequestKeys();
1154
+ if (request.abort) {
1155
+ if (typeof request.response === "function") {
1156
+ const response = {
1157
+ finalUrl: request.url,
1158
+ status: 200,
1159
+ responseHeaders: {},
1160
+ };
1161
+ await req.waitForResponseKeys(response);
1162
+ const key = fetchResponses.find((k) => k in response);
1163
+ let val = response[key];
1164
+ if (key === "json" && typeof val === "object") {
1165
+ val = catchError(JSON.stringify.bind(JSON), val);
1166
+ }
1167
+ const res = new Response(val, {
1168
+ status: 200,
1169
+ statusText: "OK",
1170
+ });
1171
+ defineProp(res, "type", () => "basic");
1172
+ defineProp(res, "url", () => request.url);
1173
+ resolve(res);
1174
+ }
1175
+ else {
1176
+ reject(new DOMException("aborted", "AbortError"));
1177
+ }
1178
+ return;
1179
+ }
1180
+ init.method = request.method;
1181
+ init.headers = request.headers;
1182
+ init.body = request.data;
1183
+ for (const prop of fetchExtraProps) {
1184
+ if (prop in request)
1185
+ init[prop] = request[prop];
1186
+ }
1187
+ winAh.realFetch.call(win, request.url, init).then((res) => {
1188
+ if (typeof request.response === "function") {
1189
+ const response = {
1190
+ finalUrl: res.url,
1191
+ status: res.status,
1192
+ responseHeaders: parseHeaders(res.headers),
1193
+ };
1194
+ if (res.ok) {
1195
+ fetchResponses.forEach((key) => (res[key] = function () {
1196
+ if (key in response)
1197
+ return Promise.resolve(response[key]);
1198
+ return resProto[key].call(this).then((val) => {
1199
+ response[key] = val;
1200
+ return req
1201
+ .waitForResponseKeys(response)
1202
+ .then(() => (key in response ? response[key] : val));
1203
+ });
1204
+ }));
1205
+ }
1206
+ else {
1207
+ catchError(request.response, response);
1208
+ }
1209
+ }
1210
+ resolve(res);
1211
+ }, reject);
1212
+ });
1213
+ }
1214
+ function fakeFetchClone() {
1215
+ const descriptors = Object.getOwnPropertyDescriptors(this);
1216
+ const res = winAh.realFetchClone.call(this);
1217
+ Object.defineProperties(res, descriptors);
1218
+ return res;
1219
+ }
1220
+ winAh = win.__ajaxHooker = winAh || {
1221
+ version,
1222
+ fakeXHR,
1223
+ fakeFetch,
1224
+ fakeFetchClone,
1225
+ realXHR: win.XMLHttpRequest,
1226
+ realFetch: win.fetch,
1227
+ realFetchClone: resProto.clone,
1228
+ hookInsts: new Set(),
1229
+ };
1230
+ if (winAh.version !== version)
1231
+ console.warn("检测到不同版本的ajaxHooker,可能发生冲突!");
1232
+ win.XMLHttpRequest = winAh.fakeXHR;
1233
+ win.fetch = winAh.fakeFetch;
1234
+ resProto.clone = winAh.fakeFetchClone;
1235
+ winAh.hookInsts.add(hookInst);
1236
+ // 针对头条、抖音 secsdk.umd.js 的兼容性处理
1237
+ class AHFunction extends Function {
1238
+ call(thisArg, ...args) {
1239
+ if (thisArg && thisArg.__ajaxHooker && thisArg.__ajaxHooker.proxyXhr === thisArg) {
1240
+ thisArg = thisArg.__ajaxHooker.originalXhr;
1241
+ }
1242
+ return Reflect.apply(this, thisArg, args);
1243
+ }
1244
+ apply(thisArg, args) {
1245
+ if (thisArg && thisArg.__ajaxHooker && thisArg.__ajaxHooker.proxyXhr === thisArg) {
1246
+ thisArg = thisArg.__ajaxHooker.originalXhr;
1247
+ }
1248
+ return Reflect.apply(this, thisArg, args || []);
1249
+ }
1250
+ }
1251
+ function hookSecsdk(csrf) {
1252
+ Object.setPrototypeOf(csrf.nativeXMLHttpRequestSetRequestHeader, AHFunction.prototype);
1253
+ Object.setPrototypeOf(csrf.nativeXMLHttpRequestOpen, AHFunction.prototype);
1254
+ Object.setPrototypeOf(csrf.nativeXMLHttpRequestSend, AHFunction.prototype);
1255
+ }
1256
+ if (win.secsdk) {
1257
+ if (win.secsdk.csrf && win.secsdk.csrf.nativeXMLHttpRequestOpen)
1258
+ hookSecsdk(win.secsdk.csrf);
1259
+ }
1260
+ else {
1261
+ defineProp(win, "secsdk", emptyFn, (secsdk) => {
1262
+ delete win.secsdk;
1263
+ win.secsdk = secsdk;
1264
+ defineProp(secsdk, "csrf", emptyFn, (csrf) => {
1265
+ delete secsdk.csrf;
1266
+ secsdk.csrf = csrf;
1267
+ if (csrf.nativeXMLHttpRequestOpen)
1268
+ hookSecsdk(csrf);
1269
+ });
1270
+ });
1271
+ }
1272
+ return {
1273
+ hook: (fn) => hookInst.hookFns.push(fn),
1274
+ filter: (arr) => {
1275
+ if (Array.isArray(arr))
1276
+ hookInst.filters = arr;
1277
+ },
1278
+ protect: () => {
1279
+ readonly(win, "XMLHttpRequest", winAh.fakeXHR);
1280
+ readonly(win, "fetch", winAh.fakeFetch);
1281
+ readonly(resProto, "clone", winAh.fakeFetchClone);
1282
+ },
1283
+ unhook: () => {
1284
+ winAh.hookInsts.delete(hookInst);
1285
+ if (!winAh.hookInsts.size) {
1286
+ writable(win, "XMLHttpRequest", winAh.realXHR);
1287
+ writable(win, "fetch", winAh.realFetch);
1288
+ writable(resProto, "clone", winAh.realFetchClone);
1289
+ delete win.__ajaxHooker;
1290
+ }
1291
+ },
1292
+ };
1263
1293
  };
1264
1294
 
1265
- // ==UserScript==
1266
- // @name ajaxHooker
1267
- // @author cxxjackie
1268
- // @version 1.2.4
1269
- // @supportURL https://bbs.tampermonkey.net.cn/thread-3284-1-1.html
1270
- // ==/UserScript==
1271
-
1272
- const AjaxHooker1_2_4 = function () {
1273
- return (function () {
1274
- const win = window.unsafeWindow || document.defaultView || window;
1275
- const hookFns = [];
1276
- const realXhr = win.XMLHttpRequest;
1277
- const resProto = win.Response.prototype;
1278
- const toString = Object.prototype.toString;
1279
- const realFetch = win.fetch;
1280
- const xhrResponses = ["response", "responseText", "responseXML"];
1281
- const fetchResponses = ["arrayBuffer", "blob", "formData", "json", "text"];
1282
- const xhrAsyncEvents = ["readystatechange", "load", "loadend"];
1283
- let filter;
1284
- function emptyFn() {}
1285
- function errorFn(err) {
1286
- console.error(err);
1287
- }
1288
- function defineProp(obj, prop, getter, setter) {
1289
- Object.defineProperty(obj, prop, {
1290
- configurable: true,
1291
- enumerable: true,
1292
- get: getter,
1293
- set: setter,
1294
- });
1295
- }
1296
- function readonly(obj, prop, value = obj[prop]) {
1297
- defineProp(obj, prop, () => value, emptyFn);
1298
- }
1299
- function writable(obj, prop, value = obj[prop]) {
1300
- Object.defineProperty(obj, prop, {
1301
- configurable: true,
1302
- enumerable: true,
1303
- writable: true,
1304
- value: value,
1305
- });
1306
- }
1307
- function toFilterObj(obj) {
1308
- return {
1309
- type: obj.type,
1310
- url: obj.url,
1311
- method: obj.method && obj.method.toUpperCase(),
1312
- };
1313
- }
1314
- function shouldFilter(type, url, method) {
1315
- return (
1316
- filter &&
1317
- !filter.find(
1318
- (obj) =>
1319
- (!obj.type || obj.type === type) &&
1320
- (!obj.url ||
1321
- (toString.call(obj.url) === "[object String]"
1322
- ? url.includes(obj.url)
1323
- : obj.url.test(url))) &&
1324
- (!obj.method || obj.method === method.toUpperCase())
1325
- )
1326
- );
1327
- }
1328
- function lookupGetter(obj, prop) {
1329
- let getter;
1330
- let proto = obj;
1331
- while (proto) {
1332
- const descriptor = Object.getOwnPropertyDescriptor(proto, prop);
1333
- getter = descriptor && descriptor.get;
1334
- if (getter) break;
1335
- proto = Object.getPrototypeOf(proto);
1336
- }
1337
- return getter ? getter.bind(obj) : emptyFn;
1338
- }
1339
- function waitForHookFns(request) {
1340
- return Promise.all(
1341
- hookFns.map((fn) => Promise.resolve(fn(request)).then(emptyFn, errorFn))
1342
- );
1343
- }
1344
- function waitForRequestKeys(request, requestClone) {
1345
- return Promise.all(
1346
- ["url", "method", "abort", "headers", "data"].map((key) => {
1347
- return Promise.resolve(request[key]).then(
1348
- (val) => (request[key] = val),
1349
- () => (request[key] = requestClone[key])
1350
- );
1351
- })
1352
- );
1353
- }
1354
- function fakeEventSIP() {
1355
- this.ajaxHooker_stopped = true;
1356
- }
1357
- function xhrDelegateEvent(e) {
1358
- const xhr = e.target;
1359
- e.stopImmediatePropagation = fakeEventSIP;
1360
- xhr.__ajaxHooker.hookedEvents[e.type].forEach(
1361
- (fn) => !e.ajaxHooker_stopped && fn.call(xhr, e)
1362
- );
1363
- const onEvent = xhr.__ajaxHooker.hookedEvents["on" + e.type];
1364
- typeof onEvent === "function" && onEvent.call(xhr, e);
1365
- }
1366
- function xhrReadyStateChange(e) {
1367
- if (e.target.readyState === 4) {
1368
- e.target.dispatchEvent(
1369
- new CustomEvent("ajaxHooker_responseReady", { detail: e })
1370
- );
1371
- } else {
1372
- e.target.__ajaxHooker.delegateEvent(e);
1373
- }
1374
- }
1375
- function xhrLoadAndLoadend(e) {
1376
- e.target.__ajaxHooker.delegateEvent(e);
1377
- }
1378
- function fakeXhrOpen(method, url, ...args) {
1379
- const ah = this.__ajaxHooker;
1380
- ah.url = url.toString();
1381
- ah.method = method.toUpperCase();
1382
- ah.openArgs = args;
1383
- ah.headers = {};
1384
- return ah.originalMethods.open(method, url, ...args);
1385
- }
1386
- function fakeXhr() {
1387
- const xhr = new realXhr();
1388
- let ah = xhr.__ajaxHooker;
1389
- if (!ah) {
1390
- ah = xhr.__ajaxHooker = {
1391
- headers: {},
1392
- hookedEvents: {
1393
- readystatechange: new Set(),
1394
- load: new Set(),
1395
- loadend: new Set(),
1396
- },
1397
- delegateEvent: xhrDelegateEvent,
1398
- originalGetters: {},
1399
- originalMethods: {},
1400
- };
1401
- xhr.addEventListener("readystatechange", xhrReadyStateChange);
1402
- xhr.addEventListener("load", xhrLoadAndLoadend);
1403
- xhr.addEventListener("loadend", xhrLoadAndLoadend);
1404
- for (const key of xhrResponses) {
1405
- ah.originalGetters[key] = lookupGetter(xhr, key);
1406
- }
1407
- for (const method of [
1408
- "open",
1409
- "setRequestHeader",
1410
- "addEventListener",
1411
- "removeEventListener",
1412
- ]) {
1413
- ah.originalMethods[method] = xhr[method].bind(xhr);
1414
- }
1415
- xhr.open = fakeXhrOpen;
1416
- xhr.setRequestHeader = (header, value) => {
1417
- ah.originalMethods.setRequestHeader(header, value);
1418
- if (xhr.readyState === 1) {
1419
- if (ah.headers[header]) {
1420
- ah.headers[header] += ", " + value;
1421
- } else {
1422
- ah.headers[header] = value;
1423
- }
1424
- }
1425
- };
1426
- xhr.addEventListener = function (...args) {
1427
- if (xhrAsyncEvents.includes(args[0])) {
1428
- ah.hookedEvents[args[0]].add(args[1]);
1429
- } else {
1430
- ah.originalMethods.addEventListener(...args);
1431
- }
1432
- };
1433
- xhr.removeEventListener = function (...args) {
1434
- if (xhrAsyncEvents.includes(args[0])) {
1435
- ah.hookedEvents[args[0]].delete(args[1]);
1436
- } else {
1437
- ah.originalMethods.removeEventListener(...args);
1438
- }
1439
- };
1440
- xhrAsyncEvents.forEach((evt) => {
1441
- const onEvt = "on" + evt;
1442
- defineProp(
1443
- xhr,
1444
- onEvt,
1445
- () => {
1446
- return ah.hookedEvents[onEvt] || null;
1447
- },
1448
- (val) => {
1449
- ah.hookedEvents[onEvt] = typeof val === "function" ? val : null;
1450
- }
1451
- );
1452
- });
1453
- }
1454
- const realSend = xhr.send.bind(xhr);
1455
- xhr.send = function (data) {
1456
- if (xhr.readyState !== 1) return realSend(data);
1457
- ah.delegateEvent = xhrDelegateEvent;
1458
- xhrResponses.forEach((prop) => {
1459
- delete xhr[prop]; // delete descriptor
1460
- });
1461
- if (shouldFilter("xhr", ah.url, ah.method)) {
1462
- xhr.addEventListener("ajaxHooker_responseReady", (e) => {
1463
- ah.delegateEvent(e.detail);
1464
- });
1465
- return realSend(data);
1466
- }
1467
- try {
1468
- const request = {
1469
- type: "xhr",
1470
- url: ah.url,
1471
- method: ah.method,
1472
- abort: false,
1473
- headers: ah.headers,
1474
- data: data,
1475
- response: null,
1476
- };
1477
- const requestClone = { ...request };
1478
- waitForHookFns(request).then(() => {
1479
- waitForRequestKeys(request, requestClone).then(() => {
1480
- if (request.abort) return;
1481
- ah.originalMethods.open(
1482
- request.method,
1483
- request.url,
1484
- ...ah.openArgs
1485
- );
1486
- for (const header in request.headers) {
1487
- ah.originalMethods.setRequestHeader(
1488
- header,
1489
- request.headers[header]
1490
- );
1491
- }
1492
- data = request.data;
1493
- xhr.addEventListener("ajaxHooker_responseReady", (e) => {
1494
- try {
1495
- if (typeof request.response === "function") {
1496
- const arg = {
1497
- finalUrl: xhr.responseURL,
1498
- status: xhr.status,
1499
- responseHeaders: {},
1500
- };
1501
- for (const line of xhr
1502
- .getAllResponseHeaders()
1503
- .trim()
1504
- .split(/[\r\n]+/)) {
1505
- const parts = line.split(/:\s*/);
1506
- if (parts.length === 2) {
1507
- const lheader = parts[0].toLowerCase();
1508
- if (arg.responseHeaders[lheader]) {
1509
- arg.responseHeaders[lheader] += ", " + parts[1];
1510
- } else {
1511
- arg.responseHeaders[lheader] = parts[1];
1512
- }
1513
- }
1514
- }
1515
- xhrResponses.forEach((prop) => {
1516
- defineProp(
1517
- arg,
1518
- prop,
1519
- () => {
1520
- return (arg[prop] = ah.originalGetters[prop]());
1521
- },
1522
- (val) => {
1523
- delete arg[prop];
1524
- arg[prop] = val;
1525
- }
1526
- );
1527
- defineProp(xhr, prop, () => {
1528
- const val = ah.originalGetters[prop]();
1529
- xhr.dispatchEvent(
1530
- new CustomEvent("ajaxHooker_readResponse", {
1531
- detail: { prop, val },
1532
- })
1533
- );
1534
- return val;
1535
- });
1536
- });
1537
- xhr.addEventListener("ajaxHooker_readResponse", (e) => {
1538
- arg[e.detail.prop] = e.detail.val;
1539
- });
1540
- const resPromise = Promise.resolve(
1541
- request.response(arg)
1542
- ).then(() => {
1543
- const task = [];
1544
- xhrResponses.forEach((prop) => {
1545
- const descriptor = Object.getOwnPropertyDescriptor(
1546
- arg,
1547
- prop
1548
- );
1549
- if (descriptor && "value" in descriptor) {
1550
- task.push(
1551
- Promise.resolve(descriptor.value).then((val) => {
1552
- arg[prop] = val;
1553
- defineProp(xhr, prop, () => {
1554
- xhr.dispatchEvent(
1555
- new CustomEvent("ajaxHooker_readResponse", {
1556
- detail: { prop, val },
1557
- })
1558
- );
1559
- return val;
1560
- });
1561
- }, emptyFn)
1562
- );
1563
- }
1564
- });
1565
- return Promise.all(task);
1566
- }, errorFn);
1567
- const eventsClone = {};
1568
- xhrAsyncEvents.forEach((type) => {
1569
- eventsClone[type] = new Set([...ah.hookedEvents[type]]);
1570
- eventsClone["on" + type] = ah.hookedEvents["on" + type];
1571
- });
1572
- ah.delegateEvent = (event) =>
1573
- resPromise.then(() => {
1574
- event.stopImmediatePropagation = fakeEventSIP;
1575
- eventsClone[event.type].forEach(
1576
- (fn) =>
1577
- !event.ajaxHooker_stopped && fn.call(xhr, event)
1578
- );
1579
- const onEvent = eventsClone["on" + event.type];
1580
- typeof onEvent === "function" &&
1581
- onEvent.call(xhr, event);
1582
- });
1583
- }
1584
- } catch (err) {
1585
- console.error(err);
1586
- }
1587
- ah.delegateEvent(e.detail);
1588
- });
1589
- realSend(data);
1590
- });
1591
- });
1592
- } catch (err) {
1593
- console.error(err);
1594
- realSend(data);
1595
- }
1596
- };
1597
- return xhr;
1598
- }
1599
- function hookFetchResponse(response, arg, callback) {
1600
- fetchResponses.forEach((prop) => {
1601
- response[prop] = () =>
1602
- new Promise((resolve, reject) => {
1603
- resProto[prop].call(response).then((res) => {
1604
- if (prop in arg) {
1605
- resolve(arg[prop]);
1606
- } else {
1607
- try {
1608
- arg[prop] = res;
1609
- Promise.resolve(callback(arg)).then(() => {
1610
- if (prop in arg) {
1611
- Promise.resolve(arg[prop]).then(
1612
- (val) => resolve((arg[prop] = val)),
1613
- () => resolve(res)
1614
- );
1615
- } else {
1616
- resolve(res);
1617
- }
1618
- }, errorFn);
1619
- } catch (err) {
1620
- console.error(err);
1621
- resolve(res);
1622
- }
1623
- }
1624
- }, reject);
1625
- });
1626
- });
1627
- }
1628
- function fakeFetch(url, init) {
1629
- if (url && typeof url.toString === "function") {
1630
- url = url.toString();
1631
- init = init || {};
1632
- init.method = init.method || "GET";
1633
- init.headers = init.headers || {};
1634
- if (shouldFilter("fetch", url, init.method))
1635
- return realFetch.call(win, url, init);
1636
- const request = {
1637
- type: "fetch",
1638
- url: url,
1639
- method: init.method.toUpperCase(),
1640
- abort: false,
1641
- headers: {},
1642
- data: init.body,
1643
- response: null,
1644
- };
1645
- if (toString.call(init.headers) === "[object Headers]") {
1646
- for (const [key, val] of init.headers) {
1647
- request.headers[key] = val;
1648
- }
1649
- } else {
1650
- request.headers = { ...init.headers };
1651
- }
1652
- const requestClone = { ...request };
1653
- return new Promise((resolve, reject) => {
1654
- try {
1655
- waitForHookFns(request).then(() => {
1656
- waitForRequestKeys(request, requestClone).then(() => {
1657
- if (request.abort) return reject("aborted");
1658
- url = request.url;
1659
- init.method = request.method;
1660
- init.headers = request.headers;
1661
- init.body = request.data;
1662
- realFetch.call(win, url, init).then((response) => {
1663
- if (typeof request.response === "function") {
1664
- const arg = {
1665
- finalUrl: response.url,
1666
- status: response.status,
1667
- responseHeaders: {},
1668
- };
1669
- for (const [key, val] of response.headers) {
1670
- arg.responseHeaders[key] = val;
1671
- }
1672
- hookFetchResponse(response, arg, request.response);
1673
- response.clone = () => {
1674
- const resClone = resProto.clone.call(response);
1675
- hookFetchResponse(resClone, arg, request.response);
1676
- return resClone;
1677
- };
1678
- }
1679
- resolve(response);
1680
- }, reject);
1681
- });
1682
- });
1683
- } catch (err) {
1684
- console.error(err);
1685
- return realFetch.call(win, url, init);
1686
- }
1687
- });
1688
- } else {
1689
- return realFetch.call(win, url, init);
1690
- }
1691
- }
1692
- win.XMLHttpRequest = fakeXhr;
1693
- Object.keys(realXhr).forEach((key) => (fakeXhr[key] = realXhr[key]));
1694
- fakeXhr.prototype = realXhr.prototype;
1695
- win.fetch = fakeFetch;
1696
- return {
1697
- hook: (fn) => hookFns.push(fn),
1698
- filter: (arr) => {
1699
- filter = Array.isArray(arr) && arr.map(toFilterObj);
1700
- },
1701
- protect: () => {
1702
- readonly(win, "XMLHttpRequest", fakeXhr);
1703
- readonly(win, "fetch", fakeFetch);
1704
- },
1705
- unhook: () => {
1706
- writable(win, "XMLHttpRequest", realXhr);
1707
- writable(win, "fetch", realFetch);
1708
- },
1709
- };
1710
- })();
1295
+ // ==UserScript==
1296
+ // @name ajaxHooker
1297
+ // @author cxxjackie
1298
+ // @version 1.2.4
1299
+ // @supportURL https://bbs.tampermonkey.net.cn/thread-3284-1-1.html
1300
+ // ==/UserScript==
1301
+ const AjaxHooker1_2_4 = function () {
1302
+ return (function () {
1303
+ const win = window.unsafeWindow || document.defaultView || window;
1304
+ const hookFns = [];
1305
+ const realXhr = win.XMLHttpRequest;
1306
+ const resProto = win.Response.prototype;
1307
+ const toString = Object.prototype.toString;
1308
+ const realFetch = win.fetch;
1309
+ const xhrResponses = ["response", "responseText", "responseXML"];
1310
+ const fetchResponses = ["arrayBuffer", "blob", "formData", "json", "text"];
1311
+ const xhrAsyncEvents = ["readystatechange", "load", "loadend"];
1312
+ let filter;
1313
+ function emptyFn() { }
1314
+ function errorFn(err) {
1315
+ console.error(err);
1316
+ }
1317
+ function defineProp(obj, prop, getter, setter) {
1318
+ Object.defineProperty(obj, prop, {
1319
+ configurable: true,
1320
+ enumerable: true,
1321
+ get: getter,
1322
+ set: setter,
1323
+ });
1324
+ }
1325
+ function readonly(obj, prop, value = obj[prop]) {
1326
+ defineProp(obj, prop, () => value, emptyFn);
1327
+ }
1328
+ function writable(obj, prop, value = obj[prop]) {
1329
+ Object.defineProperty(obj, prop, {
1330
+ configurable: true,
1331
+ enumerable: true,
1332
+ writable: true,
1333
+ value: value,
1334
+ });
1335
+ }
1336
+ function toFilterObj(obj) {
1337
+ return {
1338
+ type: obj.type,
1339
+ url: obj.url,
1340
+ method: obj.method && obj.method.toUpperCase(),
1341
+ };
1342
+ }
1343
+ function shouldFilter(type, url, method) {
1344
+ return (filter &&
1345
+ !filter.find((obj) => (!obj.type || obj.type === type) &&
1346
+ (!obj.url ||
1347
+ (toString.call(obj.url) === "[object String]"
1348
+ ? url.includes(obj.url)
1349
+ : obj.url.test(url))) &&
1350
+ (!obj.method || obj.method === method.toUpperCase())));
1351
+ }
1352
+ function lookupGetter(obj, prop) {
1353
+ let getter;
1354
+ let proto = obj;
1355
+ while (proto) {
1356
+ const descriptor = Object.getOwnPropertyDescriptor(proto, prop);
1357
+ getter = descriptor && descriptor.get;
1358
+ if (getter)
1359
+ break;
1360
+ proto = Object.getPrototypeOf(proto);
1361
+ }
1362
+ return getter ? getter.bind(obj) : emptyFn;
1363
+ }
1364
+ function waitForHookFns(request) {
1365
+ return Promise.all(hookFns.map((fn) => Promise.resolve(fn(request)).then(emptyFn, errorFn)));
1366
+ }
1367
+ function waitForRequestKeys(request, requestClone) {
1368
+ return Promise.all(["url", "method", "abort", "headers", "data"].map((key) => {
1369
+ return Promise.resolve(request[key]).then((val) => (request[key] = val), () => (request[key] = requestClone[key]));
1370
+ }));
1371
+ }
1372
+ function fakeEventSIP() {
1373
+ this.ajaxHooker_stopped = true;
1374
+ }
1375
+ function xhrDelegateEvent(e) {
1376
+ const xhr = e.target;
1377
+ e.stopImmediatePropagation = fakeEventSIP;
1378
+ xhr.__ajaxHooker.hookedEvents[e.type].forEach((fn) => !e.ajaxHooker_stopped && fn.call(xhr, e));
1379
+ const onEvent = xhr.__ajaxHooker.hookedEvents["on" + e.type];
1380
+ typeof onEvent === "function" && onEvent.call(xhr, e);
1381
+ }
1382
+ function xhrReadyStateChange(e) {
1383
+ if (e.target.readyState === 4) {
1384
+ e.target.dispatchEvent(new CustomEvent("ajaxHooker_responseReady", { detail: e }));
1385
+ }
1386
+ else {
1387
+ e.target.__ajaxHooker.delegateEvent(e);
1388
+ }
1389
+ }
1390
+ function xhrLoadAndLoadend(e) {
1391
+ e.target.__ajaxHooker.delegateEvent(e);
1392
+ }
1393
+ function fakeXhrOpen(method, url, ...args) {
1394
+ const ah = this.__ajaxHooker;
1395
+ ah.url = url.toString();
1396
+ ah.method = method.toUpperCase();
1397
+ ah.openArgs = args;
1398
+ ah.headers = {};
1399
+ return ah.originalMethods.open(method, url, ...args);
1400
+ }
1401
+ function fakeXhr() {
1402
+ const xhr = new realXhr();
1403
+ let ah = xhr.__ajaxHooker;
1404
+ if (!ah) {
1405
+ ah = xhr.__ajaxHooker = {
1406
+ headers: {},
1407
+ hookedEvents: {
1408
+ readystatechange: new Set(),
1409
+ load: new Set(),
1410
+ loadend: new Set(),
1411
+ },
1412
+ delegateEvent: xhrDelegateEvent,
1413
+ originalGetters: {},
1414
+ originalMethods: {},
1415
+ };
1416
+ xhr.addEventListener("readystatechange", xhrReadyStateChange);
1417
+ xhr.addEventListener("load", xhrLoadAndLoadend);
1418
+ xhr.addEventListener("loadend", xhrLoadAndLoadend);
1419
+ for (const key of xhrResponses) {
1420
+ ah.originalGetters[key] = lookupGetter(xhr, key);
1421
+ }
1422
+ for (const method of [
1423
+ "open",
1424
+ "setRequestHeader",
1425
+ "addEventListener",
1426
+ "removeEventListener",
1427
+ ]) {
1428
+ ah.originalMethods[method] = xhr[method].bind(xhr);
1429
+ }
1430
+ xhr.open = fakeXhrOpen;
1431
+ xhr.setRequestHeader = (header, value) => {
1432
+ ah.originalMethods.setRequestHeader(header, value);
1433
+ if (xhr.readyState === 1) {
1434
+ if (ah.headers[header]) {
1435
+ ah.headers[header] += ", " + value;
1436
+ }
1437
+ else {
1438
+ ah.headers[header] = value;
1439
+ }
1440
+ }
1441
+ };
1442
+ xhr.addEventListener = function (...args) {
1443
+ if (xhrAsyncEvents.includes(args[0])) {
1444
+ ah.hookedEvents[args[0]].add(args[1]);
1445
+ }
1446
+ else {
1447
+ ah.originalMethods.addEventListener(...args);
1448
+ }
1449
+ };
1450
+ xhr.removeEventListener = function (...args) {
1451
+ if (xhrAsyncEvents.includes(args[0])) {
1452
+ ah.hookedEvents[args[0]].delete(args[1]);
1453
+ }
1454
+ else {
1455
+ ah.originalMethods.removeEventListener(...args);
1456
+ }
1457
+ };
1458
+ xhrAsyncEvents.forEach((evt) => {
1459
+ const onEvt = "on" + evt;
1460
+ defineProp(xhr, onEvt, () => {
1461
+ return ah.hookedEvents[onEvt] || null;
1462
+ }, (val) => {
1463
+ ah.hookedEvents[onEvt] = typeof val === "function" ? val : null;
1464
+ });
1465
+ });
1466
+ }
1467
+ const realSend = xhr.send.bind(xhr);
1468
+ xhr.send = function (data) {
1469
+ if (xhr.readyState !== 1)
1470
+ return realSend(data);
1471
+ ah.delegateEvent = xhrDelegateEvent;
1472
+ xhrResponses.forEach((prop) => {
1473
+ delete xhr[prop]; // delete descriptor
1474
+ });
1475
+ if (shouldFilter("xhr", ah.url, ah.method)) {
1476
+ xhr.addEventListener("ajaxHooker_responseReady", (e) => {
1477
+ ah.delegateEvent(e.detail);
1478
+ });
1479
+ return realSend(data);
1480
+ }
1481
+ try {
1482
+ const request = {
1483
+ type: "xhr",
1484
+ url: ah.url,
1485
+ method: ah.method,
1486
+ abort: false,
1487
+ headers: ah.headers,
1488
+ data: data,
1489
+ response: null,
1490
+ };
1491
+ const requestClone = { ...request };
1492
+ waitForHookFns(request).then(() => {
1493
+ waitForRequestKeys(request, requestClone).then(() => {
1494
+ if (request.abort)
1495
+ return;
1496
+ ah.originalMethods.open(request.method, request.url, ...ah.openArgs);
1497
+ for (const header in request.headers) {
1498
+ ah.originalMethods.setRequestHeader(header, request.headers[header]);
1499
+ }
1500
+ data = request.data;
1501
+ xhr.addEventListener("ajaxHooker_responseReady", (e) => {
1502
+ try {
1503
+ if (typeof request.response === "function") {
1504
+ const arg = {
1505
+ finalUrl: xhr.responseURL,
1506
+ status: xhr.status,
1507
+ responseHeaders: {},
1508
+ };
1509
+ for (const line of xhr
1510
+ .getAllResponseHeaders()
1511
+ .trim()
1512
+ .split(/[\r\n]+/)) {
1513
+ const parts = line.split(/:\s*/);
1514
+ if (parts.length === 2) {
1515
+ const lheader = parts[0].toLowerCase();
1516
+ if (arg.responseHeaders[lheader]) {
1517
+ arg.responseHeaders[lheader] += ", " + parts[1];
1518
+ }
1519
+ else {
1520
+ arg.responseHeaders[lheader] = parts[1];
1521
+ }
1522
+ }
1523
+ }
1524
+ xhrResponses.forEach((prop) => {
1525
+ defineProp(arg, prop, () => {
1526
+ return (arg[prop] = ah.originalGetters[prop]());
1527
+ }, (val) => {
1528
+ delete arg[prop];
1529
+ arg[prop] = val;
1530
+ });
1531
+ defineProp(xhr, prop, () => {
1532
+ const val = ah.originalGetters[prop]();
1533
+ xhr.dispatchEvent(new CustomEvent("ajaxHooker_readResponse", {
1534
+ detail: { prop, val },
1535
+ }));
1536
+ return val;
1537
+ });
1538
+ });
1539
+ xhr.addEventListener("ajaxHooker_readResponse", (e) => {
1540
+ arg[e.detail.prop] = e.detail.val;
1541
+ });
1542
+ const resPromise = Promise.resolve(request.response(arg)).then(() => {
1543
+ const task = [];
1544
+ xhrResponses.forEach((prop) => {
1545
+ const descriptor = Object.getOwnPropertyDescriptor(arg, prop);
1546
+ if (descriptor && "value" in descriptor) {
1547
+ task.push(Promise.resolve(descriptor.value).then((val) => {
1548
+ arg[prop] = val;
1549
+ defineProp(xhr, prop, () => {
1550
+ xhr.dispatchEvent(new CustomEvent("ajaxHooker_readResponse", {
1551
+ detail: { prop, val },
1552
+ }));
1553
+ return val;
1554
+ });
1555
+ }, emptyFn));
1556
+ }
1557
+ });
1558
+ return Promise.all(task);
1559
+ }, errorFn);
1560
+ const eventsClone = {};
1561
+ xhrAsyncEvents.forEach((type) => {
1562
+ eventsClone[type] = new Set([...ah.hookedEvents[type]]);
1563
+ eventsClone["on" + type] = ah.hookedEvents["on" + type];
1564
+ });
1565
+ ah.delegateEvent = (event) => resPromise.then(() => {
1566
+ event.stopImmediatePropagation = fakeEventSIP;
1567
+ eventsClone[event.type].forEach((fn) => !event.ajaxHooker_stopped && fn.call(xhr, event));
1568
+ const onEvent = eventsClone["on" + event.type];
1569
+ typeof onEvent === "function" &&
1570
+ onEvent.call(xhr, event);
1571
+ });
1572
+ }
1573
+ }
1574
+ catch (err) {
1575
+ console.error(err);
1576
+ }
1577
+ ah.delegateEvent(e.detail);
1578
+ });
1579
+ realSend(data);
1580
+ });
1581
+ });
1582
+ }
1583
+ catch (err) {
1584
+ console.error(err);
1585
+ realSend(data);
1586
+ }
1587
+ };
1588
+ return xhr;
1589
+ }
1590
+ function hookFetchResponse(response, arg, callback) {
1591
+ fetchResponses.forEach((prop) => {
1592
+ response[prop] = () => new Promise((resolve, reject) => {
1593
+ resProto[prop].call(response).then((res) => {
1594
+ if (prop in arg) {
1595
+ resolve(arg[prop]);
1596
+ }
1597
+ else {
1598
+ try {
1599
+ arg[prop] = res;
1600
+ Promise.resolve(callback(arg)).then(() => {
1601
+ if (prop in arg) {
1602
+ Promise.resolve(arg[prop]).then((val) => resolve((arg[prop] = val)), () => resolve(res));
1603
+ }
1604
+ else {
1605
+ resolve(res);
1606
+ }
1607
+ }, errorFn);
1608
+ }
1609
+ catch (err) {
1610
+ console.error(err);
1611
+ resolve(res);
1612
+ }
1613
+ }
1614
+ }, reject);
1615
+ });
1616
+ });
1617
+ }
1618
+ function fakeFetch(url, init) {
1619
+ if (url && typeof url.toString === "function") {
1620
+ url = url.toString();
1621
+ init = init || {};
1622
+ init.method = init.method || "GET";
1623
+ init.headers = init.headers || {};
1624
+ if (shouldFilter("fetch", url, init.method))
1625
+ return realFetch.call(win, url, init);
1626
+ const request = {
1627
+ type: "fetch",
1628
+ url: url,
1629
+ method: init.method.toUpperCase(),
1630
+ abort: false,
1631
+ headers: {},
1632
+ data: init.body,
1633
+ response: null,
1634
+ };
1635
+ if (toString.call(init.headers) === "[object Headers]") {
1636
+ for (const [key, val] of init.headers) {
1637
+ request.headers[key] = val;
1638
+ }
1639
+ }
1640
+ else {
1641
+ request.headers = { ...init.headers };
1642
+ }
1643
+ const requestClone = { ...request };
1644
+ return new Promise((resolve, reject) => {
1645
+ try {
1646
+ waitForHookFns(request).then(() => {
1647
+ waitForRequestKeys(request, requestClone).then(() => {
1648
+ if (request.abort)
1649
+ return reject("aborted");
1650
+ url = request.url;
1651
+ init.method = request.method;
1652
+ init.headers = request.headers;
1653
+ init.body = request.data;
1654
+ realFetch.call(win, url, init).then((response) => {
1655
+ if (typeof request.response === "function") {
1656
+ const arg = {
1657
+ finalUrl: response.url,
1658
+ status: response.status,
1659
+ responseHeaders: {},
1660
+ };
1661
+ for (const [key, val] of response.headers) {
1662
+ arg.responseHeaders[key] = val;
1663
+ }
1664
+ hookFetchResponse(response, arg, request.response);
1665
+ response.clone = () => {
1666
+ const resClone = resProto.clone.call(response);
1667
+ hookFetchResponse(resClone, arg, request.response);
1668
+ return resClone;
1669
+ };
1670
+ }
1671
+ resolve(response);
1672
+ }, reject);
1673
+ });
1674
+ });
1675
+ }
1676
+ catch (err) {
1677
+ console.error(err);
1678
+ return realFetch.call(win, url, init);
1679
+ }
1680
+ });
1681
+ }
1682
+ else {
1683
+ return realFetch.call(win, url, init);
1684
+ }
1685
+ }
1686
+ win.XMLHttpRequest = fakeXhr;
1687
+ Object.keys(realXhr).forEach((key) => (fakeXhr[key] = realXhr[key]));
1688
+ fakeXhr.prototype = realXhr.prototype;
1689
+ win.fetch = fakeFetch;
1690
+ return {
1691
+ hook: (fn) => hookFns.push(fn),
1692
+ filter: (arr) => {
1693
+ filter = Array.isArray(arr) && arr.map(toFilterObj);
1694
+ },
1695
+ protect: () => {
1696
+ readonly(win, "XMLHttpRequest", fakeXhr);
1697
+ readonly(win, "fetch", fakeFetch);
1698
+ },
1699
+ unhook: () => {
1700
+ writable(win, "XMLHttpRequest", realXhr);
1701
+ writable(win, "fetch", realFetch);
1702
+ },
1703
+ };
1704
+ })();
1711
1705
  };
1712
1706
 
1713
1707
  class GMMenu {
@@ -2125,31 +2119,31 @@ class GMMenu {
2125
2119
  }
2126
2120
  /**
2127
2121
  * 设置当enable为true时默认显示在菜单中前面的emoji图标
2128
- * @param emojiString
2122
+ * @param emojiString emoji字符串
2129
2123
  */
2130
2124
  setEnableTrueEmoji(emojiString) {
2131
2125
  if (typeof emojiString !== "string") {
2132
- throw new Error("参数emojiString必须是string类型");
2126
+ throw new TypeError("参数emojiString必须是string类型");
2133
2127
  }
2134
2128
  this.MenuHandle.$emoji.success = emojiString;
2135
2129
  }
2136
2130
  /**
2137
2131
  * 设置当enable为false时默认显示在菜单中前面的emoji图标
2138
- * @param emojiString
2132
+ * @param emojiString emoji字符串
2139
2133
  */
2140
2134
  setEnableFalseEmoji(emojiString) {
2141
2135
  if (typeof emojiString !== "string") {
2142
- throw new Error("参数emojiString必须是string类型");
2136
+ throw new TypeError("参数emojiString必须是string类型");
2143
2137
  }
2144
2138
  this.MenuHandle.$emoji.error = emojiString;
2145
2139
  }
2146
2140
  /**
2147
2141
  * 设置本地存储的菜单外部的键名
2148
- * @param keyName
2142
+ * @param keyName 键名
2149
2143
  */
2150
2144
  setLocalStorageKeyName(keyName) {
2151
2145
  if (typeof keyName !== "string") {
2152
- throw new Error("参数keyName必须是string类型");
2146
+ throw new TypeError("参数keyName必须是string类型");
2153
2147
  }
2154
2148
  this.MenuHandle.$data.key = keyName;
2155
2149
  }
@@ -4932,403 +4926,401 @@ const clearTimeout$1 = (timerId) => loadOrReturnBroker().clearTimeout(timerId);
4932
4926
  const setInterval = (...args) => loadOrReturnBroker().setInterval(...args);
4933
4927
  const setTimeout$1 = (...args) => loadOrReturnBroker().setTimeout(...args);
4934
4928
 
4935
- /* eslint-disable */
4936
- // ==UserScript==
4937
- // @name ModuleRaid.js
4938
- // @namespace http://tampermonkey.net/
4939
- // @version 6.2.0
4940
- // @description 检索调用webpackJsonp模块,可指定检索的window
4941
- // @author empyrealtear
4942
- // @license MIT
4943
- // @original-script https://github.com/pixeldesu/moduleRaid
4944
- // ==/UserScript==
4945
-
4946
-
4947
- /**
4948
- * Main moduleRaid class
4949
- * @link https://scriptcat.org/zh-CN/script-show-page/2628
4950
- */
4951
- class ModuleRaid {
4952
- /**
4953
- * moduleRaid constructor
4954
- *
4955
- * @example
4956
- * Constructing an instance without any arguments:
4957
- * ```ts
4958
- * const mR = new ModuleRaid()
4959
- * ```
4960
- *
4961
- * Constructing an instance with the optional `opts` object:
4962
- * ```ts
4963
- * const mR = new ModuleRaid({ entrypoint: 'webpackChunk_custom_name' })
4964
- * ```
4965
- *
4966
- * @param opts a object containing options to initialize moduleRaid with
4967
- * - **opts:**
4968
- * - _target_: the window object being searched for
4969
- * - _entrypoint_: the Webpack entrypoint present on the global window object
4970
- * - _debug_: whether debug mode is enabled or not
4971
- * - _strict_: whether strict mode is enabled or not
4972
- */
4973
- constructor(opts) {
4974
- /**
4975
- * A random generated module ID we use for injecting into Webpack
4976
- */
4977
- this.moduleID = Math.random().toString(36).substring(7);
4978
- /**
4979
- * An array containing different argument injection methods for
4980
- * Webpack (before version 4), and subsequently pulling out methods and modules
4981
- * @internal
4982
- */
4983
- this.functionArguments = [
4984
- [
4985
- [0],
4986
- [
4987
- (_e, _t, i) => {
4988
- this.modules = i.c;
4989
- this.constructors = i.m;
4990
- this.get = i;
4991
- },
4992
- ],
4993
- ],
4994
- [
4995
- [1e3],
4996
- {
4997
- [this.moduleID]: (_e, _t, i) => {
4998
- this.modules = i.c;
4999
- this.constructors = i.m;
5000
- this.get = i;
5001
- },
5002
- },
5003
- [[this.moduleID]],
5004
- ],
5005
- ];
5006
- /**
5007
- * An array containing different argument injection methods for
5008
- * Webpack (after version 4), and subsequently pulling out methods and modules
5009
- * @internal
5010
- */
5011
- this.arrayArguments = [
5012
- [
5013
- [this.moduleID],
5014
- {},
5015
- (e) => {
5016
- const mCac = e.m;
5017
- Object.keys(mCac).forEach((mod) => {
5018
- try {
5019
- this.modules[mod] = e(mod);
5020
- }
5021
- catch (err) {
5022
- this.log(`[arrayArguments/1] Failed to require(${mod}) with error:\n${err}\n${err.stack}`);
5023
- }
5024
- });
5025
- this.get = e;
5026
- },
5027
- ],
5028
- this.functionArguments[1],
5029
- ];
5030
- /**
5031
- * Storage for the modules we extracted from Webpack
5032
- */
5033
- this.modules = {};
5034
- /**
5035
- * Storage for the constructors we extracted from Webpack
5036
- */
5037
- this.constructors = [];
5038
- let options = {
5039
- target: window,
5040
- entrypoint: 'webpackJsonp',
5041
- debug: false,
5042
- strict: false,
5043
- };
5044
- if (typeof opts === 'object') {
5045
- options = Object.assign(Object.assign({}, options), opts);
5046
- }
5047
- this.target = options.target;
5048
- this.entrypoint = options.entrypoint;
5049
- this.debug = options.debug;
5050
- this.strict = options.strict;
5051
- this.detectEntrypoint();
5052
- this.fillModules();
5053
- this.replaceGet();
5054
- this.setupPushEvent();
5055
- }
5056
- /**
5057
- * Debug logging method, outputs to the console when {@link ModuleRaid.debug} is true
5058
- *
5059
- * @param {*} message The message to be logged
5060
- * @internal
5061
- */
5062
- log(message) {
5063
- if (this.debug) {
5064
- console.warn(`[moduleRaid] ${message}`);
5065
- }
5066
- }
5067
- /**
5068
- * Method to set an alternative getter if we weren't able to extract __webpack_require__
5069
- * from Webpack
5070
- * @internal
5071
- */
5072
- replaceGet() {
5073
- if (this.get === null) {
5074
- this.get = (key) => this.modules[key];
5075
- }
5076
- }
5077
- /**
5078
- * Method that will try to inject a module into Webpack or get modules
5079
- * depending on it's success it might be more or less brute about it
5080
- * @internal
5081
- */
5082
- fillModules() {
5083
- if (typeof this.target[this.entrypoint] === 'function') {
5084
- this.functionArguments.forEach((argument, index) => {
5085
- try {
5086
- if (this.modules && Object.keys(this.modules).length > 0)
5087
- return;
5088
- this.target[this.entrypoint](...argument);
5089
- }
5090
- catch (err) {
5091
- this.log(`moduleRaid.functionArguments[${index}] failed:\n${err}\n${err.stack}`);
5092
- }
5093
- });
5094
- }
5095
- else {
5096
- this.arrayArguments.forEach((argument, index) => {
5097
- try {
5098
- if (this.modules && Object.keys(this.modules).length > 0)
5099
- return;
5100
- this.target[this.entrypoint].push(argument);
5101
- }
5102
- catch (err) {
5103
- this.log(`Pushing moduleRaid.arrayArguments[${index}] into ${this.entrypoint} failed:\n${err}\n${err.stack}`);
5104
- }
5105
- });
5106
- }
5107
- if (this.modules && Object.keys(this.modules).length == 0) {
5108
- let moduleEnd = false;
5109
- let moduleIterator = 0;
5110
- if (typeof this.target[this.entrypoint] != 'function' || !this.target[this.entrypoint]([], [], [moduleIterator])) {
5111
- throw Error('Unknown Webpack structure');
5112
- }
5113
- while (!moduleEnd) {
5114
- try {
5115
- this.modules[moduleIterator] = this.target[this.entrypoint]([], [], [moduleIterator]);
5116
- moduleIterator++;
5117
- }
5118
- catch (err) {
5119
- moduleEnd = true;
5120
- }
5121
- }
5122
- }
5123
- }
5124
- /**
5125
- * Method to hook into `window[this.entrypoint].push` adding a listener for new
5126
- * chunks being pushed into Webpack
5127
- *
5128
- * @example
5129
- * You can listen for newly pushed packages using the `moduleraid:webpack-push` event
5130
- * on `document`
5131
- *
5132
- * ```ts
5133
- * document.addEventListener('moduleraid:webpack-push', (e) => {
5134
- * // e.detail contains the arguments push() was called with
5135
- * console.log(e.detail)
5136
- * })
5137
- * ```
5138
- * @internal
5139
- */
5140
- setupPushEvent() {
5141
- const originalPush = this.target[this.entrypoint].push;
5142
- this.target[this.entrypoint].push = (...args) => {
5143
- const result = Reflect.apply(originalPush, this.target[this.entrypoint], args);
5144
- document.dispatchEvent(new CustomEvent('moduleraid:webpack-push', { detail: args }));
5145
- return result;
5146
- };
5147
- }
5148
- /**
5149
- * Method to try autodetecting a Webpack JSONP entrypoint based on common naming
5150
- *
5151
- * If the default entrypoint, or the entrypoint that's passed to the moduleRaid constructor
5152
- * already matches, the method exits early
5153
- *
5154
- * If `options.strict` has been set in the constructor and the initial entrypoint cannot
5155
- * be found, this method will error, demanding a strictly set entrypoint
5156
- * @internal
5157
- */
5158
- detectEntrypoint() {
5159
- if (this.target[this.entrypoint] != undefined) {
5160
- return;
5161
- }
5162
- if (this.strict) {
5163
- throw Error(`Strict mode is enabled and entrypoint at window.${this.entrypoint} couldn't be found. Please specify the correct one!`);
5164
- }
5165
- let windowObjects = Object.keys(this.target);
5166
- windowObjects = windowObjects
5167
- .filter((object) => object.toLowerCase().includes('chunk') || object.toLowerCase().includes('webpack'))
5168
- .filter((object) => typeof this.target[object] === 'function' || Array.isArray(this.target[object]));
5169
- if (windowObjects.length > 1) {
5170
- throw Error(`Multiple possible endpoints have been detected, please create a new moduleRaid instance with a specific one:\n${windowObjects.join(', ')}`);
5171
- }
5172
- if (windowObjects.length === 0) {
5173
- throw Error('No Webpack JSONP entrypoints could be detected');
5174
- }
5175
- this.log(`Entrypoint has been detected at window.${windowObjects[0]} and set for injection`);
5176
- this.entrypoint = windowObjects[0];
5177
- }
5178
- /**
5179
- * Recursive object-search function for modules
5180
- *
5181
- * @param object the object to search through
5182
- * @param query the query the object keys/values are searched for
5183
- * @returns boolean state of `object` containing `query` somewhere in it
5184
- * @internal
5185
- */
5186
- searchObject(object, query) {
5187
- for (const key in object) {
5188
- const value = object[key];
5189
- const lowerCaseQuery = query.toLowerCase();
5190
- if (typeof value != 'object') {
5191
- const lowerCaseKey = key.toString().toLowerCase();
5192
- if (lowerCaseKey.includes(lowerCaseQuery))
5193
- return true;
5194
- if (typeof value != 'object') {
5195
- const lowerCaseValue = value.toString().toLowerCase();
5196
- if (lowerCaseValue.includes(lowerCaseQuery))
5197
- return true;
5198
- }
5199
- else {
5200
- if (this.searchObject(value, query))
5201
- return true;
5202
- }
5203
- }
5204
- }
5205
- return false;
5206
- }
5207
- /**
5208
- * Method to search through the module object, searching for the fitting content
5209
- * if a string is supplied
5210
- *
5211
- * If query is supplied as a function, everything that returns true when passed
5212
- * to the query function will be returned
5213
- *
5214
- * @example
5215
- * With a string as query argument:
5216
- * ```ts
5217
- * const results = mR.findModule('feature')
5218
- * // => Array of module results
5219
- * ```
5220
- *
5221
- * With a function as query argument:
5222
- * ```ts
5223
- * const results = mR.findModule((module) => { typeof module === 'function' })
5224
- * // => Array of module results
5225
- * ```
5226
- *
5227
- * @param query query to search the module list for
5228
- * @return a list of modules fitting the query
5229
- */
5230
- findModule(query) {
5231
- const results = [];
5232
- const modules = Object.keys(this.modules);
5233
- if (modules.length === 0) {
5234
- throw new Error('There are no modules to search through!');
5235
- }
5236
- modules.forEach((key) => {
5237
- const module = this.modules[key.toString()];
5238
- if (module === undefined)
5239
- return;
5240
- try {
5241
- if (typeof query === 'string') {
5242
- query = query.toLowerCase();
5243
- switch (typeof module) {
5244
- case 'string':
5245
- if (module.toLowerCase().includes(query))
5246
- results.push(module);
5247
- break;
5248
- case 'function':
5249
- if (module.toString().toLowerCase().includes(query))
5250
- results.push(module);
5251
- break;
5252
- case 'object':
5253
- if (this.searchObject(module, query))
5254
- results.push(module);
5255
- break;
5256
- }
5257
- }
5258
- else if (typeof query === 'function') {
5259
- if (query(module))
5260
- results.push(module);
5261
- }
5262
- else {
5263
- throw new TypeError(`findModule can only find via string and function, ${typeof query} was passed`);
5264
- }
5265
- }
5266
- catch (err) {
5267
- this.log(`There was an error while searching through module '${key}':\n${err}\n${err.stack}`);
5268
- }
5269
- });
5270
- return results;
5271
- }
5272
- /**
5273
- * Method to search through the constructor array, searching for the fitting content
5274
- * if a string is supplied
5275
- *
5276
- * If query is supplied as a function, everything that returns true when passed
5277
- * to the query function will be returned
5278
- *
5279
- * @example
5280
- * With a string as query argument:
5281
- * ```ts
5282
- * const results = mR.findConstructor('feature')
5283
- * // => Array of constructor/module tuples
5284
- * ```
5285
- *
5286
- * With a function as query argument:
5287
- * ```ts
5288
- * const results = mR.findConstructor((constructor) => { constructor.prototype.value !== undefined })
5289
- * // => Array of constructor/module tuples
5290
- * ```
5291
- *
5292
- * Accessing the resulting data:
5293
- * ```ts
5294
- * // With array destructuring (ES6)
5295
- * const [constructor, module] = results[0]
5296
- *
5297
- * // ...or...
5298
- *
5299
- * // regular access
5300
- * const constructor = results[0][0]
5301
- * const module = results[0][1]
5302
- * ```
5303
- *
5304
- * @param query query to search the constructor list for
5305
- * @returns a list of constructor/module tuples fitting the query
5306
- */
5307
- findConstructor(query) {
5308
- const results = [];
5309
- const constructors = Object.keys(this.constructors);
5310
- if (constructors.length === 0) {
5311
- throw new Error('There are no constructors to search through!');
5312
- }
5313
- constructors.forEach((key) => {
5314
- const constructor = this.constructors[key];
5315
- try {
5316
- if (typeof query === 'string') {
5317
- query = query.toLowerCase();
5318
- if (constructor.toString().toLowerCase().includes(query))
5319
- results.push([this.constructors[key], this.modules[key]]);
5320
- }
5321
- else if (typeof query === 'function') {
5322
- if (query(constructor))
5323
- results.push([this.constructors[key], this.modules[key]]);
5324
- }
5325
- }
5326
- catch (err) {
5327
- this.log(`There was an error while searching through constructor '${key}':\n${err}\n${err.stack}`);
5328
- }
5329
- });
5330
- return results;
5331
- }
4929
+ /* eslint-disable */
4930
+ // ==UserScript==
4931
+ // @name ModuleRaid.js
4932
+ // @namespace http://tampermonkey.net/
4933
+ // @version 6.2.0
4934
+ // @description 检索调用webpackJsonp模块,可指定检索的window
4935
+ // @author empyrealtear
4936
+ // @license MIT
4937
+ // @original-script https://github.com/pixeldesu/moduleRaid
4938
+ // ==/UserScript==
4939
+ /**
4940
+ * Main moduleRaid class
4941
+ * @link https://scriptcat.org/zh-CN/script-show-page/2628
4942
+ */
4943
+ class ModuleRaid {
4944
+ /**
4945
+ * moduleRaid constructor
4946
+ *
4947
+ * @example
4948
+ * Constructing an instance without any arguments:
4949
+ * ```ts
4950
+ * const mR = new ModuleRaid()
4951
+ * ```
4952
+ *
4953
+ * Constructing an instance with the optional `opts` object:
4954
+ * ```ts
4955
+ * const mR = new ModuleRaid({ entrypoint: 'webpackChunk_custom_name' })
4956
+ * ```
4957
+ *
4958
+ * @param opts a object containing options to initialize moduleRaid with
4959
+ * - **opts:**
4960
+ * - _target_: the window object being searched for
4961
+ * - _entrypoint_: the Webpack entrypoint present on the global window object
4962
+ * - _debug_: whether debug mode is enabled or not
4963
+ * - _strict_: whether strict mode is enabled or not
4964
+ */
4965
+ constructor(opts) {
4966
+ /**
4967
+ * A random generated module ID we use for injecting into Webpack
4968
+ */
4969
+ this.moduleID = Math.random().toString(36).substring(7);
4970
+ /**
4971
+ * An array containing different argument injection methods for
4972
+ * Webpack (before version 4), and subsequently pulling out methods and modules
4973
+ * @internal
4974
+ */
4975
+ this.functionArguments = [
4976
+ [
4977
+ [0],
4978
+ [
4979
+ (_e, _t, i) => {
4980
+ this.modules = i.c;
4981
+ this.constructors = i.m;
4982
+ this.get = i;
4983
+ },
4984
+ ],
4985
+ ],
4986
+ [
4987
+ [1e3],
4988
+ {
4989
+ [this.moduleID]: (_e, _t, i) => {
4990
+ this.modules = i.c;
4991
+ this.constructors = i.m;
4992
+ this.get = i;
4993
+ },
4994
+ },
4995
+ [[this.moduleID]],
4996
+ ],
4997
+ ];
4998
+ /**
4999
+ * An array containing different argument injection methods for
5000
+ * Webpack (after version 4), and subsequently pulling out methods and modules
5001
+ * @internal
5002
+ */
5003
+ this.arrayArguments = [
5004
+ [
5005
+ [this.moduleID],
5006
+ {},
5007
+ (e) => {
5008
+ const mCac = e.m;
5009
+ Object.keys(mCac).forEach((mod) => {
5010
+ try {
5011
+ this.modules[mod] = e(mod);
5012
+ }
5013
+ catch (err) {
5014
+ this.log(`[arrayArguments/1] Failed to require(${mod}) with error:\n${err}\n${err.stack}`);
5015
+ }
5016
+ });
5017
+ this.get = e;
5018
+ },
5019
+ ],
5020
+ this.functionArguments[1],
5021
+ ];
5022
+ /**
5023
+ * Storage for the modules we extracted from Webpack
5024
+ */
5025
+ this.modules = {};
5026
+ /**
5027
+ * Storage for the constructors we extracted from Webpack
5028
+ */
5029
+ this.constructors = [];
5030
+ let options = {
5031
+ target: window,
5032
+ entrypoint: 'webpackJsonp',
5033
+ debug: false,
5034
+ strict: false,
5035
+ };
5036
+ if (typeof opts === 'object') {
5037
+ options = Object.assign(Object.assign({}, options), opts);
5038
+ }
5039
+ this.target = options.target;
5040
+ this.entrypoint = options.entrypoint;
5041
+ this.debug = options.debug;
5042
+ this.strict = options.strict;
5043
+ this.detectEntrypoint();
5044
+ this.fillModules();
5045
+ this.replaceGet();
5046
+ this.setupPushEvent();
5047
+ }
5048
+ /**
5049
+ * Debug logging method, outputs to the console when {@link ModuleRaid.debug} is true
5050
+ *
5051
+ * @param {*} message The message to be logged
5052
+ * @internal
5053
+ */
5054
+ log(message) {
5055
+ if (this.debug) {
5056
+ console.warn(`[moduleRaid] ${message}`);
5057
+ }
5058
+ }
5059
+ /**
5060
+ * Method to set an alternative getter if we weren't able to extract __webpack_require__
5061
+ * from Webpack
5062
+ * @internal
5063
+ */
5064
+ replaceGet() {
5065
+ if (this.get === null) {
5066
+ this.get = (key) => this.modules[key];
5067
+ }
5068
+ }
5069
+ /**
5070
+ * Method that will try to inject a module into Webpack or get modules
5071
+ * depending on it's success it might be more or less brute about it
5072
+ * @internal
5073
+ */
5074
+ fillModules() {
5075
+ if (typeof this.target[this.entrypoint] === 'function') {
5076
+ this.functionArguments.forEach((argument, index) => {
5077
+ try {
5078
+ if (this.modules && Object.keys(this.modules).length > 0)
5079
+ return;
5080
+ this.target[this.entrypoint](...argument);
5081
+ }
5082
+ catch (err) {
5083
+ this.log(`moduleRaid.functionArguments[${index}] failed:\n${err}\n${err.stack}`);
5084
+ }
5085
+ });
5086
+ }
5087
+ else {
5088
+ this.arrayArguments.forEach((argument, index) => {
5089
+ try {
5090
+ if (this.modules && Object.keys(this.modules).length > 0)
5091
+ return;
5092
+ this.target[this.entrypoint].push(argument);
5093
+ }
5094
+ catch (err) {
5095
+ this.log(`Pushing moduleRaid.arrayArguments[${index}] into ${this.entrypoint} failed:\n${err}\n${err.stack}`);
5096
+ }
5097
+ });
5098
+ }
5099
+ if (this.modules && Object.keys(this.modules).length == 0) {
5100
+ let moduleEnd = false;
5101
+ let moduleIterator = 0;
5102
+ if (typeof this.target[this.entrypoint] != 'function' || !this.target[this.entrypoint]([], [], [moduleIterator])) {
5103
+ throw Error('Unknown Webpack structure');
5104
+ }
5105
+ while (!moduleEnd) {
5106
+ try {
5107
+ this.modules[moduleIterator] = this.target[this.entrypoint]([], [], [moduleIterator]);
5108
+ moduleIterator++;
5109
+ }
5110
+ catch (err) {
5111
+ moduleEnd = true;
5112
+ }
5113
+ }
5114
+ }
5115
+ }
5116
+ /**
5117
+ * Method to hook into `window[this.entrypoint].push` adding a listener for new
5118
+ * chunks being pushed into Webpack
5119
+ *
5120
+ * @example
5121
+ * You can listen for newly pushed packages using the `moduleraid:webpack-push` event
5122
+ * on `document`
5123
+ *
5124
+ * ```ts
5125
+ * document.addEventListener('moduleraid:webpack-push', (e) => {
5126
+ * // e.detail contains the arguments push() was called with
5127
+ * console.log(e.detail)
5128
+ * })
5129
+ * ```
5130
+ * @internal
5131
+ */
5132
+ setupPushEvent() {
5133
+ const originalPush = this.target[this.entrypoint].push;
5134
+ this.target[this.entrypoint].push = (...args) => {
5135
+ const result = Reflect.apply(originalPush, this.target[this.entrypoint], args);
5136
+ document.dispatchEvent(new CustomEvent('moduleraid:webpack-push', { detail: args }));
5137
+ return result;
5138
+ };
5139
+ }
5140
+ /**
5141
+ * Method to try autodetecting a Webpack JSONP entrypoint based on common naming
5142
+ *
5143
+ * If the default entrypoint, or the entrypoint that's passed to the moduleRaid constructor
5144
+ * already matches, the method exits early
5145
+ *
5146
+ * If `options.strict` has been set in the constructor and the initial entrypoint cannot
5147
+ * be found, this method will error, demanding a strictly set entrypoint
5148
+ * @internal
5149
+ */
5150
+ detectEntrypoint() {
5151
+ if (this.target[this.entrypoint] != undefined) {
5152
+ return;
5153
+ }
5154
+ if (this.strict) {
5155
+ throw Error(`Strict mode is enabled and entrypoint at window.${this.entrypoint} couldn't be found. Please specify the correct one!`);
5156
+ }
5157
+ let windowObjects = Object.keys(this.target);
5158
+ windowObjects = windowObjects
5159
+ .filter((object) => object.toLowerCase().includes('chunk') || object.toLowerCase().includes('webpack'))
5160
+ .filter((object) => typeof this.target[object] === 'function' || Array.isArray(this.target[object]));
5161
+ if (windowObjects.length > 1) {
5162
+ throw Error(`Multiple possible endpoints have been detected, please create a new moduleRaid instance with a specific one:\n${windowObjects.join(', ')}`);
5163
+ }
5164
+ if (windowObjects.length === 0) {
5165
+ throw Error('No Webpack JSONP entrypoints could be detected');
5166
+ }
5167
+ this.log(`Entrypoint has been detected at window.${windowObjects[0]} and set for injection`);
5168
+ this.entrypoint = windowObjects[0];
5169
+ }
5170
+ /**
5171
+ * Recursive object-search function for modules
5172
+ *
5173
+ * @param object the object to search through
5174
+ * @param query the query the object keys/values are searched for
5175
+ * @returns boolean state of `object` containing `query` somewhere in it
5176
+ * @internal
5177
+ */
5178
+ searchObject(object, query) {
5179
+ for (const key in object) {
5180
+ const value = object[key];
5181
+ const lowerCaseQuery = query.toLowerCase();
5182
+ if (typeof value != 'object') {
5183
+ const lowerCaseKey = key.toString().toLowerCase();
5184
+ if (lowerCaseKey.includes(lowerCaseQuery))
5185
+ return true;
5186
+ if (typeof value != 'object') {
5187
+ const lowerCaseValue = value.toString().toLowerCase();
5188
+ if (lowerCaseValue.includes(lowerCaseQuery))
5189
+ return true;
5190
+ }
5191
+ else {
5192
+ if (this.searchObject(value, query))
5193
+ return true;
5194
+ }
5195
+ }
5196
+ }
5197
+ return false;
5198
+ }
5199
+ /**
5200
+ * Method to search through the module object, searching for the fitting content
5201
+ * if a string is supplied
5202
+ *
5203
+ * If query is supplied as a function, everything that returns true when passed
5204
+ * to the query function will be returned
5205
+ *
5206
+ * @example
5207
+ * With a string as query argument:
5208
+ * ```ts
5209
+ * const results = mR.findModule('feature')
5210
+ * // => Array of module results
5211
+ * ```
5212
+ *
5213
+ * With a function as query argument:
5214
+ * ```ts
5215
+ * const results = mR.findModule((module) => { typeof module === 'function' })
5216
+ * // => Array of module results
5217
+ * ```
5218
+ *
5219
+ * @param query query to search the module list for
5220
+ * @return a list of modules fitting the query
5221
+ */
5222
+ findModule(query) {
5223
+ const results = [];
5224
+ const modules = Object.keys(this.modules);
5225
+ if (modules.length === 0) {
5226
+ throw new Error('There are no modules to search through!');
5227
+ }
5228
+ modules.forEach((key) => {
5229
+ const module = this.modules[key.toString()];
5230
+ if (module === undefined)
5231
+ return;
5232
+ try {
5233
+ if (typeof query === 'string') {
5234
+ query = query.toLowerCase();
5235
+ switch (typeof module) {
5236
+ case 'string':
5237
+ if (module.toLowerCase().includes(query))
5238
+ results.push(module);
5239
+ break;
5240
+ case 'function':
5241
+ if (module.toString().toLowerCase().includes(query))
5242
+ results.push(module);
5243
+ break;
5244
+ case 'object':
5245
+ if (this.searchObject(module, query))
5246
+ results.push(module);
5247
+ break;
5248
+ }
5249
+ }
5250
+ else if (typeof query === 'function') {
5251
+ if (query(module))
5252
+ results.push(module);
5253
+ }
5254
+ else {
5255
+ throw new TypeError(`findModule can only find via string and function, ${typeof query} was passed`);
5256
+ }
5257
+ }
5258
+ catch (err) {
5259
+ this.log(`There was an error while searching through module '${key}':\n${err}\n${err.stack}`);
5260
+ }
5261
+ });
5262
+ return results;
5263
+ }
5264
+ /**
5265
+ * Method to search through the constructor array, searching for the fitting content
5266
+ * if a string is supplied
5267
+ *
5268
+ * If query is supplied as a function, everything that returns true when passed
5269
+ * to the query function will be returned
5270
+ *
5271
+ * @example
5272
+ * With a string as query argument:
5273
+ * ```ts
5274
+ * const results = mR.findConstructor('feature')
5275
+ * // => Array of constructor/module tuples
5276
+ * ```
5277
+ *
5278
+ * With a function as query argument:
5279
+ * ```ts
5280
+ * const results = mR.findConstructor((constructor) => { constructor.prototype.value !== undefined })
5281
+ * // => Array of constructor/module tuples
5282
+ * ```
5283
+ *
5284
+ * Accessing the resulting data:
5285
+ * ```ts
5286
+ * // With array destructuring (ES6)
5287
+ * const [constructor, module] = results[0]
5288
+ *
5289
+ * // ...or...
5290
+ *
5291
+ * // regular access
5292
+ * const constructor = results[0][0]
5293
+ * const module = results[0][1]
5294
+ * ```
5295
+ *
5296
+ * @param query query to search the constructor list for
5297
+ * @returns a list of constructor/module tuples fitting the query
5298
+ */
5299
+ findConstructor(query) {
5300
+ const results = [];
5301
+ const constructors = Object.keys(this.constructors);
5302
+ if (constructors.length === 0) {
5303
+ throw new Error('There are no constructors to search through!');
5304
+ }
5305
+ constructors.forEach((key) => {
5306
+ const constructor = this.constructors[key];
5307
+ try {
5308
+ if (typeof query === 'string') {
5309
+ query = query.toLowerCase();
5310
+ if (constructor.toString().toLowerCase().includes(query))
5311
+ results.push([this.constructors[key], this.modules[key]]);
5312
+ }
5313
+ else if (typeof query === 'function') {
5314
+ if (query(constructor))
5315
+ results.push([this.constructors[key], this.modules[key]]);
5316
+ }
5317
+ }
5318
+ catch (err) {
5319
+ this.log(`There was an error while searching through constructor '${key}':\n${err}\n${err.stack}`);
5320
+ }
5321
+ });
5322
+ return results;
5323
+ }
5332
5324
  }
5333
5325
 
5334
5326
  class DOMUtils {
@@ -5500,7 +5492,7 @@ class DOMUtils {
5500
5492
  }
5501
5493
  const domUtils = new DOMUtils();
5502
5494
 
5503
- const version = "2.9.4";
5495
+ const version = "2.9.6";
5504
5496
 
5505
5497
  class Utils {
5506
5498
  windowApi;