ChatGPT 助手

支持降智检测(IP 使用情况风险判断)以及跳转 ChatGPT 镜像站的语音功能到 LiveKit Meet 而不是镜像站的 LiveKit Meet。

目前为 2024-11-28 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name ChatGPT 助手
  3. // @author Hmjz100
  4. // @namespace github.com/hmjz100
  5. // @version 1.0.3
  6. // @description 支持降智检测(IP 使用情况风险判断)以及跳转 ChatGPT 镜像站的语音功能到 LiveKit Meet 而不是镜像站的 LiveKit Meet。
  7. // @icon data:image/webp;base64,UklGRnICAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSI8AAAABd6A2kg06/T5EcqGIzkREILWfPrI5RTitte1pXprvniGwKHzbgMMgjMAkHAaB9HhUevJ9U+T/6T6i/wzctm0kde/27hVA1LJgHQAoiCX2Hvw3S62UnAc0rkOEtyHir7C+QPMbSg48lz0vKU1fl0ZUNyHRH8Pf4Tt0/Wb4Ow5/53QI3XzKKRVk8v/IxeB/BgBWUDggvAEAABALAJ0BKiAAIAA+aSqQRaQioZv6rABABoS0gAnAHJxnoP3rMm/kBkKFPL/yvok/1/878zXyp/g/cC/jv9A/x35fdjN7Hf7ANdIJ5yn1lcylRWD9msH+g4lCVG3DwlB27AAA/v/b7x/k//Og4rBakFx39Od09+fJ1x4C+DLOrn57IHc1Tex5U/5cB8l2f3fZBiD+5/7/IowL/9h145ee+E8Tn9Mx//ya2jMrsF0MWumdxn7zRJ7f7BEicBZVfKYEwPy/e9nUZPOkR9QyW1vCwpKLT4HeJVe7ayZ4dcQRfzMR0TeGbvw8U5I+jHciOCqPs43iwvLybOhARP2A/54Jx8B2XKJ3NgHNzVi3W2HkTpisnFaSV2NcPAhURRCHCp9wAwR12+PxPlPateeyuKpswYl1DEzNt/SkXzDPkSq/LyuOSsHuqlcmY8ch6wMlzG7JlrkvSIL3kuB6BS6sByZZXCNqJoa/4vqVhG8NdvpsjyX6hpz5DqxYMDrvqnl1//oedvi9KU2f/8I6YzFjiLYx5bH31D5eTMZBT8kqCpoNVnHwfQz5KqJRnU9pk4JVJRwRO0gt0GsehqDyZAAAAA==
  8. // @license MIT
  9. // @match *://chatgpt.com/*
  10. // @match *://chat.openai.com/*
  11. // @match *://*.oaifree.com/*
  12. // @match *://chat.rawchat.top/*
  13. // @match *://chat.rawchat.cc/*
  14. // @match *://chat.sharedchat.cn/*
  15. // @match *://chat.chatgptplus.cn/*
  16. // @match *://free.share-ai.top/*
  17. // @match *://gpt.github.cn.com/*
  18. // @match *://share.github.cn.com/*
  19. // @match *://go.gptdie.com/*
  20. // @match *://chat.gptdie.com/*
  21. // @match *://free.xyhelper.cn/*
  22. // @match *://*.xyhelper.com.cn/*
  23. // @match *://chat.freegpts.org/*
  24. // @match *://go.gptdsb.com/*
  25. // @match *://chat.gptdsb.com/*
  26. // @match *://www.opkfc.com/*
  27. // @match *://chatgpt.dairoot.cn/*
  28. // @match *://web.tu-zi.com/*
  29. // @match *://share.tu-zi.com/*
  30. // @grant GM_setValue
  31. // @grant GM_getValue
  32. // @grant GM_openInTab
  33. // @grant unsafeWindow
  34. // @run-at document-start
  35. // @require https://unpkg.com/jquery@3.6.3/dist/jquery.min.js
  36. // ==/UserScript==
  37.  
  38. (function () {
  39. 'use strict';
  40. // 非常有意思的不知道是什么模型的网站
  41. // chatgptchatapp.com
  42.  
  43. // 监听 fetch 请求的响应
  44. const originalFetch = window.fetch;
  45. unsafeWindow.fetch = async function (url, options) {
  46. try {
  47. const response = await originalFetch(url, options);
  48. // 检测是否是目标 API 请求
  49. if (url.includes('sentinel/chat-requirements') && options.method === 'POST') {
  50. let res = await response.clone().text();
  51. try {
  52. res = JSON.parse(res);
  53. } catch (e) { }
  54. console.log('风险数据: \n', res);
  55.  
  56. const difficulty = res?.proofofwork?.difficulty ? res?.proofofwork?.difficulty : 'N/A';
  57. updateDifficultyIndicator(difficulty);
  58. }
  59. return response;
  60. } catch (error) {
  61. throw error;
  62. }
  63. };
  64.  
  65. // 更新难易度指示器
  66. function updateDifficultyIndicator(difficulty) {
  67. const result = difficulty === 'N/A' ? {
  68. color: '#888', secondaryColor: '#666', textColor: '#888', level: '未知', detail: '服务器未提供 PoW 难度值,可能是因为当前站点不是镜像站点\n(即使网站的 UI 界面与官方相似)'
  69. } : (() => {
  70. const hexValue = difficulty.replace('0x', '').replace(/^0+/, '');
  71. const hexLength = hexValue.length;
  72. const decimalValue = parseInt(hexValue, 16); // 转换为十进制数值
  73. const percentage = Math.min(decimalValue / 0x00FFFF * 100, 100).toFixed(2); // 精确到小数点后两位
  74. console.log(difficulty, hexLength, decimalValue, percentage)
  75. if (hexLength <= 2) return { color: '#F44336', secondaryColor: '#d32f2f', level: '困难', detail: `PoW${difficulty} (${percentage}%),高级模型以及功能可能无法正常使用。` };
  76. if (hexLength === 3) return { color: '#FFC107', secondaryColor: '#ffa000', level: '中等', detail: `PoW${difficulty} (${percentage}%),可能会影响部分高级功能。` };
  77. if (hexLength === 4) return { color: '#8BC34A', secondaryColor: '#689f38', level: '简单', detail: `PoW${difficulty} (${percentage}%),可正常使用 ChatGPT。` };
  78. return { color: '#4CAF50', secondaryColor: '#388e3c', level: '极易', detail: `PoW${difficulty} (${percentage}%),可舒适使用 ChatGPT。` };
  79. })();
  80.  
  81. // 更新 UI
  82. $('#ChatGPTPow-Button svg defs linearGradient stop[offset="0%"]').css('stop-color', result.color);
  83. $('#ChatGPTPow-Button svg defs linearGradient stop[offset="100%"]').css('stop-color', result.secondaryColor);
  84. $('#ChatGPTPow-Button span').text(result.level);
  85. $('#ChatGPTPow-Button').attr('title', result.detail);
  86. $('#ChatGPTPow-Button')[0].style.background = `linear-gradient(140.91deg, ${result.color} 12.61%, ${result.secondaryColor} 76.89%)`;
  87. }
  88.  
  89. // 隐藏原来的按钮
  90. waitForKeyElements('div:not(#ChatGPTVoice-On-LiveKitMeet-Button, #immersive-translate-popup) svg.icon[width="25"][height="25"], div#voiceButton svg, #of-custom-floating-ball svg, div > div#livekit', function (element) {
  91. element.parent().hide();
  92. });
  93.  
  94. waitForKeyElements("body main div.mb-7.text-center, div.btn-voice, body main div.flex-shrink-0 svg", function (element) {
  95. if (element.hasClass("voice")) return;
  96.  
  97. let checkOpacity = function () {
  98. if (element.hasClass("mb-7")) {
  99. if (element.find("h1.result-streaming").css('opacity') == '0') {
  100. let clone = element.clone(true);
  101. clone.addClass("voice")
  102. element.replaceWith(clone);
  103.  
  104. clone.css({
  105. 'cursor': 'pointer',
  106. 'user-select': 'none',
  107. '-webkit-user-select': 'none',
  108. '-ms-user-select': 'none',
  109. '-moz-user-select': 'none'
  110. });
  111.  
  112. clone.on('click touchend', handleVoiceClick);
  113. } else {
  114. setTimeout(checkOpacity, 100);
  115. }
  116. } else {
  117. element.addClass("voice")
  118.  
  119. element.css({
  120. 'cursor': 'pointer',
  121. 'user-select': 'none',
  122. '-webkit-user-select': 'none',
  123. '-ms-user-select': 'none',
  124. '-moz-user-select': 'none'
  125. });
  126. element.on('click touchend', handleVoiceClick);
  127. }
  128. };
  129.  
  130. // 初次调用检查函数
  131. checkOpacity();
  132. });
  133.  
  134. let html = $(`<div id="ChatGPTVoice-On-LiveKitMeet">
  135. <div id="ChatGPTVoice-On-LiveKitMeet-Button">
  136. <svg width="25" height="25" class="icon" fill="none" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg">
  137. <path d="M18 12C18 15.3137 15.3137 18 12 18M12 18C8.68629 18 6 15.3137 6 12M12 18V21M12 21H15M12 21H9M15 6V12C15 13.6569 13.6569 15 12 15C10.3431 15 9 13.6569 9 12V6C9 4.34315 10.3431 3 12 3C13.6569 3 15 4.34315 15 6Z" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
  138. </svg>
  139. <span>语音</span>
  140. </div>
  141. <div id="ChatGPTPow-Button" style="background: linear-gradient(140.91deg, #2ecc71 12.61%, #3498db 76.89%);" title="等待站点请求中...">
  142. <svg width="25" height="25" class="icon" fill="none" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" style="transition: all 0.3s ease;">
  143. <defs>
  144. <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
  145. <stop offset="0%" style="stop-color:#3498db;stop-opacity:1"></stop>
  146. <stop offset="100%" style="stop-color:#2ecc71;stop-opacity:1"></stop>
  147. </linearGradient>
  148. <filter id="glow">
  149. <feGaussianBlur stdDeviation="2" result="coloredBlur"></feGaussianBlur>
  150. <feMerge>
  151. <feMergeNode in="coloredBlur"></feMergeNode>
  152. <feMergeNode in="SourceGraphic"></feMergeNode>
  153. </feMerge>
  154. </filter>
  155. </defs>
  156. <g id="icon-group" filter="url(#glow)">
  157. <circle cx="32" cy="32" r="28" fill="url(#gradient)" stroke="#fff" stroke-width="2"></circle>
  158. <circle cx="32" cy="32" r="20" fill="none" stroke="#fff" stroke-width="2" stroke-dasharray="100">
  159. <animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 32 32" to="360 32 32" dur="8s" repeatCount="indefinite"></animateTransform>
  160. </circle>
  161. <circle cx="32" cy="32" r="12" fill="none" stroke="#fff" stroke-width="2">
  162. <animate attributeName="r" values="12;14;12" dur="2s" repeatCount="indefinite"></animate>
  163. </circle>
  164. <circle id="center-dot" cx="32" cy="32" r="4" fill="#fff">
  165. <animate attributeName="r" values="4;6;4" dur="2s" repeatCount="indefinite"></animate>
  166. </circle>
  167. </g>
  168. </svg>
  169. <span>状态</span>
  170. </div>
  171. <style id="ChatGPTVoice-On-LiveKitMeet-Style">
  172. div#ChatGPTVoice-On-LiveKitMeet-Button, div#ChatGPTPow-Button {
  173. border-top-left-radius: 34px;
  174. border-bottom-left-radius: 34px;
  175. background: linear-gradient(140.91deg, #7367F0 12.61%, #574AB8 76.89%);
  176. height: 34px;
  177. width: 80px;
  178. margin: 1px;
  179. display: flex !important;
  180. align-items: center;
  181. position: fixed;
  182. right: -35px;
  183. top: calc(80% - 34px);
  184. cursor: pointer;
  185. padding-left: 7px;
  186. z-index: 114514;
  187. opacity: 0.75;
  188. transition: right 0.3s, opacity 0.3s !important;
  189. }
  190. div#ChatGPTVoice-On-LiveKitMeet-Button:hover,
  191. div#ChatGPTVoice-On-LiveKitMeet-Button.is-dragging,
  192. div#ChatGPTPow-Button:hover,
  193. div#ChatGPTPow-Button.is-dragging {
  194. right: -5px;
  195. opacity: 1;
  196. }
  197. div#ChatGPTVoice-On-LiveKitMeet-Button span,
  198. div#ChatGPTPow-Button span {
  199. color:#ffffff;
  200. font-size:15px;
  201. margin-left:3px;
  202. white-space: nowrap;
  203. }
  204. </style>
  205. </div>`)
  206.  
  207. let button = html.find('#ChatGPTVoice-On-LiveKitMeet-Button');
  208. let isDragging = false;
  209. let offsetY = 0;
  210. let dragStartTime;
  211. button.css('z-index', 114514 + 1)
  212.  
  213. // 从 GM 获取按钮位置
  214. if (GM_getValue('buttonTop')) {
  215. button.css('top', GM_getValue('buttonTop') + 'px');
  216. } else {
  217. button.css('top', 'calc(30% - 34px)');
  218. }
  219.  
  220. // 点击事件处理
  221. button.on('click touchend', handleVoiceClick);
  222.  
  223. // 鼠标按下事件
  224. button.on('mousedown touchstart', function (e) {
  225. e.preventDefault();
  226. dragStartTime = Date.now(); // 记录拖动开始时间
  227. offsetY = e.clientY - button.offset().top;
  228. });
  229.  
  230. // 鼠标移动事件
  231. $(document).on('mousemove touchmove', function (e) {
  232. if (offsetY !== undefined) {
  233. let newTop = e.clientY - offsetY;
  234. const buttonHeight = button.outerHeight();
  235. const windowHeight = $(window).height();
  236.  
  237. // 限制按钮位置
  238. if (newTop < 0) newTop = 0;
  239. if (newTop + buttonHeight > windowHeight) newTop = windowHeight - buttonHeight;
  240.  
  241. // 判断是否拖动
  242. if (isDragging || (Date.now() - dragStartTime > 100)) { // 如果已经拖动或拖动时间超过100ms
  243. isDragging = true;
  244. button.addClass('is-dragging');
  245. button.css('top', newTop + 'px');
  246. GM_setValue('buttonTop', newTop);
  247. }
  248. }
  249. });
  250.  
  251. // 鼠标抬起事件
  252. $(document).on('mouseup touchend', function () {
  253. if (isDragging) {
  254. setTimeout(function () {
  255. isDragging = false;
  256. button.removeClass('is-dragging');
  257. }, 100)
  258. }
  259. offsetY = undefined; // 重置 offsetY
  260. });
  261.  
  262. setInterval(function () {
  263. if (!$('#ChatGPTVoice-On-LiveKitMeet-Button').length || !$('#ChatGPTVoice-On-LiveKitMeet-Style').length) {
  264. $('#ChatGPTVoice-On-LiveKitMeet').remove()
  265. $('body').append(html);
  266. }
  267. }, 500)
  268.  
  269. // 绑定点击事件到新创建的按钮
  270. async function handleVoiceClick(event) {
  271. if (!event?.currentTarget || isDragging) return;
  272. let element = $(event.currentTarget);
  273. if (element.attr('data-clicked') === 'true') return;
  274. element.attr('data-clicked', 'true');
  275.  
  276. // 异步获取语音链接
  277. await goVoice(element).catch(function (error) {
  278. alert('获取语音对话(会议)链接错误: \n' + error.message);
  279. console.error(error);
  280. element.removeAttr('data-clicked');
  281. });
  282. };
  283.  
  284. async function goVoice(element) {
  285. // 定义不同服务器的配置
  286. let servers = {
  287. "new.oaifree.com": {
  288. apiPath: "/api/voice/link",
  289. apiType: "POST",
  290. url: "wss://webrtc.oaifree.com",
  291. model: new URL(location.href).searchParams.get('model'),
  292. mode: [['标准语音', '高级语音'], ['std', 'adv']],
  293. getToken: data => new URL(data.url).searchParams.get('token'),
  294. getHash: data => new URL(data.url).hash
  295. },
  296. "chat.rawchat.top": {
  297. apiPath: "/backend-api/voice_token",
  298. apiType: "GET",
  299. url: data => data.url,
  300. getToken: data => data.token,
  301. getHash: data => data.e2ee_key
  302. },
  303. "chat.rawchat.cc": {
  304. apiPath: "/backend-api/voice_token",
  305. apiType: "GET",
  306. url: data => data.url,
  307. getToken: data => data.token,
  308. getHash: data => data.e2ee_key
  309. },
  310. "chat.sharedchat.cn": {
  311. apiPath: "/backend-api/voice_token",
  312. apiType: "GET",
  313. url: data => data.url,
  314. getToken: data => data.token,
  315. getHash: data => data.e2ee_key
  316. },
  317. "chat.chatgptplus.cn": {
  318. apiPath: "/backend-api/voice_token",
  319. apiType: "GET",
  320. url: data => data.url,
  321. getToken: data => data.token,
  322. getHash: data => data.e2ee_key
  323. },
  324. "free.share-ai.top": {
  325. apiPath: "/frontend-api/getVoice",
  326. apiType: "GET",
  327. url: data => (data.data.voiceServerUrl || data.data.url),
  328. getToken: data => data.data.token,
  329. getHash: data => data.data.e2ee_key
  330. },
  331. "gpt.github.cn.com": {
  332. apiPath: "/backend-api/voice_token",
  333. apiType: "GET",
  334. url: data => data.url,
  335. getToken: data => data.token,
  336. getHash: data => data.e2ee_key
  337. },
  338. "share.github.cn.com": {
  339. apiPath: "/backend-api/voice_token",
  340. apiType: "GET",
  341. url: data => data.url,
  342. getToken: data => data.token,
  343. getHash: data => data.e2ee_key
  344. },
  345. "go.gptdie.com": {
  346. apiPath: "/backend-api/voice_token",
  347. apiType: "GET",
  348. url: data => data.url,
  349. getToken: data => data.token,
  350. getHash: data => data.e2ee_key
  351. },
  352. "chat.gptdie.com": {
  353. apiPath: "/backend-api/voice_token",
  354. apiType: "GET",
  355. url: data => data.url,
  356. getToken: data => data.token,
  357. getHash: data => data.e2ee_key
  358. },
  359. "free.xyhelper.cn": {
  360. apiPath: "/backend-api/voice_token",
  361. apiType: "GET",
  362. url: data => data.url,
  363. getToken: data => data.token,
  364. getHash: data => data.e2ee_key
  365. },
  366. "free.xyhelper.com.cn": {
  367. apiPath: "/backend-api/voice_token",
  368. apiType: "GET",
  369. url: data => data.url,
  370. getToken: data => data.token,
  371. getHash: data => data.e2ee_key
  372. },
  373. "chat.freegpts.org": {
  374. apiPath: "/backend-api/voice_token",
  375. apiType: "GET",
  376. url: data => data.url,
  377. getToken: data => data.token,
  378. getHash: data => data.e2ee_key
  379. },
  380. "go.gptdsb.com": {
  381. apiPath: "/backend-api/voice_token",
  382. apiType: "GET",
  383. url: data => data.url,
  384. getToken: data => data.token,
  385. getHash: data => data.e2ee_key
  386. },
  387. "chat.gptdsb.com": {
  388. apiPath: "/backend-api/voice_token",
  389. apiType: "GET",
  390. url: data => data.url,
  391. getToken: data => data.token,
  392. getHash: data => data.e2ee_key
  393. },
  394. "www.opkfc.com": {
  395. apiPath: "/backend-api/voice_token",
  396. apiType: "GET",
  397. url: data => data.url,
  398. getToken: data => data.token,
  399. getHash: data => data.e2ee_key
  400. },
  401. "chatgpt.dairoot.cn": {
  402. apiPath: "/api/livekit",
  403. apiType: "GET",
  404. url: data => new URL(data.data).searchParams.get('liveKitUrl'),
  405. getToken: data => new URL(data.data).searchParams.get('token'),
  406. getHash: data => new URL(data.data).hash
  407. },
  408. "web.tu-zi.com": {
  409. apiPath: "/backend-api/voice_token",
  410. apiType: "GET",
  411. url: data => data.url,
  412. getToken: data => data.token,
  413. getHash: data => data.e2ee_key
  414. },
  415. "share.tu-zi.com": {
  416. apiPath: "/backend-api/voice_token",
  417. apiType: "GET",
  418. url: data => data.url,
  419. getToken: data => data.token,
  420. getHash: data => data.e2ee_key
  421. },
  422. };
  423.  
  424. // 获取当前服务器的域名
  425. let host = location.hostname;
  426.  
  427. // 获取服务器配置
  428. let config = servers[host];
  429. if (!config) {
  430. throw new Error(`未支持当前站点: ${host}`);
  431. }
  432.  
  433. let extra = {
  434. method: config.apiType,
  435. headers: { 'Content-Type': 'application/json' }
  436. }
  437.  
  438. if (config.model !== undefined && config.mode !== undefined && config.apiType === 'POST') {
  439. let model = config.model;
  440. let mode = config.mode;
  441.  
  442. let modeChoice;
  443. if (mode && mode.length) {
  444. let modeOptions = mode[0]
  445. .map((name, index) => `(${index + 1}) ${name}`)
  446. .join(" ");
  447. let userChoice = prompt(`请选择语音模式: (不输入则使用${mode[0][0]})\n${modeOptions}`);
  448.  
  449. let choiceIndex = parseInt(userChoice) - 1;
  450. if (choiceIndex >= 0 && choiceIndex < mode[1].length) {
  451. modeChoice = mode[1][choiceIndex];
  452. } else if (userChoice === null) {
  453. return element.removeAttr('data-clicked');
  454. } else {
  455. modeChoice = mode[1][0];
  456. }
  457. }
  458.  
  459. if (!model) {
  460. let userInput = prompt("请输入模型名称: (不输入则使用默认模型)");
  461. if (userInput === null) {
  462. return element.removeAttr('data-clicked');
  463. }
  464. model = userInput;
  465. }
  466.  
  467. extra.body = JSON.stringify({ model, mode: modeChoice });
  468. }
  469.  
  470. // 发送请求到语音API
  471. let response = await unsafeWindow.fetch(config.apiPath, extra);
  472.  
  473. // 解析返回的JSON数据
  474. let data = await response.json();
  475. console.log('服务数据: \n', data);
  476.  
  477. // 检查返回的模式,如果是高级模式,修改颜色
  478. if (data.mode === "advanced") {
  479. element.css('color', '#f00');
  480. }
  481.  
  482. // 检查是否有url或者token,否则抛出错误
  483. function hasUrl(obj) {
  484. if (obj && typeof obj === 'object') {
  485. if ('url' in obj) return true; // 如果当前对象包含 url 属性,返回 true
  486. if ('data' in obj) return true; // 如果当前对象包含 url 属性,返回 true
  487. return Object.values(obj).some(hasUrl); // 递归检查嵌套的对象
  488. }
  489. return false;
  490. }
  491.  
  492. if (data && !hasUrl(data)) {
  493. throw new Error(data.detail || '语音服务未返回所需数据');
  494. }
  495.  
  496. // 获取url、token、hash
  497. let url = typeof config.url === 'function' ? config.url(data) : config.url;
  498. let token = config.getToken ? config.getToken(data) : null;
  499. let hash = config.getHash ? config.getHash(data) : null;
  500.  
  501. // 打印日志方便调试
  502. console.log('会议数据: \n', { token, hash, url });
  503.  
  504. // 检查是否有url或者token,否则抛出错误
  505. if (!url || !token || !hash) throw new Error(data.detail || '语音服务未返回数据');
  506.  
  507. // 构建 meetUrl
  508. let meetUrl = new URL('https://meet.livekit.io/custom');
  509. if (url) meetUrl.searchParams.set('liveKitUrl', url);
  510. if (token) meetUrl.searchParams.set('token', token);
  511. if (hash) meetUrl.hash = hash;
  512.  
  513. // 打开新页面
  514. GM_openInTab(meetUrl.href, { active: true, insert: true, setParent: true })
  515. element.removeAttr('data-clicked');
  516. }
  517.  
  518. function waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector) {
  519. function findInShadowRoots(root, selector) {
  520. let elements = $(root).find(selector).toArray();
  521. $(root).find('*').each(function () {
  522. let shadowRoot = this.shadowRoot;
  523. if (shadowRoot) {
  524. elements = elements.concat(findInShadowRoots(shadowRoot, selector));
  525. }
  526. });
  527. return elements;
  528. }
  529. var targetElements;
  530. if (iframeSelector) {
  531. targetElements = $(iframeSelector).contents();
  532. } else {
  533. targetElements = $(document);
  534. }
  535. let allElements = findInShadowRoots(targetElements, selectorTxt);
  536. if (allElements.length > 0) {
  537. allElements.forEach(function (element) {
  538. var jThis = $(element);
  539. var uniqueIdentifier = 'alreadyFound';
  540. var alreadyFound = jThis.data(uniqueIdentifier) || false;
  541. if (!alreadyFound) {
  542. var cancelFound = actionFunction(jThis);
  543. if (cancelFound) {
  544. return false;
  545. } else {
  546. jThis.data(uniqueIdentifier, true);
  547. }
  548. }
  549. });
  550. }
  551. var controlObj = waitForKeyElements.controlObj || {};
  552. var controlKey = selectorTxt.replace(/[^\w]/g, "_");
  553. var timeControl = controlObj[controlKey];
  554. if (allElements.length > 0 && bWaitOnce && timeControl) {
  555. clearInterval(timeControl);
  556. delete controlObj[controlKey];
  557. } else {
  558. if (!timeControl) {
  559. timeControl = setInterval(function () {
  560. waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector);
  561. }, 1000);
  562. controlObj[controlKey] = timeControl;
  563. }
  564. }
  565. waitForKeyElements.controlObj = controlObj;
  566. }
  567. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址