1// ==================== base.js ==================== 2(function (global) { 3 'use strict'; 4 5 // ç§æè·¯ç±é ç½® 6 const ROUTES = [ 7 {code: "/", text: "home"}, 8 {code: "product", text: "product"}, 9 {code: "collection", text: "collection"}, 10 {code: "page", text: "page"}, 11 {code: "blog_collection", text: "blog_collection"}, 12 {code: "blog_article", text: "blog_article"}, 13 {code: "search", text: "search"} 14 ]; 15 16 /** 17 * æ ¹æ®å½åURLè·¯å¾å¹é è·¯ç± 18 * @returns {String} å¹é å°çtextå¼ï¼æªå¹é å°è¿åundefined 19 */ 20 function getCurrentRouteText() { 21 // è·åå½åè·¯å¾å¹¶è§èå 22 const path = (global.location.pathname || '').replace(/^\/|\/$/g, '').toLowerCase(); 23 24 // ç¹æ®å¤çæ ¹è·¯å¾ 25 if (path === '' || path === 'en') { 26 const rootRoute = ROUTES.find(r => r.code === '/'); 27 return rootRoute ? rootRoute.text : undefined; 28 } 29 30 // æ¥æ¾å¹é çè·¯ç± 31 for (const route of ROUTES) { 32 if (route.code === '/') continue; // è·³è¿æ ¹è·¯ç± 33 34 const routeCode = route.code.toLowerCase(); 35 const regex = new RegExp(`(^|/)${routeCode}(/|$)`); 36 37 if (regex.test(path)) { 38 return route.text; 39 } 40 } 41 42 return undefined; 43 } 44 45 // å°æ¹æ³å®å ¨å°éå å°å ¨å±å¯¹è±¡ 46 if (typeof global.getCurrentRouteText === 'undefined') { 47 global.getCurrentRouteText = getCurrentRouteText; 48 } 49 50})(typeof window !== 'undefined' ? window : this); 51/** 52 * Cookie æä½å·¥å ·ç±» 53 */ 54window.Cookies = { 55 /** 56 * 设置 Cookie 57 * @param {string} name Cookie åç§° 58 * @param {string} value Cookie å¼ 59 * @param {Object} [options] é ç½®é项 60 * @param {number} [options.expires] è¿æå¤©æ° 61 * @param {string} [options.path] è·¯å¾ï¼é»è®¤ä¸º '/' 62 * @param {string} [options.domain] åå 63 * @param {boolean} [options.secure] æ¯å¦ä» HTTPS 64 * @param {string} [options.sameSite] SameSite 屿§ 65 */ 66 set: function (name, value, options = {}) { 67 let cookieText = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`; 68 69 // å¤çè¿ææ¶é´ 70 if (options.expires) { 71 const date = new Date(); 72 date.setTime(date.getTime() + options.expires * 24 * 60 * 60 * 1000); 73 cookieText += `; expires=${date.toUTCString()}`; 74 } 75 76 // è®¾ç½®è·¯å¾ 77 cookieText += `; path=${options.path || '/'}`; 78 79 // å¯éé ç½® 80 if (options.domain) { 81 cookieText += `; domain=${options.domain}`; 82 } 83 if (options.secure) { 84 cookieText += '; secure'; 85 } 86 if (options.sameSite) { 87 cookieText += `; samesite=${options.sameSite}`; 88 } 89 90 document.cookie = cookieText; 91 }, 92 93 /** 94 * è·å Cookie 95 * @param {string} name Cookie åç§° 96 * @returns {string|null} Cookie å¼ï¼ä¸å卿¶è¿å null 97 */ 98 get: function (name) { 99 const cookies = document.cookie.split('; '); 100 for (const cookie of cookies) { 101 const [cookieName, cookieValue] = cookie.split('='); 102 if (decodeURIComponent(cookieName) === name) { 103 return decodeURIComponent(cookieValue); 104 } 105 } 106 return null; 107 }, 108 109 /** 110 * å é¤ Cookie 111 * @param {string} name Cookie åç§° 112 * @param {string} [path] è·¯å¾ï¼é»è®¤ä¸º '/' 113 * @param {string} [domain] åå 114 */ 115 remove: function (name, path = '/', domain) { 116 this.set(name, '', { 117 expires: -1, 118 path, 119 domain 120 }); 121 } 122}; 123 124 125/** 126 * æ ¹æ®å½åURLè·¯å¾å¹é è·¯ç±å¹¶è¿å对åºçtextå¼ 127 * @param {Array} routes è·¯ç±é ç½®æ°ç» 128 * @returns {String} å¹é å°çtextå¼ï¼æªå¹é å°è¿åundefined 129 */ 130function getRouteText(routes) { 131 // è·åå½åè·¯å¾å¹¶è§èåï¼ç§»é¤é¦å°¾ææ ï¼ 132 const path = window.location.pathname.replace(/^\/|\/$/g, '').toLowerCase(); 133 134 // å¹é è·¯ç± 135 for (const route of routes) { 136 // è§èåè·¯ç±ä»£ç ï¼ç§»é¤é¦å°¾ææ ï¼
137 const routeCode = route.code.replace(/^\/|\/$/g, '').toLowerCase(); 138 139 // ç¹æ®å¤çæ ¹è·¯ç± 140 if (routeCode === '' && (path === '' || path === 'en')) { 141 return route.text; 142 } 143 144 // å建æ£å表达å¼ï¼å¹é è·¯ç±ä»£ç åºç°å¨è·¯å¾æ«å°¾æåé¢æææ 145 const regex = new RegExp(`(^|/)${routeCode}(/|$)`); 146 147 // æ£æ¥æ¯å¦å¹é 148 if (regex.test(path)) { 149 return route.text; 150 } 151 } 152 153 // 没æå¹é å°ä»»ä½è·¯ç± 154 return undefined; 155} 156 157// è·¯ç±é ç½® 158const routes = [ 159 {code: "/", text: "home"},//é¦é¡µ 160 {code: "product", text: "product"},//åå详æ 161 {code: "collection", text: "collection"},//ååä¸è¾ 162 {code: "page", text: "page"},//èªå®ä¹é¡µ 163 {code: "blog_collection", text: "blog_collection"},//å客ä¸è¾ 164 {code: "blog_article", text: "blog_article"},//å客æç« 165 {code: "search", text: "search"}//æç´¢ 166]; 167 168/** 169 * è·åURLåæ° 170 * @returns 171 */ 172function getQueryParams() { 173 const search = window.location.search; 174 const params = new URLSearchParams(search); 175 const result = {}; 176 177 // éåææåæ° 178 for (const [key, value] of params.entries()) { 179 result[key] = value; 180 } 181 182 return result; 183} 184 185/** 186 * å æ³ 187 * @param arg1 188 * @param arg2 189 * @returns 190 */ 191function accAdd(arg1, arg2) { 192 var r1, r2, m; 193 try { 194 r1 = arg1.toString().split(".")[1].length 195 } catch (e) { 196 r1 = 0 197 } 198 ; 199 try { 200 r2 = arg2.toString().split(".")[1].length 201 } catch (e) { 202 r2 = 0 203 } 204 ; 205 m = Math.pow(10, Math.max(r1, r2)); 206 return (arg1 * m + arg2 * m) / m; 207} 208 209/** 210 * åæ³ 211 * @param arg1 212 * @param arg2 213 * @returns 214 */ 215function accSubtr(arg1, arg2) { 216 var r1, r2, m, n; 217 try { 218 r1 = arg1.toString().split(".")[1].length; 219 } catch (e) { 220 r1 = 0; 221 } 222 try { 223 r2 = arg2.toString().split(".")[1].length; 224 } catch (e) { 225 r2 = 0; 226 } 227 m = Math.pow(10, Math.max(r1, r2)); 228 //卿æ§å¶ç²¾åº¦é¿åº¦ 229 n = (r1 >= r2) ? r1 : r2; 230 return ((arg1 * m - arg2 * m) / m).toFixed(n); 231} 232 233/*** 234 * 乿³ï¼è·åç²¾ç¡®ä¹æ³çç»æå¼ 235 * @param arg1 236 * @param arg2 237 * @returns 238 */ 239function accMul(arg1, arg2) { 240 var m = 0, s1 = arg1.toString(), s2 = arg2.toString(); 241 try { 242 m += s1.split(".")[1].length 243 } catch (e) { 244 } 245 ; 246 try { 247 m += s2.split(".")[1].length 248 } catch (e) { 249 } 250 ; 251 return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m); 252} 253 254/*** 255 * 餿³ï¼è·åç²¾ç¡®ä¹æ³çç»æå¼ 256 * @param arg1 257 * @param arg2 258 * @returns 259 */ 260 261//é¤ 262function floatDiv(arg1, arg2) { 263 var t1 = 0, t2 = 0, r1, r2; 264 try { 265 t1 = arg1.toString().split(".")[1].length 266 } catch (e) { 267 } 268 try { 269 t2 = arg2.toString().split(".")[1].length 270 } catch (e) { 271 } 272 273 r1 = Number(arg1.toString().replace(".", "")); 274 275 r2 = Number(arg2.toString().replace(".", "")); 276 return (r1 / r2) * Math.pow(10, t2 - t1); 277} 278 279 280/** 281 * AJAX请æ±å°è£ 282 * @param {Object} config 请æ±é ç½® 283 * @param {string} config.baseURL åºç¡URL 284 * @param {number} config.timeout è¶ æ¶æ¶é´(毫ç§) 285 * @param {Object} config.headers é»è®¤è¯·æ±å¤´ 286 */ 287// function createRequest(config = {}) { 288// // å卿æè¯·æ±çæ§å¶å¨ 289// const activeRequests = new Map(); 290// // é»è®¤é ç½® 291// const defaults = { 292// baseURL: '', 293// timeout: 10_000, 294// headers: { 295// 'Content-Type': 'application/json' 296// }, 297// responseType: 'json', 298// credentials: 'include' 299// }; 300// 301// // åå¹¶é ç½® 302// const requestConfig = { 303// ...defaults, 304// ...config, 305// headers: { 306// ...defaults.headers, 307// ...(config.headers || {}) 308// } 309// }; 310// 311// // æ¦æªå¨å®¹å¨ 312// const interceptors = { 313// request: [], 314// response: [] 315// }; 316// 317// // æ·»å æ¦æªå¨ 318// function addInterceptor(type, handler) { 319// if (interceptors[type]) { 320// interceptors[type].push(handler); 321// } 322// } 323// 324// /** 325// * å起请æ±ä¸»å½æ° 326// * @param {Object} options 请æ±é ç½® 327// * @returns {Promise} 328// */ 329// async function request(options) { 330// const controller = new AbortController(); 331// const requestId = Symbol('requestId'); 332// const timeoutId = setTimeout(() => controller.abort(), options.timeout || requestConfig.timeout); 333// 334// // å卿§å¶å¨å宿¶å¨ 335// const requestInfo = { 336// controller, 337// timeoutId, 338// promise: null 339// }; 340// activeRequests.set(requestId, requestInfo); 341// 342// try { 343// // åå¹¶é ç½® 344// const merged = { 345// ...requestConfig, 346// ...options, 347// headers: { 348// ...requestConfig.headers, 349// ...(options.headers || {}) 350// } 351// }; 352// 353// // å¤ç baseURL 354// if (merged.baseURL && !/^https?:\/\//.test(merged.url)) { 355// merged.url = merged.baseURL.replace(/\/$/, '') + '/' + merged.url.replace(/^\//, ''); 356// } 357// 358// // æ§è¡è¯·æ±æ¦æªå¨ 359// let chain = Promise.resolve(merged);
360// interceptors.request.forEach(fn => { 361// chain = chain.then(fn); 362// }); 363// 364// // åèµ·è¯·æ± 365// const fetchPromise = chain.then(processed => { 366// let {url, method = 'GET', data, headers, responseType} = processed; 367// 368// // å¤ç GET 请æ±åæ° 369// if (method.toUpperCase() === 'GET' && data && typeof data === 'object') { 370// const qs = new URLSearchParams(data).toString(); 371// url += (url.includes('?') ? '&' : '?') + qs; 372// } 373// 374// // æé fetch é项 375// const fetchOptions = { 376// method: method.toUpperCase(), 377// headers, 378// signal: controller.signal, 379// credentials: requestConfig.credentials 380// }; 381// 382// if (method.toUpperCase() !== 'GET' && data != null) { 383// if (headers['Content-Type']?.includes('application/json')) { 384// fetchOptions.body = JSON.stringify(data); 385// } else if (data instanceof FormData) { 386// fetchOptions.body = data; 387// delete fetchOptions.headers['Content-Type']; 388// } else { 389// fetchOptions.body = data; 390// } 391// } 392// 393// return fetch(url, fetchOptions) 394// .then(res => { 395// clearTimeout(timeoutId); 396// if (!res.ok) { 397// throw {status: res.status, statusText: res.statusText, res}; 398// } 399// switch (responseType) { 400// case 'text': 401// return res.text(); 402// case 'blob': 403// return res.blob(); 404// case 'arrayBuffer': 405// return res.arrayBuffer(); 406// default: 407// return res.json(); 408// } 409// }) 410// .then(resData => { 411// // æ§è¡ååºæ¦æªå¨ 412// let respChain = Promise.resolve(resData); 413// interceptors.response.forEach(fn => { 414// respChain = respChain.then(fn); 415// }); 416// return respChain; 417// }); 418// }); 419// 420// // åå¨ promise å¼ç¨ 421// requestInfo.promise = fetchPromise; 422// return await fetchPromise; 423// } catch (err) { 424// if (err.name === 'AbortError') { 425// throw {message: `Request aborted`}; 426// } 427// throw err; 428// } finally { 429// activeRequests.delete(requestId); 430// } 431// } 432// 433// // å¿«æ·æ¹æ³ 434// ['get', 'post', 'put', 'delete'].forEach(method => { 435// request[method] = function (url, data, opts = {}) { 436// return request({ 437// url, 438// method, 439// data, 440// ...opts 441// }); 442// }; 443// }); 444// 445// // æ¦æªå¨æ³¨å 446// request.interceptors = { 447// request: {use: fn => addInterceptor('request', fn)}, 448// response: {use: fn => addInterceptor('response', fn)} 449// }; 450// 451// // åæ¶åä¸ªè¯·æ± 452// request.cancel = function (promise) { 453// for (const [id, {controller, promise: storedPromise}] of activeRequests) { 454// if (promise === storedPromise) { 455// controller.abort(); 456// activeRequests.delete(id); 457// break; 458// } 459// } 460// }; 461// 462// // åæ¶ææè¯·æ± 463// request.cancelAll = function () {
464// activeRequests.forEach(({controller}) => { 465// controller.abort(); 466// }); 467// activeRequests.clear(); 468// }; 469// 470// return request; 471// } 472 473function createRequest(config = {}) { 474 const activeRequests = new Map(); 475 476 const defaults = { 477 baseURL: '', 478 timeout: 10_000, 479 headers: { 480 'Content-Type': 'application/json' 481 }, 482 responseType: 'json', 483 withCredentials: true, 484 async: true // æ¥å£è¯·æ±æ¯åæ¥è¿æ¯å¼æ¥ é»è®¤ true弿¥ 485 }; 486 487 const requestConfig = { 488 ...defaults, 489 ...config, 490 headers: { 491 ...defaults.headers, 492 ...(config.headers || {}) 493 } 494 }; 495 496 const interceptors = { 497 request: [], 498 response: [] 499 }; 500 501 function addInterceptor(type, handler) { 502 if (interceptors[type]) { 503 interceptors[type].push(handler); 504 } 505 } 506 507 // âä¿®å¤ï¼æå»ºæ¥è¯¢å符串çå·¥å ·ï¼æ¯ææ°ç»ä¸å¯¹è±¡ï¼ 508 function buildQuery(obj) { 509 if (!obj) return ''; 510 const parts = []; 511 Object.keys(obj).forEach(key => { 512 const val = obj[key]; 513 if (val === null || typeof val === 'undefined') return; 514 if (Array.isArray(val)) { 515 val.forEach(v => parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`)); 516 } else if (typeof val === 'object') { 517 // 对象类åç¨ JSON å符串å 518 parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`); 519 } else { 520 parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`); 521 } 522 }); 523 return parts.join('&'); 524 } 525 526 /** 527 * å起请æ±ä¸»å½æ° (XHR å®ç°) 528 */ 529 function request(options) { 530 const requestId = Symbol('requestId'); 531 // å æ¾ä¸ä¸ªå ä½ï¼åé¢ä¼èµ xhr / timeoutId / promise 532 const requestInfo = {xhr: null, timeoutId: null, promise: null}; 533 activeRequests.set(requestId, requestInfo); 534 535 const merged = { 536 ...requestConfig, 537 ...options, 538 headers: { 539 ...requestConfig.headers, 540 ...(options.headers || {}) 541 } 542 }; 543 544 // å¤ç baseURL 545 if (merged.baseURL && !/^https?:\/\//.test(merged.url)) { 546 merged.url = merged.baseURL.replace(/\/$/, '') + '/' + merged.url.replace(/^\//, ''); 547 } 548 549 // âä¼å ä»»ä½è¯·æ±ååºåå¿ é¡»å ç¡®ä¿ sessionId å·²å°±ç»ªï¼æ²¡æåå è°ç¨è´¦å·æ¥å£çæï¼ 550 function ensureSessionReady() { 551 // 廿æ¥è¯¢åæ°åæ«å°¾ææ ï¼é¿å /api/customer/account/info?xx=1 è¿ç±»åæ³æ¼å¤ 552 const normalizedUrl = (merged.url || '').split('?')[0].replace(/\/+$/, ''); 553 if (normalizedUrl === '/api/customer/account/info') { 554 // 该æ¥å£æ¬èº«ç¨äºè·å/çæ sessionIdï¼è·³è¿çå¾ ï¼é¿å æ»å¾ªç¯ 555 return Promise.resolve(); 556 } 557 if (typeof requestConfig.ensureSession === 'function') { 558 return requestConfig.ensureSession(); 559 } 560 return Promise.resolve(); 561 } 562 563 // æ§è¡è¯·æ±æ¦æªå¨ï¼å ç¡®ä¿ sessionId 就绪ï¼åè¿æ¦æªå¨ï¼ä¿è¯æ¦æªå¨éè½æ¿å°ææ° sessionIdï¼ 564 let chain = Promise.resolve() 565 .then(ensureSessionReady) 566 .then(() => merged); 567 interceptors.request.forEach(fn => { 568 chain = chain.then(fn); 569 }); 570 571 // âä¿®å¤ï¼å¨è¿éå¤ç GET ç params/dataï¼å¹¶æ body ä½¿ç¨ payloadï¼ 572 const xhrPromise = chain.then(processed => { 573 return new Promise((resolve, reject) => { 574 // 注æï¼åæ¶æ¯æ data å paramsï¼ä¼å dataï¼ 575 let {url, method = 'GET', data, params, headers, responseType, timeout} = processed; 576 const payload = (typeof data !== 'undefined') ? data : params; // âä¿®å¤ï¼æ¯æ params å«å 577 // â¡æ°å¢é»è¾ï¼GET ä¸ format=page_html æ¶ï¼å¦æå½åé¡µé¢ URL å« preview_theme_idï¼åéå ä¸ 578 // console.log('妿å½åé¡µé¢ URL å« preview_theme_idï¼åéå ä¸', payload) 579 if (method.toUpperCase() === 'GET' && payload && payload.format) { 580 try { 581 const currentUrl = window.location.href; 582 const match = currentUrl.match(/[?&]preview_theme_id=([^&]+)/); 583 if (match && match[1] && !payload.preview_theme_id) { 584 payload.preview_theme_id = match[1]; 585 } 586 } catch (e) { 587 console.warn('preview_theme_id check failed', e); 588 } 589 } 590 // âä¿®å¤ï¼å¦ææ¯ GETï¼æ payload åºååå° URL ä¸ 591 if (method.toUpperCase() === 'GET' && payload && typeof payload === 'object') { 592 const qs = buildQuery(payload); 593 if (qs) { 594 url += (url.includes('?') ? '&' : '?') + qs; 595 } 596 } 597 598 const xhr = new XMLHttpRequest(); 599 requestInfo.xhr = xhr; 600 601 xhr.open(method.toUpperCase(), url, merged.async); 602 xhr.timeout = timeout || requestConfig.timeout; 603 xhr.withCredentials = requestConfig.withCredentials; 604 605 // 设置 headers 606 for (const key in headers) { 607 if (headers[key] != null) { 608 xhr.setRequestHeader(key, headers[key]); 609 } 610 } 611 612 // 设置 responseTypeï¼XHR ä¸ç´æ¥æ¯æ jsonï¼ä½¿ç¨ responseText è§£æï¼ 613 if (['blob', 'arraybuffer', 'text'].includes(responseType)) { 614 xhr.responseType = responseType; 615 } 616 617 xhr.onload = () => { 618 clearTimeout(requestInfo.timeoutId); 619 // æ£æ¥HTTPç¶æç æ¯å¦å¨200-299ä¹é´ï¼è¡¨ç¤ºè¯·æ±æåï¼ 620 if (xhr.status >= 200 && xhr.status < 300) { 621 // 声æåéç¨äºåå¨è§£æåçååºæ°æ® 622 let result; 623 624 // å°è¯è§£æååºæ°æ® 625 try { 626 // 妿ååºç±»åæ¯JSONææªæå®ç±»åï¼åè§£æä¸ºJSON 627 if (responseType === 'json' || !responseType) { 628 // 妿æååºææ¬åè§£æä¸ºJSONï¼å¦å使ç¨ç©ºå¯¹è±¡ 629 result = xhr.responseText ? JSON.parse(xhr.responseText) : {}; 630 } else { 631 // éJSONç±»åï¼å¦ææ¬ãäºè¿å¶çï¼ç´æ¥ä½¿ç¨åå§ååº 632 result = xhr.response; 633 } 634 } catch (err) { 635 // 妿JSONè§£æå¤±è´¥ï¼è¿åè§£æé误 636 return reject({message: 'Parse errorï¼' + url, error: err, xhr}); 637 } 638 if (options.returnFullResponse) {//妿éè¦è¿å宿´çresultä¿¡æ¯ï¼é£ä¹å ä¸ä¸å± 639 $log('éè¦è¿å宿´ä¿¡æ¯') 640 result = { 641 ...result,//顺åºä¸è½ä¹±ï¼...resultè¦å¨ä¸é¢ 642 data: { 643 data: result, 644 }, 645 646 } 647 } 648 // å°è§£æåçç»æå è£ æPromise对
648象 649 let respChain = Promise.resolve(result); 650 651 // éåæææ³¨åçååºæ¦æªå¨ 652 interceptors.response.forEach(fn => { 653 // å°æ¦æªå¨ä¸²èèµ·æ¥ï¼ä¸ä¸ä¸ªæ¦æªå¨çè¾åºä½ä¸ºä¸ä¸ä¸ªçè¾å ¥ 654 respChain = respChain.then(fn); 655 }); 656 657 // è¿åå¤çåçPromiseé¾ 658 resolve(respChain); 659 } else { 660 // 妿HTTPç¶æç ä¸å¨200-299èå´å ï¼è¿åéè¯¯ä¿¡æ¯ 661 reject({status: xhr.status, statusText: xhr.statusText, xhr}); 662 } 663 }; 664 665 xhr.onerror = () => { 666 clearTimeout(requestInfo.timeoutId); 667 reject({message: 'Network error', xhr}); 668 }; 669 670 xhr.ontimeout = () => { 671 reject({message: 'Request timeout', xhr}); 672 }; 673 674 // âä¿®å¤ï¼åéæ°æ®æ¶ä½¿ç¨ payloadï¼è䏿¯åªç dataï¼ 675 if (method.toUpperCase() === 'GET' || payload == null) { 676 xhr.send(); 677 } else { 678 if ( 679 headers['Content-Type'] && 680 headers['Content-Type'].includes('application/json') && 681 !(payload instanceof FormData) 682 ) { 683 xhr.send(JSON.stringify(payload)); 684 } else { 685 xhr.send(payload); 686 } 687 } 688 689 // è®°å½ timeoutIdï¼è½ç¶ XHR æ xhr.timeoutï¼ä½æä»¬ä¹è·è¸ªï¼ 690 // 注ï¼ä¸é¢ä½¿ç¨ xhr.timeout + ontimeout å·²å¤ç¨ï¼ä½ä¿çæ¤åæ®µä»¥å ¼å®¹ä¹åé»è¾ 691 requestInfo.timeoutId = null; 692 }); 693 }); 694 695 requestInfo.promise = xhrPromise; 696 697 return xhrPromise.finally(() => { 698 activeRequests.delete(requestId); 699 }); 700 } 701 702 // å¿«æ·æ¹æ³ 703 ['get', 'post', 'put', 'delete'].forEach(method => { 704 request[method] = function (url, data, opts = {}) { 705 return request({ 706 url, 707 method, 708 data, 709 ...opts 710 }); 711 }; 712 }); 713 714 // æ¦æªå¨æ³¨å 715 request.interceptors = { 716 request: {use: fn => addInterceptor('request', fn)}, 717 response: {use: fn => addInterceptor('response', fn)} 718 }; 719 720 // åæ¶åä¸ªè¯·æ± 721 request.cancel = function (promise) { 722 for (const [id, {xhr, promise: storedPromise}] of activeRequests) { 723 if (promise === storedPromise && xhr) { 724 xhr.abort(); 725 activeRequests.delete(id); 726 break; 727 } 728 } 729 }; 730 731 // åæ¶ææè¯·æ± 732 request.cancelAll = function () { 733 activeRequests.forEach(({xhr}) => { 734 if (xhr) xhr.abort(); 735 }); 736 activeRequests.clear(); 737 }; 738 739 return request; 740} 741 742 743// åèµ·è¯·æ± 744// api.get('/users', { page: 1 }) 745// .then(res => console.log(res)) 746// .catch(err => console.error(err)); 747 748 749// åèµ·è¯·æ± 750// const req1 = api.get('/api/data1'); 751// const req2 = api.post('/api/data2', { key: 'value' }); 752 753// åæ¶åä¸ªè¯·æ± 754// api.cancel(req1) 755 756// åæ¶ææè¯·æ± 757// api.cancelAll(); 758 759const CLIENT_TYPE = '40' 760let language_code = '' //è¯è¨ 761 762//è¯è¨ 763const LANGUAGE_CODE = window.baseData.language_code 764//å½å®¶ 765const CURRENCY_CODE = window.baseData.currency_code 766/* 767 1.åç«¯å¦ææ²¡æsessionId, é£ä¹ä»cookie[ic_session_id]ä¸è¯»å, 妿åå¨ï¼åç´æ¥ä½¿ç¨è¯¥å¼ï¼ä¸éè¦è°ç¨apiçæsessionIdã妿ä¸åå¨ï¼åè°ç¨åæ¥çapiçæsessionId
768 2.妿å端æsessionIdï¼é£ä¹åå ¥ cookie[ic_session_id] ï¼å°sessionåå ¥cookieï¼è¦çï¼ï¼æ³¨æcookieçç½å为`'/'` ï¼å¦æåå¨ 769 */ 770 771// âä¼åï¼è·åææ° sessionIdï¼localStorage ä¼å ï¼å ¶æ¬¡ cookie[ic_session_id]ï¼ 772function getCurrentSessionId() { 773 return localStorage.getItem('sessionId') || ''; 774} 775 776// âä¼åï¼fecify-session-id ä¸ååæ»ï¼æ¹ä¸ºæ¯æ¬¡è¯·æ±åå¨è¯·æ±æ¦æªå¨ä¸å¨æè¯»åææ°å¼ 777const api = createRequest({ 778 baseURL: '', 779 timeout: 60000,//è¶ æ¶æ¶é´ 1åé 780 headers: { 781 'client-type': CLIENT_TYPE, 782 'language-code': LANGUAGE_CODE,//è¯è¨code 783 'currency-code': CURRENCY_CODE//å½å®¶code 784 }, 785 ensureSession: getSessionFn, // âæ°å¢ï¼è¯·æ±ååºåèªå¨å¤æ/çæ sessionIdï¼åè Vue é¡¹ç® myAxiosï¼ 786 returnFullResponse: false // å ¨å±é»è®¤è¿å response.data å¦æä¸º true å°è¿å ææçresponse å å« code ç 787}); 788 789 790// æ·»å è¯·æ±æ¦æªå¨ 791api.interceptors.request.use(config => { 792 // âä¼åï¼æ¯æ¬¡è¯·æ±å¨æè®¾ç½® fecify-session-idï¼ç¡®ä¿è¯·æ±ç¨çæ¯ææ° sessionId 793 const sessionID = getCurrentSessionId(); 794 if (config.url === '/api/customer/account/info') { 795 // è´¦å·ä¿¡æ¯æ¥å£æ¬èº«ç¨äºè·å/çæ sessionIdï¼æå带ä¸ï¼æ²¡æå空 796 config.headers['fecify-session-id'] = sessionID || ''; 797 } else { 798 // å ¶å®æ¥å£ï¼æå带ä¸ï¼æ²¡æåç¨ fec å ä½ï¼sessionId è·å失败ä¸é»å¡è¯·æ±ï¼ 799 config.headers['fecify-session-id'] = sessionID || ''; 800 } 801 $log('请æ±ååºå:', config); 802 return config; 803}); 804 805// æ·»å ååºæ¦æªå¨ 806api.interceptors.response.use(response => { 807 // $log('æ¶å°ååºle ------:', response); 808 // å¯ä»¥å¨è¿éå¤çååºæ°æ® 809 // å¦ææ¯ Blob 810 if (response instanceof Blob) { 811 $log('blob----------') 812 return {data: response}; 813 } 814 // 夿 code åæ®µ 815 // response å¯è½æ¯å¯¹è±¡ï¼è§£æåºæ¥ç jsonï¼ï¼é常é¿è¿æ ·ï¼{code: 200, data: {...}, message: ''} 816 // å ¼å®¹å端åç§æ å½¢ 817 818 if (response && typeof response === 'object' && 'code' in response) {//æ¯ä¸ªæ£å¸¸ç对象 819 if (response.code === 200) { 820 $log('æ£å¸¸æµç¨', response,) 821 return response.data; // æ£å¸¸æµç¨ 822 } else { 823 $log(' code ä¸ä¸º 200') 824 // code ä¸ä¸º 200ï¼æåºé误~ï¼è®©å¤é¢ catchï¼å¯ä»¥ææ´ä¸ª response æ message 齿åºå» 825 return Promise.reject(response); // æè Promise.reject(response.message) 826 } 827 } else {//è¿å¯¹è±¡é½ä¸æ¯ å±äºéè¯¯æ°æ®æ ¼å¼ 828 return Promise.reject(response); // æè Promise.reject(response.message) 829 830 } 831 // å ¼å®¹ä»¥åé»è¾ 832 return response && response.data !== undefined ? response.data : response; 833}); 834// 使ç¨å¿«æ·æ¹æ³ 835// api.get('/users') 836// .then(users => { 837// console.log('ç¨æ·å表:', users); 838// }); 839// 840// api.post('/users', { name: 'Alice', age: 25 }) 841// .then(result => { 842// console.log('åå»ºç»æ:', result); 843// }); 844 845 846// âä¼åï¼å ±äº«åä¸ä¸ªâsession çå¾ âPromiseï¼é¿å å¹¶åè¯·æ±æ¶éå¤è½®è¯¢/éå¤è¯·æ±è´¦å·æ¥å£ 847let sessionReadyPromise = null 848 849// è·åsessionId 850function getSessionFn() { 851 // å·²æ sessionIdï¼localStorage æ cookie[ic_session_id]ï¼ç´æ¥è¿å 852 const sessionID = getCurrentSessionId() 853 if (sessionID) { 854 // cookie æè localStorage æ²¡ææ¶ï¼åå localStorage 855 if (!localStorage.getItem('sessionId')) { 856 localStorage.setItem('sessionId', sessionID) 857 } 858 return Promise.resolve(true) 859 } 860 861 // å¤ä¸ªè¯·æ±åæ¶è¿æ¥æ¶å¤ç¨åä¸ä¸ªçå¾ ä¸ç Promise 862 if (sessionReadyPromise) { 863 return sessionReadyPromise 864 } 865 866 sessionReadyPromise = new Promise(resolve => { 867 let Interval = 0 868 let InterTime = null 869 870 getAccountInfo()// éè¿è´¦å·ä¿¡æ¯æ¥å£è·å/çæ sessionId 871 872 clearInterval(InterTime) 873 InterTime = setInterval(() => { 874 const id = getCurrentSessionId() 875 if (id) { 876 // è·åæåï¼åå ¥ localStorageï¼cookie ç± getAccountInfo è´è´£åæ¥ï¼ 877 localStorage.setItem('sessionId', id) 878 clearInterval(InterTime) 879 Interval = 0 880 sessionReadyPromise = null 881 resolve(true) 882 return 883 } 884 if (Interval > 1000) { 885 // æé¿çå¾ 20 ç§ï¼1000 次 à 20msï¼ï¼å¤±è´¥ä¸é»å¡è¯·æ±ï¼ç»§ç»ç¨å ä½ sessionId ååº 886 clearInterval(InterTime) 887 sessionReadyPromise = null 888 console.error('Failed to obtain the login status. Procedure') 889 resolve(false) 890 return 891 } 892 Interval++ 893 }, 20)//æ¯20æ¯«ç§ + 1 1000次 è¿äº20ç§é æé¿çå¾ æ¶é´20ç§ 894 }) 895 896 return sessionReadyPromise 897} 898 899// 页é¢å è½½æ¶é¢å sessionIdï¼æååèµ·è´¦å·ä¿¡æ¯è¯·æ±ï¼åç»è¯·æ±åºæ¬è½ç´æ¥å½ä¸ç¼åï¼ 900getSessionFn() 901 902// ================= æ¥å£è¯·æ± ================== 903/** 904 * è·åè´¦æ·ä¿¡æ¯ 905 * @param {Object} store Vuex store 对象 906 * @returns {Promise} è¿åè´¦æ·ä¿¡æ¯çPromise 907 */ 908function getAccountInfo(store) { 909 910 return new Promise(resolve => { 911 912 913 // æ£æ¥å ¨å±é 914 if (window.globalLockP) { 915 clearTimeout(window.tempTimeout); 916 window.tempTimeout = setTimeout(() => { 917 if (window.globalLockP) { 918 window.globalLockP = false; // 10ç§èªå¨è§£é 919 } 920 }, 1000); 921 return resolve(null);//è¿å¨éå®ç¶æï¼è¿å null 922 } 923 924 925 // ä¸é 926 window.globalLockP = true; 927 const currentTime = new Date().getTime(); 928 const time = 1000 * 60 * 60 * 24; // 24å°æ¶è¿æ 929 const loginStatus = localStorage.getItem('loginStatus'); 930 const localStorageSessionID = localStorage.getItem('sessionId'); 931 const ic_cookie_session_id = Cookies.get('ic_session_id') || ''; 932 let sessionID = ''; 933 934 // å¤ç session ID 935 if (localStorageSessionID) { 936 sessionID = localStorageSessionID; 937 Cookies.set('ic_session_id', sessionID, {expires: 730, path: '/'}); 938 } else if (ic_cookie_session_id) { 939 sessionID = ic_cookie_session_id; 940 localStorage.setItem('sessionId', sessionID); 941 } 942 943 const visit_country_code = Cookies.get('visit_country_code'); 944 945 // æ£æ¥æ¬å°ç¼åæ¯å¦ææ 946 if (loginStatus && sessionID && visit_country_code) { 947 const cookieSessionID = Cookies.get('ff_session_id'); 948 if (cookieSessionID !== sessionID) { 949 Cookies.set('ff_session_id', sessionID, {expires: 730, path: '/'}); 950 } 951 952 const parsedLoginStatus = JSON.parse(loginStatus); 953 parsedLoginStatus.sessionId = sessionID; 954 955 if (parsedLoginStatus._expiration && (currentTime - parsedLoginStatus._expiration) < time) { 956 window.loginStatus = parsedLoginStatus; 957 window.globalLockP = false; 958 return resolve(window.loginStatus); 959 } 960 } 961 962 963 // 使ç¨å°è£ çAPIæ¹æ³åèµ·è¯·æ± 964 api.get("/api/customer/account/info") 965 .then(res => { 966 967 968 res._expiration = currentTime; 969 const sessionID = localStorage.getItem('sessionId') || ''; 970 971 if (sessionID) { 972 res.sessionId = sessionID; 973 const cookieSessionID = Cookies.get('ff_session_id'); 974 if (cookieSessionID !== sessionID) { 975 Cookies.set('ff_session_id', sessionID, {expires: 730, path: '/'}); 976 } 977 } 978 979 const visit_country_code = res.visit_country_code || 'null'; 980 const local_visit_country_code = Cookies.get(visit_country_code); 981 982 if (!local_visit_country_code || visit_country_code === 'null') { 983 Cookies.set('visit_country_code', visit_country_code, {expires: 730, path: '/'}); 984 } 985 986 const setItem = { 987 isLogined: res.isLogined, 988 sessionId: res.sessionId, 989 customer: res.customer, 990 _expiration: res._expiration 991 }; 992 993 localStorage.setItem('loginStatus', JSON.stringify(setItem)); 994 window.loginStatus = setItem; 995 996 if (res.is_block_access) { 997 const blockAccessInfo = { 998 is_block_access: res.is_block_access || '', 999 _expiration: currentTime 1000 }; 1001 sessionStorage.setItem('blockAccessInfo', JSON.stringify(blockAccessInfo)); 1002 } 1003 1004 if (!sessionID) { 1005 const id = res.sessionId; 1006 localStorage.setItem('sessionId', id); 1007 Cookies.set('ff_session_id', id, {expires: 730, path: '/'}); 1008 Cookies.set('ic_session_id', id, {expires: 730, path: '/'}); 1009 } 1010 1011 window.globalLockP = false; 1012 resolve(window.loginStatus); 1013 }) 1014 .catch(error => { 1015 // console.error('è·åè´¦æ·ä¿¡æ¯å¤±è´¥:', error); 1016 1017 const obj = {
1018 isLogined: false, 1019 customer: { 1020 email: "", 1021 first_name: "", 1022 last_name: "" 1023 }, 1024 _expiration: currentTime 1025 }; 1026 1027 localStorage.setItem('loginStatus', JSON.stringify(obj)); 1028 window.globalLockP = false; 1029 resolve(null); 1030 }); 1031 }); 1032} 1033 1034document.addEventListener('DOMContentLoaded', function () { 1035 function loadRealImage(img) { 1036 var realSrc = img.getAttribute('data-src'); 1037 if (realSrc) { 1038 img.src = realSrc; 1039 } 1040 } 1041 1042 // æå è½½ 1043 var imgs = document.querySelectorAll('.liquid-image-lazy'); 1044 if ('IntersectionObserver' in window) { 1045 let observer = new IntersectionObserver(function (entries, obs) { 1046 entries.forEach(function (entry) { 1047 if (entry.isIntersecting) { 1048 loadRealImage(entry.target); 1049 obs.unobserve(entry.target); 1050 } 1051 }); 1052 }, {rootMargin: '0px 0px 100px 0px'}); 1053 1054 imgs.forEach(function (img) { 1055 observer.observe(img); 1056 }); 1057 } else { 1058 // fallback: ç´æ¥å è½½ 1059 imgs.forEach(loadRealImage); 1060 } 1061 1062 // å è½½å¤±è´¥æ¿æ¢ 1063 document.body.addEventListener('error', function (e) { 1064 var img = e.target; 1065 if (img.classList && img.classList.contains('liquid-image')) { 1066 var errImg = img.getAttribute('data-error-img'); 1067 if (img.src !== errImg && errImg) { 1068 img.src = errImg; 1069 } 1070 } 1071 }, true); 1072}); 1073 1074/** 1075 * 1076 * @url {string} 'https://xxxx.com/xxx/demo/type/1/id/2?search=123' 1077 * @param {string} ' urléçkeyå' 'search' 1078 * @param_data {string} urléçkeyå¼ '456' 1079 * @returns {string} 'https://xxxx.com/xxx/demo/type/1/id/2?search=456' 1080 * ç»url å¢å è·³è½¬åæ° ä¹å¯ä»¥æ¹ååæåæ°å¼ 1081 */ 1082function changeURLArg(url, param, param_data) { 1083 var pattern = param + '=([^&]*)'; 1084 var replaceText = param + '=' + param_data; 1085 if (url.match(pattern)) { 1086 var tmp = '/(' + param + '=)([^&]*)/gi'; 1087 tmp = url.replace(eval(tmp), replaceText); 1088 return tmp; 1089 } else { 1090 if (url.match('[\?]')) { 1091 return url + '&' + replaceText; 1092 } else { 1093 return url + '?' + replaceText; 1094 } 1095 } 1096} 1097 1098// å°å符串å®å ¨å°è½¬æ¢ä¸º JavaScript æ°ç»ï¼å¹¶å¨å符串ä¸ç¬¦åè½¬æ¢æ¡ä»¶æ¶è¿å空æ°ç» 1099function safeJsonStringToArray(jsonStr) { 1100 try { 1101 // å°è¯è§£æ JSON å符串 1102 const parsed = JSON.parse(jsonStr); 1103 1104 // æ£æ¥è§£æç»ææ¯å¦æ¯ä¸ä¸ªæ°ç» 1105 if (Array.isArray(parsed)) { 1106 return parsed; 1107 } else { 1108 // 䏿¯æ°ç»ï¼è¿å空æ°ç» 1109 return []; 1110 } 1111 } catch (error) { 1112 // è§£æå¤±è´¥ï¼è¿å空æ°ç» 1113 return []; 1114 } 1115} 1116 1117/** 1118 * 1119 * @param val {string} 夿æ¯å¦æ¯é®ç®± é®ä»¶æ ¼å¼æ ¡éª é®ç®±æ ¼å¼æ ¡éª 1120 * @returns {boolean} 1121 */ 1122function testEmail(val) { 1123 // ä¸è½åºç°è¿ç»ç .. 1124 if (val.includes('..')) { 1125 return false; 1126 } 1127 let reg = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 1128 return reg.test(val) 1129} 1130 1131function $log(...info) {//è¾åºconsole æå°ä¿¡æ¯ 1132 if (window.baseData && window.baseData.debug) {//å¼åç¯å¢ 1133 console.log('[debug]', ...info) 1134 } 1135} 1136 1137// çé页 1138function get_fist_loading_url() { 1139 let fist_loading_url = sessionStorage.getItem('fist_loading_url') || null 1140 if (!fist_loading_url) {//æ¥ä¸å°è¯´ææ²¡æåå¨è½å°é¡µ 1141 sessionStorage.setItem('fist_loading_url', location.href) 1142 return location.href 1143 } 1144 return fist_loading_url //æ¥å°äºè¿åè½å°é¡µ 1145} 1146 1147get_fist_loading_url() 1148 1149function getGclidQuery() {//æ¯å¦åå¨åæ°gclidï¼å¦æåå¨ï¼é£ä¹éè¦å¨äºä»¶ gtag("event ä¸å å ¥ gclid:"xxxxxx" 1150 // åå¨30å¤©æææ 1151 const gclid = new URLSearchParams(window.location.search).get("gclid");//è°·æå¹¿ååæ° 1152 if (gclid) { 1153 Cookies.set( 1154 'gg_gclid', 1155 gclid, 1156 {expires: 30, path: '/'}//30å¤©è¿æ 1157 ) 1158 // Cookies.get('gg_gclid') 1159 } 1160 1161} 1162 1163getGclidQuery() 1164 1165function fReloadAccountInfo() {//éåºè´¦æ·å·æ°ç¶æ 1166 return new Promise(resolve => { 1167 localStorage.removeItem('loginStatus')
1168 sessionStorage.removeItem('cart_qty')//æ¨åºç»å½äºæä»¥è´ç©è½¦ä¹è¦éæ°å·æ° 1169 localStorage.removeItem('userIsAffiliate')//éåºç»å½äºç½çåå é¤ 1170 resolve() 1171 }) 1172} 1173 1174/* 1175* æ¹åæµè§å¨ URL ä¸ç query ä¿¡æ¯ å¦æ baidu.com?step=2 => baidu.com?step=3 1176* 1177* setUrlVariant('step', this.mapCode[dataStep]) 1178* */ 1179function setUrlVariant(key, val) { //æ¹åæµè§å¨ URL ä¸ç query ä¿¡æ¯ å¦æ baidu.com?step=2 => baidu.com?step=3 1180 $log('setUrlVariant', key, val) 1181 1182 function changeURLArg(url, param, param_data) { 1183 var pattern = param + '=([^&]*)'; 1184 var replaceText = param + '=' + param_data; 1185 if (url.match(pattern)) { 1186 var tmp = '/(' + param + '=)([^&]*)/gi'; 1187 tmp = url.replace(eval(tmp), replaceText); 1188 return tmp; 1189 } else { 1190 if (url.match('[\?]')) { 1191 return url + '&' + replaceText; 1192 } else { 1193 return url + '?' + replaceText; 1194 } 1195 } 1196 } 1197 1198 let newUrl = changeURLArg(window.location.href, key, val) 1199 history.replaceState("", "", newUrl);//æ¿æ¢ä¸ºå½åurl 1200} 1201 1202//设å¤ä¿¡æ¯ 1203function fOsInfo() { 1204 var ua = navigator.userAgent, 1205 isWindowsPhone = /(?:Windows Phone)/.test(ua), 1206 isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone, 1207 isAndroid = /(?:Android)/.test(ua), 1208 isFireFox = /(?:Firefox)/.test(ua), 1209 isChrome = /(?:Chrome|CriOS)/.test(ua), 1210 isSafari = ua.indexOf("Safari") != -1 && ua.indexOf("Version") != -1, //éè¿æµè§å¨ï¼æºååtouchäºä»¶å¤æ 1211 isIphone = ua.indexOf("iPhone") != -1 && ua.indexOf("Version") != -1, 1212 isIPad = isSafari && !isIphone && !isAndroid && 'ontouchend' in document, 1213 isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && 1214 /(?:Tablet)/.test(ua)) || isIPad, 1215 isPhone2 = /(?:iPhone)/.test(ua) && !isTablet, 1216 isPc = !isPhone2 && !isAndroid && !isSymbian; 1217 1218 return { 1219 isPad: isTablet, 1220 isPhone: isPhone2, 1221 isAndroid: isAndroid, 1222 isPc: isPc 1223 }; 1224} 1225 1226// 夿æ¯å¦ä¸ºå¾®ä¿¡æµè§å¨ 1227function isWeixinBrowser() { 1228 let ua = navigator.userAgent.toLowerCase(); 1229 return /micromessenger/.test(ua); 1230} 1231 1232function $getAddonsPerm(addonsName) { // ä½¿ç¨æ¹æ³ $getAddonsPerm.call(this,æä»¶å)
1233 let addons = window?.baseData?.addons || [] 1234 return addons.includes(addonsName) 1235} 1236 1237function browserJudge(needversion = false) { //æµè§å¨agent 1238 var document = window.document, 1239 navigator = window.navigator, 1240 agent = navigator.userAgent.toLowerCase(), 1241 //IE8+æ¯æ.è¿åæµè§å¨æ¸²æå½åææ¡£æç¨çæ¨¡å¼ 1242 //IE6,IE7:undefined.IE8:8(å ¼å®¹æ¨¡å¼è¿å7).IE9:9(å ¼å®¹æ¨¡å¼è¿å7||8) 1243 //IE10:10(å ¼å®¹æ¨¡å¼7||8||9) 1244 IEMode = document.documentMode, 1245 //chorme 1246 chrome = window.chrome || false, 1247 System = { 1248 //user-agent 1249 agent: agent, 1250 //æ¯å¦ä¸ºIE 1251 isIE: /msie/.test(agent), 1252 //Geckoå æ ¸ 1253 isGecko: agent.indexOf("gecko") > 0 && agent.indexOf("like gecko") < 0, 1254 //webkitå æ ¸ 1255 isWebkit: agent.indexOf("webkit") > 0, 1256 //æ¯å¦ä¸ºæ 忍¡å¼ 1257 isStrict: document.compatMode === "CSS1Compat", 1258 //æ¯å¦æ¯æsubtitle 1259 supportSubTitle: function () { 1260 return "track" in document.createElement("track"); 1261 }, 1262 //æ¯å¦æ¯æscoped 1263 supportScope: function () { 1264 return "scoped" in document.createElement("style"); 1265 }, 1266 //è·åIEççæ¬å· 1267 ieVersion: function () { 1268 try { 1269 return agent.match(/msie ([\d.]+)/)[1] || 0; 1270 } catch (e) { 1271 console.log("error"); 1272 return IEMode; 1273 } 1274 }, 1275 //Operaçæ¬å· 1276 operaVersion: function () { 1277 try { 1278 if (window.opera) { 1279 return agent.match(/opera.([\d.]+)/)[1]; 1280 } else if (agent.indexOf("opr") > 0) { 1281 return agent.match(/opr\/([\d.]+)/)[1]; 1282 } 1283 } catch (e) { 1284 console.log("error"); 1285 return 0; 1286 } 1287 }, 1288 //æè¿°:versionè¿æ»¤.å¦31.0.252.152 åªä¿ç31.0 1289 versionFilter: function () { 1290 let start = '' 1291 let end = '' 1292 if (arguments.length === 1 && typeof arguments[0] === "string") { 1293 var version = arguments[0]; 1294 start = version.indexOf("."); 1295 if (start > 0) { 1296 end = version.indexOf(".", start + 1); 1297 if (end !== -1) { 1298 return version.substr(0, end); 1299 } 1300 } 1301 return version; 1302 } else if (arguments.length === 1) { 1303 return arguments[0]; 1304 } 1305 return 0; 1306 } 1307 }; 1308 1309 try { 1310 //æµè§å¨ç±»å(IEãOperaãChromeãSafariãFirefox) 1311 System.type = System.isIE ? "IE" : 1312 window.opera || (agent.indexOf("opr") > 0) ? "Opera" : 1313 (agent.indexOf("chrome") > 0) ? "Chrome" : 1314 //safari乿ä¾äºä¸é¨çå¤å®æ¹å¼ 1315 agent.indexOf("safari") > 0 ? "Safari" : 1316 (agent.indexOf("firefox") > 0) ? "Firefox" : 1317 'unknow'; 1318 isWeixinBrowser() ? System.type = 'micromessenger' : '' 1319 1320 //çæ¬å· 1321 System.version = (System.type === "IE") ? System.ieVersion() : 1322 (System.type === "Firefox") ? agent.match(/firefox\/([\d.]+)/)[1] : 1323 (System.type === "Chrome") ? agent.match(/chrome\/([\d.]+)/)[1] : 1324 (System.type === "Opera") ? System.operaVersion() : 1325 (System.type === "Safari") ? agent.match(/version\/([\d.]+)/)[1] : 1326 "0"; 1327 1328 //æµè§å¨å¤å£³ 1329 System.shell = function () { 1330 if (isWeixinBrowser()) { 1331 return "微信æµè§å¨"; 1332 } 1333 //鍿¸¸æµè§å¨ 1334 if (agent.indexOf("maxthon") > 0) { 1335 System.version = agent.match(/maxthon\/([\d.]+)/)[1] || System.version; 1336 return "岿¸¸æµè§å¨"; 1337 } 1338 //QQæµè§å¨ 1339 if (agent.indexOf("qqbrowser") > 0) { 1340 System.version = agent.match(/qqbrowser\/([\d.]+)/)[1] || System.version; 1341 return "QQæµè§å¨"; 1342 } 1343 1344 //æçæµè§å¨ 1345 if (agent.indexOf("se 2.x") > 0) {
1346 return 'æçæµè§å¨'; 1347 } 1348 1349 //Chrome:ä¹å¯ä»¥ä½¿ç¨window.chrome && window.chrome.webstore夿 1350 if (chrome && System.type !== "Opera") { 1351 var external = window.external, 1352 clientInfo = window.clientInformation, 1353 //客æ·ç«¯è¯è¨:zh-cn,zh.360ä¸é¢ä¼è¿åundefined 1354 clientLanguage = clientInfo.languages; 1355 1356 //çè±¹æµè§å¨:æè agent.indexOf("lbbrowser")>0 1357 if (external && 'LiebaoGetVersion' in external) { 1358 return 'çè±¹æµè§å¨'; 1359 } 1360 //ç¾åº¦æµè§å¨ 1361 if (agent.indexOf("bidubrowser") > 0) { 1362 System.version = agent.match(/bidubrowser\/([\d.]+)/)[1] || 1363 agent.match(/chrome\/([\d.]+)/)[1]; 1364 return "ç¾åº¦æµè§å¨"; 1365 } 1366 //360æéæµè§å¨å360å®å ¨æµè§å¨ 1367 if (System.supportSubTitle() && typeof clientLanguage === "undefined") { 1368 //object.key()è¿åä¸ä¸ªæ°ç».å å«å¯æä¸¾å±æ§åæ¹æ³åç§° 1369 var storeKeyLen = Object.keys(chrome.webstore).length, 1370 v8Locale = "v8Locale" in window; 1371 return storeKeyLen > 1 ? '360æéæµè§å¨' : '360å®å ¨æµè§å¨'; 1372 } 1373 return "Chrome"; 1374 } 1375 }; 1376 1377 //æµè§å¨åç§°(妿æ¯å£³æµè§å¨,åè¿å壳åç§°) 1378 System.name = System.shell(); 1379 //å¯¹çæ¬å·è¿è¡è¿æ»¤è¿å¤ç 1380 System.version = System.versionFilter(System.version); 1381 return System 1382 1383 } catch (e) { 1384 console.log("error"); 1385 } 1386 if (needversion) { 1387 return System.type + ' ' + System.version 1388 } else { 1389 return System.type 1390 } 1391} 1392 1393function fGetClientInfo() { 1394 let os = fOsInfo() 1395 let fist_loading_url = get_fist_loading_url()//è·åçé页 1396 let client_type = '' 1397 var w = document.documentElement.clientWidth || document.body.clientWidth; 1398 var h = document.documentElement.clientHeight || document.body.clientHeight; 1399 if (isWeixinBrowser()) {//微信æµè§å¨ 1 PC 2 å¹³æ¿ 3 ææº 1400 client_type = 3 1401 } else if (os.isPad) { 1402 client_type = 2 1403 } else if (os.isPc) { 1404 client_type = 1 1405 } else if (os.isPhone || os.isAndroid) { 1406 client_type = 3 1407 } else {//è·åä¸å°å°±é»è®¤ä¸º1 pc 1408 client_type = 1 1409 } 1410 // // let user_agent = isWeixinBrowser() ? 'micromessenger': browserJudge() 1411 // let user_agent = browserJudge() 1412 // console.log('user_agent',user_agent) 1413 let browserJudgeInfo = browserJudge() 1414 1415 let resultParams = { 1416 client_type: client_type,//设å¤ç±»åï¼1代表pcï¼2代表平æ¿ï¼3ä»£è¡¨ææº 1417 browser_lang: navigator.browserLanguage || navigator.language,//æµè§å¨è¯è¨ 1418 browser_height: h,//æµè§å¨é«åº¦ 1419 browser_width: w,//æµè§å¨å®½åº¦ 1420 // user_agent: browserJudgeInfo.agent,//æµè§å¨agent 1421 user_agent: browserJudgeInfo.name || browserJudgeInfo.type, //æ æç ä¸ç¨ä¼ agentå°±ä¼ æµè§å¨ç±»å 1422 referring_site: document.referrer || '', 1423 fist_loading_url: fist_loading_url,//çé页url 1424 browser_name: browserJudgeInfo.name || browserJudgeInfo.type 1425 } 1426 let baseData = window.baseData || {} 1427 if (baseData && baseData.addons && baseData.addons.indexOf("affiliate") > -1) {//ç½çæä»¶åå¨å¹¶ä¸æè¿½è¸ªID 1428 let f_tracking_id = Cookies.get('f_tracking_id') || '' 1429 if (f_tracking_id) { 1430 resultParams['affiliate_tracking_id'] = f_tracking_id 1431 } 1432 } 1433 return resultParams 1434 1435} 1436 1437 1438function $T(txt, params) {//æ ¹æ®å½åéæ©è¯è¨æ¸²æå¯¹åºè¯è¨
1439 let code = window['baseData']?.language_code 1440 if (!code) { 1441 return txt 1442 } 1443 if (!params) { 1444 return txt 1445 } 1446 if (params.hasOwnProperty(code)) return params[code] 1447 return txt 1448} 1449 1450/** 1451 * è·åç»å½ä¿¡æ¯ï¼å¦æç»å½è¿å customerï¼å¦åè¿å false 1452 * @returns {Object|boolean} 1453 */ 1454function $getLoginInfo() { 1455 const obj = localStorage.getItem('loginStatus'); 1456 if (!obj) return false; 1457 1458 let info; 1459 try { 1460 info = JSON.parse(obj); 1461 } catch (e) { 1462 // åå¨çæ°æ®ä¸æ¯ææ JSON 1463 return false; 1464 } 1465 1466 if (info && info.isLogined === true && info.customer) { 1467 return info.customer; 1468 } 1469 return false; 1470} 1471 1472// å ¨å±åéï¼è®°å½å½åæ¯å¦ç§»å¨ç«¯ 1473var $isMobile = isMobileDevice(); 1474var $isPad = isClientIsPad(); 1475 1476 1477/** 1478 * 鲿彿°ï¼å¨é¢ç¹è§¦åçæ åµä¸ï¼å¦ resizeï¼ï¼åªææå䏿¬¡ä¼çæ 1479 * @param {function} fn è¦æ§è¡ç彿° 1480 * @param {number} delay å»¶è¿å¤å°æ¯«ç§æ§è¡ï¼åä½ msï¼ 1481 * @returns {function} 鲿å è£ åçæ°å½æ° 1482 */ 1483function debounce(fn, delay) { 1484 let timer; 1485 return function () { 1486 clearTimeout(timer); 1487 timer = setTimeout(fn, delay); 1488 } 1489} 1490 1491/** 1492 * å±å¹å°ºå¯¸ååæ¶çå¤ç彿° 1493 * 1. è·åå½åæ¯å¦ä¸ºç§»å¨ç«¯ 1494 * 2. 妿åä¸ä¸æ¬¡çç¶æåçäºååï¼åæ´æ° $isMobile 1495 * 3. æ´¾åèªå®ä¹äºä»¶ï¼ä¾¿äºå ¶å®æ¨¡åçå¬ååº 1496 */ 1497const onResize = debounce(() => { 1498 // 夿å½åç¶æ 1499 const nowIsMobile = isMobileDevice(); 1500 // ç¶æåçååæ¶æå¤ç 1501 if (nowIsMobile !== $isMobile) { 1502 $isMobile = nowIsMobile; // æ´æ°å ¨å±åé 1503 1504 // æ ¹æ®ç¶ææ´¾åäºä»¶ 1505 if ($isMobile) { 1506 // 忢å°ç§»å¨ç«¯ 1507 window.FecTheme.config.mqlMobile = true; 1508 document.dispatchEvent(new Event('matchMobile')); 1509 } else { 1510 // 忢尿¡é¢ç«¯ 1511 window.FecTheme.config.mqlMobile = false; 1512 document.dispatchEvent(new Event('unmatchMobile')); 1513 } 1514 } 1515 // 夿å½åç¶æ 1516 const nowIsPad = isClientIsPad(); 1517 // ç¶æåçååæ¶æå¤ç 1518 if (nowIsPad !== $isPad) { 1519 $isPad = nowIsPad; // æ´æ°å ¨å±åé 1520 } 1521}, 200); // 200 毫ç§é²æ 1522 1523// ç嬿µè§å¨çªå£å°ºå¯¸ååäºä»¶ 1524window.addEventListener('resize', onResize); 1525 1526 1527/** 1528 * 夿å½åç¯å¢æ¯å¦ä¸ºç§»å¨ç«¯ 1529 * 1. å±å¹å®½åº¦å°äº 768 åç´ ï¼æè 1530 * 2. userAgent å å«ä¸»æµç§»å¨è®¾å¤å ³é®è¯ 1531 * è¿å true 表示移å¨ç«¯ï¼false 表示æ¡é¢ç«¯ 1532 */ 1533function isMobileDevice() { 1534 return window.innerWidth < 768 || 1535 /mobile|android|iphone|ipod|ipad|windows phone|blackberry/i.test(navigator.userAgent.toLowerCase()); 1536} 1537 1538// 夿æ¯å¦ä¸ºPad 1539function isClientIsPad() { 1540 1541 var ua = navigator.userAgent.toLowerCase(); 1542 let status1 = /ipad/i.test(ua); 1543 let status2 = (navigator.platform === 'iPad'); 1544 let clientWidth = document.documentElement.clientWidth 1545 if (status1 || status2 || (clientWidth > 750 && clientWidth <= 1200)) { 1546 return true 1547 } else { 1548 return false 1549 } 1550} 1551 1552 1553/** 1554 * 1555 * @param {string} 'https://xxxx.com/xxx/demo/type/1/id/2' 1556 * @returns {object} {type:'1',id:'2'} 1557 * æ ¹æ®url è·å 对象 1558 */ 1559function getUrlParam(url) { 1560 let reg_url = /^[^\?]+\?([\w\W]+)$/, 1561 reg_para = /([^&=]+)=([\w\W]*?)(&|$|#)/g, 1562 arr_url = reg_url.exec(url), 1563 ret = {}; 1564 1565 if (arr_url && arr_url[1]) { 1566 let str_para = arr_url[1], result; 1567 1568 while ((result = reg_para.exec(str_para)) != null) { 1569 1570 let key = decodeURIComponent( 1571 result[1].replace('[]', '') 1572 ); 1573 1574 // å ³é®ä¿®å¤ï¼+ è½¬ç©ºæ ¼ 1575 let value = decodeURIComponent( 1576 result[2].replace(/\+/g, ' ') 1577 ); 1578 1579 if (ret[key] || ret[key] === '0' || result[1].indexOf('[]') > -1) { 1580 1581 if (Array.isArray(ret[key])) { 1582 1583 ret[key].push(value); 1584 1585 } else if (!ret[key]) { 1586 1587 ret[key] = [value]; 1588 1589 } else { 1590 1591 let val = ret[key]; 1592 ret[key] = [val, value]; 1593 } 1594 1595 } else { 1596 1597 ret[key] = value; 1598 } 1599 } 1600 } 1601 1602 return ret; 1603} 1604 1605/**å°å¯¹è±¡è½¬æ¢ä¸ºurlåæ°å½¢å¼ 1606 * @property {Object} param å°è¦è½¬æ¢ä¸ºURLåæ°çå符串对象 1607 * @property {String} key URL åæ°å符串çåç¼ 1608 * @property {Boolean} encode æ¯å¦è¿è¡URLç¼ç ï¼é»è®¤ä¸ºtrue 1609 * @return {String} URLåæ°å符串 1610 */ 1611function urlEncode(param, key, encode) { 1612 if (param == null) return ''; 1613 var paramStr = ''; 1614 var t = typeof (param); 1615 if (t == 'string' || t == 'number' || t == 'boolean') { 1616 paramStr += '&' + key + '=' + ((encode == null || encode) ? encodeURIComponent(param) : param); 1617 } else { 1618 for (var i in param) { 1619 var k = key == null ? i : key + (param instanceof Array ? '[' + ']' : '.' + i);//廿䏿 1620 // var k = key == null ? i: key + (param instanceof Array ? '[' + i + ']': '.' + i); //带æä¸æ 1621 paramStr += urlEncode(param[i], k, encode); 1622 } 1623 } 1624 return paramStr; 1625} 1626 1627function google_trace() {//å¢å¼ºåçµååå¡ 1628 return window?.site_base_data?.google_trace || ''; 1629} 1630 1631function google_conversion() { 1632 return window.site_base_data.google_conversion || [] 1633} 1634 1635 1636function bind_enquiry_trace() { 1637 /* 1638 * contacts 1639 * ååå询价 1640 * å¤åå询价 1641 * 1642 * */ 1643 console.log('询价 + contacts --------- bing') 1644 1645 let bing_trace = window?.baseData?.bing_trace || false 1646 if (!bing_trace) return 1647 window.uetq = window.uetq || []; 1648 window.uetq.push('event', 'submit_lead_form', {}); 1649} 1650 1651function formatMoney(amount, el) { 1652 // åªåæ¨¡æ¿ 1653 let scriptElement = document.querySelector(el); 1654 if (!scriptElement) return ''; 1655 1656 // ç¼ååå§æ¨¡æ¿ï¼åªå䏿¬¡ 1657 if (!scriptElement._cachedTemplate) { 1658 scriptElement._cachedTemplate = scriptElement.innerHTML.trim(); 1659 } 1660 const template = scriptElement._cachedTemplate; 1661 1662 // æåå§ä»·æ ¼ 1663 const priceSpanElement = template.match(/<span>([^<]+)<\/span>/); 1664 if (!priceSpanElement) return template; 1665 1666 let originalPriceText = priceSpanElement[1];
1667 1668 // å¤ææ ¼å¼ 1669 const hasDecimal = originalPriceText.includes('.'); 1670 const hasComma = originalPriceText.includes(','); 1671 let formattedPrice; 1672 1673 if (hasDecimal && /\.\d{2}$/.test(originalPriceText)) { 1674 // å°æ°ç¹ï¼ä¸¤ä½å°æ° 1675 let [intPart, decimalPart] = parseFloat(amount).toFixed(2).split('.'); 1676 intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ","); 1677 formattedPrice = intPart + '.' + decimalPart; 1678 } else if (hasDecimal && !/\.\d{2}$/.test(originalPriceText)) { 1679 // . ä½ä¸ºååä½ 1680 formattedPrice = Math.round(amount) 1681 .toString() 1682 .replace(/\B(?=(\d{3})+(?!\d))/g, "."); 1683 } else if (hasComma) { 1684 // , ä½ä¸ºååä½ 1685 formattedPrice = Math.round(amount) 1686 .toString() 1687 .replace(/\B(?=(\d{3})+(?!\d))/g, ","); 1688 } else { 1689 // çº¯æ´æ° 1690 formattedPrice = Math.round(amount).toString(); 1691 } 1692 1693 // è¿åæ°çç»æ 1694 return template.replace(/<span>[^<]+<\/span>/, `<span>${formattedPrice}</span>`); 1695} 1696 1697/** 1698 * 卿å è½½ç¬¬ä¸æ¹ SDK 1699 * @param {string} url - SDK èæ¬å°å 1700 * @param {Object} [attributes={}] - å¯éï¼èªå®ä¹å±æ§ï¼ä¾å¦ { "data-namespace": "paypalSubscription" }ï¼ 1701 * @returns {Promise} - èæ¬å è½½å®æå resolve 1702 * 使ç¨ç¤ºä¾ï¼ 1703 * loadSdk('https://pay.google.com/gp/p/js/pay.js', { "data-namespace": "googlePay" }).then(...) 1704 */ 1705// ç¨ä¸ä¸ªå¯¹è±¡ç®¡çä¸å URL ç Promise 1706const sdkPromises = {}; 1707 1708function loadSdk(url, attributes = {}) { 1709 // çæå¯ä¸ keyï¼url + JSON.stringify(attributes) 1710 const key = url + JSON.stringify(attributes); 1711 1712 // 妿已æå¯¹åº key ç Promiseï¼ç´æ¥è¿å 1713 if (sdkPromises[key]) { 1714 return sdkPromises[key]; 1715 } 1716 1717 // å建æ°ç Promise 1718 sdkPromises[key] = new Promise((resolve, reject) => { 1719 // æ£æ¥ DOM 鿝å¦å·²æå®å ¨ç¸å屿§ç script 1720 const selectorParts = [`script[src="${url}"]`]; 1721 for (const [k, v] of Object.entries(attributes)) { 1722 selectorParts.push(`[${k}="${v}"]`); 1723 } 1724 const selector = selectorParts.join(''); 1725 if (document.querySelector(selector)) { 1726 resolve(); 1727 return; 1728 } 1729 1730 const script = document.createElement('script'); 1731 script.src = url; 1732 script.async = true; 1733 1734 // 设置èªå®ä¹å±æ§ 1735 for (const [key, value] of Object.entries(attributes)) { 1736 script.setAttribute(key, value); 1737 } 1738 1739 script.onload = () => { 1740 $log(`SDK å è½½æå: ${url}`, attributes); 1741 resolve(window.paypal || null); // è¿éæéè°æ´è¿å对象 1742 }; 1743 1744 script.onerror = () => { 1745 console.error(`SDK å 载失败: ${url}`, attributes); 1746 delete sdkPromises[key]; // 失败åå 许éè¯ 1747 reject(new Error(`SDK å 载失败: ${url}`)); 1748 }; 1749 1750 document.head.appendChild(script); 1751 }); 1752 1753 return sdkPromises[key]; 1754} 1755 1756function SetToArr(params) { 1757 if (!params) return params 1758 if (Array.isArray(params)) return params 1759 let arr = [] 1760 for (let k in params) { 1761 arr.push(params[k]) 1762 } 1763 return arr 1764 1765} 1766 1767function setCurrencyCookie(code) {// 1768 /* 1769 phpä¼åå ¥cookieï¼è¿æ¯æåå ¥ç 1770 使¯å¦æç¨æ·ä½¿ç¨äºcfï¼ 1771 é£ä¹æºä»£ç é¨åä¼ä»cfç´æ¥æ¿å°ï¼ 1772 phpè¿è¾¹å°±ä¸æ§è¡äºï¼ 1773 å æ¤ï¼éè¦jsåå ¥cookieï¼ç¸å½äºå¸®å©phpï¼å°å½åçè´§å¸åå ¥cookieï¼ã 1774 1775 if (baseData.currency.default_currency_codeï¼é»è®¤è´§å¸ï¼ == baseData.currency.current_currency_code (å½åè´§å¸)) åï¼ 1776 æ¥çæ¯å¦åå¨cookieï¼f2_currency_codeï¼å¦æåå¨ï¼åå 餿è¿ä¸ªcookieï¼æ³¨ææ¯å 餿ï¼è䏿¯æ¸ 空å¼ï¼ 1777 1778 if (baseData.currency.default_currency_codeï¼é»è®¤è´§å¸ï¼ != baseData.currency.current_currency_code (å½åè´§å¸)) åï¼ 1779 æ¥çæ¯å¦åå¨cookieï¼f2_currency_codeï¼å¦æåå¨cookieï¼åå°å¼è®¾ç½®ä¸ºå½åè´§å¸ï¼å¦æä¸åå¨cookieï¼åå建该cookieå¹¶èµå¼ 1780 1781 该cookieï¼å ³éæµè§å¨ï¼åæ¸ ç©ºå³å¯ 1782 1783 */ 1784 let baseData = window?.baseData
1785 let current_currency_code = code || baseData?.currency_code || '' 1786 let default_currency_code = baseData?.default_currency_code || '' 1787 // console.log('setCurrencyCookie-è°ç¨äºå') 1788 1789 if (current_currency_code) { 1790 Cookies.set( 1791 'f_currency_code', 1792 current_currency_code, 1793 {expires: 730, path: '/'}//ä¸¤å¹´è¿æ 1794 ) 1795 } 1796 1797 if (current_currency_code && default_currency_code) {//é»è®¤è´§å¸åå½åè´§å¸ é½åå¨ 1798 if (current_currency_code === default_currency_code) { 1799 // ç¸åç´æ¥å é¤ f2_currency_code 1800 Cookies.remove('f2_currency_code') 1801 } else {//ä¸ç¸åéè¦åä¸ä¸ª f2_currency_code 1802 Cookies.set( 1803 'f2_currency_code', 1804 current_currency_code, 1805 {expires: 730, path: '/'}//ä¸¤å¹´è¿æ 1806 ) 1807 } 1808 } 1809} 1810 1811document.addEventListener("DOMContentLoaded", function () { 1812 setCurrencyCookie(); 1813}); 1814 1815// ç»å½å®æåäºä»¶ 1816/* 1817 formData ç»å½åæ° 1818 targetUrl è·³è½¬é¾æ¥ 1819 */ 1820async function accountBaseLoginEvent(formData, targetUrl, callback) { 1821 const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); 1822 try { 1823 await baseGetAccountInfo();//设置账å·ç¶æï¼åªè®¾ç½®è´¦æ·ä¿¡æ¯åç»å½ç¶æ 1824 $log('å¼å§è·åæ¶èæ°é') 1825 await baseGetFavoriteCount()//è·åååæ¶èä¸ªæ° 1826 $log('æ¶èæ°é宿¯') 1827 if (window.common_shop_type == 10) {//B2Cåºéº 1828 //åç» è®¾ç½®ç½çåææä»¶åç¶æ çå¾ è¯¥å½æ°æ§è¡å®æ¯ 1829 await setAffiliateInfo()//设置ç½çåææä»¶åç¶æ çå¾ è¯¥å½æ°æ§è¡å®æ¯ 1830 } 1831 1832 // ç»å½äºä»¶ 1833 if (formData.email) { 1834 FecEvents.emit(FecTheme.pubSubEvents.accountLogin, { 1835 email: formData.email, 1836 }) 1837 } 1838 1839 //妿å¼å¯äº omnisend æç¹äºä»¶ çå¾ ä¸ç§ 1840 if (window.common_shop_type == 10) {//B2Cåºéº 1841 if (window.$omnisendIdentifyContact && typeof window.$omnisendIdentifyContact === 'function') { 1842 await wait(1000); 1843 } 1844 } 1845 1846 if (targetUrl) { 1847 window.location.href = targetUrl 1848 } 1849 if (callback && typeof callback === 'function') { 1850 callback() 1851 } 1852 } catch (err) { 1853 console.error('login erorr:', err); 1854 } 1855 1856} 1857 1858function baseGetAccountInfo() { 1859 $log('login-getAccountInfo------------'); 1860 // after login ï¼ update account info 1861 return new Promise((resolve, reject) => { 1862 api.get('/api/customer/account/info').then(response => { 1863 //è®¾ç½®ç¨æ·ä¿¡æ¯ å¦é®ç®±å·ï¼ç¨æ·åï¼ç»å½ç¶æã ä¸è¦æ´æ°å ¶ä»ä¿¡æ¯ 1864 $log('getAccountInfo success'); 1865 let loginStatus = localStorage.getItem('loginStatus') 1866 if (loginStatus) { 1867 loginStatus = JSON.parse(loginStatus) 1868 loginStatus.customer = response.customer 1869 loginStatus.isLogined = response.isLogined 1870 localStorage.setItem('loginStatus', JSON.stringify(loginStatus)) 1871 } 1872 $log('getAccountInfo fail-åé订é äºä»¶'); 1873 // äºä»¶è®¢é => åéç»å¤´é¨å¯¼èªç个人ä¸å¿icon æ´æ°é¼ æ æ¾å ¥æ¶æ¾ç¤ºçUIæ¯å»ç»å½ è¿æ¯å» å»ä¸ªäººä¸å¿ 1874 1875 FecEvents.emit(FecTheme.pubSubEvents.logInAndLogOutChange, { 1876 data: {}// 1877 }); 1878 resolve() 1879 1880 }).catch(res => { 1881 resolve() 1882 1883 }).finally(res => { 1884 1885 1886 }); 1887 }) 1888 1889} 1890 1891function baseGetFavoriteCount() {//è·åååæ¶èä¸ªæ° 1892 return new Promise(resolve => { 1893 let isStatus = window?.common_prodfavorite || false 1894 $log('æ¶èæ°é') 1895 if (!isStatus) { 1896 resolve() 1897 return 1898 } 1899 api.post('/api/apps/prodfavorite/favorite/get-favorite-count').then(res => { 1900 $log('æ¶èæ°é', res) 1901 1902 let favoriteCountList = res?.product_ids || []; 1903 // ä½¿ç¨ Set å»é 1904 favoriteCountList = [...new Set(favoriteCountList)]; 1905 let count = favoriteCountList.length || res?.count; 1906 // ä¿åå° localStorage 1907 localStorage.setItem('f-wishlist-products-ids', JSON.stringify(favoriteCountList)); 1908 localStorage.setItem('wishlist_qty', JSON.stringify(count)); 1909 setTimeout(() => { 1910 resolve() 1911 }, 100) 1912 }).catch(() => { 1913 setTimeout(() => { 1914 resolve() 1915 }, 100) 1916 1917 }) 1918 }) 1919} 1920 1921function setAffiliateInfo() {//设置ç½çç¶æ 1922 return new Promise(resolve => { 1923 if (!window.baseData.addons.includes('affiliate')) {//å æ¥çææ²¡æè¯¥æä»¶ 1924 resolve()//没æä»¶æéç´æ¥è¿å 1925 return 1926 } 1927 // è·åçå®è·¯å¾ï¼å»é¤å¤è¯è¨åç¼ï¼ 1928 let pathName = getRealPath(location.pathname) 1929 if (pathName === '/affiliate/guide') {//å¼å¯¼æ³¨å页ä¸éè¦æ§è¡ 1930 resolve()//没æä»¶æéç´æ¥è¿å 1931 return 1932 } 1933 api.get('/api/apps/affiliate/account/get-status').then(res => {//è·åè´¦å·ç¶æ 1934 /* 1935 å¼1ï¼è´¦æ·æªç»å½ 1936 å¼2ï¼å·²ç»ç»å½ && æªç³è¯·ç½çåç¨æ· 1937 å¼3ï¼å·²ç»ç»å½ && æ¿æ´»çç½çåç¨æ· 1938 å¼4ï¼å·²ç»ç»å½ && å·²ç³è¯·ä½æªæ¿æ´»ç½çç¨æ·ï¼åå§ç¶æ æè æç»ç¶æï¼ 1939 * */ 1940 let status = res?.status || '' 1941 if (status == 3) { 1942 let sessionId = localStorage.getItem('sessionId'); 1943 localStorage.setItem('userIsAffiliate', sessionId)//å½åç¨æ·æ¯ç½çåï¼åå°ç¼åé 1944 } 1945 resolve()//ææä»¶æéå¹¶ä¸å·²ç»åå¨å®æ¯ 1946 }) 1947 1948// è¾ å©å½æ°ï¼è·åçå®è·¯å¾ï¼å»é¤è¯è¨åç¼ï¼ 1949 function getRealPath(pathname) { 1950 // å¹é 常è§çè¯è¨ä»£ç ï¼cn, en, fr, de, ja, ko çï¼2-3ä¸ªåæ¯ï¼ 1951 const langPattern = /^\/[a-z]{2,3}(\/|$)/ 1952 1953 if (langPattern.test(pathname)) { 1954 // å»æç¬¬ä¸æ®µï¼è¯è¨åç¼ï¼ï¼ä¿çå©ä½è·¯å¾ 1955 const parts = pathname.split('/') 1956 parts.shift() // ç§»é¤ç¬¬ä¸ä¸ªç©ºå符串 1957 parts.shift() // ç§»é¤è¯è¨ä»£ç 1958 return '/' + parts.join('/') 1959 } 1960 1961 return pathname 1962 } 1963 }) 1964} 1965 1966/** 1967 * 1968 * @param targetUrl "https://xxxx.xxxxx.cn/ja?preview_theme_id=775"; 1969 * @param path 跳转çå ·ä½é¡µ 1970 * @returns {string} æ¼æ¥å®æ 1971 */ 1972function $buildUrl(targetUrl, path) { 1973 // 妿 path æ¯ä»¥ http å¼å¤´ï¼ç´æ¥è¿å path 1974 if (path.startsWith('http')) { 1975 return path; 1976 } 1977 // ç¡®ä¿ targetUrl 以å个 / ç»å°¾ 1978 if (targetUrl.endsWith('/')) { 1979 targetUrl = targetUrl.slice(0, -1); // 廿æåçææ 1980 } 1981 1982 // ç¡®ä¿ path ä¸ä»¥ / å¼å¤´ 1983 if (path.startsWith('/')) { 1984 path = path.slice(1); // 廿 path å¼å¤´çææ 1985 } 1986 1987 // å离 targetUrl çè·¯å¾é¨ååæ¥è¯¢åæ°é¨å 1988 let [baseUrl, queryString] = targetUrl.split('?'); 1989 1990 // 妿 path å 嫿¥è¯¢åæ°ï¼æå path é¨ååæ¥è¯¢åæ°é¨å 1991 let [pathUrl, pathQuery] = path.split('?'); 1992 1993 // æ¼æ¥è·¯å¾é¨å 1994 let finalUrl = baseUrl + '/' + pathUrl; 1995 1996 // å¤çæ¥è¯¢åæ° 1997 let queryParams = []; 1998 1999 // 妿 targetUrl ææ¥è¯¢åæ°ï¼å æ·»å å®ä»¬ 2000 if (queryString) { 2001 queryParams.push(queryString); 2002 } 2003 2004 // 妿 path ææ¥è¯¢åæ°ï¼æ·»å å®ä»¬ï¼é¿å éå¤ï¼ 2005 if (pathQuery) { 2006 // æ path çæ¥è¯¢åæ°åæä¸ä¸ªæ°ç»ï¼å¹¶åå¹¶å° targetUrl çæ¥è¯¢åæ°ä¸ 2007 let pathParams = new URLSearchParams(pathQuery); 2008 let targetParams = new URLSearchParams(queryParams.join('&')); 2009 2010 // åå¹¶æ¥è¯¢åæ°
2011 pathParams.forEach((value, key) => { 2012 targetParams.set(key, value); 2013 }); 2014 2015 queryParams = [...targetParams.entries()].map(([key, value]) => `${key}=${value}`).join('&'); 2016 } 2017 2018 // æç»æ¼æ¥ URL 2019 if (queryParams.length > 0) { 2020 finalUrl += '?' + queryParams; 2021 } 2022 2023 return finalUrl; 2024} 2025 2026// æ ¼å¼åè´§å¸ 2027function $formatMoney(amount) { 2028 // åªåæ¨¡æ¿ 2029 let scriptElement = document.querySelector('script[data-common-price-template]'); 2030 if (!scriptElement) return ''; 2031 2032 // ç¼ååå§æ¨¡æ¿ï¼åªå䏿¬¡ 2033 if (!scriptElement._cachedTemplate) { 2034 scriptElement._cachedTemplate = scriptElement.innerHTML.trim(); 2035 } 2036 const template = scriptElement._cachedTemplate; 2037 2038 // æåå§ä»·æ ¼ 2039 const priceSpanElement = template.match(/<span>([^<]+)<\/span>/); 2040 if (!priceSpanElement) return template; 2041 2042 let originalPriceText = priceSpanElement[1]; 2043 2044 // å¤ææ ¼å¼ 2045 const hasDecimal = originalPriceText.includes('.'); 2046 const hasComma = originalPriceText.includes(','); 2047 let formattedPrice; 2048 2049 if (hasDecimal && /\.\d{2}$/.test(originalPriceText)) { 2050 // å°æ°ç¹ï¼ä¸¤ä½å°æ° 2051 let [intPart, decimalPart] = parseFloat(amount).toFixed(2).split('.'); 2052 intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ","); 2053 formattedPrice = intPart + '.' + decimalPart; 2054 } else if (hasDecimal && !/\.\d{2}$/.test(originalPriceText)) { 2055 // . ä½ä¸ºååä½ 2056 formattedPrice = Math.round(amount) 2057 .toString() 2058 .replace(/\B(?=(\d{3})+(?!\d))/g, "."); 2059 } else if (hasComma) { 2060 // , ä½ä¸ºååä½ 2061 formattedPrice = Math.round(amount) 2062 .toString() 2063 .replace(/\B(?=(\d{3})+(?!\d))/g, ","); 2064 } else { 2065 // çº¯æ´æ° 2066 formattedPrice = Math.round(amount).toString(); 2067 } 2068 2069 // è¿åæ°çç»æ 2070 return template.replace(/<span>[^<]+<\/span>/, `<span>${formattedPrice}</span>`); 2071} 2072 2073function $media(url) { 2074 if (!url) return 2075 if (!url.indexOf) return 2076 let attachment_url = window.common_image_url 2077 if (url.indexOf('http') === 0 || url.indexOf('https') === 0 || isBase64Image(url)) {//妿æå®æ´URLé£ä¹ç´æ¥è¿å宿´çURL æè å¦ææ¯ base64ä½å¾çä¹ç´æ¥è¿å 2078 return url 2079 } 2080 if (url.indexOf('/') === 0) { 2081 return attachment_url + url 2082 } else { 2083 return attachment_url + '/' + url 2084 } 2085 2086 function isBase64Image(str) { 2087 // Base64æ ¼å¼éªè¯ 2088 const base64Regex = /^data:image\/([a-zA-Z]*);base64,([^\s]*)$/; 2089 2090 if (!base64Regex.test(str)) { 2091 return false; 2092 } 2093 2094 // æåMIMEç±»ååBase64æ°æ® 2095 const matches = str.match(base64Regex); 2096 const mimeType = matches[1]; 2097 const base64Data = matches[2]; 2098 2099 // æ£æ¥MIMEç±»åæ¯å¦ä¸ºå¸¸è§å¾çæ ¼å¼ 2100 const validImageTypes = ['jpeg', 'jpg', 'png', 'gif', 'bmp', 'webp', 'svg+xml']; 2101 if (!validImageTypes.includes(mimeType.toLowerCase())) { 2102 return false; 2103 } 2104 2105 // éªè¯Base64æ°æ®æ ¼å¼ 2106 const base64DataRegex = /^[A-Za-z0-9+/]*={0,2}$/; 2107 if (!base64DataRegex.test(base64Data)) { 2108 return false; 2109 } 2110 2111 // æ£æ¥æ°æ®é¿åº¦æ¯å¦ä¸º4çåæ°ï¼Base64è¦æ±ï¼ 2112 if (base64Data.length % 4 !== 0) { 2113 return false; 2114 } 2115 2116 return true; 2117 } 2118 2119} 2120 2121//è·åé®ä»¶æç¹ 2122function $getAdvEmail(googleConversion) { 2123 let arr = SetToArr(googleConversion) 2124 let strs = [] 2125 arr.forEach(item => { 2126 let str = null 2127 if (item.conversion_goal === 'email' && item.conversion_id.length && item.conversion_label.length) { 2128 str = `${item.conversion_id}/${item.conversion_label}` 2129 strs.push(str) 2130 } 2131 }) 2132 if (strs && strs.length) return strs 2133 return false 2134} 2135 2136//è·åé®ä»¶ 2137function $getAdvWhatsapp(googleConversion) { 2138 let arr = SetToArr(googleConversion) 2139 let strs = [] 2140 arr.forEach(item => { 2141 let str = null 2142 if (item.conversion_goal === 'whatsapp' && item.conversion_id.length && item.conversion_label.length) { 2143 str = `${item.conversion_id}/${item.conversion_label}` 2144 strs.push(str) 2145 } 2146 }) 2147 if (strs && strs.length) return strs 2148 return false 2149} 2150 2151//è·åææºäºä»¶ 2152function $getAdvPhone(googleConversion) { 2153 let arr = SetToArr(googleConversion) 2154 let strs = [] 2155 arr.forEach(item => { 2156 let str = null 2157 if (item.conversion_goal === 'phone' && item.conversion_id.length && item.conversion_label.length) { 2158 str = `${item.conversion_id}/${item.conversion_label}` 2159 strs.push(str) 2160 } 2161 }) 2162 if (strs && strs.length) return strs 2163 return false 2164} 2165 2166//è·åè系人 2167function $getAdvPerson(googleConversion) { 2168 let arr = SetToArr(googleConversion) 2169 let strs = [] 2170 arr.forEach(item => { 2171 let str = null 2172 if (item.conversion_goal === 'person' && item.conversion_id.length && item.conversion_label.length) { 2173 str = `${item.conversion_id}/${item.conversion_label}` 2174 strs.push(str) 2175 } 2176 }) 2177 if (strs && strs.length) return strs 2178 return false 2179} 2180 2181function $getAdvInquiry(googleConversion) { // æäº¤æ½å¨å®¢æ·è¡¨å: contacts ååå询价 å¤åå询价 2182 let arr = SetToArr(googleConversion) 2183 console.log(arr) 2184 let strs = [] 2185 arr.forEach(item => { 2186 let str = null 2187 if (item.conversion_goal === 'inquiry' && item.conversion_id.length && item.conversion_label.length) { 2188 str = `${item.conversion_id}/${item.conversion_label}` 2189 strs.push(str) 2190 } 2191 }) 2192 if (strs && strs.length) return strs 2193 return false 2194} 2195 2196function $getAdvCart(googleConversion) { // æäº¤æ½å¨å®¢æ·è¡¨å: contacts ååå询价 å¤åå询价 2197 let arr = SetToArr(googleConversion) 2198 let strs = [] 2199 arr.forEach(item => { 2200 let str = null 2201 if (item.conversion_goal === 'cart' && item.conversion_id.length && item.conversion_label.length) { 2202 str = `${item.conversion_id}/${item.conversion_label}` 2203 strs.push(str) 2204 } 2205 }) 2206 if (strs && strs.length) return strs 2207 return false 2208} 2209 2210function $getAdvCheckout(googleConversion) { 2211 let arr = SetToArr(googleConversion) 2212 let strs = [] 2213 arr.forEach(item => { 2214 if (item.conversion_goal === 'checkout' && item.conversion_id.length && item.conversion_label.length) { 2215 strs.push(`${item.conversion_id}/${item.conversion_label}`) 2216 } 2217 }) 2218 if (strs && strs.length) return strs 2219 return false 2220} 2221 2222function $getAdvPurchase(googleConversion) { 2223 let arr = SetToArr(googleConversion) 2224 let strs = [] 2225 arr.forEach(item => { 2226 if (item.conversion_goal === 'purchase' && item.conversion_id.length && item.conversion_label.length) { 2227 strs.push(`${item.conversion_id}/${item.conversion_label}`) 2228 } 2229 }) 2230 if (strs && strs.length) return strs 2231 return false 2232} 2233 2234 2235let overlayTimeout; // ç¨äºåå¨å计æ¶ç setTimeout å¼ç¨ 2236//å ¨å±é®ç½©æå¤§æ¾ç¤º 10ç§ï¼ä¼ å ¥ true æè false 2237function $toggleOverlay(show) { 2238 // æ£æ¥æ¯å¦å·²ç»åå¨é®ç½©å±
2239 let overlay = document.getElementById('f-overlay'); 2240 2241 if (show) { 2242 // 妿é®ç½©å±ä¸åå¨ï¼åå建ä¸ä¸ªæ°ç 2243 if (!overlay) { 2244 overlay = document.createElement('div'); 2245 overlay.id = 'f-overlay'; 2246 overlay.style.position = 'fixed'; // å®ä½ä¸ºåºå®ï¼è¦çå ¨å± 2247 overlay.style.top = '0'; 2248 overlay.style.left = '0'; 2249 overlay.style.width = '100%'; 2250 overlay.style.height = '100%'; 2251 // overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; // åéæçé»è²èæ¯ 2252 overlay.style.backgroundColor = 'rgba(0, 0, 0, 0)'; // çº¯éæ 2253 overlay.style.zIndex = '999999'; // ç¡®ä¿é®ç½©å±å¨æä¸å± 2254 overlay.style.display = 'flex'; 2255 overlay.style.justifyContent = 'center'; 2256 overlay.style.alignItems = 'center'; 2257 document.body.appendChild(overlay); 2258 2259 // 妿åå¨ä¸ä¸æ¬¡çå计æ¶ï¼åæ¶æ 2260 if (overlayTimeout) { 2261 clearTimeout(overlayTimeout); 2262 } 2263 2264 // èªå¨å¨ 10 ç§åç§»é¤é®ç½©å± 2265 overlayTimeout = setTimeout(() => { 2266 $toggleOverlay(false); // 10 ç§åèªå¨å ³éé®ç½©å± 2267 }, 10000); // 10 ç§ = 10000 æ¯«ç§ 2268 } 2269 } else { 2270 // 妿é®ç½©å±åå¨ï¼åç§»é¤å® 2271 if (overlay) { 2272 overlay.remove(); 2273 } 2274 // åæ¶ä¸ä¸æ¬¡çåè®¡æ¶ 2275 if (overlayTimeout) { 2276 clearTimeout(overlayTimeout); 2277 } 2278 } 2279} 2280 2281function $mobileAndTabletCheck() { 2282 let flag = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|mibrowser|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i); 2283 return flag ? true : false; 2284} 2285 2286function $htmlToText(html) { 2287 // å建ä¸ä¸ª DOM å ç´ æ¥è§£æ HTML å符串 2288 const doc = new DOMParser().parseFromString(html, 'text/html'); 2289 2290 // è·åçº¯ææ¬ 2291 let text = doc.body.textContent || doc.body.innerText; 2292 2293 // 妿éè¦æ´å¤å¤çï¼å¦æ¸ çé¢å¤çç©ºæ ¼ææ ç¾ï¼ï¼å¯ä»¥æ·»å é¢å¤çæ£åæé»è¾ 2294 text = text.replace(/\s+/g, ' ').trim(); // å»é¤å¤ä½çç©ºæ ¼å¹¶ä¿®åªææ¬ 2295 2296 return text; 2297} 2298 2299/** 2300 * è´§å¸æ ¼å¼åè½¬æ¢ 2301 * @param {string} moneyIcon è´§å¸ç¬¦å· 2302 * @param {number|string} price æ¾ç¤ºçéé¢ 2303 * @param {number|string} currentCurrencyCode è´§å¸codeå¼ 2304 * @returns {string} å¤çå®æåæ¾ç¤ºçè´§å¸æ ¼å¼ 2305 */ 2306function $currencyConversion(moneyIcon, price, currentCurrencyCode) { 2307 const currentLanguage = window?.baseData?.language_code || ''; 2308 2309 // 空å¼å¤ç 2310 if (price === null || price === undefined || price === '') { 2311 return ''; 2312 } 2313 2314 // åªæè¿äºè´§å¸éè¦åèäºå ¥åæ´ï¼æ²¡æå°æ°ç¹ï¼ 2315 const noDecimalCurrencies = ['HUF', 'TWD', 'JPY', 'VND', 'IDR']; 2316 const needRound = noDecimalCurrencies.includes(currentCurrencyCode); 2317 2318 let numericPrice = parseFloat(price); 2319 let formattedNumber = ''; 2320 2321 if (needRound) { 2322 // éè¦åèäºå ¥åæ´çè´§å¸ 2323 numericPrice = Math.round(numericPrice); 2324 // è·åååä½åé符ï¼å°å°¼ç¾åè¶åç¾ç¨ .ï¼å ¶ä»ç¨ , 2325 const thousandSeparator = (currentCurrencyCode === 'IDR' || currentCurrencyCode === 'VND') ? '.' : ','; 2326 formattedNumber = this.analysis(numericPrice, thousandSeparator, false); 2327 } else { 2328 // å ¶ä»è´§å¸ï¼ä¿çåå§å°æ°ï¼é常两ä½ï¼ï¼åå符ç¨éå· 2329 // å æ ¼å¼å为两ä½å°æ° 2330 numericPrice = numericPrice.toFixed(2); 2331 formattedNumber = this.analysis(numericPrice, ',', false); 2332 } 2333 2334 if (typeof formattedNumber !== 'string') formattedNumber = String(formattedNumber); 2335 2336 // ä¿ç½æ¯è´§å¸ç¹æ®å¤çï¼éè¦å¨ç¬¦å·åå ç©ºæ ¼ 2337 const isRUB = currentCurrencyCode === 'RUB'; 2338 const shouldAddSpace = isRUB && currentLanguage !== 'ar'; 2339 const spaceBetween = shouldAddSpace ? ' ' : ''; 2340 2341 const isArabic = currentLanguage === 'ar'; 2342 const isVND = currentCurrencyCode === 'VND'; 2343 2344 // è´§å¸ç¬¦å·æ ·å¼ 2345 const iconStyle = `font-family: Tahoma, Arial, sans-serif;${isArabic ? 'margin-right: 4px' : ''}`; 2346 const iconElement = `<i style="${iconStyle}">${moneyIcon}</i>`; 2347 const numberElement = `<span>${formattedNumber}</span>`; 2348 2349 let priceFormat = ''; 2350 2351 if (isVND) { 2352 // è¶åç¾ï¼æ°åå¨åï¼ç¬¦å·å¨å 2353 priceFormat = `${numberElement}<i style="margin-left: 4px;font-family: Tahoma, Arial, sans-serif;">${moneyIcon}</i>`; 2354 } else { 2355 // é»è®¤ï¼ç¬¦å·å¨åï¼æ°åå¨å 2356 priceFormat = `${iconElement}${spaceBetween}${numberElement}`; 2357 } 2358 2359 return priceFormat; 2360} 2361 2362/** 2363 * é颿°åæ·»å ååä½åé符 2364 * @param {number|string} opt ä»·æ ¼ï¼æ°åææ°ååç¬¦ä¸²ï¼ 2365 * @param {string} symbol ååä½åé符å·ï¼é»è®¤ . ï¼ 2366 * @param {boolean}
2366 point æ¯å¦éè¦æ·»å .00 åç¼ï¼å·²åºå¼ï¼ä¿çå ¼å®¹æ§ï¼ 2367 * @returns {string} 带åé符çéé¢å符串 2368 */ 2369function analysis(opt, symbol = '.', point = false) { 2370 if (opt === null || opt === undefined || opt === '') return '0'; 2371 2372 let str = opt.toString(); 2373 2374 // å¤çå°æ°æ åµ 2375 if (str.includes('.')) { 2376 const parts = str.split('.'); 2377 let integerPart = parts[0]; 2378 const decimalPart = parts[1]; 2379 // æ´æ°é¨åæ·»å ååä½åé符ï¼ä½¿ç¨ä¼ å ¥ç symbolï¼ 2380 integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, symbol); 2381 return `${integerPart}.${decimalPart}`; 2382 } else { 2383 // æ´æ°é¨åæ·»å ååä½åé符 2384 const ret = str.replace(/\B(?=(\d{3})+(?!\d))/g, symbol); 2385 if (point) { 2386 return ret + '.00'; 2387 } 2388 return ret; 2389 } 2390}
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.