1/** 2 * çæä¸ä¸ªæå®åºé´ æå®æ¥é¿æ°å¼ççå·®list 3 * @param start listçå·¦åºé´ 4 * @param end listçå³åºé´ 5 * @param step æ¥é¿æ°å¼ 6 * @returns {*[]} çå·®æ°å 7 */ 8function range(start, end, step) { 9 const res = []; 10 let index = start; 11 while (index < end) { 12 res.push(index); 13 index += step; 14 } 15 return res; 16} 17 18// åå¤urlè·¯å¾æåæéè¦ æ£å对象 19const urlPathReg1 = new RegExp("\\?"); 20const urlPathReg2 = new RegExp("/+"); 21 22/** 23 * å°å½åç url è¿è¡æå 24 * @returns {{pram: string, url: string[]}} {"url" :[urlåè®®ï¼url主æºï¼è·¯å¾1ï¼è·¯å¾2ï¼.... ]ï¼ pram :"åæ°å符串"} 25 */ 26function parseUrl() { 27 // é¦å è·åå°urlå°å 28 const url = window.location.href; 29 // è·åå° è¯·æ±æ°æ®é¨å [urlåè·¯å¾ï¼åæ°1ï¼åæ°2] 30 const strings1 = url.split(urlPathReg1); 31 // ç¶åæç §è§åè·åå°{"url" :[url请æ±é¨åï¼è·¯å¾1ï¼è·¯å¾2ï¼.... ]ï¼ pram :"åæ°å符串"} 32 return {"url": strings1[0].split(urlPathReg2), "pram": strings1[1]}; 33} 34 35/** 36 * å°å½åç url é¨åä¸çservlet请æ±é¨åè¿è¡æ¿æ¢ 37 * @param servlet_str æå¡ç±»åç¬¦ä¸²æ°æ® 38 * @param prams é¢å¤è¿½å çåæ°å符串 39 * @returns {string} 转æ¢ä¹åçæ°å符串 40 */ 41function setUrlServlet(servlet_str, prams) { 42 const d = parseUrl(); 43 let res = d['url'][0] + '/' + d['url'][1] + '/' + servlet_str + '?'; 44 const p = d['pram']; 45 if (!(p == null || p.length === 0)) { 46 res += p; 47 res += '&'; 48 } 49 if (prams != null) { 50 for (let pram of prams) { 51 res += prams; 52 } 53 } 54 return res; 55} 56 57/** 58 * 夿ä¸ä¸ªå¯¹è±¡æ¯å¦å¯ä»¥è¿è¡è¿ä»£ 59 * @param obj éè¦è¢«å¤æç对象 60 * @returns {boolean} 妿å¯è¿ä»£åè¿å true 61 */ 62function isIterable(obj) { 63 return typeof obj === 'object' && obj !== null && !Array.isArray(obj) && typeof obj[Symbol.iterator] === 'function'; 64} 65 66function isJson(obj) { 67 return obj !== null && obj instanceof Object && typeof obj === 'object'; 68} 69 70/** 71 * å°ä¸ä¸ª json å符串ä¸çæ°æ®æç §æå®ç keys è·å 72 * @param jsonStr jsonå符串 73 * @param showKeys éè¦è¢«æåçå¼å¯¹åºçè·¯å¾ 74 * @returns {any} 75 */ 76function jsonShow(jsonStr, showKeys) { 77 // é¦å å°å符串转æ¢ä¸º json ç¶åè·å key 对åºçvalue 78 let res = JSON.parse(jsonStr); 79 for (let name in showKeys) { 80 res = res[name]; 81 } 82 return res; 83} 84 85/** 86 * 87 * @param jsonString éè¦è¢«æ ¼å¼åç json å符串 88 * @returns {string} æ ¼å¼å好çjsonå符串 89 */ 90function formatJson(jsonString) { 91 if (isJson(jsonString)) { 92 return JSON.stringify(jsonString, null, 2); 93 } 94 const jsonObj = JSON.parse(jsonString); 95 return JSON.stringify(jsonObj, null, 2); 96} 97 98/** 99 * å°urlä¸ç params ä¸çåç¬¦ä¸²éæ°è®¾ç½® 100 * @param params åæ° 101 */ 102function setParamToHref(params) { 103 // è·åå½åå°åæ çURL 104 let currentUrl = window.location.href; 105 // æç § ? æåä¹å éæ°è®¾ç½®åæ° 106 window.location.href = currentUrl.split('?')[0] + '?' + params; 107} 108 109/** 110 * æç´¢URLä¸çæå®çåæ° 111 * @return æç´¢å°çåååæ°ç»åç list 112 */ 113function search_Params(paramName) { 114 // å建ä¸ä¸ª URLSearchParams 对象 115 const params = new URLSearchParams(window.location.search); 116 // è·åå为 "paramName" çåæ°å¼ 117 const paramValues = params.getAll(paramName); 118 // å°åæ°å¼å°è£ 为å表 119 return Array.from(paramValues); 120} 121 122/** 123 * è·åå½å页é¢çè·¯å¾ 124 * @return {string[]} è¿åå½å页é¢çè·¯å¾ 125 */ 126function search_pathFromUrl() { 127 const search = window.location.search; 128 if (search.length === 0) { 129 return []; 130 } 131 return search.split('?')[0].split('/'); 132} 133 134/** 135 * æ£æ¥ä¸ä¸ªå符串æ¯å¦æ¯ä¸ä¸ªæ°å¼ 136 * @param str éè¦è¢«æ£æ¥çå符串 137 * @return {boolean} 妿å符串ä¸ä¸ºæ°å¼å½¢å¼ï¼åè¿å true 138 */ 139function isNumericString(str) { 140 // ä½¿ç¨ parseFloat å°è¯å°å符串转æ¢ä¸ºæ°å¼ 141 const num = parseFloat(str); 142 143 // æ£æ¥æ¯å¦ä¸º NaNï¼å¦æä¸º NaNï¼å说æä¸æ¯æ°å¼ 144 return !isNaN(num); 145} 146 147/** 148 * å¤æä¸¤ä¸ª json 对象æ¯å¦ä¸è´ 149 * @param j1 è¢«å¤æç第ä¸ä¸ª json 对象 150 * @param j2 è¢«å¤æç第äºä¸ª json 对象 151 * @return {boolean} 妿 j1 ä¸ç ææ key åå¨äº j2 ä¸ åæ¶ å¼ä¸è´ åè¿å true 152 */ 153function eq_json(j1, j2) { 154 if (isJson(j1) && isJson(j2)) { 155 let res = j1 !== undefined && j2 !== undefined; 156 157 for (let k in j1) { 158 if (k === 'ip' || k === 'address') { 159 continue; 160 } 161 if (res) { 162 // 夿 j1 ä¸ç key æ¯å¦å¨ j2 ä¸ å¹¶ä¸ å¼ä¸è´ 163 res = k in j2 && j1[k] === j2[k]; 164 } else { 165 break; 166 } 167 } 168 return res; 169 } else { 170 return false; 171 } 172} 173 174/** 175 * æ£æ¥å½å页颿¯å¦å±äºèªå·±çæ£æ¥çº§å«å½æ°åå¨ç®å½ 176 * @type {*[]} 177 */ 178const checkPl_List = [ 179 () => eq_json(my, my_temp), 180 () => eq_json(USER_CONF, USER_CONF_TEMP), 181 () => TRENDS_DATA_TEMP.sendId === parseInt(my.id), 182 () => MALI_DATA_TEMP.sendId === parseInt(my.id), 183 () =>
183 MALI_DATA_TEMP.recId === parseInt(my.id), 184 () => ARTICLE_DATA_TEMP.sendId === parseInt(my.id) 185]; 186 187/** 188 * 夿å½å页颿¯å¦å±äºèªå·± 189 * 妿å±äºèªå·± user_other ç±»çæ§ä»¶å°ä¼è¢«éè 190 * 妿ä¸å±äºèªå·± user_self ç±»çæ§ä»¶å°ä¼è¢«éè 191 * @param checkPl æ£æ¥ççº§å« è¶é«æ£æ¥çè¶å¤ 妿æ¯è´æ°ä»£è¡¨æå®æ£æ¥çæ°æ®çç´¢å¼ 192 * @param checkStart é¾å¼æ£æ¥çº§å«çèµ·å§ç´¢å¼ ä¾å¦ checkPL 为 2 checkStart=0 ä»£è¡¨æ£æ¥ 0 1 2 ä¹å°±æ¯ checkStart å° checkPL 193 * @param fun é¢å¤çæ£æ¥å½æ° 妿ä¸éè¦è¿è¡èªå®ä¹æ£æ¥ ç´æ¥è³ä¸ºç©º 194 * @param isOr 夿è§åæ¯å¦ä¸º or å¦ææ¯ åæææ£æ¥ä¸æä¸ä¸ªéè¿æ£æ¥å代表æ¯èªå·± 195 * @return boolean å½åç»å½çç¨æ·æ¯å¦ä¸ºèªå·± 196 */ 197function checkIsSelf(checkPl = 2, fun = undefined, checkStart = 0, isOr = false) { 198 if (isNotLogin()) { 199 const es = document.getElementsByClassName("user_self"); 200 for (let i = 0; i < es.length; i++) { 201 es[i].style.display = 'none'; 202 } 203 return false; 204 } 205 let isLogin = false; 206 if (checkPl > 0) { 207 const max = checkPl_List.length < checkPl ? checkPl_List.length : checkPl; 208 // ä»£è¡¨çæ¯çº§å« 209 if (isOr) { 210 for (let i = checkStart; i < max; i++) { 211 const fun = checkPl_List[i]; 212 if (fun === undefined) { 213 break; 214 } 215 if (fun()) { 216 isLogin = true; 217 break; 218 } 219 } 220 } else { 221 for (let i = checkStart; i < max; i++) { 222 const fun = checkPl_List[i]; 223 if (fun === undefined) { 224 break; 225 } 226 isLogin = fun(); 227 } 228 } 229 } else { 230 // ä»£è¡¨çæ¯ç´¢å¼çè´æ° 231 isLogin = checkPl_List[-checkPl](); 232 } 233 if (isLogin) { 234 // ä»£è¡¨æ¯æ¬äººè®¿é® éèææçå ¶å®ç¨æ·ç±»çæé® 235 const es = document.getElementsByClassName("user_other"); 236 for (let i = 0; i < es.length; i++) { 237 es[i].style.display = 'none'; 238 } 239 } else { 240 // 代表æ¯å«äººè®¿é® éèææçèªå·±ç¨æ·ç±»çæé® 241 const es = document.getElementsByClassName("user_self"); 242 for (let i = 0; i < es.length; i++) { 243 es[i].style.display = 'none'; 244 } 245 } 246 return isLogin; 247} 248 249/** 250 * æ¸ é¤æä¸ä¸ªå ç´ ä¸çææåå ç´ 251 * @param e éè¦è¢«æ¸ ççå ç´ 252 */ 253function clearChildren(e) { 254 const delete_list = []; 255 e.childNodes.forEach(child => { 256 const childNodes = child.childNodes; 257 if (childNodes.length > 0) { 258 childNodes.forEach(node => clearChildren(node)); 259 } 260 delete_list.push(child); 261 }); 262 for (let i = 0; i < delete_list.length; i++) { 263 delete_list[i].remove(); 264 } 265} 266 267/** 268 * ç¦ç¨æé® 并䏿´æ°æé®çæ¾ç¤ºçææ¬ 269 * @param button éè¦è¢«ç¦ç¨çæé® 270 */ 271function hiddenButton(button) { 272 button.hidden = true; 273} 274 275/** 276 * ç¦ç¨æé® 并䏿´æ°æé®çæ¾ç¤ºçææ¬ 277 * @param button éè¦è¢«ç¦ç¨çæé® 278 */ 279function showHiddenButton(button) { 280 button.hidden = false; 281} 282 283/** 284 * ç¦ç¨æé® 并䏿´æ°æé®çæ¾ç¤ºçææ¬ 285 * @param button éè¦è¢«ç¦ç¨çæé® 286 * @param newString æé®ä¸æ¾ç¤ºçæ°çææ¬ 287 */ 288function disableAndUpdate(button, newString) { 289 button.disabled = true; 290 button.innerText = newString; 291} 292 293/** 294 * å¯ç¨æé® 并䏿´æ°æé®çæ¾ç¤ºçææ¬ 295 * @param button éè¦è¢«ç¦ç¨çæé® 296 * @param newString æé®ä¸æ¾ç¤ºçæ°çææ¬ 297 */ 298function openAndUpdate(button, newString) { 299 button.disabled = false; 300 button.innerText = newString; 301} 302 303/** 304 * è·åå½å客æ·ç«¯çæ¶åº 305 * @return {number} å½å客æ·ç«¯çæ¶åº æ£ä»£è¡¨ä¸ï¼è´ä»£è¡¨è¥¿ å使¯åé! 306 */ 307function getTimeZone() { 308 return -new Date().getTimezoneOffset(); 309} 310 311/** 312 * æ¶é´è½¬æ¢å·¥å ·ç±» 313 * @param aOffset æºæ¶åºç¸å¯¹äºUTCçåç§»éï¼ç§ï¼ä¸æ£è¥¿è´ï¼ 314 * @param timeInMsForA é转æ¢çæºæ¶åºæ¶é´ï¼æ¯«ç§ï¼ 315 * @param bOffset ç®æ æ¶åºç¸å¯¹äºUTCçåç§»éï¼ç§ï¼é»è®¤ä¸ºå®¢æ·ç«¯æ¶åºï¼ 316 * @return {number} 转æ¢åçæ¶é´æ¯«ç§å¼ 317 */ 318function convertTimezone(aOffset, timeInMsForA, bOffset = getTimeZone() * 60) { 319 if (timeInMsForA instanceof String) { 320 timeInMsForA = parseInt(timeInMsForA); 321 } 322 323 // å°æ¶åºåç§»éä»ç§è½¬ä¸ºæ¯«ç§ 324 aOffset *= 1000; 325 bOffset *= 1000; 326 327 // è°æ´æ¶é´å°UTCï¼ç¶åè½¬ä¸ºç®æ æ¶åº 328 // 注æï¼ä»aOffsetå°UTCï¼åä»UTCå°bOffsetï¼ç´æ¥åºç¨åç§»éçå·®å³å¯ 329 return timeInMsForA - aOffset + bOffset; 330} 331 332/** 333 * æ ¼å¼åæ¥æåæ¶é´ã 334 * @param {Date} date - è¦æ ¼å¼åç Date 对象ã 335 * @returns {string} æ ¼å¼ååçæ¥ææ¶é´åç¬¦ä¸²ï¼æ ¼å¼ä¸º YYYY-MM-DD HH:MM:SSã 336 */ 337function formatDateTime(date) { 338 let year = date.getFullYear(),
339 month = date.getMonth() + 1, // æä»½ä»0å¼å§è®¡æ°ï¼æä»¥éè¦å 1 340 day = date.getDate(), 341 hour = date.getHours(), 342 minute = date.getMinutes(), 343 second = date.getSeconds(); 344 345 // 使ç¨ä¸å è¿ç®ç¬¦æ¥æ ¼å¼åå°äº10çæ°åï¼åé¢å é¶ 346 return `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day} ` 347 + `${hour}:${minute < 10 ? '0' + minute : minute}:${second < 10 ? '0' + second : second}`; 348} 349 350/** 351 * è®¡ç®æ¶é´å·®ã 352 * @param {number} timestamp1 - 第ä¸ä¸ªæ¶é´æ³ï¼æ¯«ç§ï¼ãå¦ææ¯ -1 åè¿å空åç¬¦ä¸²ï¼ 353 * @param {number} ifTime1IsUndef - 第ä¸ä¸ªæ¶é´æ³æ¯å¦ä¸ºundefinedï¼å¦ææ¯åä¼ä½¿ç¨æ¤æ°å¼ å¦ææ¯ -1 åè¿å空åç¬¦ä¸²ï¼ 354 * @param {number} timestamp2 - 第äºä¸ªæ¶é´æ³ï¼æ¯«ç§ï¼ã 355 * @returns {string} æ¶é´å·®åç¬¦ä¸²ï¼æ ¼å¼ä¸º "X day(s)" æ "X hour(s)" æ "X minute(s)" æ "X second(s)"ã 356 */ 357function timeDifference(timestamp1, ifTime1IsUndef = 0, timestamp2 = Date.now()) { 358 if (timestamp1 === undefined) { 359 timestamp1 = ifTime1IsUndef; 360 } 361 if (timestamp1 === -1) { 362 return ""; 363 } 364 // Calculate the difference in milliseconds 365 const diffMilliseconds = Math.abs(timestamp1 - timestamp2); 366 367 // Convert to seconds, minutes, hours, days, months, and years 368 let diffSeconds = Math.floor(diffMilliseconds / 1000); 369 let diffMinutes = Math.floor(diffSeconds / 60); 370 let diffHours = Math.floor(diffMinutes / 60); 371 let diffDays = Math.floor(diffHours / 24); 372 let diffMonths = Math.floor(diffDays / 30.44); 373 let diffYears = Math.floor(diffDays / 365.25); 374 375 // Format the output 376 if (diffYears > 0) { 377 return `${diffYears}å¹´`; 378 } else if (diffMonths > 0) { 379 return `${diffMonths}æ`; 380 } else if (diffDays > 0) { 381 return `${diffDays}天`; 382 } else if (diffHours > 0) { 383 return `${diffHours}å°æ¶`; 384 } else if (diffMinutes > 0) { 385 return `${diffMinutes}åé`; 386 } else { 387 return `${diffSeconds}ç§`; 388 } 389} 390 391/** 392 * è·åæ ¼å¼åçæ¥ææ¶é´å符串ã 393 * @param {Date|string} n - è¦æ ¼å¼åçæ¥ææ¶é´ï¼å¯ä»¥æ¯ Date 对象æè½è¢« Date æé å¨è§£æçå符串ã 394 * @returns {string} æ ¼å¼ååçæ¥ææ¶é´å符串ã 395 * @throws {Error} 妿æä¾çåæ°æ æ³è¢«è§£æä¸ºææçæ¥æï¼åæåºé误ã 396 */ 397function getDate(n) { 398 let date; 399 if (n === undefined) { 400 return getDate(new Date()); 401 } 402 // å¦æåæ°å·²ç»æ¯ Date ç±»åï¼ç´æ¥ä½¿ç¨ 403 if (n instanceof Date) { 404 date = n; 405 } 406 // å¦åå°è¯å°å符串转æ¢ä¸º Date 对象 407 else { 408 date = new Date(n); 409 // æ£æ¥è½¬æ¢åç Date 对象æ¯å¦ææ 410 if (isNaN(date.getTime())) { 411 throw new Error('Invalid date'); 412 } 413 } 414 415 // è°ç¨ formatDateTime 彿°æ¥æ ¼å¼åæ¥ææ¶é´ 416 return formatDateTime(date); 417} 418 419/** 420 * è·åå°ä¸å¤©çæ¶é´ 421 * @param n éè¦è¢«è½¬æ¢çæ¶é´ç毫ç§å¼ 422 * @return {string} 转æ¢ä¹åçæ¶é´æ³æ°å¼ 423 */ 424function getDateByDay(n) { 425 if (n instanceof Date) { 426 return n.toTimeString().substring(0, 8) 427 } 428 return new Date(n).toTimeString().substring(0, 8); 429} 430 431/** 432 * å°æ¶é´å符串转æ¢ä¸ºDate 433 * @param dateStr æ¶é´å符串 æ¥æåç¬¦ä¸²æ ¼å¼ä¸ºYYYY-MM-DD HH:mm:ss.SSS 434 * @return {Date} date å¯¹è±¡ï¼ 435 */ 436function dateStringToMilliseconds(dateStr) { 437 let parts = dateStr.split(/[- :.]/); 438 // parts[0]: å¹´, parts[1]: æ, parts[2]: æ¥, parts[3]: æ¶, parts[4]: å, parts[5]: ç§, parts[6]: æ¯«ç§ 439 return new Date(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5], parts[6]); 440} 441 442/** 443 * IP ä¸å°åçæ å°å卿± key æ¯ip value æ¯IP 对åºçå°å 444 * @type {{string:string}} 445 */ 446let ipAddress_SAVE = {}; 447 448/** 449 * IP è§£æå¨çå®ç°ç±» key æ¯è¦è¯ç¨ç IP è§£æçæ¨¡å¼ value å°±æ¯è§£æå¨çå®ç°å½æ° 450 * @type {{}} 451 */ 452const getIPLocation_parser = { 453 "AI_WEN": (ipAddress) => { 454 return axios( 455 { 456 url: `https://api.ipplus360.com/ip/geo/v1/city/`, 457 params: { 458 lang: 'zh-CN', 459 key: getWebConfByKey("IP_SK"), 460 ip: ipAddress, 461 coordsys: 'WGS84' 462 } 463 } 464 ).then( 465 (response) => { 466 let data = response.data; 467 if (data && data.code === 'Success') { 468 data = data.data; 469 return { 470 country: data.country, 471 region: data.city, 472 city: data.city, 473 lat: data.lat, 474 lon: data.lng, 475 district: data.district === undefined ? "" : data.district, 476 } 477 } else { 478 // 妿 åæ åºéäº å°±ä¸ç¨åæ 479 console.error("getIPLocation is åæ åºéäº:", data.code); 480 return getIPLocation(ipAddress, false); 481 } 482 } 483 ) 484 }, 485 "ipDataCloud": (ipAddress) => { 486 return axios( 487 { 488 url: `https://api.ipdatacloud.com/v2/query`, 489 method: 'post', 490 params: { 491 lang: 'zh-CN', 492 key: getWebConfByKey("IP_SK"), 493 ip: ipAddress, 494 } 495 } 496 ).then( 497 (response) => { 498 let data = response.data; 499 if (data && data.code === 200 && data.data) { 500 data = data.data.location; 501 return { 502 country: data.country, 503 region: data.city, 504 city: data.city, 505 lat: data.latitude, 506 lon: data.longitude, 507 district: data.district === undefined ? "" : data.district, 508 } 509 } else {
510 // 妿 IP äº åºéäº å°±ä¸ç¨ IP äº 511 console.error("getIPLocation is IPæ°æ®äº åºéäº:", data.msg); 512 return getIPLocation(ipAddress, false); 513 } 514 } 515 ) 516 }, 517 "apiSpace.com": (ipAddress) => { 518 return axios( 519 { 520 "url": "https://eolink.o.apispace.com/ipguishu/ip/geo/v1/district?ip=" + ipAddress + "&coordsys=WGS84", 521 "method": "GET", 522 "headers": { 523 "X-APISpace-Token": getWebConfByKey("IP_SK") 524 }, 525 "data": {}, 526 } 527 ).then( 528 (response) => { 529 let data = response.data; 530 if (data && data.code === 'Success') { 531 data = data.data; 532 return { 533 country: data.country, 534 region: data.city, 535 city: data.city, 536 lat: data.lat, 537 lon: data.lng, 538 district: data.district === undefined ? "" : data.district, 539 } 540 } else { 541 // 妿 apiSpace åºéäº å°±ä¸ç¨ apiSpace 542 console.error("getIPLocation is apiSpace åºéäº:", data.code); 543 return getIPLocation(ipAddress, false); 544 } 545 } 546 ) 547 }, 548 "codeBookShop": (ipAddress) => { 549 return axios( 550 { 551 url: `https://shop.lingyuzhao.top/api/ipAddress/${ipAddress}`, 552 params: { 553 id: 17, 554 sk: "-2462274026238529984", 555 } 556 } 557 ).then( 558 (response) => { 559 const data = response.data; 560 if (data && data.status === 'success') { 561 return { 562 country: data.country, 563 region: data.region, 564 city: data.city, 565 lat: data.lat, 566 lon: data.lon, 567 district: data.district === undefined ? "" : data.district, 568 }; 569 } 570 } 571 ) 572 } 573} 574 575/** 576 * getMyIPAddress 彿°å¯¹åºçç¼åç IP 577 */ 578let getMyIPAddress_cacheIP = null; 579 580/** 581 * è·åæçIPå°å` 582 */ 583async function getMyIPAddress() { 584 const orgFormatJson = ip_server_href() + "/API/ipAddress"; 585 if (getMyIPAddress_cacheIP === null) { 586 // è¯·æ±æåä¹å ä¼ç´æ¥ç¼åä¸ä¸ª cookie å ¶ä¸åå¨IPä¿¡æ¯ è¿ä¸ª IP åªè½è¢«å端解æ 587 return axios.get(orgFormatJson) 588 .then( 589 (response) => { 590 const d = response.data; 591 if (d.ip !== undefined) { 592 getMyIPAddress_cacheIP = d.ip; 593 return Promise.resolve({ip: getMyIPAddress_cacheIP}); 594 } else { 595 console.error("getMyIPAddress: ip error~~ networkError " + orgFormatJson); 596 } 597 } 598 ); 599 } else { 600 return Promise.resolve({ip: getMyIPAddress_cacheIP}); 601 } 602} 603 604/** 605 * å° ip å°å转æ¢ä¸º æåçå°å 606 * @param ipAddress ipå°å 607 * @param useParser 妿è¦ä½¿ç¨ å®ç°å¥½çæ¨¡å¼ å设置为 true 608 * @param useIpParseName è¦è¯ç¨çIPè§£æå¨çååï¼å¦æ useParser 609 * @return {Promise<null|{zip, country, city, lon, region: *, lat}>} 610 */ 611async function getIPLocation(ipAddress, useParser = true, useIpParseName = 'AI_WEN') { 612 axios.defaults.withCredentials = false; 613 try { 614 console.info(`${ipAddress} parsing~`); 615 const parserName = useParser ? useIpParseName : "codeBookShop"; 616 console.info(`getIPLocation is using parser: ${parserName}`); 617 return getIPLocation_parser[parserName](ipAddress); 618 } catch (error) { 619 console.error('Error getting IP location:', error); 620 return null; 621 } finally { 622 axios.defaults.withCredentials = true; 623 } 624} 625 626/** 627 * å° ip å°å转æ¢ä¸º æåçå°å 妿æ å°æ± ä¸åå¨äºå°åå°±ä½¿ç¨æ å°å°å 628 * @param ipAddress ipå°å 629 * @param useParser 妿è¦ä½¿ç¨ å 置解æå¨ å设置为 true 630 * @return {Promise<null|{res, address, lon, lat}>} 631 */ 632async function getIPLocationAndCache(ipAddress, useParser = true) { 633 if (useParser) { 634 useParser = getWebConfByKey("IP_SK") !== undefined; 635 } 636 if (ipAddress === undefined) { 637 const res = {}; 638 res[WEB_CONF.RES_RETURN] = WEB_CONF.RES_RETURN_OK_VALUE;
639 res['address'] = "é¶æ²³ç³»"; 640 res['lon'] = NaN; 641 res['lat'] = NaN; 642 console.error("ipAddress is undefined"); 643 return Promise.resolve(res); 644 } 645 const ipAddressSAVEElement = ipAddress_SAVE[ipAddress]; 646 if (ipAddressSAVEElement !== undefined) { 647 // å¼å§è¿è¡ç»ææå»ºåè¿å 648 const res = {}; 649 res[WEB_CONF.RES_RETURN] = WEB_CONF.RES_RETURN_OK_VALUE; 650 res['address'] = ipAddressSAVEElement.address; 651 res['lon'] = ipAddressSAVEElement.lon; 652 res['lat'] = ipAddressSAVEElement.lat; 653 return Promise.resolve(res); 654 } else { 655 // å¦ææ²¡æå°±è°ç¨ API 656 return getIPLocation(ipAddress, useParser, "ipDataCloud").then( 657 (res) => { 658 if (res !== null) { 659 // é¦å è¿è¡ç¼å 660 ipAddress_SAVE[ipAddress] = { 661 'address': res.country + ' ' + res.city + ' ' + res.district, 662 'lon': res.lon, 663 'lat': res.lat, 664 }; 665 const ipAddressSAVEElement1 = ipAddress_SAVE[ipAddress]; 666 // ç¶åè¿è¡æé åè¿å 667 res[WEB_CONF.RES_RETURN] = WEB_CONF.RES_RETURN_OK_VALUE; 668 res['address'] = ipAddressSAVEElement1.address; 669 res['lon'] = ipAddressSAVEElement1.lon; 670 res['lat'] = ipAddressSAVEElement1.lat; 671 return res; 672 } 673 } 674 ).catch( 675 (error) => { 676 if (useParser) { 677 // 妿 åºéäº å°±ä¸ç¨è§£æå¨äº 678 console.error("getIPLocation åºéäºï¼å°è¯å¯å¨å¤ç¨æ¹æ¡:", error); 679 return getIPLocationAndCache(ipAddress, false); 680 } else { 681 // å¤ç¨æ¹æ¡ä¹åºéäº 682 jokerBoxPopUp.show("æ æ³æ£å¸¸ä½¿ç¨ IP æå¡å¨:" + JSON.stringify(error)); 683 const temp = {}; 684 Ring.playRing('dengDeng') 685 temp[WEB_CONF.RES_RETURN] = "è¦åï¼æ æ³ä½¿ç¨ IP æå¡å¨:" + JSON.stringify(error); 686 return Promise.resolve(temp) 687 } 688 } 689 ); 690 } 691} 692 693/** 694 * å¤çåªè´´æ¿ä¸çæ°æ®å表ï¼ä» ä» è½å¤å¤çç²è´´æ¿ä¸çæ°æ®å表ï¼ä¸è½èªå¨åè° 695 * 696 * @param items ç²è´´æ¿ä¸çæ°æ®å表ã 697 * @param callbackImage å¾çåè°å½æ°ã 698 * @param callbackText ææ¬åè°å½æ°ã 699 * @param callbackVideo è§é¢åè°å½æ°ã 700 * @param callbackFile æä»¶åè°å½æ°ã 701 * @param useLastData æ¯å¦åªè·åå°ææ°å¤å¶æ°æ® å¦æéæ©æ¯ åä¸ä¼è·åå°ææçæ°æ® 702 */ 703function handleClipboardData(items, callbackImage, callbackText, callbackVideo, callbackFile, useLastData = true) { 704 console.info("handleClipboardData run!!!") 705 if (!items || items.length === 0) { 706 return; 707 } 708 let blob; 709 710 function run(item) { 711 if (item.type.indexOf('image') !== -1) { 712 // å¾çæ°æ® 713 convertImageToWebP(item.getAsFile(), (blob) => callbackImage(blob), (blob) => { 714 console.warn("å¾ç转 webp 失败äº~"); 715 callbackImage(blob); 716 }); 717 } else if (item.type.indexOf('video/') === 0) { 718 // è§é¢æ°æ® 719 blob = item.getAsFile(); 720 callbackVideo(blob); 721 } else if (item.kind === 'string') { 722 // ææ¬æ°æ® 723 item.getAsString(callbackText) 724 } else if (item.kind === 'file') { 725 // å ¶å®çæä»¶æ°æ® 726 blob = item.getAsFile(); 727 callbackFile(blob); 728 } 729 } 730 731 if (useLastData) { 732 run(items[items.length - 1]); 733 return; 734 } 735 for (let i = 0; i < items.length; i++) { 736 run(items[i]); 737 } 738} 739 740 741/** 742 * 读ååªè´´æ¿ä¸çææ°ä¸æ¡æ°æ®ï¼å¹¶æ ¹æ®æ°æ®ç±»åè°ç¨ä¸åçåè°å½æ°ã 743 * 744 * @param e éè¦è¢«è®¾ç½®ç²è´´äºä»¶çå ç´ å表ã 745 * @param {Function} callbackImage å¤çå¾çæ°æ®çåè°å½æ°ã 746 * @param {Function} callbackText å¤çææ¬æ°æ®çåè°å½æ°ãé¤äºè¿ä¸ªå½æ°çåæ°æ¯å ç´ åå符串 å ¶å®ç齿¯ å ç´ åfile 对象 747 * @param {Function} callbackVideo å¤çè§é¢æ°æ®çåè°å½æ°ã 748 * @param {Function} callbackFile å¤çä»»æç±»åæä»¶æ°æ®çåè°å½æ°ã 749 * @param {boolean} preventDefault æ¯å¦é»æ¢é»è®¤äºä»¶ãé»è®¤å¼ä¸º falseã 750 * @param eventName 对åºçäºä»¶å¯¹è±¡çåç§°ã 751 * @param preventDefault æ¯å¦é»æ¢é»è®¤äºä»¶ãé»è®¤å¼ä¸º falseã 752 * @param useLastData æ¯å¦åªè·åå°ææ°å¤å¶æ°æ® å¦æéæ©æ¯ åä¸ä¼è·åå°ææçæ°æ® 753 */ 754function readClipboard(e, callbackImage, callbackText, callbackVideo, callbackFile, eventName = 'paste', preventDefault = false, useLastData = true) { 755 function hf(event) { 756 if (preventDefault) { 757 event.preventDefault(); 758 } 759 const items = (event.clipboardData || window.clipboardData).items; 760 handleClipboardData(items, callbackImage, callbackText, callbackVideo, callbackFile, useLastData); 761 } 762 763 e.forEach(function (element) { 764 element.addEventListener(eventName, hf); 765 });
766} 767 768/** 769 * å°ä¸ä¸ªææ¬å¤å¶å°åªè´´æ¿ å¹¶ä¸è¿åå¤å¶æä½æ¯å¦æå 770 * @param textToCopy éè¦è¢«å¤å¶çæå 771 * @return boolean 妿å¤å¶æä½æå è¿å true 772 */ 773async function copyText(textToCopy) { 774 try { 775 await window.navigator.clipboard.writeText(textToCopy); 776 return true; 777 } catch (err) { 778 return false; 779 } 780} 781 782/** 783 * å°ä¸ä¸ªææ¬å¤å¶å°åªè´´æ¿ å¹¶ä¸è¿åå¤å¶æä½æ¯å¦æå å¤ç¨çå¤å¶æä½ 784 * @param text éè¦è¢«å¤å¶çæå 785 * @return boolean 妿å¤å¶æä½æå è¿å true 786 */ 787function copyTextToClipboard(text) { 788 try { 789 const textArea = document.createElement("textarea"); 790 textArea.value = text; 791 document.body.appendChild(textArea); 792 textArea.select(); 793 document.execCommand("copy"); 794 document.body.removeChild(textArea); 795 return true; 796 } catch (e) { 797 return false; 798 } 799} 800 801/** 802 * 夿ä¸ä¸ªåç¼åæ¯å¦ä¸ºè§é¢çåç¼ 803 * @param name éè¦è¢«å¤æçæä»¶åç¼ 804 * @returns {boolean} 妿æ¯è§é¢æä»¶çåç¼å å¨è¿éè¿åçå°±æ¯ true 805 */ 806function isVideoName(name) { 807 if (name == null) { 808 return false; 809 } 810 switch (name.toLowerCase()) { 811 case "webm": 812 case "mp4": 813 case "ogg": 814 return true; 815 default: 816 return false; 817 } 818} 819 820/** 821 * 计ç®åä½åº¦é 822 * @param bytes éè¦è¢«è®¡ç®ç byte åä½ 823 * @param decimals 计ç®ç»æçç²¾ç¡®å¼ å°æ°ç¹åçä¿ç使° 824 * @return {string} 计ç®ç»æ 825 */ 826function formatBytes(bytes, decimals = 2) { 827 if (bytes <= 0) return '0 Bytes'; 828 829 const k = 1024; 830 const dm = decimals < 0 ? 0 : decimals; 831 const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 832 833 const i = Math.floor(Math.log2(bytes) / Math.log2(k)); 834 835 return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; 836} 837 838/** 839 * å°æ°å¼è½¬æ¢ä¸ºå符串 840 * @param value éè¦è¢«è½¬æ¢çæ°å¼ 841 * @return {string} 转æ¢ä¹åçå符串 842 */ 843function convertUnit(value) { 844 // æ£æ¥è¾å ¥å¼æ¯å¦ä¸ºæ°å 845 if (typeof value !== 'number') { 846 console.error("convertUnit: Invalid input:" + value); 847 return '--'; 848 } 849 850 let result; 851 852 // 夿è¾å ¥åä½ï¼å¦æå使¯ä¸ï¼åè¿åä¸çæ°å¼ï¼å¦åè¿ååçå¼ 853 if (value >= 10000000) { 854 result = (value / 10000000).toFixed(2) + 'kw'; 855 } else if (value >= 10000) { 856 result = (value / 10000).toFixed(2) + 'w'; 857 } else if (value >= 1000) { 858 result = (value / 1000).toFixed(2) + 'k'; 859 } else { 860 result = value; 861 } 862 863 return result; 864} 865 866/** 867 * å°åä½å符串 转æ¢ä¸º æ°å¼ å¯è½ä¼æç²¾åº¦æå¤± 868 * @param str éè¦è¢«è½¬æ¢çå符串 869 */ 870function convertUnitToNumber(str) { 871 // è·åå°æ°å¼ååä½ 872 let number_str = ''; 873 let d = ''; 874 for (const c of str) { 875 if (isNumericString(c)) { 876 number_str += c; 877 } else { 878 d += c; 879 } 880 } 881 // æ ¹æ®åä½è®¡ç®ç»æ 882 switch (d) { 883 case 'kw': 884 return parseFloat(number_str) * 10000000; 885 case 'w': 886 return parseFloat(number_str) * 10000; 887 case 'k': 888 return parseFloat(number_str) * 1000; 889 default: 890 return parseFloat(number_str); 891 } 892} 893 894/** 895 * 计ç®ä¸ä¸ªé¢è²æ°å¼çåè² 896 * @param color éè¦è¢«è®¡ç®çé¢è²æ°å¼ 897 * @return {string} 计ç®ç»æ åè²çé¢è²æ°å¼ 16 è¿å¶ 898 */ 899function invertColor(color) { 900 // å°é¢è²å¼è½¬æ¢ä¸º RGB æ ¼å¼ 901 const r = parseInt(color.substr(1, 2), 16); 902 const g = parseInt(color.substr(3, 2), 16); 903 const b = parseInt(color.substr(5, 2), 16); 904 905 // 计ç®åè²å¼ 906 const invertedR = 255 - r; 907 const invertedG = 255 - g; 908 const invertedB = 255 - b; 909 910 // å°åè²å¼è½¬æ¢ä¸ºåå è¿å¶æ ¼å¼ï¼å¹¶è¿åç»æ 911 return '#' + invertedR.toString(16).padStart(2, '0') + invertedG.toString(16).padStart(2, '0') + invertedB.toString(16).padStart(2, '0'); 912} 913 914/** 915 * çæä¸ä¸ªå¨æå®éåºé´ä¹é´çéæºæ° 916 * @param min åºé´æå°å¼ 917 * @param max åºé´æå¤§å¼ 918 * @return {number} 919 */ 920function getRandomInt(min, max) { 921 min = Math.ceil(min); 922 return Math.floor(Math.random() * (Math.floor(max) - min + 1)) + min; 923} 924 925/** 926 * 927 * @return {string} çæç16è¿å¶RGBå¼ 928 */ 929function getRandomRGB() { 930 let r, g, b; 931 // åå¤ä¸ä¸ªéæºæ°å¼ 932 r = getRandomInt(0, 100); // éæºçæ0-100ä¹é´çæ´æ° 933 g = getRandomInt(0, 100); // éæºçæ0-100ä¹é´çæ´æ° 934 b = getRandomInt(0, 100); // éæºçæ0-100ä¹é´çæ´æ° 935 if (avg([r, g, b]) > 90) { 936 // 代表é¢è²è¾¾å°äºä¸é´éå¼ éæ°çæé«äº®è² ç©å¼ä¸é´é¨å 937 r = getRandomInt(200, 255); // éæºçæ200-255ä¹é´çæ´æ° 938 g = getRandomInt(200, 255); // éæºçæ200-255ä¹é´çæ´æ° 939 b = getRandomInt(200, 255); // éæºçæ200-255ä¹é´çæ´æ° 940 } 941 return "#" + ("000000" + rgbToHex(r, g, b).toUpperCase()).slice(-6); // è¿åçæç16è¿å¶RGBå¼ 942} 943 944/** 945 * ä¸éè¦åºé´çéæºé¢è² 946 * @return {string} çæç16è¿å¶RGBå¼ 947 */ 948function getRandomAllRGB() { 949 let r, g, b; 950 // 代表é¢è²è¾¾å°äºä¸é´éå¼ éæ°çæé«äº®è² ç©å¼ä¸é´é¨å 951 r = getRandomInt(0, 255); // éæºçæ0-255ä¹é´çæ´æ° 952 g = getRandomInt(0, 255); // éæºçæ0-255ä¹é´çæ´æ° 953 b = getRandomInt(0, 255); // éæºçæ0-255ä¹é´çæ´æ° 954 return "#" + ("000000" + rgbToHex(r, g, b).toUpperCase()).slice(-6); // è¿åçæç16è¿å¶RGBå¼ 955} 956 957/** 958 * è·åå° 16 è¿å¶ç rgb æ°å¼ 959 * @param r 960 * @param g 961 * @param b 962 * @return {string} 963 */ 964function rgbToHex(r, g, b) { 965 return ((r << 16) | (g << 8) | b).toString(16); 966} 967 968/** 969 * è·åå½åæ¶é´çæ ¼å¼åå符串 970 * @return {string} å½åæ¶é´çæ ¼å¼åå符串 971 * @param useCh 妿éè¦ä¸ææ ¼å¼è®¾ç½®ä¸º true å乿¯è±æ 972 * @param now éè¦è¢«æ ¼å¼åçæ¶é´å¯¹è±¡ 973 */ 974function getFormattedDateTime(useCh = true, now = new Date()) { 975 const year = now.getFullYear();
976 const month = now.getMonth() + 1; 977 const day = now.getDate(); 978 const hours = now.getHours(); 979 const minutes = now.getMinutes(); 980 const seconds = now.getSeconds(); 981 const weekday = (useCh ? ["æææ¥", "ææä¸", "ææäº", "ææä¸", "ææå", "ææäº", "ææå "] : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"])[now.getDay()]; 982 983 return `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day} ${hours < 10 ? '0' + hours : hours}:${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds} ${weekday}`; 984} 985 986/** 987 * å½åç¨æ·è®¾å¤ç±»åç¼å 988 * @type {null|boolean} 989 */ 990let isMobileDevice_cache = null; 991 992/** 993 * æ£æ¥å½åç¨æ·æ¯å¦æ£å¨ä½¿ç¨ç§»å¨è®¾å¤è¿è¡æµè§ 994 * @return {boolean} 妿æ¯ä½¿ç¨çç§»å¨è®¾å¤ å¨è¿éä¼è¿å true 995 */ 996function isMobileDevice() { 997 if (isMobileDevice_cache !== null) { 998 return isMobileDevice_cache; 999 } 1000 const userAgent = navigator.userAgent || navigator.vendor || window.opera; 1001 1002 // 对äºiPadï¼è¿å¯è½ä¼ä¸æ¡é¢çSafariçç¨æ·ä»£çå符串混æ·ï¼æä»¥éè¦å æ£æ¥ 1003 if (/iPad|Macintosh/.test(userAgent) && 'ontouchend' in document) { 1004 isMobileDevice_cache = true; 1005 return isMobileDevice_cache; 1006 } 1007 1008 // æ£æ¥æ¯å¦ä¸ºç§»å¨è®¾å¤ 1009 isMobileDevice_cache = /Mobi|Android/i.test(userAgent); 1010 return isMobileDevice_cache; 1011} 1012 1013/** 1014 * åå¼è®¡ç®å½æ° 1015 * @param nums {[]} éè¦è¢«è®¡ç®åå¼çæ°å 1016 */ 1017function avg(nums) { 1018 let res = 0; 1019 for (let num of nums) { 1020 res += num; 1021 } 1022 return res / nums.length; 1023} 1024 1025/** 1026 * æ£æ¥å½åäºä»¶åçæ¶çæé® 1027 * @param event äºä»¶å¯¹è±¡ 1028 * @param keyDown 妿äºä»¶å¯¹è±¡çå¼åæé® ä¸ æ¤å¼ç¸å æåè¿å 2 1029 * @return {number} 妿 äºä»¶çæé®ä¸ç®æ æé®ç¸å è¿å 2 妿æä½äº ctrl åè¿å -çç»æ 1030 */ 1031function checkKeyDownValue(event, keyDown) { 1032 let res; 1033 switch (event.key) { 1034 case keyDown: 1035 res = 2 1036 break; 1037 default: 1038 res = 1 1039 break; 1040 } 1041 if (event.ctrlKey) { 1042 return -res; 1043 } else { 1044 return res; 1045 } 1046} 1047 1048/** 1049 * å½å¾åå è½½åééè¯¯çæ¶å åæ´å¾ç src 1050 * @param img éè¦è¢«è®¾ç½®é误äºä»¶çå¾ 1051 * @param def é»è®¤çå¾ å¦æè®¾ç½®ä¸º undefined åå½å¾åçéè¯¯çæ¶åç´æ¥éèå¾ 1052 */ 1053function imgErrorFun(img, def = undefined) { 1054 if (def === undefined) { 1055 img.onerror = () => img.hidden = true; 1056 } else { 1057 img.onerror = () => img.src = def; 1058 } 1059} 1060 1061/** 1062 * 夿ä¸ä¸ªå¯¹è±¡æ¯å¦ä¸ºå符串 1063 * @param obj 1064 * @return {boolean} 1065 */ 1066function isString(obj) { 1067 return typeof obj === 'string'; 1068} 1069 1070class HtmlStrTool { 1071 /** 1072 * çæ video æ ç¾ 1073 * @param src è§é¢å°å 1074 * @param style è§é¢æ ·å¼ 1075 * @return {string} è§é¢æ ç¾ 1076 */ 1077 static videoStr(src, style = 'width:50vw') { 1078 return "<video src='" + src + "' style='" + style + "' controls/>"; 1079 } 1080} 1081 1082/** 1083 * çæä¸ä¸ªç¥ç¦è¯ 1084 * @return {string} 1085 */ 1086function generateCondolenceMessage(userName = "ä½ ") { 1087 const messages = [ 1088 "å¿ï¼ææ¯ root å¾é«å ´ä½ 忥!", 1089 `${userName}ï¼ä½ æ¥äºå,ä¼¼ä¹å徿´å¼ºäºï¼æ¿æå®ç°äºå`, 1090 `${userName} å¥½ä¹ ä¸è§ï¼ï¼ï¼`, 1091 `欢è¿${userName}ç彿¥ï¼`, 1092 `å¿ï¼${userName} 欢è¿åæ¥!` 1093 ]; 1094 return messages[Math.floor(Math.random() * messages.length)]; 1095} 1096 1097/** 1098 * çæåå¸å¼ 1099 * @param str éè¦è¢«åå¸çå符串 1100 * @return {number} 对åºç hashå¼ 1101 */ 1102function hashString(str) { 1103 let hash = 0; // åå§ååå¸å¼ 1104 for (let char of str) { // è¿ä»£æ¯ä¸ªå符 1105 hash = (hash << 5) - hash + char.charCodeAt(0); // 计ç®åå¸å¼ 1106 } 1107 return hash; // è¿åæç»çåå¸å¼ 1108} 1109 1110/** 1111 * è·åæä»¶åå¸å¼ 1112 * @param file æä»¶å¯¹è±¡ 1113 * @param okFun å¤çç»æç彿° 1114 * @param errorFun å¤çé误ç彿° 1115 */ 1116function getFileHash(file, okFun, errorFun) { 1117 return new Promise((resolve, reject) => { 1118 try { 1119 const reader = new FileReader(); 1120 reader.onload = function(event) { 1121 const arrayBuffer = event.target.result; 1122 if (!crypto.subtle) { 1123 errorFun("å½åç¯å¢ä¸æ¯ææä»¶æ ¡éªï¼"); 1124 return; 1125 } 1126 const hashBuffer = crypto.subtle.digest('SHA-256', arrayBuffer); 1127 hashBuffer.then(hashArray => { 1128 const hashArray8 = Array.from(new Uint8Array(hashArray)); 1129 const hashHex = hashArray8.map(b => b.toString(16).padStart(2, '0')).join(''); 1130 okFun(hashHex); 1131 }).catch(error => { 1132 errorFun(error); 1133 }); 1134 }; 1135 reader.onerror = function(error) { 1136 errorFun(error); 1137 }; 1138 reader.readAsArrayBuffer(file); 1139 } catch (e) { 1140 errorFun(e); 1141 } 1142 });
1143} 1144 1145/** 1146 * å°å¾ç转æ¢ä¸º JPG æ ¼å¼ 1147 * @param file å¾çæä»¶å¯¹è±¡ 1148 * @param okFun è½¬æ¢æååçåè°å½æ° 1149 * @param errFun 转æ¢å¤±è´¥åçåè°å½æ° 1150 */ 1151function convertImageToJPG(file, okFun, errFun) { 1152 const reader = new FileReader(); 1153 reader.onload = (event) => { 1154 const img = new Image(); 1155 img.onload = () => { 1156 const canvas = document.createElement('canvas'); 1157 canvas.width = img.width; 1158 canvas.height = img.height; 1159 const ctx = canvas.getContext('2d'); 1160 ctx.drawImage(img, 0, 0); 1161 canvas.toBlob((blob) => { 1162 if (blob) { 1163 okFun(blob); 1164 } else { 1165 errFun(file); // å¦æè½¬æ¢å¤±è´¥ï¼è¿ååå§æä»¶ 1166 } 1167 }, 'image/jpeg', 0.92); // 0.92 æ¯å¾çè´¨éï¼å¯ä»¥æ ¹æ®éè¦è°æ´ 1168 }; 1169 img.onerror = () => errFun(file); // å¾çå 载失败æ¶è°ç¨ errFun 1170 img.src = event.target.result; 1171 }; 1172 reader.onerror = () => errFun(file); // æä»¶è¯»å失败æ¶è°ç¨ errFun 1173 reader.readAsDataURL(file); 1174} 1175 1176/** 1177 * å°å¾ç转æ¢ä¸º JPG æ ¼å¼ 1178 * @param file å¾çæä»¶å¯¹è±¡ 1179 * @param okFun è½¬æ¢æååçåè°å½æ° 1180 * @param errFun 转æ¢å¤±è´¥åçåè°å½æ° 1181 */ 1182function convertImageToWebP(file, okFun, errFun) { 1183 const reader = new FileReader(); 1184 reader.onload = (event) => { 1185 const img = new Image(); 1186 img.onload = () => { 1187 const canvas = document.createElement('canvas'); 1188 canvas.width = img.width; 1189 canvas.height = img.height; 1190 const ctx = canvas.getContext('2d'); 1191 ctx.drawImage(img, 0, 0); 1192 canvas.toBlob((blob) => { 1193 if (blob) { 1194 okFun(blob); 1195 } else { 1196 errFun(file); // å¦æè½¬æ¢å¤±è´¥ï¼è¿ååå§æä»¶ 1197 } 1198 }, 'image/webp', 0.92); // 0.92 æ¯å¾çè´¨éï¼å¯ä»¥æ ¹æ®éè¦è°æ´ 1199 }; 1200 img.onerror = () => errFun(file); // å¾çå 载失败æ¶è°ç¨ errFun 1201 img.src = event.target.result; 1202 }; 1203 reader.onerror = () => errFun(file); // æä»¶è¯»å失败æ¶è°ç¨ errFun 1204 reader.readAsDataURL(file); 1205} 1206 1207 1208/** 1209 * æ°çæ¬ä¸ä¸ºäºé¿å ä¸äºå¸¸ç¨ç彿°ä¼è¢«è¦çï¼æä»¥è¿éå°ä¸äºå¸¸ç¨çæ¹æ³éæ°å°è£ ä¸ä¸ 1210 */ 1211class LY_Utils { 1212 1213 /** 1214 * è§é¢è½¬é³é¢ 1215 * @param file è§é¢æä»¶å¯¹è±¡ 1216 * @param progressBar è¿åº¦æ¡ 1217 * @param okFun è½¬æ¢æåä¹åçåè° 1218 */ 1219 static videoToAudio(file, progressBar, okFun) { 1220 console.log('videoToAudio function called'); 1221 1222 const videoElement = document.createElement('video'); 1223 videoElement.preload = 'metadata'; 1224 videoElement.src = URL.createObjectURL(file); 1225 1226 videoElement.onloadedmetadata = () => { 1227 console.log('Video metadata loaded'); 1228 1229 // å建é³é¢ä¸ä¸æ 1230 const audioContext = new AudioContext(); 1231 const mediaElementSource = audioContext.createMediaElementSource(videoElement); 1232 const gainNode = audioContext.createGain(); 1233 gainNode.gain.value = 0; // 设置é³é为 0 1234 1235 mediaElementSource.connect(gainNode); 1236 gainNode.connect(audioContext.destination); 1237 1238 // æè·è§é¢çåªä½æµ 1239 const videoStream = videoElement.captureStream(); 1240 const audioTrack = videoStream.getAudioTracks()[0]; 1241 1242 if (!audioTrack) { 1243 console.error('No audio track found in the video.'); 1244 okFun(null); // éç¥åè°æ²¡æé³é¢è½¨é 1245 return; 1246 } 1247 1248 // å建æ°çåªä½æµåªå å«é³é¢è½¨é 1249 const audioStream = new MediaStream([audioTrack]); 1250 const mediaRecorder = new MediaRecorder(audioStream, {mimeType: 'audio/webm'}); 1251 const audioChunks = []; 1252 1253 mediaRecorder.addEventListener('dataavailable', event => { 1254 if (event.data.size > 0) { 1255 audioChunks.push(event.data); 1256 } 1257 }); 1258 1259 mediaRecorder.addEventListener('stop', () => { 1260 const audioBlob = new Blob(audioChunks, {type: 'audio/webm'}); 1261 if (audioBlob.size === 0) { 1262 console.error('Generated audio blob is empty.'); 1263 okFun(null); 1264 } else { 1265 okFun(audioBlob); 1266 } 1267 }); 1268 1269 mediaRecorder.start(); 1270 1271 videoElement.play().then(() => { 1272 console.log('Video started playing'); 1273 // æ´æ°è¿åº¦æ¡ 1274 videoElement.addEventListener('timeupdate', () => { 1275 const number = (videoElement.currentTime / videoElement.duration) * 100; 1276 progressBar.value = number; 1277 if (number >= 100) { 1278 mediaRecorder.stop(); 1279 videoElement.pause(); 1280 } 1281 }); 1282 }).catch(error => { 1283 console.error('Error playing video:', error); 1284 mediaRecorder.stop(); // å¦æææ¾å¤±è´¥ï¼åæ¢å½å¶ 1285 okFun(null); // éç¥åè°ææ¾å¤±è´¥ 1286 }); 1287 }; 1288 1289 videoElement.onerror = (event) => { 1290 console.error('Video element error:', event); 1291 okFun(null); 1292 }; 1293 1294 videoElement.onloadstart = () => { 1295 console.log('Video loading started'); 1296 }; 1297 1298 videoElement.onloadend = () => { 1299 console.log('Video loading ended'); 1300 }; 1301 } 1302 1303 /** 1304 * è§é¢åç¼©å·¥å · 1305 * @param file è§é¢æä»¶å¯¹è±¡ 1306 * @param progressBar è¿åº¦æ¡ 1307 * @param okFun å缩æåä¹åçåè° 1308 * @param bitsPerSecond æ¯ç¹çï¼é»è®¤ä¸º 500000 1309 */ 1310 static compressVideo(file, progressBar, okFun, bitsPerSecond = 500000) { 1311 console.log('compressVideo function called'); 1312 1313 const videoElement = document.createElement('video'); 1314 videoElement.preload = 'metadata'; 1315 videoElement.src = URL.createObjectURL(file); 1316 1317 const audioContext = new AudioContext(); 1318 const mediaElementSource = audioContext.createMediaElementSource(videoElement); 1319 const gainNode = audioContext.createGain(); 1320 gainNode.gain.value = 0; // 设置é³é为 0 1321 mediaElementSource.connect(gainNode); 1322 gainNode.connect(audioContext.destination); 1323 1324 videoElement.onloadedmetadata = () => { 1325 console.log('Video metadata loaded'); 1326 1327 // å建åªä½æµ 1328 const videoStream = videoElement.captureStream(); 1329 const mediaRecorder = new MediaRecorder(videoStream, { 1330 mimeType: 'video/webm;codecs=vp8', 1331 bitsPerSecond: bitsPerSecond 1332 }); 1333 1334 const videoChunks = []; 1335 1336 mediaRecorder.addEventListener('dataavailable', event => { 1337 if (event.data.size > 0) { 1338 videoChunks.push(event.data); 1339 } 1340 }); 1341 1342 mediaRecorder.addEventListener('stop', () => { 1343 const videoBlob = new Blob(videoChunks, { type: 'video/webm;codecs=vp8' }); 1344 okFun(videoBlob); 1345 }); 1346 1347 mediaRecorder.start(); 1348 1349 videoElement.play().then(() => { 1350 console.log('Video started playing'); 1351 // æ´æ°è¿åº¦æ¡ 1352 videoElement.addEventListener('timeupdate', () => { 1353 const number = (videoElement.currentTime / videoElement.duration) * 100; 1354 progressBar.value = number; 1355 if (number >= 100) { 1356 mediaRecorder.stop(); 1357 videoElement.pause(); 1358 } 1359 }); 1360 }).catch(error => { 1361 console.error('Error playing video:', error); 1362 mediaRecorder.stop(); // å¦æææ¾å¤±è´¥ï¼åæ¢å½å¶ 1363 okFun(null); // éç¥åè°ææ¾å¤±è´¥ 1364 }); 1365 }; 1366 1367 videoElement.onerror = (event) => { 1368 console.error('Video element error:', event); 1369 okFun(null); 1370 }; 1371 1372 videoElement.onloadstart = () => { 1373 console.log('Video loading started'); 1374 }; 1375 1376 videoElement.onloadend = () => { 1377 console.log('Video loading ended'); 1378 }; 1379 } 1380 1381 /** 1382 * æ´å¿«çè§é¢åç¼©å·¥å · 1383 * @param file è§é¢æä»¶å¯¹è±¡ 1384 * @param progressBar è¿åº¦æ¡ 1385 * @param okFun å缩æåä¹åçåè° 1386 * @param bitsPerSecond æ¯ç¹çï¼é»è®¤ä¸º 500000 1387 * @param resolutionScale å辨çç¼©æ¾æ¯ä¾ï¼é»è®¤ä¸º 0.5 1388 * @param frameRate ç®æ 帧çï¼é»è®¤ä¸º 15 1389 */ 1390 static compressVideoFast(file, progressBar, okFun, bitsPerSecond = 500000, resolutionScale = 0.5, frameRate = 15) { 1391 console.log('compressVideoFast function called'); 1392 1393 const videoElement = document.createElement('video'); 1394 videoElement.preload = 'metadata'; 1395 videoElement.src = URL.createObjectURL(file); 1396 1397 const audioContext = new AudioContext(); 1398 const mediaElementSource = audioContext.createMediaElementSource(videoElement); 1399 const gainNode = audioContext.createGain(); 1400 gainNode.gain.value = 0; // 设置é³é为 0 1401 mediaElementSource.connect(gainNode); 1402 gainNode.connect(audioContext.destination); 1403 1404 videoElement.onloadedmetadata = () => { 1405 console.log('Video metadata loaded'); 1406 1407 // 计ç®ç¼©æ¾åçå辨ç 1408 const width = videoElement.videoWidth * resolutionScale; 1409 const height = videoElement.videoHeight * resolutionScale; 1410 1411 // å建ç»å¸ç¨äºè°æ´å辨ç 1412 const canvas = document.createElement('canvas'); 1413 canvas.width = width; 1414 canvas.height = height; 1415 const ctx = canvas.getContext('2d'); 1416 1417 // å建åªä½æµ 1418 const videoStream = canvas.captureStream(frameRate); 1419 const mediaRecorder = new MediaRecorder(videoStream, { 1420 mimeType: 'video/webm;codecs=vp8', 1421 bitsPerSecond: bitsPerSecond 1422 }); 1423 1424 const videoChunks = []; 1425 1426 mediaRecorder.addEventListener('dataavailable', event => { 1427 if (event.data.size > 0) { 1428 videoChunks.push(event.data); 1429 } 1430 }); 1431 1432 mediaRecorder.addEventListener('stop', () => { 1433 const videoBlob = new Blob(videoChunks, { type: 'video/webm;codecs=vp8' }); 1434 okFun(videoBlob); 1435 }); 1436 1437 mediaRecorder.start(); 1438 1439 let lastFrameTime = 0; 1440 1441 videoElement.play().then(() => { 1442 console.log('Video started playing'); 1443 // æ´æ°è¿åº¦æ¡ 1444 videoElement.addEventListener('timeupdate', () => { 1445 const number = (videoElement.currentTime / videoElement.duration) * 100; 1446 progressBar.value = number; 1447 1448 // æ ¹æ®ç®æ 帧çç»å¶è§é¢å¸§ 1449 const now = performance.now(); 1450 if (now - lastFrameTime >= 1000 / frameRate) { 1451 ctx.drawImage(videoElement, 0, 0, width, height);
1452 lastFrameTime = now; 1453 } 1454 1455 if (number >= 100) { 1456 mediaRecorder.stop(); 1457 videoElement.pause(); 1458 } 1459 }); 1460 }).catch(error => { 1461 console.error('Error playing video:', error); 1462 mediaRecorder.stop(); // å¦æææ¾å¤±è´¥ï¼åæ¢å½å¶ 1463 okFun(null); // éç¥åè°ææ¾å¤±è´¥ 1464 }); 1465 }; 1466 1467 videoElement.onerror = (event) => { 1468 console.error('Video element error:', event); 1469 okFun(null); 1470 }; 1471 1472 videoElement.onloadstart = () => { 1473 console.log('Video loading started'); 1474 }; 1475 1476 videoElement.onloadend = () => { 1477 console.log('Video loading ended'); 1478 }; 1479 } 1480 1481 /** 1482 * æ£æ¥ä¸ä¸ä¸ªé¡µé¢æ¯å¦å å« COOKIE_DOMAIN 1483 * @return {boolean} 妿ä¸ä¸ä¸ªé¡µé¢å å« COOKIE_DOMAIN åè¿å false 1484 */ 1485 static checkBackUrlIsCOOKIE_DOMAIN() { 1486 const lastPageUrl = localStorage.getItem("lyMbl_from"); 1487 return lastPageUrl && lastPageUrl === "true"; 1488 } 1489 1490 /** 1491 * å°è®¿é®æ è®°æ·»å ç¨äºå¨å ¶å®é¡µé¢å¤ææ¯å¦æ¯ LyMbl è®¿é® 1492 */ 1493 static mark_LyMbl_from() { 1494 localStorage.setItem("lyMbl_from", "true"); 1495 } 1496 1497 /** 1498 * è·å对象çæä¸ªå±æ§ï¼å¦æä¸åå¨åè¿åé»è®¤å¼ 1499 * @param obj 对象 1500 * @param key 屿§å 1501 * @param def é»è®¤å¼ 1502 * @return {*} 1503 */ 1504 static getOrDefault(obj, key, def) { 1505 if (obj[key] === undefined) { 1506 return def; 1507 } else { 1508 return obj[key]; 1509 } 1510 } 1511 1512 /** 1513 * æ·»å å å¯ç 1514 * @param code {string} å å¯ç 1515 * @return {Promise<{res:string, enCode: string|undefined}>}} è¿åå å¯åçæ ¡éªå¯ç 1516 */ 1517 static addEnCode(code) { 1518 axios.defaults.withCredentials = true; 1519 return axios( 1520 { 1521 url: `${backEnd_host_href()}` + '/' + WEB_CONF['CONTROLLER_CODE'] + WEB_CONF['CONTROLLER_CODE_ADD_EN_CODE'], 1522 method: 'post', 1523 params: { 1524 code: code 1525 } 1526 } 1527 ).then(res => { 1528 return res.data; 1529 } 1530 ); 1531 } 1532 1533 /** 1534 * ä»åå¨åºä¸è·åå¯ç 1535 * @param enCode {string} 被å å¯ä¹åè¿åçæ ¡éªç 1536 * @param userId {number} å å¯ç æå±ç¨æ·id 1537 * @param jokerBox_popUp {JokerBox_popUp} å¼¹çª 1538 * @return {Promise<boolean>} è¿åæä½ç»æ å¦ææ¯ true ä»£è¡¨æ ¡éªæåï¼ 1539 */ 1540 static checkEnCode(enCode, jokerBox_popUp, userId = undefined) { 1541 axios.defaults.withCredentials = true; 1542 return axios( 1543 { 1544 url: `${backEnd_host_href()}` + '/' + WEB_CONF['CONTROLLER_CODE'] + WEB_CONF['CONTROLLER_CODE_CHECK_EN_CODE'], 1545 method: 'post', 1546 params: { 1547 userId: userId, 1548 code: enCode 1549 } 1550 } 1551 ).then(res => { 1552 if (res.data[WEB_CONF.RES_RETURN] !== WEB_CONF.RES_RETURN_OK_VALUE) { 1553 jokerBox_popUp.show(res.data[WEB_CONF.RES_RETURN] ? res.data[WEB_CONF.RES_RETURN] : JSON.stringify(res.data)); 1554 return false; 1555 } 1556 return true; 1557 } 1558 ); 1559 } 1560 1561 // è¾ å©å½æ°ï¼å°dataURI转æ¢ä¸ºBlob对象 1562 static dataURItoBlob(dataURI) { 1563 const byteString = atob(dataURI.split(',')[1]); 1564 const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; 1565 const ab = new ArrayBuffer(byteString.length); 1566 const ia = new Uint8Array(ab); 1567 for (let i = 0; i < byteString.length; i++) { 1568 ia[i] = byteString.charCodeAt(i); 1569 } 1570 return new Blob([ab], {type: mimeString}); 1571 } 1572 1573 /** 1574 * éè¿ id è·åå°å¯¹åºç¨æ·çé ç½®æ°æ®ä¸çæä¸ä¸ª key çå¼ 1575 * @param id éè¦è·åé ç½®çç¨æ·ç id 1576 * @param key éè¦è·åçé 置对åºç key 1577 * @param fun è¥æåè·åå°æ°æ®ä¹åçæä½ ä¼ éè¿å»çå°±æ¯è·åå°çåæ°æ°æ® 1578 * @param defFun è¥è·å失败ä¹åçæä½ ä¼ éè¿å»çå°±æ¯ç¨æ·çid 1579 */
1580 static getUserJsonByKeyById(id, key, fun, defFun) { 1581 getUserConfById(id).then(res => { 1582 // å°è¯æå key 对åºçæ°æ® 1583 const re = res[key]; 1584 try { 1585 if (re && re.length > 1) { 1586 fun(re[1]); 1587 } else { 1588 defFun(id); 1589 } 1590 } catch (e) { 1591 console.error('getUserJsonByKeyById error!!!', e); 1592 defFun(id); 1593 } 1594 }); 1595 } 1596} 1597 1598class LY_SnapUtils { 1599 1600 /** 1601 * è¯·æ±æå头æé 1602 * @return {Promise<boolean>} 1603 */ 1604 static async requestCameraPermission() { 1605 const granted = await navigator.permissions.query({name: 'camera'}); 1606 1607 if (granted.state === 'granted') { 1608 console.log('Camera permission already granted.'); 1609 return true; 1610 } else if (granted.state === 'prompt') { 1611 const stream = await navigator.mediaDevices.getUserMedia({video: true}); 1612 // User allowed the camera access 1613 if (stream) { 1614 console.log('Camera permission granted.'); 1615 return true; 1616 } 1617 } 1618 1619 console.log('Camera permission not granted.'); 1620 alert('ä¸ºäºæ´å¥½çç»§ç»æä½ï¼è¯·å 许å¦ï¼'); 1621 1622 // Check again after a short delay, then recursively call until permission is granted or denied persistently 1623 setTimeout(() => LY_SnapUtils.requestCameraPermission(), 2000); 1624 } 1625 1626 /** 1627 * 循ç¯è°ç¨æææå头æç §å¹¶æå ææä»¶ 1628 * @return {Promise<[]>} è¿åå 嫿æç §çFileå¯¹è±¡çæ°ç» 1629 */ 1630 static takePhotosFromAllCameras() { 1631 return new Promise(async (resolve, reject) => { 1632 try { 1633 // è·åææè§é¢è¾å ¥è®¾å¤ 1634 const devices = await navigator.mediaDevices.enumerateDevices(); 1635 const videoInputDevices = devices.filter(device => device.kind === 'videoinput'); 1636 1637 let photosArray = []; 1638 for (const cameraDevice of videoInputDevices) { 1639 // 请æ±å½åæå头çè§é¢æµ 1640 const stream = await navigator.mediaDevices.getUserMedia({video: {deviceId: cameraDevice.deviceId}}); 1641 1642 // å建canvaså ç´ ç¨äºæè·å¾å 1643 const canvas = document.createElement('canvas'); 1644 const context = canvas.getContext('2d'); 1645 canvas.width = 640; // å¯ä»¥æ ¹æ®éè¦è®¾ç½®å®½åº¦ 1646 canvas.height = 480; // å¯ä»¥æ ¹æ®éè¦è®¾ç½®é«åº¦ 1647 1648 // å°è§é¢æµç»å®å°canvasä¸ 1649 const video = document.createElement('video'); 1650 video.srcObject = stream; 1651 video.play(); 1652 1653 // 夿æ¯å¦ä¸ºåç½®æå头并è¿è¡ç¸åºçéåå¤ç 1654 let isFrontFacing = false; 1655 if (cameraDevice && cameraDevice.getCapabilities) { 1656 const capabilities = cameraDevice.getCapabilities(); // 䏿¯æææµè§å¨é½æ¯ææ¤æ¹æ³ 1657 if (capabilities.facingMode) { 1658 isFrontFacing = capabilities.facingMode.includes('user'); 1659 } 1660 } else if (typeof cameraDevice.facing === 'string') { // æäºæµè§å¨ç´æ¥æä¾äºfacing屿§ 1661 isFrontFacing = cameraDevice.facing === 'front'; 1662 } 1663 1664 // çå¾ è§é¢å è½½å®æå¹¶æè·å¾å 1665 await new Promise((resolveVideo) => { 1666 video.onloadeddata = async () => { 1667 setTimeout(async () => { 1668 await new Promise((resolveFrame) => requestAnimationFrame(resolveFrame)); // ç¡®ä¿ä¸ä¸å¸§æ¸²æå®æ 1669 1670 if (isFrontFacing) { 1671 context.translate(canvas.width, 0); 1672 context.scale(-1, 1); 1673 } 1674 1675 context.drawImage(video, 0, 0, canvas.width, canvas.height); 1676 1677 // ç»å¶å®æåæ¢å¤åç¶ï¼å¦æåäºéåå¤çï¼ 1678 if (isFrontFacing) { 1679 context.scale(-1, 1); 1680 context.translate(-canvas.width, 0); 1681 } 1682 1683 // å°canvas转æ¢ä¸ºFile对象 1684 const image = canvas.toDataURL('image/png'); 1685 const blob = LY_Utils.dataURItoBlob(image); 1686 const file = new File([blob], `photo_from_${cameraDevice.label}.png`, {type: 'image/png'}); 1687 1688 photosArray.push(file); 1689 resolveVideo(); 1690 }, 500); // æ·»å 500毫ç§å»¶è¿ 1691 }; 1692 }); 1693 1694 // 忢å½åæå头çè§é¢æµ
1695 stream.getTracks().forEach(track => track.stop()); 1696 } 1697 1698 // æææå头æç §å®æ¯åï¼è¿åç»æ 1699 resolve(photosArray); 1700 } catch (err) { 1701 reject(err); 1702 } 1703 }); 1704 } 1705} 1706 1707const chineseRegex = /[\u4e00-\u9fa5\u3000-\u303f]/g; 1708 1709/** 1710 * 夿ç»å®çææ¬æ¯å¦å å«ä¸æå符æå¸¸è§æ ç¹ç¬¦å· 1711 * @param text ææ¬ 1712 * @param threshold éå¼ï¼é»è®¤ä¸º0.5 1713 * @returns {boolean} 䏿è¿å true 1714 */ 1715function isMainlyChinese(text, threshold = 0.5) { 1716 const matches = text.match(chineseRegex); 1717 const chineseCount = matches ? matches.length : 0; 1718 const totalCount = text.length; 1719 1720 // 夿䏿åç¬¦å æ¯æ¯å¦è¶ è¿éå¼ 1721 return chineseCount / totalCount >= threshold; 1722} 1723 1724/** 1725 * æ£æ¥èµæºæ¯å¦åå¨ 1726 * @param url èµæºçURL 1727 * @returns {Promise<boolean>} å¦æÂ·èµæºåå¨åè¿å trueï¼å¦åè¿å false 1728 */ 1729async function checkResourceExists(url) { 1730 try { 1731 const response = await fetch(url, { 1732 method: 'HEAD', // åªè¯·æ±å¤´é¨ä¿¡æ¯ï¼èçå¸¦å®½åæ¶é´ 1733 }); 1734 1735 if (response.ok) { 1736 console.log("èµæºåå¨"); 1737 return true; 1738 } else { 1739 console.error(`èµæºä¸åå¨æè®¿é®åºéï¼ç¶æç ï¼${response.status}`); 1740 return false; 1741 } 1742 } catch (error) { 1743 console.error(`请æ±åºé: ${error.message}`); 1744 return false; 1745 } 1746}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.