@xrpl-wallet-kit/browser 0.1.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.
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # @xrpl-wallet-kit/browser
2
+
3
+ Browser-ready XRPL Wallet Kit bundle for vanilla JavaScript and legacy HTML sites.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @xrpl-wallet-kit/browser@beta
9
+ ```
10
+
11
+ The package publishes:
12
+
13
+ - `dist/xrpl-wallet-kit.iife.js`
14
+ - `dist/xrpl-wallet-kit.iife.min.js`
15
+ - `dist/xrpl-wallet-kit-legacy-bridge.js`
16
+
17
+ ## Basic HTML
18
+
19
+ ```html
20
+ <div id="connect-wallet"></div>
21
+ <script src="./xrpl-wallet-kit.iife.min.js"></script>
22
+ <script>
23
+ const kit = window.XRPLWalletKit.createWalletKit({
24
+ appName: "My XRPL App",
25
+ network: "mainnet",
26
+ autoConnect: true,
27
+ walletConnectProjectId: "YOUR_WALLETCONNECT_PROJECT_ID",
28
+ ui: {
29
+ layout: "list",
30
+ themeMode: "light",
31
+ walletConnect: { mode: "default" },
32
+ showWeb3Name: true,
33
+ showBalance: true
34
+ }
35
+ });
36
+
37
+ kit.button.mount("#connect-wallet");
38
+ </script>
39
+ ```
40
+
41
+ ## Legacy Bridge
42
+
43
+ Use `xrpl-wallet-kit-legacy-bridge.js` when a legacy page needs a smaller integration surface around the browser bundle. The bridge is intended for HTML/jQuery apps that want to mount the connect button and read wallet state without migrating the whole app at once.
44
+
45
+ ## Notes
46
+
47
+ - Always inject `walletConnectProjectId` from the host app.
48
+ - Do not put private keys, seeds, or secrets in browser code.
49
+ - Use HTTPS in production for wallet browser APIs and mobile deep links.
@@ -0,0 +1,440 @@
1
+ (function (window, document) {
2
+ 'use strict';
3
+
4
+ function mapAdapterIdToLegacy(adapterId) {
5
+ var map = {
6
+ staticbit: 'staticbitwallet',
7
+ bitget: 'bitgetwallet',
8
+ joey: 'joeywallet',
9
+ girin: 'girinwallet',
10
+ bifrost: 'bifrostwallet'
11
+ };
12
+ return map[adapterId] || adapterId;
13
+ }
14
+
15
+ function mapLegacyAdapterIdToKit(adapterId) {
16
+ var map = {
17
+ staticbitwallet: 'staticbit',
18
+ bitgetwallet: 'bitget',
19
+ joeywallet: 'joey',
20
+ girinwallet: 'girin',
21
+ bifrostwallet: 'bifrost',
22
+ ledgerwallet: 'ledger'
23
+ };
24
+ return map[adapterId] || adapterId;
25
+ }
26
+
27
+ function getPathValue(source, path) {
28
+ return path.split('.').reduce(function (value, key) {
29
+ return value && typeof value === 'object' ? value[key] : undefined;
30
+ }, source);
31
+ }
32
+
33
+ function inferMethodHint(txJson, options) {
34
+ if (options && options.methodHint) return options.methodHint;
35
+ if (options && options.gemMethod === 'sendPayment') return 'payment';
36
+ if (options && options.gemMethod === 'createNFTOffer') return 'createNFTOffer';
37
+ if (options && options.gemMethod === 'acceptNFTOffer') return 'acceptNFTOffer';
38
+ if (options && options.gemMethod === 'cancelNFTOffer') return 'cancelNFTOffer';
39
+ if (options && options.gemMethod === 'burnNFT') return 'burnNFT';
40
+ if (options && (options.gemMethod === 'setTrustline' || options.gemMethod === 'addTrustline')) return 'setTrustline';
41
+
42
+ var transactionType = txJson && txJson.TransactionType;
43
+ if (transactionType === 'Payment') return 'payment';
44
+ if (transactionType === 'NFTokenCreateOffer') return 'createNFTOffer';
45
+ if (transactionType === 'NFTokenAcceptOffer') return 'acceptNFTOffer';
46
+ if (transactionType === 'NFTokenCancelOffer') return 'cancelNFTOffer';
47
+ if (transactionType === 'NFTokenBurn') return 'burnNFT';
48
+ if (transactionType === 'TrustSet') return 'setTrustline';
49
+ return 'generic';
50
+ }
51
+
52
+ function createTxJsonFromWalletPayload(options, address) {
53
+ var payload = options && (options.walletPayload || options.gemPayload);
54
+ if (!payload || !address) return null;
55
+
56
+ if (options.gemMethod === 'sendPayment' || options.methodHint === 'payment') {
57
+ return cleanTxJson({
58
+ TransactionType: 'Payment',
59
+ Account: address,
60
+ Destination: payload.destination || payload.Destination,
61
+ Amount: payload.amount || payload.Amount
62
+ });
63
+ }
64
+
65
+ if (options.gemMethod === 'createNFTOffer' || options.methodHint === 'createNFTOffer') {
66
+ return cleanTxJson({
67
+ TransactionType: 'NFTokenCreateOffer',
68
+ Account: address,
69
+ NFTokenID: payload.NFTokenID || payload.nftokenID || payload.nfTokenId,
70
+ Amount: payload.amount || payload.Amount,
71
+ Flags: payload.flags || payload.Flags,
72
+ Owner: payload.owner || payload.Owner,
73
+ Destination: payload.destination || payload.Destination
74
+ });
75
+ }
76
+
77
+ if (options.gemMethod === 'acceptNFTOffer' || options.methodHint === 'acceptNFTOffer') {
78
+ return cleanTxJson({
79
+ TransactionType: 'NFTokenAcceptOffer',
80
+ Account: address,
81
+ NFTokenSellOffer: payload.NFTokenSellOffer || payload.sellOffer,
82
+ NFTokenBuyOffer: payload.NFTokenBuyOffer || payload.buyOffer,
83
+ NFTokenBrokerFee: payload.NFTokenBrokerFee || payload.brokerFee
84
+ });
85
+ }
86
+
87
+ if (options.gemMethod === 'cancelNFTOffer' || options.methodHint === 'cancelNFTOffer') {
88
+ return cleanTxJson({
89
+ TransactionType: 'NFTokenCancelOffer',
90
+ Account: address,
91
+ NFTokenOffers: payload.NFTokenOffers || payload.offers
92
+ });
93
+ }
94
+
95
+ if (options.gemMethod === 'burnNFT' || options.methodHint === 'burnNFT') {
96
+ return cleanTxJson({
97
+ TransactionType: 'NFTokenBurn',
98
+ Account: address,
99
+ NFTokenID: payload.NFTokenID || payload.nftokenID || payload.nfTokenId,
100
+ Owner: payload.Owner || payload.owner
101
+ });
102
+ }
103
+
104
+ if (options.gemMethod === 'setTrustline' || options.gemMethod === 'addTrustline' || options.methodHint === 'setTrustline' || options.methodHint === 'trustSet' || options.methodHint === 'trustset') {
105
+ return cleanTxJson({
106
+ TransactionType: 'TrustSet',
107
+ Account: address,
108
+ LimitAmount: payload.LimitAmount || payload.limitAmount,
109
+ Flags: payload.Flags || payload.flags,
110
+ QualityIn: payload.QualityIn || payload.qualityIn,
111
+ QualityOut: payload.QualityOut || payload.qualityOut
112
+ });
113
+ }
114
+
115
+ return null;
116
+ }
117
+
118
+ function cleanTxJson(txJson) {
119
+ if (!txJson || typeof txJson !== 'object') return txJson;
120
+ var cleaned = {};
121
+ Object.keys(txJson).forEach(function (key) {
122
+ if (txJson[key] !== undefined && txJson[key] !== null && txJson[key] !== '') cleaned[key] = txJson[key];
123
+ });
124
+ return cleaned;
125
+ }
126
+
127
+ function normalizeWalletKitTxResult(result) {
128
+ if (!result) return { hash: undefined, status: undefined, raw: result };
129
+ return {
130
+ hash: result.hash || getPathValue(result, 'raw.hash') || getPathValue(result, 'raw.result.hash'),
131
+ status: result.status || getPathValue(result, 'raw.engine_result') || getPathValue(result, 'raw.result.meta.TransactionResult'),
132
+ signed: result.signed,
133
+ rejected: result.rejected,
134
+ raw: result.raw || result
135
+ };
136
+ }
137
+
138
+ function defaultNotify(type, message) {
139
+ if (typeof window.notify === 'function') window.notify(type, message);
140
+ }
141
+
142
+ function resolveElement(target) {
143
+ if (!target) return null;
144
+ if (typeof target === 'string') return document.querySelector(target);
145
+ return target;
146
+ }
147
+
148
+ function ensureMount(target, options) {
149
+ var mount = resolveElement(target);
150
+ if (mount) return mount;
151
+
152
+ var anchor = resolveElement(options && options.anchor);
153
+ mount = document.createElement('div');
154
+ mount.id = options && options.id ? options.id : 'xrpl-wallet-kit-button';
155
+ mount.className = options && options.className ? options.className : 'xwk-legacy-mount';
156
+ if (anchor && anchor.parentNode) {
157
+ anchor.parentNode.insertBefore(mount, anchor.nextSibling);
158
+ } else {
159
+ document.body.appendChild(mount);
160
+ }
161
+ return mount;
162
+ }
163
+
164
+ function injectFallbackStyles() {
165
+ if (document.getElementById('xwk-legacy-bridge-styles')) return;
166
+ var style = document.createElement('style');
167
+ style.id = 'xwk-legacy-bridge-styles';
168
+ style.textContent = '.xwk-legacy-mount{align-items:center;display:flex;justify-content:center;min-height:48px}.xwk-legacy-fallback{align-items:center;background:#f8fafc;border:1px solid rgba(15,23,42,.12);border-radius:14px;box-shadow:none;color:#172033;cursor:pointer;display:inline-flex;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;font-size:14px;font-weight:560;gap:8px;min-height:46px;overflow:hidden;padding:0 8px 0 10px;touch-action:manipulation;transition:background-color .16s ease,opacity .16s ease;white-space:nowrap}.xwk-legacy-fallback:hover{background:#eef3f8;box-shadow:none;transform:none}.xwk-legacy-fallback:active{opacity:.78;transform:none}.xwk-legacy-icon,.xwk-legacy-chevron{align-items:center;background:#fff;border:1px solid rgba(15,23,42,.12);box-sizing:border-box;color:#64748b;display:inline-flex;justify-content:center;overflow:hidden}.xwk-legacy-icon{border-radius:10px;flex:0 0 30px;height:30px;line-height:0;width:30px}.xwk-legacy-icon svg{display:block;flex:0 0 17px;height:17px;width:17px}.xwk-legacy-label{min-width:0;overflow:hidden;text-overflow:ellipsis}.xwk-legacy-chevron{border-radius:999px;flex:0 0 28px;height:28px;line-height:1;width:28px}';
169
+ document.head.appendChild(style);
170
+ }
171
+
172
+ function renderFallbackButton(label) {
173
+ return '<span class="xwk-legacy-icon" aria-hidden="true"><svg width="17" height="17" viewBox="0 0 24 24" fill="none"><path d="M4.5 7.5A2.5 2.5 0 0 1 7 5h11.5A1.5 1.5 0 0 1 20 6.5V9H7A2.5 2.5 0 0 1 4.5 6.5v1Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M4 9h15.5A2.5 2.5 0 0 1 22 11.5v5A2.5 2.5 0 0 1 19.5 19h-13A2.5 2.5 0 0 1 4 16.5V9Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M17 13.5h5V17h-5a1.75 1.75 0 1 1 0-3.5Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg></span><span class="xwk-legacy-label">' + label + '</span><span class="xwk-legacy-chevron" aria-hidden="true">v</span>';
174
+ }
175
+
176
+ function ensureFallbackButton(mount, kit, label) {
177
+ if (!mount) return null;
178
+ injectFallbackStyles();
179
+ var button = mount.querySelector('.xwk-legacy-fallback');
180
+ if (!button) {
181
+ button = document.createElement('button');
182
+ button.type = 'button';
183
+ button.className = 'xwk-legacy-fallback';
184
+ button.innerHTML = renderFallbackButton(label || 'Connect Wallet');
185
+ mount.appendChild(button);
186
+ }
187
+ button.onclick = function () {
188
+ if (kit && typeof kit.openModal === 'function') kit.openModal();
189
+ };
190
+ return button;
191
+ }
192
+
193
+ function createIdentityResolver(options) {
194
+ if (options.identityResolver) return options.identityResolver;
195
+ if (!options.identityEndpoint && !options.identityProfileEndpoint) return undefined;
196
+ if (!window.XRPLWalletKit || !window.XRPLWalletKit.createXrpDomainsResolver) return undefined;
197
+ return window.XRPLWalletKit.createXrpDomainsResolver({
198
+ endpoint: options.identityEndpoint,
199
+ profileEndpoint: options.identityProfileEndpoint
200
+ });
201
+ }
202
+
203
+ function createDefaultUi(options) {
204
+ return {
205
+ mode: options.themeMode || 'light',
206
+ customTheme: Object.assign({
207
+ accent: '#0078ae',
208
+ radius: '18px',
209
+ walletRadius: '18px',
210
+ shadow: 'none'
211
+ }, options.theme || {}),
212
+ modal: Object.assign({
213
+ title: 'Connect Wallet',
214
+ width: 'default',
215
+ footerText: 'XRPL Wallet Kit'
216
+ }, options.modal || {}),
217
+ walletList: Object.assign({
218
+ layout: 'list',
219
+ wallets: 'all',
220
+ showGroup: true,
221
+ showInstalledBadge: true
222
+ }, options.walletList || {}),
223
+ walletConnect: Object.assign({ mode: 'default', cta: 'both', qr: { style: 'dots', showLogo: false } }, options.walletConnect || {}),
224
+ connectButton: Object.assign({
225
+ label: options.connectLabel || 'Connect Wallet',
226
+ showAdapterIcon: true,
227
+ showChevron: true,
228
+ showBalance: options.showBalance !== false,
229
+ showWeb3Name: options.showWeb3Name !== false,
230
+ size: options.buttonSize || 'lg',
231
+ variant: 'default'
232
+ }, options.connectButton || {}),
233
+ accountPanel: Object.assign({
234
+ mode: options.accountPanelMode || 'modal',
235
+ copyAddress: true,
236
+ disconnect: true,
237
+ explorer: false
238
+ }, options.accountPanel || {}),
239
+ identity: Object.assign({
240
+ enabled: options.showWeb3Name !== false,
241
+ fallbackToAddress: true,
242
+ resolver: createIdentityResolver(options)
243
+ }, options.identity || {})
244
+ };
245
+ }
246
+
247
+
248
+ function mergeUiConfig(base, overrides) {
249
+ overrides = overrides || {};
250
+ return Object.assign({}, base, overrides, {
251
+ customTheme: Object.assign({}, base.customTheme || {}, overrides.customTheme || {}),
252
+ modal: Object.assign({}, base.modal || {}, overrides.modal || {}),
253
+ walletList: Object.assign({}, base.walletList || {}, overrides.walletList || {}),
254
+ walletConnect: Object.assign({}, base.walletConnect || {}, overrides.walletConnect || {}, {
255
+ qr: Object.assign({}, (base.walletConnect && base.walletConnect.qr) || {}, (overrides.walletConnect && overrides.walletConnect.qr) || {})
256
+ }),
257
+ connectButton: Object.assign({}, base.connectButton || {}, overrides.connectButton || {}),
258
+ accountPanel: Object.assign({}, base.accountPanel || {}, overrides.accountPanel || {}),
259
+ identity: Object.assign({}, base.identity || {}, overrides.identity || {})
260
+ });
261
+ }
262
+ function createKitOptions(options, mount) {
263
+ var ui = mergeUiConfig(createDefaultUi(options), options.ui);
264
+ return Object.assign({
265
+ appName: options.appName || document.title || 'XRPL dApp',
266
+ appDescription: options.appDescription || '',
267
+ appUrl: options.appUrl || window.location.origin,
268
+ appIcons: options.appIcons || [],
269
+ network: options.network || 'mainnet',
270
+ storage: options.storage || 'localStorage',
271
+ autoReconnect: options.autoReconnect !== false,
272
+ xamanClientId: options.xamanClientId,
273
+ walletConnectProjectId: options.walletConnectProjectId,
274
+ walletConnectSignMessageDestination: options.walletConnectSignMessageDestination,
275
+ wallets: options.wallets || ['xaman', 'gemwallet', 'crossmark', 'dropfi', 'xrplsnap', 'walletconnect'],
276
+ ui: ui,
277
+ identity: options.identityRoot,
278
+ connectButton: Object.assign({
279
+ target: mount,
280
+ label: options.connectLabel || 'Connect Wallet',
281
+ showAdapterIcon: true,
282
+ showChevron: true,
283
+ showBalance: options.showBalance !== false,
284
+ showWeb3Name: options.showWeb3Name !== false,
285
+ size: options.buttonSize || 'lg',
286
+ variant: 'default'
287
+ }, options.connectButton || {})
288
+ }, options.kit || {});
289
+ }
290
+
291
+ function mount(options) {
292
+ options = options || {};
293
+ if (!window.XRPLWalletKit || typeof window.XRPLWalletKit.create !== 'function') {
294
+ console.warn('XRPLWalletKit browser bundle is not loaded.');
295
+ return null;
296
+ }
297
+
298
+ var legacyButton = resolveElement(options.legacyButton || options.button);
299
+ var mountEl = ensureMount(options.target || options.mount || '#xrpl-wallet-kit-button', {
300
+ anchor: legacyButton,
301
+ className: options.mountClassName || 'xwk-legacy-mount',
302
+ id: options.mountId
303
+ });
304
+ ensureFallbackButton(mountEl, null, options.connectLabel || 'Connect Wallet');
305
+
306
+ var kit;
307
+ try {
308
+ kit = window.XRPLWalletKit.create(createKitOptions(options, mountEl));
309
+ } catch (error) {
310
+ console.error('XRPL Wallet Kit create failed:', error);
311
+ ensureFallbackButton(mountEl, null, options.connectLabel || 'Connect Wallet');
312
+ if (legacyButton && legacyButton.style) legacyButton.style.display = '';
313
+ return null;
314
+ }
315
+
316
+ if (!mountEl.children.length) ensureFallbackButton(mountEl, kit, options.connectLabel || 'Connect Wallet');
317
+ var fallback = mountEl.querySelector('.xwk-legacy-fallback');
318
+ if (fallback) fallback.onclick = function () { kit.openModal(); };
319
+ if (legacyButton && mountEl.children.length && options.hideLegacyButton !== false) legacyButton.style.display = 'none';
320
+
321
+ var hiddenAddress = resolveElement(options.hiddenAddress);
322
+ var notify = options.notify || defaultNotify;
323
+ var completingConnection = false;
324
+
325
+ function setLegacyLoading(isLoading) {
326
+ if (typeof options.setLegacyLoading === 'function') options.setLegacyLoading(isLoading);
327
+ }
328
+
329
+ async function syncSession(session) {
330
+ if (!session || completingConnection || (options.getIsConnected && options.getIsConnected())) return;
331
+ completingConnection = true;
332
+ setLegacyLoading(true);
333
+ try {
334
+ var legacy = {
335
+ adapter: mapAdapterIdToLegacy(session.adapterId),
336
+ adapterId: session.adapterId,
337
+ icon: session.wallet && session.wallet.icon ? session.wallet.icon : '',
338
+ address: session.account.address,
339
+ session: session
340
+ };
341
+ if (hiddenAddress) hiddenAddress.value = legacy.address;
342
+ if (typeof options.onSession === 'function') await options.onSession(session, legacy, kit);
343
+ if (typeof options.onConnected === 'function') await options.onConnected(session, legacy, kit);
344
+ } catch (error) {
345
+ notify('error', error && error.message ? error.message : String(error || 'Wallet connection failed'));
346
+ if (typeof options.onError === 'function') options.onError(error, kit);
347
+ } finally {
348
+ completingConnection = false;
349
+ setLegacyLoading(false);
350
+ }
351
+ }
352
+
353
+ kit.manager.on('connecting', function () { setLegacyLoading(true); });
354
+ kit.manager.on('connected', function (event) { syncSession(event.session || kit.manager.getSession()); });
355
+ kit.manager.on('disconnected', function () {
356
+ setLegacyLoading(false);
357
+ if (typeof options.onDisconnected === 'function') options.onDisconnected(kit);
358
+ if (options.reloadOnDisconnect === true) window.setTimeout(function () { window.location.reload(); }, 80);
359
+ });
360
+ kit.manager.on('error', function (event) {
361
+ setLegacyLoading(false);
362
+ if (typeof options.onError === 'function') options.onError(event.error, kit);
363
+ });
364
+
365
+ var restoredSession = kit.manager.getSession();
366
+ if (restoredSession) syncSession(restoredSession);
367
+ if (options.autoReconnect !== false) {
368
+ kit.manager.autoReconnect().then(function (session) {
369
+ if (session) syncSession(session);
370
+ }).catch(function (error) {
371
+ console.warn('XRPL Wallet Kit autoReconnect failed:', error);
372
+ });
373
+ }
374
+
375
+ return kit;
376
+ }
377
+
378
+ function patchLegacyFacade(options) {
379
+ options = options || {};
380
+ var facade = options.facade;
381
+ var kit = options.kit;
382
+ if (!facade || !kit || !kit.manager) {
383
+ console.warn('XRPLWalletKitLegacyBridge.patchLegacyFacade requires a facade and mounted kit.');
384
+ return facade;
385
+ }
386
+
387
+ facade.__legacy = {
388
+ connect: facade.connect,
389
+ disconnect: facade.disconnect,
390
+ signAuthPayload: facade.signAuthPayload,
391
+ signAndSubmit: facade.signAndSubmit
392
+ };
393
+
394
+ facade.connect = async function (adapterName) {
395
+ var kitAdapterId = mapLegacyAdapterIdToKit(adapterName || (options.getAdapter && options.getAdapter()) || '');
396
+ var session = kit.manager.getSession() || await kit.manager.connect(kitAdapterId);
397
+ return {
398
+ address: session.account.address,
399
+ networkType: session.account.network && session.account.network.networkType ? session.account.network.networkType : session.account.networkType,
400
+ adapter: mapAdapterIdToLegacy(session.adapterId),
401
+ session: session
402
+ };
403
+ };
404
+
405
+ facade.disconnect = async function () {
406
+ await kit.manager.disconnect();
407
+ if (typeof options.onDisconnect === 'function') await options.onDisconnect();
408
+ };
409
+
410
+ facade.signAuthPayload = async function (message) {
411
+ var result = await kit.manager.signMessage({ message: message });
412
+ return result.proof || result.signature || result.txBlob || result.raw;
413
+ };
414
+
415
+ facade.signAndSubmit = async function (txJson, requestOptions) {
416
+ requestOptions = requestOptions || {};
417
+ var account = kit.manager.getAccount();
418
+ var address = account && account.address ? account.address : (options.getCurrentAddress && options.getCurrentAddress());
419
+ var resolvedTxJson = cleanTxJson(txJson || createTxJsonFromWalletPayload(requestOptions, address));
420
+ if (!resolvedTxJson) throw new Error('Unable to build transaction payload for wallet-kit signAndSubmit.');
421
+ var result = await kit.manager.signAndSubmit({
422
+ txJson: resolvedTxJson,
423
+ walletPayload: requestOptions.walletPayload || requestOptions.gemPayload || resolvedTxJson,
424
+ methodHint: inferMethodHint(resolvedTxJson, requestOptions),
425
+ submit: requestOptions.submit !== false
426
+ });
427
+ return normalizeWalletKitTxResult(result);
428
+ };
429
+
430
+ facade.getWalletKit = function () { return kit; };
431
+ return facade;
432
+ }
433
+
434
+ window.XRPLWalletKitLegacyBridge = {
435
+ mount: mount,
436
+ patchLegacyFacade: patchLegacyFacade,
437
+ mapAdapterIdToLegacy: mapAdapterIdToLegacy,
438
+ mapLegacyAdapterIdToKit: mapLegacyAdapterIdToKit
439
+ };
440
+ })(window, document);