customer-chat-sdk 1.2.0 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +367 -308
- package/dist/core/CustomerSDK.d.ts +48 -25
- package/dist/core/CustomerSDK.d.ts.map +1 -1
- package/dist/core/ScreenshotManager.d.ts +5 -0
- package/dist/core/ScreenshotManager.d.ts.map +1 -1
- package/dist/core/SimpleSDK.d.ts +111 -0
- package/dist/core/SimpleSDK.d.ts.map +1 -0
- package/dist/customer-sdk.cjs.js +266 -899
- package/dist/customer-sdk.esm.js +266 -895
- package/dist/customer-sdk.min.js +4 -4
- package/dist/index.d.ts +2 -48
- package/dist/index.d.ts.map +1 -1
- package/dist/simple.d.ts +14 -0
- package/dist/simple.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -2
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -2185,741 +2185,6 @@ class IconManager {
|
|
|
2185
2185
|
}
|
|
2186
2186
|
}
|
|
2187
2187
|
|
|
2188
|
-
class IframeManager {
|
|
2189
|
-
constructor(config = {}) {
|
|
2190
|
-
this.iframeElement = null;
|
|
2191
|
-
this.containerElement = null; // 包装容器,包含iframe和关闭按钮
|
|
2192
|
-
this.isOpen = false;
|
|
2193
|
-
this.isCreated = false;
|
|
2194
|
-
this.debug = false; // debug 模式标志
|
|
2195
|
-
this.targetElement = null; // 目标元素(用于自适应宽度)
|
|
2196
|
-
this.messageHandler = null; // 消息监听器引用(用于清理)
|
|
2197
|
-
this.config = {
|
|
2198
|
-
src: '',
|
|
2199
|
-
mode: 'auto', // 默认自动检测设备类型
|
|
2200
|
-
width: 450, // PC 模式默认宽度
|
|
2201
|
-
height: 600,
|
|
2202
|
-
allowClose: true,
|
|
2203
|
-
...config
|
|
2204
|
-
};
|
|
2205
|
-
this.debug = config.debug ?? false;
|
|
2206
|
-
// 不在构造函数中添加消息监听器,延迟到 init() 中添加
|
|
2207
|
-
// 这样可以避免创建多个实例时产生多个监听器
|
|
2208
|
-
}
|
|
2209
|
-
/**
|
|
2210
|
-
* 初始化iframe(隐藏状态)
|
|
2211
|
-
* 预创建iframe并连接到SSE,但不显示
|
|
2212
|
-
*/
|
|
2213
|
-
async init() {
|
|
2214
|
-
try {
|
|
2215
|
-
// 设置消息监听器(在 init() 中而不是构造函数中,避免多次创建实例时产生多个监听器)
|
|
2216
|
-
this.setupMessageListener();
|
|
2217
|
-
// 关键修复:在初始化前,先清理页面上所有旧的容器元素
|
|
2218
|
-
// 防止切换模式或多次初始化时产生重复的元素
|
|
2219
|
-
this.cleanupOrphanedElements();
|
|
2220
|
-
// 创建隐藏的iframe(预连接到SSE)
|
|
2221
|
-
// 注意:createIframe 会将容器添加到 body
|
|
2222
|
-
this.createIframe();
|
|
2223
|
-
this.isCreated = true;
|
|
2224
|
-
if (this.debug) {
|
|
2225
|
-
console.log('CustomerSDK iframe initialized (hidden, SSE connected)');
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
catch (error) {
|
|
2229
|
-
// 错误始终输出
|
|
2230
|
-
console.error('Failed to initialize iframe:', error);
|
|
2231
|
-
// 如果初始化失败,清理消息监听器
|
|
2232
|
-
if (this.messageHandler) {
|
|
2233
|
-
window.removeEventListener('message', this.messageHandler);
|
|
2234
|
-
this.messageHandler = null;
|
|
2235
|
-
}
|
|
2236
|
-
throw error;
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
/**
|
|
2240
|
-
* 显示iframe聊天窗口
|
|
2241
|
-
*/
|
|
2242
|
-
show() {
|
|
2243
|
-
if (this.isOpen) {
|
|
2244
|
-
return;
|
|
2245
|
-
}
|
|
2246
|
-
if (!this.isCreated) {
|
|
2247
|
-
throw new Error('Iframe not initialized. Call init() first.');
|
|
2248
|
-
}
|
|
2249
|
-
try {
|
|
2250
|
-
// 显示已创建的容器
|
|
2251
|
-
// 关键优化:容器在 init() 时已经添加到正确位置,这里只改变样式,不移动 DOM
|
|
2252
|
-
// 这样可以避免 iframe 重新加载
|
|
2253
|
-
if (this.containerElement) {
|
|
2254
|
-
// 确保容器在 DOM 中(理论上已经在 init() 时添加,但为了安全)
|
|
2255
|
-
if (!this.containerElement.parentNode) {
|
|
2256
|
-
// 如果容器不在 DOM 中(异常情况),添加到 target 或 body
|
|
2257
|
-
if (this.targetElement && this.targetElement !== document.body) {
|
|
2258
|
-
// 确保 target 是相对定位
|
|
2259
|
-
const targetStyle = window.getComputedStyle(this.targetElement);
|
|
2260
|
-
if (targetStyle.position === 'static') {
|
|
2261
|
-
this.targetElement.style.position = 'relative';
|
|
2262
|
-
}
|
|
2263
|
-
this.targetElement.appendChild(this.containerElement);
|
|
2264
|
-
if (this.debug) {
|
|
2265
|
-
console.log('Iframe container re-added to target element in show()');
|
|
2266
|
-
}
|
|
2267
|
-
}
|
|
2268
|
-
else {
|
|
2269
|
-
// 没有 target 或 target 是 body,添加到 body
|
|
2270
|
-
document.body.appendChild(this.containerElement);
|
|
2271
|
-
if (this.debug) {
|
|
2272
|
-
console.log('Iframe container re-added to document.body in show()');
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
}
|
|
2276
|
-
// 根据是否有 target 设置正确的定位方式
|
|
2277
|
-
const useTargetWidth = !!this.targetElement && this.targetElement !== document.body;
|
|
2278
|
-
const displayStyles = useTargetWidth ? {
|
|
2279
|
-
// 使用 target 时,保持 absolute 定位(相对于 target)
|
|
2280
|
-
position: 'absolute',
|
|
2281
|
-
visibility: 'visible',
|
|
2282
|
-
opacity: '1',
|
|
2283
|
-
display: 'block'
|
|
2284
|
-
} : {
|
|
2285
|
-
// 没有 target 时,使用 fixed 定位(相对于视口)
|
|
2286
|
-
position: 'fixed',
|
|
2287
|
-
visibility: 'visible',
|
|
2288
|
-
opacity: '1',
|
|
2289
|
-
display: 'block'
|
|
2290
|
-
};
|
|
2291
|
-
Object.assign(this.containerElement.style, displayStyles);
|
|
2292
|
-
// 禁用body滚动,防止出现滚动条
|
|
2293
|
-
this.preventBodyScroll(true);
|
|
2294
|
-
}
|
|
2295
|
-
this.isOpen = true;
|
|
2296
|
-
if (this.debug) {
|
|
2297
|
-
console.log('CustomerSDK iframe shown');
|
|
2298
|
-
}
|
|
2299
|
-
}
|
|
2300
|
-
catch (error) {
|
|
2301
|
-
// 错误始终输出
|
|
2302
|
-
console.error('Failed to show iframe:', error);
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
/**
|
|
2306
|
-
* 打开iframe聊天窗口(保持兼容性)
|
|
2307
|
-
*/
|
|
2308
|
-
open() {
|
|
2309
|
-
this.show();
|
|
2310
|
-
}
|
|
2311
|
-
/**
|
|
2312
|
-
* 隐藏iframe聊天窗口(类似v-show)
|
|
2313
|
-
*/
|
|
2314
|
-
hide() {
|
|
2315
|
-
if (!this.isOpen) {
|
|
2316
|
-
return;
|
|
2317
|
-
}
|
|
2318
|
-
// 隐藏容器但保留DOM元素(不移动容器,避免 iframe 重新加载)
|
|
2319
|
-
if (this.containerElement) {
|
|
2320
|
-
Object.assign(this.containerElement.style, {
|
|
2321
|
-
visibility: 'hidden',
|
|
2322
|
-
opacity: '0',
|
|
2323
|
-
display: 'none'
|
|
2324
|
-
});
|
|
2325
|
-
// 注意:不移动容器,保持容器在当前位置(body),避免 iframe 重新加载
|
|
2326
|
-
}
|
|
2327
|
-
// 恢复body滚动
|
|
2328
|
-
this.preventBodyScroll(false);
|
|
2329
|
-
this.isOpen = false;
|
|
2330
|
-
if (this.debug) {
|
|
2331
|
-
console.log('CustomerSDK iframe hidden (SSE still connected)');
|
|
2332
|
-
}
|
|
2333
|
-
// 触发关闭回调
|
|
2334
|
-
if (this.config.onClose) {
|
|
2335
|
-
this.config.onClose();
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
/**
|
|
2339
|
-
* 关闭iframe聊天窗口(完全销毁,保持兼容性)
|
|
2340
|
-
*/
|
|
2341
|
-
close() {
|
|
2342
|
-
this.hide(); // 默认只隐藏,不销毁
|
|
2343
|
-
}
|
|
2344
|
-
/**
|
|
2345
|
-
* 完全销毁iframe(需要时才调用)
|
|
2346
|
-
*/
|
|
2347
|
-
destroy() {
|
|
2348
|
-
this.hide();
|
|
2349
|
-
// 移除消息监听器(防止内存泄漏)
|
|
2350
|
-
if (this.messageHandler) {
|
|
2351
|
-
window.removeEventListener('message', this.messageHandler);
|
|
2352
|
-
this.messageHandler = null;
|
|
2353
|
-
}
|
|
2354
|
-
// 移除容器
|
|
2355
|
-
if (this.containerElement) {
|
|
2356
|
-
this.containerElement.remove();
|
|
2357
|
-
this.containerElement = null;
|
|
2358
|
-
this.iframeElement = null;
|
|
2359
|
-
}
|
|
2360
|
-
this.isCreated = false;
|
|
2361
|
-
if (this.debug) {
|
|
2362
|
-
console.log('CustomerSDK container destroyed');
|
|
2363
|
-
}
|
|
2364
|
-
}
|
|
2365
|
-
/**
|
|
2366
|
-
* 检查是否已打开
|
|
2367
|
-
*/
|
|
2368
|
-
isIframeOpen() {
|
|
2369
|
-
return this.isOpen;
|
|
2370
|
-
}
|
|
2371
|
-
/**
|
|
2372
|
-
* 向iframe发送消息
|
|
2373
|
-
*/
|
|
2374
|
-
sendToIframe(data) {
|
|
2375
|
-
if (this.iframeElement && this.iframeElement.contentWindow) {
|
|
2376
|
-
this.iframeElement.contentWindow.postMessage(data, '*');
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
2379
|
-
/**
|
|
2380
|
-
* 清理页面上孤立的容器元素(防止重复创建)
|
|
2381
|
-
*/
|
|
2382
|
-
cleanupOrphanedElements() {
|
|
2383
|
-
// 清理所有旧的容器元素(不属于当前实例的)
|
|
2384
|
-
const existingContainers = document.querySelectorAll('.customer-sdk-container');
|
|
2385
|
-
existingContainers.forEach((container) => {
|
|
2386
|
-
if (container !== this.containerElement) {
|
|
2387
|
-
container.remove();
|
|
2388
|
-
if (this.debug) {
|
|
2389
|
-
console.log('清理旧的容器元素');
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
});
|
|
2393
|
-
}
|
|
2394
|
-
/**
|
|
2395
|
-
* 创建iframe(默认隐藏状态,用于SSE连接)
|
|
2396
|
-
*/
|
|
2397
|
-
createIframe() {
|
|
2398
|
-
// 创建包装容器(包含iframe和关闭按钮)
|
|
2399
|
-
this.containerElement = document.createElement('div');
|
|
2400
|
-
this.containerElement.className = 'customer-sdk-container';
|
|
2401
|
-
// 确保容器不可拖动
|
|
2402
|
-
this.containerElement.draggable = false;
|
|
2403
|
-
// 创建iframe元素
|
|
2404
|
-
this.iframeElement = document.createElement('iframe');
|
|
2405
|
-
this.iframeElement.className = 'customer-sdk-iframe';
|
|
2406
|
-
// 设置iframe属性
|
|
2407
|
-
// 注意:URL只在初始化时设置一次,之后不会刷新
|
|
2408
|
-
// 如果需要更新URL参数,需要重新初始化SDK
|
|
2409
|
-
this.iframeElement.src = this.config.src || '';
|
|
2410
|
-
this.iframeElement.frameBorder = '0';
|
|
2411
|
-
// 添加必要权限(只保留常用的)
|
|
2412
|
-
this.iframeElement.setAttribute('allow', [
|
|
2413
|
-
'microphone', // 麦克风权限
|
|
2414
|
-
'camera', // 摄像头权限
|
|
2415
|
-
'geolocation', // 地理位置权限
|
|
2416
|
-
'clipboard-read', // 剪贴板读取权限
|
|
2417
|
-
'clipboard-write', // 剪贴板写入权限
|
|
2418
|
-
'fullscreen', // 全屏权限
|
|
2419
|
-
'autoplay', // 自动播放权限
|
|
2420
|
-
'encrypted-media' // 加密媒体权限
|
|
2421
|
-
].join('; '));
|
|
2422
|
-
// 设置sandbox属性(允许所有必要功能)
|
|
2423
|
-
this.iframeElement.setAttribute('sandbox', [
|
|
2424
|
-
'allow-same-origin', // 允许同源
|
|
2425
|
-
'allow-scripts', // 允许脚本
|
|
2426
|
-
'allow-forms', // 允许表单
|
|
2427
|
-
'allow-popups', // 允许弹窗
|
|
2428
|
-
'allow-popups-to-escape-sandbox', // 允许弹窗逃逸沙箱
|
|
2429
|
-
'allow-presentation', // 允许演示
|
|
2430
|
-
'allow-top-navigation', // 允许顶级导航
|
|
2431
|
-
'allow-top-navigation-by-user-activation', // 允许用户激活的顶级导航
|
|
2432
|
-
'allow-downloads', // 允许下载
|
|
2433
|
-
'allow-modals', // 允许模态框
|
|
2434
|
-
'allow-orientation-lock', // 允许方向锁定
|
|
2435
|
-
'allow-pointer-lock', // 允许指针锁定
|
|
2436
|
-
'allow-storage-access-by-user-activation' // 允许用户激活的存储访问
|
|
2437
|
-
].join(' '));
|
|
2438
|
-
// 获取目标元素(如果提供了 target)
|
|
2439
|
-
if (this.config.target) {
|
|
2440
|
-
this.targetElement = this.getTargetElement(this.config.target);
|
|
2441
|
-
}
|
|
2442
|
-
// 根据设备类型设置模式
|
|
2443
|
-
const actualMode = this.getActualMode();
|
|
2444
|
-
const isPC = actualMode === 'popup';
|
|
2445
|
-
this.iframeElement.scrolling = 'auto'; // PC和移动端都显示滚动条
|
|
2446
|
-
// 判断是否使用 target 的自适应宽度
|
|
2447
|
-
const useTargetWidth = !!this.targetElement && this.targetElement !== document.body;
|
|
2448
|
-
// PC模式:如果传了 target,使用 target 的宽度(自适应);否则使用配置的宽度
|
|
2449
|
-
// 移动端:如果传了 target,使用 target 的宽度(自适应);否则全屏
|
|
2450
|
-
const containerStyles = useTargetWidth ? {
|
|
2451
|
-
// 使用 target 的宽度(自适应),PC 和移动端都适用
|
|
2452
|
-
// 宽度 100% 自适应 target 的实际宽度(target 本身可能有 max-width 限制)
|
|
2453
|
-
width: '100%',
|
|
2454
|
-
height: '100%',
|
|
2455
|
-
maxWidth: '100%', // 不超过 target 的宽度
|
|
2456
|
-
maxHeight: '100%',
|
|
2457
|
-
backgroundColor: '#ffffff',
|
|
2458
|
-
borderRadius: isPC ? '0' : '0',
|
|
2459
|
-
boxShadow: isPC ? 'none' : '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
2460
|
-
border: 'none',
|
|
2461
|
-
position: 'absolute', // 相对于 target 元素定位
|
|
2462
|
-
zIndex: '999999',
|
|
2463
|
-
// 占满 target 元素
|
|
2464
|
-
top: '0',
|
|
2465
|
-
left: '0',
|
|
2466
|
-
bottom: '0',
|
|
2467
|
-
right: '0',
|
|
2468
|
-
transform: 'none',
|
|
2469
|
-
overflow: 'hidden',
|
|
2470
|
-
// 初始隐藏的关键样式
|
|
2471
|
-
visibility: 'hidden',
|
|
2472
|
-
opacity: '0',
|
|
2473
|
-
display: 'none'
|
|
2474
|
-
} : (isPC ? {
|
|
2475
|
-
// PC模式:没有 target,固定在右下角(不可拖动)
|
|
2476
|
-
width: `${this.config.width || 450}px`,
|
|
2477
|
-
height: `${this.config.height || 600}px`,
|
|
2478
|
-
maxWidth: '90vw',
|
|
2479
|
-
maxHeight: '90vh',
|
|
2480
|
-
backgroundColor: '#ffffff',
|
|
2481
|
-
borderRadius: '8px',
|
|
2482
|
-
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.25)',
|
|
2483
|
-
border: 'none',
|
|
2484
|
-
position: 'fixed',
|
|
2485
|
-
zIndex: '999999',
|
|
2486
|
-
// PC模式:固定在右下角
|
|
2487
|
-
top: 'auto',
|
|
2488
|
-
left: 'auto',
|
|
2489
|
-
bottom: '20px',
|
|
2490
|
-
right: '20px',
|
|
2491
|
-
transform: 'none',
|
|
2492
|
-
overflow: 'hidden',
|
|
2493
|
-
// 防止拖动和选择
|
|
2494
|
-
userSelect: 'none',
|
|
2495
|
-
WebkitUserSelect: 'none',
|
|
2496
|
-
MozUserSelect: 'none',
|
|
2497
|
-
msUserSelect: 'none',
|
|
2498
|
-
// 初始隐藏的关键样式
|
|
2499
|
-
visibility: 'hidden',
|
|
2500
|
-
opacity: '0',
|
|
2501
|
-
display: 'none'
|
|
2502
|
-
} : {
|
|
2503
|
-
// 移动端全屏模式(没有 target,强制 100% 宽度和高度)
|
|
2504
|
-
width: '100%',
|
|
2505
|
-
height: '100%',
|
|
2506
|
-
maxWidth: '100%',
|
|
2507
|
-
maxHeight: '100%',
|
|
2508
|
-
backgroundColor: '#ffffff',
|
|
2509
|
-
borderRadius: '0',
|
|
2510
|
-
boxShadow: '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
2511
|
-
border: 'none',
|
|
2512
|
-
position: 'fixed',
|
|
2513
|
-
zIndex: '999999',
|
|
2514
|
-
// 全屏模式 - 占满整个屏幕
|
|
2515
|
-
top: '0',
|
|
2516
|
-
left: '0',
|
|
2517
|
-
bottom: '0',
|
|
2518
|
-
right: '0',
|
|
2519
|
-
transform: 'none',
|
|
2520
|
-
overflow: 'hidden',
|
|
2521
|
-
// 初始隐藏的关键样式
|
|
2522
|
-
visibility: 'hidden',
|
|
2523
|
-
opacity: '0',
|
|
2524
|
-
display: 'none'
|
|
2525
|
-
});
|
|
2526
|
-
Object.assign(this.containerElement.style, containerStyles);
|
|
2527
|
-
// iframe填充整个容器
|
|
2528
|
-
const iframeStyles = {
|
|
2529
|
-
width: '100%',
|
|
2530
|
-
height: '100%',
|
|
2531
|
-
border: 'none',
|
|
2532
|
-
borderRadius: 'inherit'
|
|
2533
|
-
};
|
|
2534
|
-
Object.assign(this.iframeElement.style, iframeStyles);
|
|
2535
|
-
// 将iframe放入容器
|
|
2536
|
-
this.containerElement.appendChild(this.iframeElement);
|
|
2537
|
-
// PC模式下:禁止拖动容器
|
|
2538
|
-
if (isPC && !useTargetWidth) {
|
|
2539
|
-
// 阻止拖动事件
|
|
2540
|
-
this.containerElement.addEventListener('dragstart', (e) => {
|
|
2541
|
-
e.preventDefault();
|
|
2542
|
-
e.stopPropagation();
|
|
2543
|
-
return false;
|
|
2544
|
-
}, false);
|
|
2545
|
-
// 阻止鼠标按下事件(防止可能的拖动行为)
|
|
2546
|
-
this.containerElement.addEventListener('mousedown', (e) => {
|
|
2547
|
-
// 只阻止在容器边缘的拖动,允许点击 iframe 内容
|
|
2548
|
-
const rect = this.containerElement.getBoundingClientRect();
|
|
2549
|
-
const isOnEdge = (e.clientX < rect.left + 10 ||
|
|
2550
|
-
e.clientX > rect.right - 10 ||
|
|
2551
|
-
e.clientY < rect.top + 10 ||
|
|
2552
|
-
e.clientY > rect.bottom - 10);
|
|
2553
|
-
if (isOnEdge) {
|
|
2554
|
-
e.preventDefault();
|
|
2555
|
-
e.stopPropagation();
|
|
2556
|
-
}
|
|
2557
|
-
}, false);
|
|
2558
|
-
}
|
|
2559
|
-
// 添加iframe加载事件监听(移动端样式优化)
|
|
2560
|
-
this.iframeElement.addEventListener('load', () => {
|
|
2561
|
-
// 移动端注入自定义样式
|
|
2562
|
-
if (!isPC) {
|
|
2563
|
-
this.injectMobileStyles();
|
|
2564
|
-
}
|
|
2565
|
-
});
|
|
2566
|
-
// 添加到目标元素或 body
|
|
2567
|
-
if (this.targetElement && this.targetElement !== document.body) {
|
|
2568
|
-
// 如果提供了 target,添加到 target 元素内
|
|
2569
|
-
// 需要设置 target 为相对定位,以便 iframe 可以相对于它定位
|
|
2570
|
-
const targetStyle = window.getComputedStyle(this.targetElement);
|
|
2571
|
-
if (targetStyle.position === 'static') {
|
|
2572
|
-
// 如果 target 是静态定位,设置为相对定位
|
|
2573
|
-
this.targetElement.style.position = 'relative';
|
|
2574
|
-
if (this.debug) {
|
|
2575
|
-
console.log('Set target element position to relative for iframe container');
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
this.targetElement.appendChild(this.containerElement);
|
|
2579
|
-
if (this.debug) {
|
|
2580
|
-
console.log('Iframe container added to target element');
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
|
-
else {
|
|
2584
|
-
// 没有 target 或 target 是 body,添加到 body
|
|
2585
|
-
document.body.appendChild(this.containerElement);
|
|
2586
|
-
if (this.debug) {
|
|
2587
|
-
console.log('Iframe container added to document.body');
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
if (this.debug) {
|
|
2591
|
-
console.log('CustomerSDK container created (hidden, ready for SSE)');
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
/**
|
|
2595
|
-
* 向iframe注入移动端优化样式(隐藏滚动条)
|
|
2596
|
-
*/
|
|
2597
|
-
injectMobileStyles() {
|
|
2598
|
-
if (!this.iframeElement)
|
|
2599
|
-
return;
|
|
2600
|
-
try {
|
|
2601
|
-
const iframeDoc = this.iframeElement.contentDocument ||
|
|
2602
|
-
this.iframeElement.contentWindow?.document;
|
|
2603
|
-
if (iframeDoc) {
|
|
2604
|
-
// 创建样式元素
|
|
2605
|
-
const style = iframeDoc.createElement('style');
|
|
2606
|
-
style.textContent = `
|
|
2607
|
-
/* 移动端iframe全屏样式优化 */
|
|
2608
|
-
* {
|
|
2609
|
-
-webkit-overflow-scrolling: touch !important;
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
body, html, #app, .container {
|
|
2613
|
-
overflow: auto !important;
|
|
2614
|
-
-webkit-overflow-scrolling: touch !important;
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
/* 隐藏所有滚动条 */
|
|
2618
|
-
::-webkit-scrollbar {
|
|
2619
|
-
width: 0px !important;
|
|
2620
|
-
height: 0px !important;
|
|
2621
|
-
background: transparent !important;
|
|
2622
|
-
}
|
|
2623
|
-
|
|
2624
|
-
/* Firefox */
|
|
2625
|
-
html {
|
|
2626
|
-
scrollbar-width: none !important;
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
/* IE/Edge */
|
|
2630
|
-
body {
|
|
2631
|
-
-ms-overflow-style: none !important;
|
|
2632
|
-
}
|
|
2633
|
-
|
|
2634
|
-
/* 确保内容可以滚动 */
|
|
2635
|
-
body {
|
|
2636
|
-
overflow: auto !important;
|
|
2637
|
-
height: 100vh !important;
|
|
2638
|
-
margin: 0 !important;
|
|
2639
|
-
padding: 0 !important;
|
|
2640
|
-
}
|
|
2641
|
-
`;
|
|
2642
|
-
// 注入样式
|
|
2643
|
-
iframeDoc.head?.appendChild(style);
|
|
2644
|
-
if (this.debug) {
|
|
2645
|
-
console.log('CustomerSDK mobile styles injected successfully');
|
|
2646
|
-
}
|
|
2647
|
-
}
|
|
2648
|
-
}
|
|
2649
|
-
catch (error) {
|
|
2650
|
-
// 跨域限制时静默忽略
|
|
2651
|
-
if (this.debug) {
|
|
2652
|
-
console.log('Cannot inject styles due to cross-origin restrictions:', error);
|
|
2653
|
-
}
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
/**
|
|
2657
|
-
* 控制页面滚动(移动端全屏模式使用)
|
|
2658
|
-
*/
|
|
2659
|
-
preventBodyScroll(prevent) {
|
|
2660
|
-
if (prevent) {
|
|
2661
|
-
// 保存当前滚动位置
|
|
2662
|
-
const scrollY = window.scrollY;
|
|
2663
|
-
// 应用防止滚动的样式
|
|
2664
|
-
Object.assign(document.body.style, {
|
|
2665
|
-
position: 'fixed',
|
|
2666
|
-
top: `-${scrollY}px`,
|
|
2667
|
-
width: '100%',
|
|
2668
|
-
overflow: 'hidden'
|
|
2669
|
-
});
|
|
2670
|
-
// 记录滚动位置以便恢复
|
|
2671
|
-
document.body.setAttribute('data-scroll-position', scrollY.toString());
|
|
2672
|
-
}
|
|
2673
|
-
else {
|
|
2674
|
-
// 获取保存的滚动位置
|
|
2675
|
-
const scrollY = parseInt(document.body.getAttribute('data-scroll-position') || '0');
|
|
2676
|
-
// 移除防止滚动的样式
|
|
2677
|
-
Object.assign(document.body.style, {
|
|
2678
|
-
position: '',
|
|
2679
|
-
top: '',
|
|
2680
|
-
width: '',
|
|
2681
|
-
overflow: ''
|
|
2682
|
-
});
|
|
2683
|
-
// 移除记录的属性
|
|
2684
|
-
document.body.removeAttribute('data-scroll-position');
|
|
2685
|
-
// 恢复正常滚动位置
|
|
2686
|
-
window.scrollTo(0, scrollY);
|
|
2687
|
-
}
|
|
2688
|
-
}
|
|
2689
|
-
/**
|
|
2690
|
-
* 检测设备类型
|
|
2691
|
-
*/
|
|
2692
|
-
isMobileDevice() {
|
|
2693
|
-
return window.innerWidth <= 768 ||
|
|
2694
|
-
/Android|iPhone|iPad|iPod|Opera Mini|IEMobile/i.test(navigator.userAgent);
|
|
2695
|
-
}
|
|
2696
|
-
/**
|
|
2697
|
-
* 获取当前显示模式
|
|
2698
|
-
* PC 模式使用弹窗,移动端使用全屏
|
|
2699
|
-
*/
|
|
2700
|
-
getActualMode() {
|
|
2701
|
-
if (this.config.mode === 'auto') {
|
|
2702
|
-
return this.isMobileDevice() ? 'fullscreen' : 'popup';
|
|
2703
|
-
}
|
|
2704
|
-
return this.config.mode;
|
|
2705
|
-
}
|
|
2706
|
-
/**
|
|
2707
|
-
* 设置消息监听
|
|
2708
|
-
*/
|
|
2709
|
-
setupMessageListener() {
|
|
2710
|
-
// 如果已存在,先移除旧的监听器(防止重复添加)
|
|
2711
|
-
if (this.messageHandler) {
|
|
2712
|
-
window.removeEventListener('message', this.messageHandler);
|
|
2713
|
-
}
|
|
2714
|
-
// 创建新的消息处理器并保存引用
|
|
2715
|
-
this.messageHandler = (event) => {
|
|
2716
|
-
// 关键:过滤掉自己通过 dispatchEvent 发送的消息,避免无限循环
|
|
2717
|
-
// 自己发送的消息 source 是 window,而 iframe 发送的消息 source 是 iframe.contentWindow
|
|
2718
|
-
if (event.source === window) {
|
|
2719
|
-
if (this.debug) {
|
|
2720
|
-
console.log('[IframeManager] Ignoring self-broadcasted message to prevent infinite loop:', event.data);
|
|
2721
|
-
}
|
|
2722
|
-
return;
|
|
2723
|
-
}
|
|
2724
|
-
// 不验证来源,直接处理所有消息(确保消息能够被接收)
|
|
2725
|
-
if (this.debug) {
|
|
2726
|
-
console.log('[IframeManager] Message received:', {
|
|
2727
|
-
data: event.data,
|
|
2728
|
-
origin: event.origin,
|
|
2729
|
-
source: event.source
|
|
2730
|
-
});
|
|
2731
|
-
}
|
|
2732
|
-
this.handleIframeMessage(event.data);
|
|
2733
|
-
};
|
|
2734
|
-
window.addEventListener('message', this.messageHandler, false);
|
|
2735
|
-
}
|
|
2736
|
-
/**
|
|
2737
|
-
* 处理来自iframe的消息
|
|
2738
|
-
*/
|
|
2739
|
-
handleIframeMessage(data) {
|
|
2740
|
-
if (this.debug) {
|
|
2741
|
-
console.log('[IframeManager] Message from iframe received:', data);
|
|
2742
|
-
}
|
|
2743
|
-
// 判断data是字符串还是对象,兼容两种格式
|
|
2744
|
-
let messageType;
|
|
2745
|
-
if (typeof data === 'string') {
|
|
2746
|
-
messageType = data;
|
|
2747
|
-
}
|
|
2748
|
-
else if (data && data.type) {
|
|
2749
|
-
messageType = data.type;
|
|
2750
|
-
}
|
|
2751
|
-
else {
|
|
2752
|
-
if (this.debug) {
|
|
2753
|
-
console.log('[IframeManager] Unknown message format:', data);
|
|
2754
|
-
}
|
|
2755
|
-
return;
|
|
2756
|
-
}
|
|
2757
|
-
if (this.debug) {
|
|
2758
|
-
console.log('[IframeManager] Parsed message type:', messageType);
|
|
2759
|
-
}
|
|
2760
|
-
// 根据消息类型处理不同的操作
|
|
2761
|
-
switch (messageType) {
|
|
2762
|
-
case 'iframe_ready':
|
|
2763
|
-
if (this.debug) {
|
|
2764
|
-
console.log('Iframe is ready');
|
|
2765
|
-
}
|
|
2766
|
-
break;
|
|
2767
|
-
case 'close_iframe':
|
|
2768
|
-
case 'close':
|
|
2769
|
-
this.close();
|
|
2770
|
-
break;
|
|
2771
|
-
case 'resize_iframe':
|
|
2772
|
-
case 'resize':
|
|
2773
|
-
// PC模式支持 resize,移动端忽略
|
|
2774
|
-
const actualMode = this.getActualMode();
|
|
2775
|
-
if (actualMode === 'popup' && data.width && data.height) {
|
|
2776
|
-
this.resizeIframe(data.width, data.height);
|
|
2777
|
-
}
|
|
2778
|
-
else if (this.debug) {
|
|
2779
|
-
console.log('Resize request ignored (fullscreen mode)');
|
|
2780
|
-
}
|
|
2781
|
-
break;
|
|
2782
|
-
case 'new-message':
|
|
2783
|
-
// 新消息通知 - 触发回调让外层处理
|
|
2784
|
-
if (this.debug) {
|
|
2785
|
-
console.log('Received new message notification');
|
|
2786
|
-
}
|
|
2787
|
-
if (this.config.onMessage) {
|
|
2788
|
-
this.config.onMessage(messageType, data);
|
|
2789
|
-
}
|
|
2790
|
-
break;
|
|
2791
|
-
case 'goto-login':
|
|
2792
|
-
// 登录跳转消息 - 广播给调用 SDK 的 Vue 页面
|
|
2793
|
-
if (this.debug) {
|
|
2794
|
-
console.log('Received goto-login message, broadcasting to Vue page');
|
|
2795
|
-
}
|
|
2796
|
-
this.broadcastMessageToPage(data);
|
|
2797
|
-
if (this.config.onMessage) {
|
|
2798
|
-
this.config.onMessage(messageType, data);
|
|
2799
|
-
}
|
|
2800
|
-
break;
|
|
2801
|
-
case 'DCR_DEPOSIT':
|
|
2802
|
-
// 存款跳转消息 - 广播给调用 SDK 的 Vue 页面
|
|
2803
|
-
// 支持两种存款类型:
|
|
2804
|
-
// 1. 钱包直充:{ type: 'DCR_DEPOSIT', payload: { depositType: 0, walletId: 69382 } }
|
|
2805
|
-
// 2. 线下充值:{ type: 'DCR_DEPOSIT', payload: { depositType: 2, offlineTypeId: 2001, accountId: 35213 } }
|
|
2806
|
-
if (this.debug) {
|
|
2807
|
-
const depositType = data.payload?.depositType;
|
|
2808
|
-
const depositTypeText = depositType === 0 ? '钱包直充' : depositType === 2 ? '线下充值' : '未知类型';
|
|
2809
|
-
console.log(`[IframeManager] Received DCR_DEPOSIT message (${depositTypeText}):`, {
|
|
2810
|
-
type: data.type,
|
|
2811
|
-
payload: data.payload,
|
|
2812
|
-
depositType: depositTypeText
|
|
2813
|
-
});
|
|
2814
|
-
}
|
|
2815
|
-
this.broadcastMessageToPage(data);
|
|
2816
|
-
if (this.config.onMessage) {
|
|
2817
|
-
this.config.onMessage(messageType, data);
|
|
2818
|
-
}
|
|
2819
|
-
break;
|
|
2820
|
-
case 'DCR_ACTIVITY':
|
|
2821
|
-
// 跳转到优惠页面 - 广播给调用 SDK 的 Vue 页面
|
|
2822
|
-
// 格式:{ type: 'DCR_ACTIVITY', payload: { activityId: 123 } }
|
|
2823
|
-
if (this.debug) {
|
|
2824
|
-
console.log('[IframeManager] Received DCR_ACTIVITY message:', {
|
|
2825
|
-
type: data.type,
|
|
2826
|
-
payload: data.payload,
|
|
2827
|
-
activityId: data.payload?.activityId
|
|
2828
|
-
});
|
|
2829
|
-
}
|
|
2830
|
-
this.broadcastMessageToPage(data);
|
|
2831
|
-
if (this.config.onMessage) {
|
|
2832
|
-
this.config.onMessage(messageType, data);
|
|
2833
|
-
}
|
|
2834
|
-
break;
|
|
2835
|
-
default:
|
|
2836
|
-
// 其他自定义消息处理
|
|
2837
|
-
if (this.debug) {
|
|
2838
|
-
console.log('[IframeManager] Custom message:', data);
|
|
2839
|
-
}
|
|
2840
|
-
break;
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
|
-
/**
|
|
2844
|
-
* 向调用 SDK 的地方(Vue 页面)广播消息
|
|
2845
|
-
* 通过 window.dispatchEvent 触发事件,让 Vue 页面中的 window.addEventListener('message') 可以收到
|
|
2846
|
-
*
|
|
2847
|
-
* 重要说明:
|
|
2848
|
-
* 1. window.dispatchEvent 创建的事件会在同一窗口内触发所有监听器,无论 origin 是什么,Vue 页面都能收到
|
|
2849
|
-
* 2. MessageEvent 的 origin 属性不能是 '*',必须是有效的 origin 字符串(如 'http://localhost:5173')
|
|
2850
|
-
* 3. 我们使用 window.location.origin 来标识消息来源(当前页面的 origin)
|
|
2851
|
-
* 4. 如果 Vue 页面需要检查 origin,应该检查 window.location.origin 而不是 '*'
|
|
2852
|
-
*/
|
|
2853
|
-
broadcastMessageToPage(data) {
|
|
2854
|
-
try {
|
|
2855
|
-
// 使用 dispatchEvent 创建 MessageEvent,在同一窗口内触发所有监听器
|
|
2856
|
-
// 这样调用 SDK 的 Vue 页面中的 window.addEventListener('message') 也能收到
|
|
2857
|
-
// 注意:MessageEvent 的 origin 属性不能是 '*',必须是有效的 origin 字符串
|
|
2858
|
-
// 我们使用 window.location.origin 来标识消息来源(当前页面的 origin)
|
|
2859
|
-
// Vue 页面可以收到消息,因为 dispatchEvent 会在同一窗口内触发所有监听器
|
|
2860
|
-
const messageEvent = new MessageEvent('message', {
|
|
2861
|
-
data: data,
|
|
2862
|
-
origin: window.location.origin, // 使用当前页面的 origin,不是 '*'(MessageEvent 不支持 '*')
|
|
2863
|
-
source: window,
|
|
2864
|
-
bubbles: true,
|
|
2865
|
-
cancelable: true
|
|
2866
|
-
});
|
|
2867
|
-
window.dispatchEvent(messageEvent);
|
|
2868
|
-
if (this.debug) {
|
|
2869
|
-
console.log('[IframeManager] Message broadcasted to Vue page via window.dispatchEvent:', {
|
|
2870
|
-
data: data,
|
|
2871
|
-
origin: window.location.origin,
|
|
2872
|
-
type: 'message',
|
|
2873
|
-
note: 'origin is window.location.origin, not "*" (MessageEvent does not support "*")'
|
|
2874
|
-
});
|
|
2875
|
-
}
|
|
2876
|
-
}
|
|
2877
|
-
catch (error) {
|
|
2878
|
-
if (this.debug) {
|
|
2879
|
-
console.error('[IframeManager] Failed to broadcast message:', error);
|
|
2880
|
-
}
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
2883
|
-
/**
|
|
2884
|
-
* 调整iframe大小(PC模式支持)
|
|
2885
|
-
*/
|
|
2886
|
-
resizeIframe(width, height) {
|
|
2887
|
-
if (this.containerElement) {
|
|
2888
|
-
this.containerElement.style.width = `${width}px`;
|
|
2889
|
-
this.containerElement.style.height = `${height}px`;
|
|
2890
|
-
}
|
|
2891
|
-
}
|
|
2892
|
-
/**
|
|
2893
|
-
* 获取目标元素(支持字符串选择器或 HTMLElement)
|
|
2894
|
-
*/
|
|
2895
|
-
getTargetElement(target) {
|
|
2896
|
-
if (typeof target === 'string') {
|
|
2897
|
-
const element = document.querySelector(target);
|
|
2898
|
-
if (element) {
|
|
2899
|
-
return element;
|
|
2900
|
-
}
|
|
2901
|
-
else {
|
|
2902
|
-
if (this.debug) {
|
|
2903
|
-
console.warn(`Target element not found: ${target}, falling back to document.body`);
|
|
2904
|
-
}
|
|
2905
|
-
return document.body;
|
|
2906
|
-
}
|
|
2907
|
-
}
|
|
2908
|
-
if (target instanceof HTMLElement) {
|
|
2909
|
-
if (document.body.contains(target)) {
|
|
2910
|
-
return target;
|
|
2911
|
-
}
|
|
2912
|
-
else {
|
|
2913
|
-
if (this.debug) {
|
|
2914
|
-
console.warn('Target element no longer in DOM, falling back to document.body');
|
|
2915
|
-
}
|
|
2916
|
-
return document.body;
|
|
2917
|
-
}
|
|
2918
|
-
}
|
|
2919
|
-
return document.body;
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
|
|
2923
2188
|
function changeJpegDpi(uint8Array, dpi) {
|
|
2924
2189
|
uint8Array[13] = 1;
|
|
2925
2190
|
uint8Array[14] = dpi >> 8;
|
|
@@ -16453,6 +15718,26 @@ class ScreenshotManager {
|
|
|
16453
15718
|
}
|
|
16454
15719
|
});
|
|
16455
15720
|
}
|
|
15721
|
+
/**
|
|
15722
|
+
* 处理截图配置(公共方法,用于外部调用)
|
|
15723
|
+
* @param configJson 配置的 JSON 字符串(BinaryConfig 格式)
|
|
15724
|
+
*/
|
|
15725
|
+
handleScreenshotConfig(configJson) {
|
|
15726
|
+
try {
|
|
15727
|
+
const event = {
|
|
15728
|
+
data: {
|
|
15729
|
+
type: 'checkScreenshot',
|
|
15730
|
+
data: configJson
|
|
15731
|
+
}
|
|
15732
|
+
};
|
|
15733
|
+
this.handleIframeMessage(event);
|
|
15734
|
+
}
|
|
15735
|
+
catch (error) {
|
|
15736
|
+
if (this.options.debug) {
|
|
15737
|
+
console.error('📸 [ScreenshotManager] Error handling screenshot config:', error);
|
|
15738
|
+
}
|
|
15739
|
+
}
|
|
15740
|
+
}
|
|
16456
15741
|
/**
|
|
16457
15742
|
* 处理 iframe postMessage 消息
|
|
16458
15743
|
*/
|
|
@@ -30048,6 +29333,244 @@ function requireBlowfish () {
|
|
|
30048
29333
|
|
|
30049
29334
|
var cryptoJsExports = cryptoJs.exports;
|
|
30050
29335
|
|
|
29336
|
+
class CustomerSDK {
|
|
29337
|
+
constructor() {
|
|
29338
|
+
this.iconManager = null;
|
|
29339
|
+
this.screenshotManager = null;
|
|
29340
|
+
this.debug = false; // debug 模式标志
|
|
29341
|
+
this.deviceId = null; // 设备ID(md5后的)
|
|
29342
|
+
this.initResult = null; // 保存初始化结果
|
|
29343
|
+
}
|
|
29344
|
+
/**
|
|
29345
|
+
* 初始化SDK
|
|
29346
|
+
* @param config SDK配置
|
|
29347
|
+
* @param screenshotCallback 截图消息回调(可选)
|
|
29348
|
+
* @returns 返回初始化信息(包含设备ID等)
|
|
29349
|
+
*/
|
|
29350
|
+
async init(config, screenshotCallback) {
|
|
29351
|
+
try {
|
|
29352
|
+
this.debug = config.debug ?? false;
|
|
29353
|
+
// 获取设备指纹ID
|
|
29354
|
+
const rawDeviceId = await this.getRawDeviceId();
|
|
29355
|
+
if (this.debug) {
|
|
29356
|
+
console.log('Raw Device ID:', rawDeviceId);
|
|
29357
|
+
}
|
|
29358
|
+
// 计算返回给前端的 deviceId:md5(deviceId + getReferrer())
|
|
29359
|
+
const referrer = config.referrer || document.referrer || document.location.href;
|
|
29360
|
+
this.deviceId = cryptoJsExports.MD5(rawDeviceId + referrer).toString();
|
|
29361
|
+
if (this.debug) {
|
|
29362
|
+
console.log('MD5 Device ID (for frontend):', this.deviceId);
|
|
29363
|
+
console.log('Referrer used:', referrer);
|
|
29364
|
+
}
|
|
29365
|
+
// 准备返回的初始化信息
|
|
29366
|
+
const initResult = {
|
|
29367
|
+
deviceId: this.deviceId,
|
|
29368
|
+
referrer,
|
|
29369
|
+
agent: config.agent,
|
|
29370
|
+
timestamp: Date.now()
|
|
29371
|
+
};
|
|
29372
|
+
// 初始化图标管理器(支持传送目标)
|
|
29373
|
+
const iconTarget = config.target || undefined;
|
|
29374
|
+
const iconPosition = config.iconPosition || undefined;
|
|
29375
|
+
this.iconManager = new IconManager(iconPosition, this.debug, iconTarget, {
|
|
29376
|
+
sideAttach: config.sideAttach,
|
|
29377
|
+
sideHideRatio: config.sideHideRatio,
|
|
29378
|
+
magnetic: config.magnetic,
|
|
29379
|
+
magneticDirection: config.magneticDirection,
|
|
29380
|
+
margin: config.margin,
|
|
29381
|
+
autoAttachDelay: config.autoAttachDelay
|
|
29382
|
+
});
|
|
29383
|
+
await this.iconManager.show();
|
|
29384
|
+
// 初始化截图管理器(如果配置了截图)
|
|
29385
|
+
if (config.screenshot) {
|
|
29386
|
+
const targetElement = typeof iconTarget === 'string'
|
|
29387
|
+
? document.querySelector(iconTarget)
|
|
29388
|
+
: (iconTarget || document.body);
|
|
29389
|
+
// 创建发送回调函数
|
|
29390
|
+
const sendToExternal = screenshotCallback?.sendData || null;
|
|
29391
|
+
this.screenshotManager = new ScreenshotManager(targetElement, config.screenshot, sendToExternal || undefined);
|
|
29392
|
+
// 如果提供了配置回调,设置消息监听
|
|
29393
|
+
if (screenshotCallback?.onConfig) {
|
|
29394
|
+
this.setupScreenshotConfigListener(screenshotCallback.onConfig);
|
|
29395
|
+
}
|
|
29396
|
+
}
|
|
29397
|
+
// 保存初始化结果
|
|
29398
|
+
this.initResult = initResult;
|
|
29399
|
+
if (this.debug) {
|
|
29400
|
+
console.log('CustomerSDK initialized successfully (icon + screenshot only)');
|
|
29401
|
+
}
|
|
29402
|
+
// 返回初始化信息
|
|
29403
|
+
return initResult;
|
|
29404
|
+
}
|
|
29405
|
+
catch (error) {
|
|
29406
|
+
console.error('Failed to initialize CustomerSDK:', error);
|
|
29407
|
+
throw error;
|
|
29408
|
+
}
|
|
29409
|
+
}
|
|
29410
|
+
/**
|
|
29411
|
+
* 设置截图配置监听器(可选,用于兼容 postMessage 场景)
|
|
29412
|
+
*/
|
|
29413
|
+
setupScreenshotConfigListener(onConfig) {
|
|
29414
|
+
const messageHandler = (event) => {
|
|
29415
|
+
try {
|
|
29416
|
+
// 验证消息类型
|
|
29417
|
+
if (!event.data || event.data.type !== 'checkScreenshot') {
|
|
29418
|
+
return;
|
|
29419
|
+
}
|
|
29420
|
+
if (this.debug) {
|
|
29421
|
+
console.log('[CustomerSDK] Received screenshot config message:', event.data);
|
|
29422
|
+
}
|
|
29423
|
+
// 调用外部回调
|
|
29424
|
+
if (event.data.data && typeof event.data.data === 'string') {
|
|
29425
|
+
onConfig(event.data.data);
|
|
29426
|
+
}
|
|
29427
|
+
}
|
|
29428
|
+
catch (error) {
|
|
29429
|
+
if (this.debug) {
|
|
29430
|
+
console.error('[CustomerSDK] Error handling screenshot config message:', error);
|
|
29431
|
+
}
|
|
29432
|
+
}
|
|
29433
|
+
};
|
|
29434
|
+
window.addEventListener('message', messageHandler);
|
|
29435
|
+
this._screenshotConfigHandler = messageHandler;
|
|
29436
|
+
}
|
|
29437
|
+
/**
|
|
29438
|
+
* 触发截图配置(用于弹窗组件场景)
|
|
29439
|
+
* @param configJson 截图配置的 JSON 字符串(BinaryConfig 格式)
|
|
29440
|
+
*/
|
|
29441
|
+
triggerScreenshotConfig(configJson) {
|
|
29442
|
+
if (!this.screenshotManager) {
|
|
29443
|
+
if (this.debug) {
|
|
29444
|
+
console.warn('[CustomerSDK] ScreenshotManager not initialized');
|
|
29445
|
+
}
|
|
29446
|
+
return;
|
|
29447
|
+
}
|
|
29448
|
+
this.screenshotManager.handleScreenshotConfig(configJson);
|
|
29449
|
+
}
|
|
29450
|
+
/**
|
|
29451
|
+
* 隐藏图标
|
|
29452
|
+
*/
|
|
29453
|
+
hideIcon() {
|
|
29454
|
+
this.iconManager?.hide();
|
|
29455
|
+
}
|
|
29456
|
+
/**
|
|
29457
|
+
* 显示图标
|
|
29458
|
+
*/
|
|
29459
|
+
showIcon() {
|
|
29460
|
+
this.iconManager?.show();
|
|
29461
|
+
}
|
|
29462
|
+
/**
|
|
29463
|
+
* 设置图标点击回调
|
|
29464
|
+
*/
|
|
29465
|
+
onIconClick(callback) {
|
|
29466
|
+
this.iconManager?.onClick(callback);
|
|
29467
|
+
}
|
|
29468
|
+
/**
|
|
29469
|
+
* 获取截图管理器实例(用于高级操作)
|
|
29470
|
+
*/
|
|
29471
|
+
getScreenshotManager() {
|
|
29472
|
+
return this.screenshotManager;
|
|
29473
|
+
}
|
|
29474
|
+
/**
|
|
29475
|
+
* 获取图标管理器实例(用于高级操作)
|
|
29476
|
+
*/
|
|
29477
|
+
getIconManager() {
|
|
29478
|
+
return this.iconManager;
|
|
29479
|
+
}
|
|
29480
|
+
/**
|
|
29481
|
+
* 获取设备ID(md5后的)
|
|
29482
|
+
*/
|
|
29483
|
+
getDeviceId() {
|
|
29484
|
+
return this.deviceId;
|
|
29485
|
+
}
|
|
29486
|
+
/**
|
|
29487
|
+
* 获取初始化信息(设备ID等)
|
|
29488
|
+
*/
|
|
29489
|
+
getInitResult() {
|
|
29490
|
+
return this.initResult;
|
|
29491
|
+
}
|
|
29492
|
+
/**
|
|
29493
|
+
* 获取设备指纹ID(原始)
|
|
29494
|
+
*/
|
|
29495
|
+
async getRawDeviceId() {
|
|
29496
|
+
if (this.debug) {
|
|
29497
|
+
console.log('🔍 Starting to get device fingerprint...');
|
|
29498
|
+
}
|
|
29499
|
+
try {
|
|
29500
|
+
if (this.debug) {
|
|
29501
|
+
console.log('📦 Loading FingerprintJS...');
|
|
29502
|
+
}
|
|
29503
|
+
const fp = await index$1.load();
|
|
29504
|
+
if (this.debug) {
|
|
29505
|
+
console.log('🎯 Getting device fingerprint...');
|
|
29506
|
+
}
|
|
29507
|
+
const result = await fp.get();
|
|
29508
|
+
if (this.debug) {
|
|
29509
|
+
console.log('✅ FingerprintJS result:', result);
|
|
29510
|
+
console.log('🆔 Device ID obtained:', result.visitorId);
|
|
29511
|
+
}
|
|
29512
|
+
return result.visitorId;
|
|
29513
|
+
}
|
|
29514
|
+
catch (error) {
|
|
29515
|
+
if (this.debug) {
|
|
29516
|
+
console.warn('❌ Failed to get device fingerprint, using fallback:', error);
|
|
29517
|
+
}
|
|
29518
|
+
const fallbackId = 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
29519
|
+
if (this.debug) {
|
|
29520
|
+
console.log('🆔 Fallback Device ID:', fallbackId);
|
|
29521
|
+
}
|
|
29522
|
+
return fallbackId;
|
|
29523
|
+
}
|
|
29524
|
+
}
|
|
29525
|
+
/**
|
|
29526
|
+
* 显示消息通知
|
|
29527
|
+
*/
|
|
29528
|
+
showNotification(badgeCount = 1, options = {}) {
|
|
29529
|
+
if (!this.iconManager) {
|
|
29530
|
+
if (this.debug) {
|
|
29531
|
+
console.warn('SDK not initialized');
|
|
29532
|
+
}
|
|
29533
|
+
return;
|
|
29534
|
+
}
|
|
29535
|
+
this.iconManager.showNotification({
|
|
29536
|
+
badgeCount: typeof badgeCount === 'string' ? 0 : badgeCount,
|
|
29537
|
+
badgeText: typeof badgeCount === 'string' ? badgeCount : '',
|
|
29538
|
+
pulse: options.pulse || false
|
|
29539
|
+
});
|
|
29540
|
+
// 自动隐藏通知
|
|
29541
|
+
if (options.autoHide && options.autoHide > 0) {
|
|
29542
|
+
setTimeout(() => {
|
|
29543
|
+
this.iconManager?.clearNotification();
|
|
29544
|
+
}, options.autoHide);
|
|
29545
|
+
}
|
|
29546
|
+
}
|
|
29547
|
+
/**
|
|
29548
|
+
* 清除消息通知
|
|
29549
|
+
*/
|
|
29550
|
+
clearNotification() {
|
|
29551
|
+
this.iconManager?.clearNotification();
|
|
29552
|
+
}
|
|
29553
|
+
/**
|
|
29554
|
+
* 销毁 SDK
|
|
29555
|
+
*/
|
|
29556
|
+
destroy() {
|
|
29557
|
+
this.iconManager?.hide();
|
|
29558
|
+
this.screenshotManager?.destroy();
|
|
29559
|
+
// 清理截图配置监听器
|
|
29560
|
+
if (this._screenshotConfigHandler) {
|
|
29561
|
+
window.removeEventListener('message', this._screenshotConfigHandler);
|
|
29562
|
+
this._screenshotConfigHandler = null;
|
|
29563
|
+
}
|
|
29564
|
+
this.iconManager = null;
|
|
29565
|
+
this.screenshotManager = null;
|
|
29566
|
+
this.initResult = null;
|
|
29567
|
+
this.deviceId = null;
|
|
29568
|
+
if (this.debug) {
|
|
29569
|
+
console.log('CustomerSDK destroyed');
|
|
29570
|
+
}
|
|
29571
|
+
}
|
|
29572
|
+
}
|
|
29573
|
+
|
|
30051
29574
|
/**
|
|
30052
29575
|
* Customer SDK 主类
|
|
30053
29576
|
* 只负责悬浮图标和iframe管理,不处理WebSocket连接
|
|
@@ -30056,7 +29579,6 @@ var cryptoJsExports = cryptoJs.exports;
|
|
|
30056
29579
|
class CustomerServiceSDK {
|
|
30057
29580
|
constructor() {
|
|
30058
29581
|
this.iconManager = null;
|
|
30059
|
-
this.iframeManager = null;
|
|
30060
29582
|
this.screenshotManager = null;
|
|
30061
29583
|
this.config = null;
|
|
30062
29584
|
this.isInitialized = false;
|
|
@@ -30094,10 +29616,8 @@ class CustomerServiceSDK {
|
|
|
30094
29616
|
if (this.debug) {
|
|
30095
29617
|
console.log('Force reinitializing SDK...');
|
|
30096
29618
|
}
|
|
30097
|
-
//
|
|
30098
|
-
this.iframeManager?.destroy();
|
|
29619
|
+
// 清理旧的截图管理器
|
|
30099
29620
|
this.screenshotManager?.destroy();
|
|
30100
|
-
this.iframeManager = null;
|
|
30101
29621
|
this.screenshotManager = null;
|
|
30102
29622
|
// 注意:图标管理器暂时保留,稍后检查配置是否变化再决定是否重新创建
|
|
30103
29623
|
// 重置初始化标志
|
|
@@ -30119,12 +29639,9 @@ class CustomerServiceSDK {
|
|
|
30119
29639
|
console.log('MD5 Device ID (for frontend):', deviceId);
|
|
30120
29640
|
console.log('Referrer used:', referrer);
|
|
30121
29641
|
}
|
|
30122
|
-
// 构建iframe URL(带参数,使用 md5 后的 deviceId)
|
|
30123
|
-
const iframeUrl = this.buildIframeUrl(config, deviceId);
|
|
30124
29642
|
// 准备返回的初始化信息(返回给前端的是 md5 后的 deviceId)
|
|
30125
29643
|
const initResult = {
|
|
30126
29644
|
deviceId,
|
|
30127
|
-
iframeUrl,
|
|
30128
29645
|
referrer,
|
|
30129
29646
|
agent: config.agent,
|
|
30130
29647
|
timestamp: Date.now()
|
|
@@ -30184,42 +29701,11 @@ class CustomerServiceSDK {
|
|
|
30184
29701
|
// 更新配置记录
|
|
30185
29702
|
this.lastIconConfig = { position: iconPosition, target: iconTarget };
|
|
30186
29703
|
}
|
|
30187
|
-
//
|
|
30188
|
-
// 如果提供了 target,iframe 会自适应 target 的宽度(PC 和移动端都适用)
|
|
30189
|
-
// 如果没有 target,PC 模式使用默认宽度,移动端全屏
|
|
30190
|
-
console.log('iframeUrl:', iframeUrl);
|
|
30191
|
-
this.iframeManager = new IframeManager({
|
|
30192
|
-
src: iframeUrl,
|
|
30193
|
-
mode: 'auto', // 自动根据设备类型选择模式(PC弹窗,移动端全屏)
|
|
30194
|
-
width: options?.width || 450, // PC模式宽度(像素,默认450px),仅在未提供 target 时使用
|
|
30195
|
-
height: options?.height || 600, // PC模式高度(像素),仅在未提供 target 时使用
|
|
30196
|
-
target: options?.target, // 目标元素(如果提供,iframe 会自适应其宽度)
|
|
30197
|
-
allowClose: true,
|
|
30198
|
-
debug: this.debug, // 传递 debug 标志
|
|
30199
|
-
onMessage: (messageType, _data) => {
|
|
30200
|
-
// 处理来自iframe的消息
|
|
30201
|
-
if (messageType === 'new-message') {
|
|
30202
|
-
// 显示红点通知(只显示红点,不显示数字)
|
|
30203
|
-
this.showNotification(0, { pulse: true });
|
|
30204
|
-
}
|
|
30205
|
-
// checkScreenshot 消息由 ScreenshotManager 处理,不需要在这里处理
|
|
30206
|
-
},
|
|
30207
|
-
onClose: () => {
|
|
30208
|
-
// iframe关闭时,清理图标拖动事件监听器,并重新显示图标
|
|
30209
|
-
this.iconManager?.forceCleanupDragEvents();
|
|
30210
|
-
this.iconManager?.show();
|
|
30211
|
-
},
|
|
30212
|
-
...options
|
|
30213
|
-
});
|
|
30214
|
-
// 预初始化iframe(隐藏状态,连接SSE)
|
|
30215
|
-
await this.iframeManager.init();
|
|
30216
|
-
// 设置点击事件:图标 → iframe
|
|
29704
|
+
// 设置点击事件:图标点击回调(由外部处理)
|
|
30217
29705
|
this.iconManager.onClick(() => {
|
|
30218
|
-
//
|
|
29706
|
+
// 图标点击事件由外部处理(例如打开弹窗)
|
|
29707
|
+
// 这里只清除通知
|
|
30219
29708
|
this.clearNotification();
|
|
30220
|
-
// 点击图标后隐藏图标
|
|
30221
|
-
this.iconManager?.hide();
|
|
30222
|
-
this.iframeManager?.show();
|
|
30223
29709
|
});
|
|
30224
29710
|
// 初始化截图管理器(如果启用了截图功能)
|
|
30225
29711
|
if (config.screenshot) {
|
|
@@ -30231,10 +29717,8 @@ class CustomerServiceSDK {
|
|
|
30231
29717
|
...config.screenshot,
|
|
30232
29718
|
debug: this.debug // 直接传递 debug 标志
|
|
30233
29719
|
};
|
|
30234
|
-
this.screenshotManager = new ScreenshotManager(targetElement, screenshotOptions,
|
|
30235
|
-
|
|
30236
|
-
this.iframeManager?.sendToIframe(data);
|
|
30237
|
-
});
|
|
29720
|
+
this.screenshotManager = new ScreenshotManager(targetElement, screenshotOptions, undefined // 不再通过 iframe 发送,由外部回调处理
|
|
29721
|
+
);
|
|
30238
29722
|
// 自动启用截图功能(用于测试,实际使用时需要通过 iframe 消息启用)
|
|
30239
29723
|
this.screenshotManager.enable(true);
|
|
30240
29724
|
if (this.debug) {
|
|
@@ -30245,7 +29729,7 @@ class CustomerServiceSDK {
|
|
|
30245
29729
|
// 保存初始化结果,以便后续获取
|
|
30246
29730
|
this.initResult = initResult;
|
|
30247
29731
|
if (this.debug) {
|
|
30248
|
-
console.log('CustomerSDK initialized successfully (
|
|
29732
|
+
console.log('CustomerSDK initialized successfully (icon + screenshot only)');
|
|
30249
29733
|
}
|
|
30250
29734
|
// 返回初始化信息
|
|
30251
29735
|
return initResult;
|
|
@@ -30259,10 +29743,6 @@ class CustomerServiceSDK {
|
|
|
30259
29743
|
this.iconManager.hide();
|
|
30260
29744
|
this.iconManager = null;
|
|
30261
29745
|
}
|
|
30262
|
-
if (this.iframeManager) {
|
|
30263
|
-
this.iframeManager.destroy();
|
|
30264
|
-
this.iframeManager = null;
|
|
30265
|
-
}
|
|
30266
29746
|
if (this.screenshotManager) {
|
|
30267
29747
|
this.screenshotManager.destroy();
|
|
30268
29748
|
this.screenshotManager = null;
|
|
@@ -30308,31 +29788,6 @@ class CustomerServiceSDK {
|
|
|
30308
29788
|
setIconStyle(style) {
|
|
30309
29789
|
this.iconManager?.setStyle(style);
|
|
30310
29790
|
}
|
|
30311
|
-
/**
|
|
30312
|
-
* 打开/关闭聊天窗口
|
|
30313
|
-
*/
|
|
30314
|
-
openChat() {
|
|
30315
|
-
// 打开iframe时清除红点通知
|
|
30316
|
-
this.clearNotification();
|
|
30317
|
-
this.iframeManager?.open();
|
|
30318
|
-
}
|
|
30319
|
-
closeChat() {
|
|
30320
|
-
// 关闭iframe时,清理图标拖动事件监听器(防止页面无法操作)
|
|
30321
|
-
this.iconManager?.forceCleanupDragEvents();
|
|
30322
|
-
this.iframeManager?.close();
|
|
30323
|
-
}
|
|
30324
|
-
/**
|
|
30325
|
-
* 检查聊天窗口是否打开
|
|
30326
|
-
*/
|
|
30327
|
-
isChatOpen() {
|
|
30328
|
-
return this.iframeManager?.isIframeOpen() || false;
|
|
30329
|
-
}
|
|
30330
|
-
/**
|
|
30331
|
-
* 向iframe发送消息(传递用户数据等)
|
|
30332
|
-
*/
|
|
30333
|
-
sendToIframe(data) {
|
|
30334
|
-
this.iframeManager?.sendToIframe(data);
|
|
30335
|
-
}
|
|
30336
29791
|
/**
|
|
30337
29792
|
* 获取连接状态
|
|
30338
29793
|
* 注意:真正的连接状态保存在iframe中的SSE连接中
|
|
@@ -30423,40 +29878,13 @@ class CustomerServiceSDK {
|
|
|
30423
29878
|
console.log('📸 截图配置已更新:', options);
|
|
30424
29879
|
}
|
|
30425
29880
|
}
|
|
30426
|
-
/**
|
|
30427
|
-
* 更新 token(用于用户登录/退出场景)
|
|
30428
|
-
* 如果已初始化,会重新创建 iframe 并更新 URL
|
|
30429
|
-
* @param token 新的 token(传空字符串或 undefined 表示移除 token)
|
|
30430
|
-
* @param options UI选项(可选,用于重新初始化时的配置)
|
|
30431
|
-
* @returns 返回更新后的初始化信息
|
|
30432
|
-
*/
|
|
30433
|
-
async updateToken(token, options) {
|
|
30434
|
-
if (!this.isInitialized) {
|
|
30435
|
-
throw new Error('SDK not initialized. Call init() first.');
|
|
30436
|
-
}
|
|
30437
|
-
if (!this.config) {
|
|
30438
|
-
throw new Error('SDK config not found');
|
|
30439
|
-
}
|
|
30440
|
-
// 更新配置中的 token
|
|
30441
|
-
const updatedConfig = {
|
|
30442
|
-
...this.config,
|
|
30443
|
-
token: token && token.trim() !== '' ? token : undefined
|
|
30444
|
-
};
|
|
30445
|
-
if (this.debug) {
|
|
30446
|
-
console.log('Updating token:', token ? 'Token provided' : 'Token removed');
|
|
30447
|
-
}
|
|
30448
|
-
// 强制重新初始化以应用新的 token
|
|
30449
|
-
return await this.init(updatedConfig, options, true);
|
|
30450
|
-
}
|
|
30451
29881
|
/**
|
|
30452
29882
|
* 销毁 SDK
|
|
30453
29883
|
*/
|
|
30454
29884
|
destroy() {
|
|
30455
29885
|
this.iconManager?.hide();
|
|
30456
|
-
this.iframeManager?.destroy(); // 使用 destroy 而不是 close,确保完全清理
|
|
30457
29886
|
this.screenshotManager?.destroy();
|
|
30458
29887
|
this.iconManager = null;
|
|
30459
|
-
this.iframeManager = null;
|
|
30460
29888
|
this.screenshotManager = null;
|
|
30461
29889
|
this.config = null;
|
|
30462
29890
|
this.initResult = null;
|
|
@@ -30541,26 +29969,6 @@ class CustomerServiceSDK {
|
|
|
30541
29969
|
getReferrer(config) {
|
|
30542
29970
|
return config.referrer || document.referrer || document.location.href;
|
|
30543
29971
|
}
|
|
30544
|
-
/**
|
|
30545
|
-
* 构建iframe URL(带用户参数和设备ID)
|
|
30546
|
-
*/
|
|
30547
|
-
buildIframeUrl(config, deviceId) {
|
|
30548
|
-
const baseUrl = config.iframeUrl;
|
|
30549
|
-
if (!baseUrl || baseUrl.trim() === '') {
|
|
30550
|
-
throw new Error('iframeUrl is required but not provided or is empty');
|
|
30551
|
-
}
|
|
30552
|
-
const url = new URL(baseUrl);
|
|
30553
|
-
// 添加用户参数(只在有值时才添加)
|
|
30554
|
-
if (config.agent && config.agent.trim() !== '') {
|
|
30555
|
-
url.searchParams.set('Agent', config.agent);
|
|
30556
|
-
}
|
|
30557
|
-
if (config.token && config.token.trim() !== '') {
|
|
30558
|
-
url.searchParams.set('Authorization', config.token);
|
|
30559
|
-
}
|
|
30560
|
-
url.searchParams.set('DeviceSign', deviceId);
|
|
30561
|
-
url.searchParams.set('Referrer', this.getReferrer(config));
|
|
30562
|
-
return url.toString();
|
|
30563
|
-
}
|
|
30564
29972
|
}
|
|
30565
29973
|
// 创建全局实例
|
|
30566
29974
|
let globalSDKInstance = null;
|
|
@@ -30609,28 +30017,6 @@ const setIconStyle = (style) => {
|
|
|
30609
30017
|
const sdk = getInstance();
|
|
30610
30018
|
sdk.setIconStyle(style);
|
|
30611
30019
|
};
|
|
30612
|
-
/**
|
|
30613
|
-
* 聊天控制API
|
|
30614
|
-
*/
|
|
30615
|
-
const openChat = () => {
|
|
30616
|
-
const sdk = getInstance();
|
|
30617
|
-
sdk.openChat();
|
|
30618
|
-
};
|
|
30619
|
-
const closeChat = () => {
|
|
30620
|
-
const sdk = getInstance();
|
|
30621
|
-
sdk.closeChat();
|
|
30622
|
-
};
|
|
30623
|
-
const isChatOpen = () => {
|
|
30624
|
-
const sdk = getInstance();
|
|
30625
|
-
return sdk.isChatOpen();
|
|
30626
|
-
};
|
|
30627
|
-
/**
|
|
30628
|
-
* iframe通信API
|
|
30629
|
-
*/
|
|
30630
|
-
const sendToIframe = (data) => {
|
|
30631
|
-
const sdk = getInstance();
|
|
30632
|
-
sdk.sendToIframe(data);
|
|
30633
|
-
};
|
|
30634
30020
|
/**
|
|
30635
30021
|
* 其他API
|
|
30636
30022
|
*/
|
|
@@ -30684,16 +30070,6 @@ const updateScreenshotOptions = (options) => {
|
|
|
30684
30070
|
const sdk = getInstance();
|
|
30685
30071
|
sdk.updateScreenshotOptions(options);
|
|
30686
30072
|
};
|
|
30687
|
-
/**
|
|
30688
|
-
* 更新 token(用于用户登录/退出场景)
|
|
30689
|
-
* @param token 新的 token(传空字符串或 undefined 表示移除 token)
|
|
30690
|
-
* @param options UI选项(可选)
|
|
30691
|
-
* @returns 返回更新后的初始化信息
|
|
30692
|
-
*/
|
|
30693
|
-
const updateToken = async (token, options) => {
|
|
30694
|
-
const sdk = getInstance();
|
|
30695
|
-
return await sdk.updateToken(token, options);
|
|
30696
|
-
};
|
|
30697
30073
|
// 默认导出
|
|
30698
30074
|
var index = {
|
|
30699
30075
|
init,
|
|
@@ -30704,10 +30080,6 @@ var index = {
|
|
|
30704
30080
|
setIconPosition,
|
|
30705
30081
|
setIconCoordinates,
|
|
30706
30082
|
setIconStyle,
|
|
30707
|
-
openChat,
|
|
30708
|
-
closeChat,
|
|
30709
|
-
isChatOpen,
|
|
30710
|
-
sendToIframe,
|
|
30711
30083
|
getConnectionStatus,
|
|
30712
30084
|
showNotification,
|
|
30713
30085
|
clearNotification,
|
|
@@ -30716,14 +30088,13 @@ var index = {
|
|
|
30716
30088
|
enableScreenshot,
|
|
30717
30089
|
getScreenshotState,
|
|
30718
30090
|
updateScreenshotOptions,
|
|
30719
|
-
updateToken,
|
|
30720
30091
|
destroy
|
|
30721
30092
|
};
|
|
30722
30093
|
|
|
30094
|
+
exports.CustomerSDK = CustomerSDK;
|
|
30723
30095
|
exports.CustomerServiceSDK = CustomerServiceSDK;
|
|
30724
30096
|
exports.captureScreenshot = captureScreenshot;
|
|
30725
30097
|
exports.clearNotification = clearNotification;
|
|
30726
|
-
exports.closeChat = closeChat;
|
|
30727
30098
|
exports.default = index;
|
|
30728
30099
|
exports.destroy = destroy;
|
|
30729
30100
|
exports.enableScreenshot = enableScreenshot;
|
|
@@ -30733,9 +30104,6 @@ exports.getInstance = getInstance;
|
|
|
30733
30104
|
exports.getScreenshotState = getScreenshotState;
|
|
30734
30105
|
exports.hideIcon = hideIcon;
|
|
30735
30106
|
exports.init = init;
|
|
30736
|
-
exports.isChatOpen = isChatOpen;
|
|
30737
|
-
exports.openChat = openChat;
|
|
30738
|
-
exports.sendToIframe = sendToIframe;
|
|
30739
30107
|
exports.setIconCoordinates = setIconCoordinates;
|
|
30740
30108
|
exports.setIconPosition = setIconPosition;
|
|
30741
30109
|
exports.setIconStyle = setIconStyle;
|
|
@@ -30743,4 +30111,3 @@ exports.setScreenshotTarget = setScreenshotTarget;
|
|
|
30743
30111
|
exports.showIcon = showIcon;
|
|
30744
30112
|
exports.showNotification = showNotification;
|
|
30745
30113
|
exports.updateScreenshotOptions = updateScreenshotOptions;
|
|
30746
|
-
exports.updateToken = updateToken;
|