qb-pc-sdk 1.2.8 → 1.3.0

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.
@@ -0,0 +1,846 @@
1
+ (function(global) {
2
+ 'use strict';
3
+
4
+ const window = typeof global !== 'undefined' && global.window ? global.window : (typeof window !== 'undefined' ? window : global);
5
+ const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
6
+
7
+ if (isBrowser) {
8
+ if (window.document && !window._qbsdkIframeBlocked) {
9
+ const OriginalCreateElement = window.document.createElement;
10
+ window.document.createElement = function(tagName, options) {
11
+ const element = OriginalCreateElement.call(this, tagName, options);
12
+
13
+ if (tagName.toLowerCase() === 'iframe') {
14
+ const originalSetAttribute = element.setAttribute;
15
+
16
+ element.setAttribute = function(name, value) {
17
+ if (name === 'src' && value && /wxgamesdkframe/i.test(value)) {
18
+ return;
19
+ }
20
+ return originalSetAttribute.apply(this, arguments);
21
+ };
22
+
23
+ let _src = '';
24
+ Object.defineProperty(element, 'src', {
25
+ set: function(value) {
26
+ if (value && /wxgamesdkframe/i.test(value)) {
27
+ return;
28
+ }
29
+ _src = value;
30
+ if (originalSetAttribute) {
31
+ originalSetAttribute.call(this, 'src', value);
32
+ }
33
+
34
+ if (value && element.contentWindow) {
35
+ try {
36
+ const iframeWindow = element.contentWindow;
37
+ if (iframeWindow && iframeWindow.XMLHttpRequest && !iframeWindow._qbsdkXHRBlocked) {
38
+ const OriginalXHR = iframeWindow.XMLHttpRequest;
39
+ iframeWindow.XMLHttpRequest = function() {
40
+ const xhr = new OriginalXHR();
41
+ const originalOpen = xhr.open;
42
+ const originalSend = xhr.send;
43
+
44
+ xhr.open = function(method, url, async, user, password) {
45
+ if (url && /localhost\.weixin\.qq\.com/i.test(url)) {
46
+ xhr._qbsdkBlocked = true;
47
+ return;
48
+ }
49
+ return originalOpen.apply(this, arguments);
50
+ };
51
+
52
+ xhr.send = function(data) {
53
+ if (xhr._qbsdkBlocked) {
54
+ return;
55
+ }
56
+ return originalSend.apply(this, arguments);
57
+ };
58
+
59
+ return xhr;
60
+ };
61
+ iframeWindow._qbsdkXHRBlocked = true;
62
+ }
63
+ } catch (e) {
64
+ }
65
+ }
66
+ },
67
+ get: function() {
68
+ return _src || this.getAttribute('src') || '';
69
+ },
70
+ configurable: true
71
+ });
72
+
73
+ element.addEventListener('load', function() {
74
+ try {
75
+ const iframeWindow = element.contentWindow;
76
+ if (iframeWindow && iframeWindow.XMLHttpRequest && !iframeWindow._qbsdkXHRBlocked) {
77
+ const OriginalXHR = iframeWindow.XMLHttpRequest;
78
+ iframeWindow.XMLHttpRequest = function() {
79
+ const xhr = new OriginalXHR();
80
+ const originalOpen = xhr.open;
81
+ const originalSend = xhr.send;
82
+
83
+ xhr.open = function(method, url, async, user, password) {
84
+ if (url && /localhost\.weixin\.qq\.com/i.test(url)) {
85
+ xhr._qbsdkBlocked = true;
86
+ return;
87
+ }
88
+ return originalOpen.apply(this, arguments);
89
+ };
90
+
91
+ xhr.send = function(data) {
92
+ if (xhr._qbsdkBlocked) {
93
+ return;
94
+ }
95
+ return originalSend.apply(this, arguments);
96
+ };
97
+
98
+ return xhr;
99
+ };
100
+ iframeWindow._qbsdkXHRBlocked = true;
101
+ }
102
+ } catch (e) {
103
+ }
104
+ });
105
+ }
106
+
107
+ return element;
108
+ };
109
+ window._qbsdkIframeBlocked = true;
110
+ }
111
+
112
+ if (window.document && window.MutationObserver && !window._qbsdkObserverAdded) {
113
+ const observer = new MutationObserver(function(mutations) {
114
+ mutations.forEach(function(mutation) {
115
+ mutation.addedNodes.forEach(function(node) {
116
+ if (node.nodeType === 1 && node.tagName && node.tagName.toLowerCase() === 'iframe') {
117
+ const src = node.src || node.getAttribute('src') || '';
118
+ if (src && /wxgamesdkframe/i.test(src)) {
119
+ node.src = '';
120
+ node.setAttribute('src', '');
121
+ try {
122
+ node.parentNode && node.parentNode.removeChild(node);
123
+ } catch (e) {
124
+ }
125
+ }
126
+ }
127
+ });
128
+ });
129
+ });
130
+
131
+ observer.observe(window.document.body || window.document.documentElement, {
132
+ childList: true,
133
+ subtree: true
134
+ });
135
+
136
+ window._qbsdkObserverAdded = true;
137
+ }
138
+
139
+ if (window.XMLHttpRequest && !window._qbsdkXHRBlocked) {
140
+ const OriginalXHR = window.XMLHttpRequest;
141
+
142
+ window.XMLHttpRequest = function() {
143
+ const xhr = new OriginalXHR();
144
+ const originalOpen = xhr.open;
145
+ const originalSend = xhr.send;
146
+
147
+ xhr._qbsdkBlocked = false;
148
+
149
+ xhr.open = function(method, url, async, user, password) {
150
+ const targetUrl = (url || '').toString().toLowerCase();
151
+
152
+ if (targetUrl.includes('localhost.weixin.qq.com') ||
153
+ targetUrl.includes('wx_game_base') ||
154
+ /127\.0\.0\.1:\d+\/wx_game_base/.test(targetUrl)) {
155
+
156
+ xhr._qbsdkBlocked = true;
157
+
158
+ return;
159
+ }
160
+
161
+ return originalOpen.apply(this, arguments);
162
+ };
163
+
164
+ xhr.send = function(data) {
165
+ if (xhr._qbsdkBlocked) {
166
+ setTimeout(() => {
167
+ if (xhr.readyState !== 4) {
168
+ Object.defineProperty(xhr, 'readyState', { value: 4, writable: true });
169
+ Object.defineProperty(xhr, 'status', { value: 0, writable: true });
170
+ Object.defineProperty(xhr, 'statusText', { value: 'Blocked by AdSDKWrapper', writable: true });
171
+ }
172
+
173
+ if (xhr.onerror) xhr.onerror(new ProgressEvent('error'));
174
+ }, 0);
175
+ return;
176
+ }
177
+ return originalSend.apply(this, arguments);
178
+ };
179
+
180
+ return xhr;
181
+ };
182
+
183
+ for (let key in OriginalXHR) {
184
+ if (Object.prototype.hasOwnProperty.call(OriginalXHR, key)) {
185
+ window.XMLHttpRequest[key] = OriginalXHR[key];
186
+ }
187
+ }
188
+
189
+ window._qbsdkXHRBlocked = true;
190
+ }
191
+
192
+ if (window.fetch && !window._qbsdkFetchBlocked) {
193
+ const originalFetch = window.fetch;
194
+ window.fetch = function(input, init) {
195
+ let url = '';
196
+ if (typeof input === 'string') {
197
+ url = input;
198
+ } else if (input instanceof Request) {
199
+ url = input.url;
200
+ }
201
+
202
+ url = url.toLowerCase();
203
+
204
+ if (url.includes('localhost.weixin.qq.com') || url.includes('wx_game_base')) {
205
+ return Promise.resolve(new Response(JSON.stringify({ code: -1, msg: 'Blocked' }), {
206
+ status: 200,
207
+ statusText: 'OK',
208
+ headers: { 'Content-Type': 'application/json' }
209
+ }));
210
+ }
211
+
212
+ return originalFetch.apply(this, arguments);
213
+ };
214
+ window._qbsdkFetchBlocked = true;
215
+ }
216
+
217
+ if (window.console && !window.console.error._qbsdkFiltered) {
218
+ const originalError = window.console.error;
219
+ const originalWarn = window.console.warn;
220
+
221
+ const shouldFilter = (args) => {
222
+ const text = args.map(String).join(' ').toLowerCase();
223
+ return (
224
+ text.includes('localhost.weixin.qq.com') ||
225
+ text.includes('wx_game_base') ||
226
+ text.includes('wxgamesdkframe') ||
227
+ (text.includes('sdk.e.qq.com') && text.includes('cors')) ||
228
+ (text.includes('sdk.e.qq.com') && text.includes('access-control-allow-origin')) ||
229
+ (text.includes('sdk.e.qq.com') && text.includes('err_failed') && text.includes('500'))
230
+ );
231
+ };
232
+
233
+ window.console.error = function(...args) {
234
+ if (!shouldFilter(args)) originalError.apply(console, args);
235
+ };
236
+
237
+ window.console.warn = function(...args) {
238
+ if (!shouldFilter(args)) originalWarn.apply(console, args);
239
+ };
240
+
241
+ window.console.error._qbsdkFiltered = true;
242
+ }
243
+ }
244
+
245
+ const API_CONFIG = {
246
+ INIT: 'https://app.qubiankeji.com/pc/init',
247
+ POSITION: 'https://app.qubiankeji.com/pc/position',
248
+ GDT_SDK_ID: 2
249
+ };
250
+
251
+
252
+ const INJECT_CSS = `
253
+ .q-ad-wrapper { display: flex; flex-direction: column; width: 100%; height: 100%; background: #fff; border: 1px solid #eee; overflow: hidden; font-family: sans-serif; position: relative; box-sizing: border-box; }
254
+ .q-media-container { width: 100%; flex: 1; min-height: 0; background: #000; position: relative; display: flex; align-items: center; justify-content: center; overflow: hidden; }
255
+ .q-ad-img, .q-ad-video { width: 100%; height: 100%; object-fit: contain; display: block; }
256
+ .q-content-container { padding: 8px; flex-shrink: 0; display: flex; flex-direction: column; justify-content: space-between; min-height: 0; }
257
+ .q-ad-title { font-size: 14px; font-weight: bold; color: #333; margin: 0 0 4px 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.2; }
258
+ .q-ad-desc { font-size: 11px; color: #666; margin: 0 0 6px 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.3; flex-shrink: 0; }
259
+ .q-cta-btn { background: #007bff; color: #fff; border: none; padding: 4px 12px; border-radius: 4px; font-size: 11px; cursor: pointer; align-self: flex-start; flex-shrink: 0; }
260
+ .q-ad-tag { position: absolute; right: 5px; bottom: 5px; background: rgba(0,0,0,0.3); color: #fff; font-size: 10px; padding: 2px 4px; border-radius: 2px; }
261
+ .q-ad-close { position: absolute; top: 5px; right: 5px; width: 20px; height: 20px; background: rgba(0,0,0,0.5); color: #fff; border: none; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 14px; line-height: 1; z-index: 10; transition: background 0.2s; flex-shrink: 0; }
262
+ .q-ad-close:hover { background: rgba(0,0,0,0.7); }
263
+ .q-ad-close:active { background: rgba(0,0,0,0.9); }
264
+ .q-ad-destroyed { display: none !important; }
265
+ .q-ad-wrapper.q-ad-small .q-media-container { flex: 1.2; }
266
+ .q-ad-wrapper.q-ad-small .q-content-container { padding: 6px; }
267
+ .q-ad-wrapper.q-ad-small .q-ad-title { font-size: 12px; }
268
+ .q-ad-wrapper.q-ad-small .q-ad-desc { font-size: 10px; margin-bottom: 4px; }
269
+ .q-ad-wrapper.q-ad-small .q-cta-btn { padding: 3px 10px; font-size: 10px; }
270
+ .q-ad-wrapper.q-ad-banner { flex-direction: row !important; align-items: stretch; }
271
+ .q-ad-wrapper.q-ad-banner .q-media-container { width: auto !important; flex: 0 0 auto !important; min-width: 0; height: 100% !important; max-width: 40% !important; min-width: 120px; position: relative; }
272
+ .q-ad-wrapper.q-ad-banner .q-ad-img, .q-ad-wrapper.q-ad-banner .q-ad-video { width: 100% !important; height: 100% !important; object-fit: cover !important; }
273
+ .q-ad-wrapper.q-ad-banner .q-content-container { flex: 1 !important; padding: 6px 10px !important; justify-content: center !important; min-width: 0; display: flex !important; flex-direction: column !important; width: auto !important; }
274
+ .q-ad-wrapper.q-ad-banner .q-ad-title { font-size: 14px !important; margin: 0 0 3px 0 !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; flex-shrink: 0; }
275
+ .q-ad-wrapper.q-ad-banner .q-ad-desc { font-size: 11px !important; margin: 0 0 4px 0 !important; -webkit-line-clamp: 1 !important; line-height: 1.3 !important; overflow: hidden !important; text-overflow: ellipsis !important; display: -webkit-box !important; -webkit-box-orient: vertical !important; flex-shrink: 0; }
276
+ .q-ad-wrapper.q-ad-banner .q-cta-btn { padding: 4px 12px !important; font-size: 11px !important; align-self: flex-start !important; flex-shrink: 0; margin-top: auto !important; }
277
+ .q-ad-wrapper.q-ad-banner .q-ad-tag { right: auto !important; left: 5px !important; }
278
+ .q-ad-wrapper.q-ad-banner.q-ad-banner-narrow .q-media-container { max-width: 35% !important; min-width: 100px !important; }
279
+ .q-ad-wrapper.q-ad-banner.q-ad-banner-narrow .q-content-container { padding: 4px 8px !important; }
280
+ .q-ad-wrapper.q-ad-banner.q-ad-banner-narrow .q-ad-title { font-size: 13px !important; margin-bottom: 2px !important; }
281
+ .q-ad-wrapper.q-ad-banner.q-ad-banner-narrow .q-ad-desc { font-size: 10px !important; margin-bottom: 3px !important; display: none !important; }
282
+ .q-ad-wrapper.q-ad-banner.q-ad-banner-narrow .q-cta-btn { padding: 3px 10px !important; font-size: 10px !important; }
283
+ .q-ad-media-hidden { display: none !important; }
284
+ `;
285
+
286
+ class AdSDKWrapper {
287
+ constructor(config) {
288
+ this.config = Object.assign({
289
+ appId: '',
290
+ placementId: '',
291
+ container: null,
292
+ onAdLoaded: null,
293
+ onAdError: null
294
+ }, config);
295
+
296
+ this.gdtAppId = null;
297
+ this.gdtPlacementId = null;
298
+ this.currentAd = null;
299
+ this._apiDataReady = false; // API 数据是否已准备好
300
+ this._isUnsupportedPlatform = false; // 不支持的平台标记
301
+
302
+ this._checkMobileEnvironment();
303
+
304
+ // 检查是否为 Windows 环境,非 Windows 环境不请求广告
305
+ if (!this._checkWindowsEnvironment()) {
306
+ // 非 Windows 环境(Mac、Linux、移动端等),静默退出,不报错
307
+ return;
308
+ }
309
+
310
+ this._injectStyles();
311
+
312
+ this.container = typeof this.config.container === 'string'
313
+ ? document.querySelector(this.config.container)
314
+ : this.config.container;
315
+
316
+ if (this.container) {
317
+ this._initWorkflow();
318
+ }
319
+ }
320
+
321
+ _checkMobileEnvironment() {
322
+ if (typeof window !== 'undefined' && window.navigator) {
323
+ const userAgent = window.navigator.userAgent || '';
324
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
325
+
326
+ if (isMobile && this.config.onAdError) {
327
+ setTimeout(() => {
328
+ this.config.onAdError(
329
+ new Error('移动端环境警告'),
330
+ '这是 PC 端 SDK,移动端可能无法正常加载广告'
331
+ );
332
+ }, 100);
333
+ }
334
+ }
335
+ }
336
+
337
+ _checkWindowsEnvironment() {
338
+ if (typeof window !== 'undefined' && window.navigator) {
339
+ const ua = (window.navigator.userAgent || '').toLowerCase();
340
+ const p = (window.navigator.platform || '').toLowerCase();
341
+ if (/android|iphone|ipad|ipod|mac|webos|blackberry|iemobile|opera mini/i.test(ua) || !/win|windows/i.test(ua + p)) {
342
+ this._isUnsupportedPlatform = true;
343
+ return false;
344
+ }
345
+ return true;
346
+ }
347
+ return true;
348
+ }
349
+
350
+ _injectStyles() {
351
+ if (!document.getElementById('q-ad-styles')) {
352
+ const style = document.createElement('style');
353
+ style.id = 'q-ad-styles';
354
+ style.textContent = INJECT_CSS;
355
+ document.head.appendChild(style);
356
+ }
357
+ }
358
+
359
+ async _initWorkflow() {
360
+ // 非 Windows 环境,不请求广告,静默退出
361
+ if (this._isUnsupportedPlatform) {
362
+ return;
363
+ }
364
+
365
+ try {
366
+ const [initRes, posRes] = await Promise.all([
367
+ fetch(`${API_CONFIG.INIT}?appId=${this.config.appId}`).then(r => r.json()),
368
+ fetch(`${API_CONFIG.POSITION}?positionId=${this.config.placementId}`).then(r => r.json())
369
+ ]);
370
+
371
+ const initData = initRes.data || initRes;
372
+ const posData = posRes.data || posRes;
373
+
374
+ this.gdtAppId = initData.thirdIdMap ? initData.thirdIdMap[String(API_CONFIG.GDT_SDK_ID)] : null;
375
+
376
+ this.gdtPlacementId = this._selectPlacementId(posData);
377
+
378
+ if (!this.gdtAppId || !this.gdtPlacementId) {
379
+ const errorMsg = `未找到匹配的(sdkId:2)配置信息 - AppId: ${this.gdtAppId}, PlacementId: ${this.gdtPlacementId}`;
380
+ throw new Error(errorMsg);
381
+ }
382
+
383
+ this._apiDataReady = true;
384
+
385
+ if (window.GDTAdSDK) {
386
+ this._loadAd();
387
+ } else {
388
+ this._checkAndRun();
389
+ }
390
+ } catch (e) {
391
+ this._handleError(e, '初始化配置失败');
392
+ }
393
+ }
394
+ _selectPlacementId(data) {
395
+ if (!data) return null;
396
+ const targetSdkId = API_CONFIG.GDT_SDK_ID;
397
+ if (data.headSetList && Array.isArray(data.headSetList) && data.headSetList.length > 0) {
398
+ const headAd = data.headSetList.find(item => {
399
+ const itemSdkId = Number(item.sdkId);
400
+ return itemSdkId === targetSdkId || String(item.sdkId) === String(targetSdkId);
401
+ });
402
+ if (headAd) return headAd.positionId;
403
+ }
404
+ if (data.positionSetList && Array.isArray(data.positionSetList) && data.positionSetList.length > 0) {
405
+ const sortedList = data.positionSetList
406
+ .filter(item => {
407
+ const itemSdkId = Number(item.sdkId);
408
+ return itemSdkId === targetSdkId || String(item.sdkId) === String(targetSdkId);
409
+ })
410
+ .sort((a, b) => (b.callbackPriority || 0) - (a.callbackPriority || 0));
411
+ if (sortedList.length > 0) return sortedList[0].positionId;
412
+ }
413
+ return null;
414
+ }
415
+
416
+ _checkAndRun() {
417
+ if (window.GDTAdSDK && this._apiDataReady) {
418
+ this._loadAd();
419
+ } else {
420
+ this._handleError(new Error('SDK Load Timeout'), '底层 SDK 未就绪');
421
+ }
422
+ }
423
+
424
+ async _loadAd() {
425
+ try {
426
+ await window.GDTAdSDK.init({ appId: this.gdtAppId });
427
+
428
+ const ads = await window.GDTAdSDK.loadNativeAdData({
429
+ placementId: this.gdtPlacementId,
430
+ count: 1
431
+ });
432
+
433
+ if (ads && ads.length > 0) {
434
+ this.renderAd(ads[0]);
435
+ } else {
436
+ this._handleError('No Ad Fill', '当前没有广告填充');
437
+ }
438
+ } catch (err) {
439
+ this._handleError(err, '拉取广告数据失败');
440
+ }
441
+ }
442
+
443
+ renderAd(ad) {
444
+ this.currentAd = ad;
445
+
446
+ let containerWidth = this.container.offsetWidth;
447
+ let containerHeight = this.container.offsetHeight;
448
+
449
+ if (!containerWidth || !containerHeight) {
450
+ const containerRect = this.container.getBoundingClientRect();
451
+ containerWidth = containerRect.width || containerWidth || 200;
452
+ containerHeight = containerRect.height || containerHeight || 120;
453
+ }
454
+
455
+ if (!containerWidth || !containerHeight) {
456
+ const computedStyle = window.getComputedStyle(this.container);
457
+ containerWidth = parseFloat(computedStyle.width) || 200;
458
+ containerHeight = parseFloat(computedStyle.height) || 120;
459
+ }
460
+
461
+ const aspectRatio = containerWidth / containerHeight;
462
+
463
+ const isBanner = aspectRatio > 3 && containerHeight < 150;
464
+ const isNarrowBanner = isBanner && containerHeight < 80;
465
+
466
+ const isSmallAd = !isBanner && (containerWidth < 300 || containerHeight < 200);
467
+
468
+ let wrapperClass = 'q-ad-wrapper';
469
+ if (isBanner) {
470
+ wrapperClass += ' q-ad-banner';
471
+ if (isNarrowBanner) {
472
+ wrapperClass += ' q-ad-banner-narrow';
473
+ }
474
+ } else if (isSmallAd) {
475
+ wrapperClass += ' q-ad-small';
476
+ }
477
+
478
+
479
+ this.container.innerHTML = `
480
+ <div class="${wrapperClass}">
481
+ <button class="q-ad-close" title="关闭广告" type="button">×</button>
482
+ <div class="q-media-container">
483
+ <div class="q-ad-video q-ad-media-hidden"></div>
484
+ <img class="q-ad-img q-ad-media-hidden" alt="">
485
+ <span class="q-ad-tag">广告</span>
486
+ </div>
487
+ <div class="q-content-container">
488
+ <h3 class="q-ad-title">${ad.getTitle()}</h3>
489
+ <p class="q-ad-desc">${ad.getDescription()}</p>
490
+ <button class="q-cta-btn" type="button">${ad.getBtnText() || '查看详情'}</button>
491
+ </div>
492
+ </div>
493
+ `;
494
+
495
+ if (isBanner) {
496
+ const mediaAspectRatio = isNarrowBanner ? 4 / 3 : 16 / 9;
497
+ const mediaWidth = Math.min(containerHeight * mediaAspectRatio, containerWidth * 0.4);
498
+ const mediaEl = this.container.querySelector('.q-media-container');
499
+ if (mediaEl) mediaEl.style.width = mediaWidth + 'px';
500
+ }
501
+
502
+
503
+ const closeBtn = this.container.querySelector('.q-ad-close');
504
+ if (closeBtn) {
505
+ closeBtn.addEventListener('click', (e) => {
506
+ e.stopPropagation();
507
+ this.destroy();
508
+ });
509
+ }
510
+
511
+ const videoEl = this.container.querySelector('.q-ad-video');
512
+ const imgEl = this.container.querySelector('.q-ad-img');
513
+ const wrapper = this.container.querySelector('.q-ad-wrapper');
514
+ const clickable = [
515
+ this.container.querySelector('.q-ad-title'),
516
+ this.container.querySelector('.q-cta-btn'),
517
+ imgEl,
518
+ videoEl
519
+ ];
520
+
521
+ ad.bindAdToView({
522
+ containerView: wrapper,
523
+ clickableElements: clickable,
524
+ adEventListener: {
525
+ onExpose: () => {
526
+ if (this.config.onAdExpose) this.config.onAdExpose();
527
+ },
528
+ onClick: () => {
529
+ if (this.config.onAdClick) this.config.onAdClick();
530
+ }
531
+ }
532
+ });
533
+
534
+ if (ad.hasVideo()) {
535
+ videoEl.classList.remove('q-ad-media-hidden');
536
+ ad.bindMediaView({
537
+ mediaView: videoEl,
538
+ videoOption: { muted: true, autoplay: true }
539
+ });
540
+ } else {
541
+ imgEl.classList.remove('q-ad-media-hidden');
542
+ imgEl.src = ad.getImageUrl();
543
+ }
544
+
545
+ if (this.config.onAdLoaded) {
546
+ this.config.onAdLoaded.call(this, ad, this);
547
+ }
548
+ }
549
+
550
+ /**
551
+ * 获取广告尺寸信息
552
+ * @returns {Object} 包含广告图片宽度、高度等信息
553
+ */
554
+ getAdSize() {
555
+ if (!this.currentAd) {
556
+ return null;
557
+ }
558
+
559
+ return {
560
+ imageWidth: this.currentAd.getImageWidth ? this.currentAd.getImageWidth() : 0,
561
+ imageHeight: this.currentAd.getImageHeight ? this.currentAd.getImageHeight() : 0,
562
+ hasVideo: this.currentAd.hasVideo ? this.currentAd.hasVideo() : false,
563
+ videoUrl: this.currentAd.getVideoUrl ? this.currentAd.getVideoUrl() : undefined
564
+ };
565
+ }
566
+
567
+ destroy() {
568
+ if (this.currentAd && typeof this.currentAd.destroy === 'function') {
569
+ this.currentAd.destroy();
570
+ this.currentAd = null;
571
+ }
572
+ if (this.container) {
573
+ this.container.innerHTML = '';
574
+ this.container.classList.add('q-ad-destroyed');
575
+ }
576
+ this.gdtAppId = null;
577
+ this.gdtPlacementId = null;
578
+ }
579
+
580
+ _handleError(error, message) {
581
+ const errStr = String(error).toLowerCase();
582
+ if (errStr.includes('localhost.weixin.qq.com') || errStr.includes('blocked by adsdkwrapper')) {
583
+ return;
584
+ }
585
+ if (this.config.onAdError) {
586
+ this.config.onAdError(error, message);
587
+ }
588
+ }
589
+ }
590
+
591
+ if (typeof window !== 'undefined' && window.document) {
592
+ window.AdSDK = AdSDKWrapper;
593
+
594
+ if (!window.qbAds) {
595
+ window.qbAds = [];
596
+ }
597
+
598
+ window._qbAdsInstances = window._qbAdsInstances || [];
599
+
600
+ function initAllAdSlots() {
601
+ if (!window.AdSDK || !window.GDTAdSDK) {
602
+ return false;
603
+ }
604
+
605
+ const adSlots = window.document.querySelectorAll('ins.qb-adsbyqubian');
606
+
607
+ if (adSlots.length === 0) {
608
+ return true;
609
+ }
610
+
611
+ adSlots.forEach(function(insElement, index) {
612
+ if (insElement._qbAdInitialized) {
613
+ return;
614
+ }
615
+
616
+ insElement._qbAdInitialized = true;
617
+ const appId = insElement.getAttribute('data-app-id');
618
+ const placementId = insElement.getAttribute('data-placement-id');
619
+ if (!appId || !placementId) {
620
+ return;
621
+ }
622
+ const computedStyle = window.getComputedStyle(insElement);
623
+ if (computedStyle.display === 'none') {
624
+ insElement.style.display = 'block';
625
+ }
626
+ insElement.innerHTML = '';
627
+ try {
628
+ const adSDKInstance = new window.AdSDK({
629
+ appId: appId,
630
+ placementId: placementId,
631
+ container: insElement,
632
+ onAdLoaded: function(ad, instance) {
633
+ if (window.qbAds._onAdLoaded && typeof window.qbAds._onAdLoaded === 'function') {
634
+ window.qbAds._onAdLoaded.call(this, ad, instance, insElement);
635
+ }
636
+ },
637
+ onAdError: function(err, msg) {
638
+ if (window.qbAds._onAdError && typeof window.qbAds._onAdError === 'function') {
639
+ window.qbAds._onAdError.call(this, err, msg, insElement);
640
+ }
641
+ },
642
+ onAdExpose: function() {
643
+ if (window.qbAds._onAdExpose && typeof window.qbAds._onAdExpose === 'function') {
644
+ window.qbAds._onAdExpose.call(this, insElement);
645
+ }
646
+ },
647
+ onAdClick: function() {
648
+ if (window.qbAds._onAdClick && typeof window.qbAds._onAdClick === 'function') {
649
+ window.qbAds._onAdClick.call(this, insElement);
650
+ }
651
+ }
652
+ });
653
+
654
+ window._qbAdsInstances.push({
655
+ element: insElement,
656
+ instance: adSDKInstance
657
+ });
658
+
659
+ } catch (error) {
660
+ }
661
+ });
662
+
663
+ return true;
664
+ }
665
+
666
+ const originalPush = window.qbAds.push.bind(window.qbAds);
667
+
668
+ if (!window._qbAdsReadyHandler) {
669
+ window._qbAdsReadyHandler = function() {
670
+ if (window.AdSDK && window.GDTAdSDK) {
671
+ setTimeout(function() {
672
+ initAllAdSlots();
673
+ }, 0);
674
+ }
675
+ };
676
+ window.addEventListener('qb-pc-sdk-ready', window._qbAdsReadyHandler);
677
+ }
678
+
679
+ window.qbAds.push = function(config) {
680
+ if (config && typeof config === 'object') {
681
+ if (config.onAdLoaded && typeof config.onAdLoaded === 'function') {
682
+ window.qbAds._onAdLoaded = config.onAdLoaded;
683
+ }
684
+ if (config.onAdError && typeof config.onAdError === 'function') {
685
+ window.qbAds._onAdError = config.onAdError;
686
+ }
687
+ if (config.onAdExpose && typeof config.onAdExpose === 'function') {
688
+ window.qbAds._onAdExpose = config.onAdExpose;
689
+ }
690
+ if (config.onAdClick && typeof config.onAdClick === 'function') {
691
+ window.qbAds._onAdClick = config.onAdClick;
692
+ }
693
+ }
694
+
695
+ // 尝试初始化所有广告位
696
+ const initialized = initAllAdSlots();
697
+
698
+ if (!initialized && window.AdSDK && window.GDTAdSDK) {
699
+ // 如果 SDK 已经加载但初始化失败,稍后重试
700
+ setTimeout(function() {
701
+ initAllAdSlots();
702
+ }, 100);
703
+ }
704
+
705
+ // 保持队列的原始行为
706
+ return originalPush(config);
707
+ };
708
+
709
+ // 如果 DOM 已经加载完成,立即尝试初始化
710
+ if (window.document.readyState === 'complete' || window.document.readyState === 'interactive') {
711
+ setTimeout(function() {
712
+ if (window.AdSDK && window.GDTAdSDK) {
713
+ initAllAdSlots();
714
+ }
715
+ }, 0);
716
+ } else {
717
+ // 监听 DOMContentLoaded 事件
718
+ window.addEventListener('DOMContentLoaded', function() {
719
+ setTimeout(function() {
720
+ if (window.AdSDK && window.GDTAdSDK) {
721
+ initAllAdSlots();
722
+ }
723
+ }, 0);
724
+ });
725
+ }
726
+
727
+ // 如果 SDK 已经加载完成(同步加载的情况),立即尝试初始化
728
+ if (window.AdSDK && window.GDTAdSDK) {
729
+ setTimeout(function() {
730
+ initAllAdSlots();
731
+ }, 0);
732
+ }
733
+
734
+ // 监听 SDK 就绪事件(异步加载的情况)
735
+ window.addEventListener('qb-pc-sdk-ready', function() {
736
+ setTimeout(function() {
737
+ initAllAdSlots();
738
+ }, 0);
739
+ });
740
+
741
+ // 使用 MutationObserver 监听动态添加的广告位(适用于 SPA 等场景)
742
+ if (window.MutationObserver) {
743
+ const observer = new window.MutationObserver(function(mutations) {
744
+ let shouldInit = false;
745
+ mutations.forEach(function(mutation) {
746
+ mutation.addedNodes.forEach(function(node) {
747
+ if (node.nodeType === 1) {
748
+ // 检查是否是广告位标签,或其包含广告位标签
749
+ if (node.tagName && node.tagName.toLowerCase() === 'ins' &&
750
+ node.classList && node.classList.contains('qb-adsbyqubian')) {
751
+ shouldInit = true;
752
+ } else if (node.querySelectorAll) {
753
+ const adSlots = node.querySelectorAll('ins.qb-adsbyqubian');
754
+ if (adSlots.length > 0) {
755
+ shouldInit = true;
756
+ }
757
+ }
758
+ }
759
+ });
760
+ });
761
+
762
+ if (shouldInit && window.AdSDK && window.GDTAdSDK) {
763
+ setTimeout(function() {
764
+ initAllAdSlots();
765
+ }, 100); // 延迟一点,确保 DOM 完全插入
766
+ }
767
+ });
768
+
769
+ // 开始观察
770
+ if (window.document.body) {
771
+ observer.observe(window.document.body, {
772
+ childList: true,
773
+ subtree: true
774
+ });
775
+ } else {
776
+ window.addEventListener('DOMContentLoaded', function() {
777
+ if (window.document.body) {
778
+ observer.observe(window.document.body, {
779
+ childList: true,
780
+ subtree: true
781
+ });
782
+ }
783
+ });
784
+ }
785
+ }
786
+
787
+ }
788
+
789
+ // CommonJS 模块导出
790
+ if (typeof module !== 'undefined' && module.exports) {
791
+ module.exports = AdSDKWrapper;
792
+ module.exports.default = AdSDKWrapper;
793
+ module.exports.AdSDKWrapper = AdSDKWrapper;
794
+ }
795
+
796
+ // AMD 模块导出
797
+ if (typeof define === 'function' && define.amd) {
798
+ define(function() {
799
+ return AdSDKWrapper;
800
+ });
801
+ }
802
+
803
+ // 浏览器环境:挂载到 window 并实现热更新接管
804
+ if (typeof window !== 'undefined') {
805
+ // 保存旧版本的引用(如果存在)
806
+ const oldAdSDK = window.AdSDK;
807
+ const oldVersion = window._qbPcSdkVersion || 'unknown';
808
+
809
+ // 保存已创建的实例(如果有)
810
+ const existingInstances = window._qbAdInstances || [];
811
+
812
+ // 标记新版本(从 package.json 读取,构建时注入)
813
+ const newVersion = '{{VERSION}}';
814
+ window._qbPcSdkVersion = newVersion;
815
+
816
+ // 如果存在旧版本,尝试迁移已创建的实例
817
+ if (oldAdSDK && oldAdSDK !== AdSDKWrapper) {
818
+ // 保留旧版本的实例引用,确保已创建的广告实例继续工作
819
+ window._qbAdOldInstances = existingInstances;
820
+
821
+ // 如果旧版本有 destroy 方法,可以在这里调用(可选)
822
+ // 但为了平滑过渡,我们保留旧实例,让它们自然销毁
823
+ }
824
+
825
+ // 用新版本覆盖 window.AdSDK
826
+ window.AdSDK = AdSDKWrapper;
827
+
828
+ // 触发版本更新事件(可选,用于调试)
829
+ if (oldAdSDK && oldAdSDK !== AdSDKWrapper) {
830
+ try {
831
+ window.dispatchEvent(new CustomEvent('qb-pc-sdk-updated', {
832
+ detail: {
833
+ oldVersion: oldVersion,
834
+ newVersion: window._qbPcSdkVersion,
835
+ preservedInstances: existingInstances.length
836
+ }
837
+ }));
838
+ } catch (e) {
839
+ // 忽略事件分发错误
840
+ }
841
+ }
842
+ }
843
+
844
+ // 返回类供直接使用
845
+ return AdSDKWrapper;
846
+ })(typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this);