@mideind/netskrafl-react 1.7.1 → 1.8.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/dist/esm/index.js CHANGED
@@ -10,7 +10,7 @@ const DEFAULT_STATE = {
10
10
  measurementId: "",
11
11
  account: "",
12
12
  userEmail: "",
13
- userId: "",
13
+ netskraflUserId: "",
14
14
  userNick: "",
15
15
  userFullname: "",
16
16
  locale: "is_IS",
@@ -74,8 +74,8 @@ const saveAuthSettings = (settings) => {
74
74
  // Only add optional fields if they are defined
75
75
  if (settings.account !== undefined)
76
76
  filteredSettings.account = settings.account;
77
- if (settings.userId !== undefined)
78
- filteredSettings.userId = settings.userId;
77
+ if (settings.netskraflUserId !== undefined)
78
+ filteredSettings.netskraflUserId = settings.netskraflUserId;
79
79
  if (settings.userNick !== undefined)
80
80
  filteredSettings.userNick = settings.userNick;
81
81
  if (settings.firebaseApiKey !== undefined)
@@ -130,7 +130,6 @@ const clearAuthSettings = () => {
130
130
  };
131
131
  // Apply persisted settings to a GlobalState object
132
132
  const applyPersistedSettings = (state) => {
133
- var _a, _b, _c, _d, _e, _f;
134
133
  const persisted = loadAuthSettings();
135
134
  if (!persisted) {
136
135
  return state;
@@ -146,18 +145,18 @@ const applyPersistedSettings = (state) => {
146
145
  return {
147
146
  ...state,
148
147
  // Only apply persisted values if current values are defaults
149
- account: state.account || persisted.account || state.userId, // Use userId as fallback
150
- userId: state.userId || persisted.userId || state.userId,
148
+ account: persisted.account || state.account || state.netskraflUserId,
149
+ netskraflUserId: persisted.netskraflUserId || state.netskraflUserId,
151
150
  // userNick is special: the container provides a "guess", but the backend login
152
151
  // returns the authoritative nickname, which should persist across re-initializations
153
152
  userNick: persisted.userNick || state.userNick,
154
153
  firebaseApiKey: state.firebaseApiKey || persisted.firebaseApiKey || state.firebaseApiKey,
155
- beginner: (_a = persisted.beginner) !== null && _a !== void 0 ? _a : state.beginner,
156
- fairPlay: (_b = persisted.fairPlay) !== null && _b !== void 0 ? _b : state.fairPlay,
157
- ready: (_c = persisted.ready) !== null && _c !== void 0 ? _c : state.ready,
158
- readyTimed: (_d = persisted.readyTimed) !== null && _d !== void 0 ? _d : state.readyTimed,
159
- audio: (_e = persisted.audio) !== null && _e !== void 0 ? _e : state.audio,
160
- fanfare: (_f = persisted.fanfare) !== null && _f !== void 0 ? _f : state.fanfare,
154
+ beginner: persisted.beginner ?? state.beginner,
155
+ fairPlay: persisted.fairPlay ?? state.fairPlay,
156
+ ready: persisted.ready ?? state.ready,
157
+ readyTimed: persisted.readyTimed ?? state.readyTimed,
158
+ audio: persisted.audio ?? state.audio,
159
+ fanfare: persisted.fanfare ?? state.fanfare,
161
160
  };
162
161
  };
163
162
 
@@ -2437,7 +2436,7 @@ async function loadMessages(state, locale) {
2437
2436
  });
2438
2437
  setLocale(locale, messages);
2439
2438
  }
2440
- catch (_a) {
2439
+ catch {
2441
2440
  setLocale(locale, {});
2442
2441
  }
2443
2442
  }
@@ -2801,7 +2800,7 @@ function escapeHtml(string) {
2801
2800
  "'": ''',
2802
2801
  "/": '/'
2803
2802
  };
2804
- return String(string).replace(/[&<>"'/]/g, (s) => { var _a; return (_a = entityMap[s]) !== null && _a !== void 0 ? _a : ""; });
2803
+ return String(string).replace(/[&<>"'/]/g, (s) => entityMap[s] ?? "");
2805
2804
  }
2806
2805
  function getUrlVars(url) {
2807
2806
  // Get values from a URL query string
@@ -2938,6 +2937,8 @@ const loginUserByEmail = async (state) => {
2938
2937
  });
2939
2938
  };
2940
2939
 
2940
+ const getDefaultsFromPostinstall = () => (undefined);
2941
+
2941
2942
  /**
2942
2943
  * @license
2943
2944
  * Copyright 2017 Google LLC
@@ -3486,7 +3487,8 @@ const getDefaultsFromCookie = () => {
3486
3487
  */
3487
3488
  const getDefaults = () => {
3488
3489
  try {
3489
- return (getDefaultsFromGlobal() ||
3490
+ return (getDefaultsFromPostinstall() ||
3491
+ getDefaultsFromGlobal() ||
3490
3492
  getDefaultsFromEnvVariable() ||
3491
3493
  getDefaultsFromCookie());
3492
3494
  }
@@ -3507,7 +3509,7 @@ const getDefaults = () => {
3507
3509
  * @returns a URL host formatted like `127.0.0.1:9999` or `[::1]:4000` if available
3508
3510
  * @public
3509
3511
  */
3510
- const getDefaultEmulatorHost = (productName) => { var _a, _b; return (_b = (_a = getDefaults()) === null || _a === void 0 ? void 0 : _a.emulatorHosts) === null || _b === void 0 ? void 0 : _b[productName]; };
3512
+ const getDefaultEmulatorHost = (productName) => getDefaults()?.emulatorHosts?.[productName];
3511
3513
  /**
3512
3514
  * Returns emulator hostname and port stored in the __FIREBASE_DEFAULTS__ object
3513
3515
  * for the given product.
@@ -3537,13 +3539,13 @@ const getDefaultEmulatorHostnameAndPort = (productName) => {
3537
3539
  * Returns Firebase app config stored in the __FIREBASE_DEFAULTS__ object.
3538
3540
  * @public
3539
3541
  */
3540
- const getDefaultAppConfig = () => { var _a; return (_a = getDefaults()) === null || _a === void 0 ? void 0 : _a.config; };
3542
+ const getDefaultAppConfig = () => getDefaults()?.config;
3541
3543
  /**
3542
3544
  * Returns an experimental setting on the __FIREBASE_DEFAULTS__ object (properties
3543
3545
  * prefixed by "_")
3544
3546
  * @public
3545
3547
  */
3546
- const getExperimentalSetting = (name) => { var _a; return (_a = getDefaults()) === null || _a === void 0 ? void 0 : _a[`_${name}`]; };
3548
+ const getExperimentalSetting = (name) => getDefaults()?.[`_${name}`];
3547
3549
 
3548
3550
  /**
3549
3551
  * @license
@@ -3600,6 +3602,53 @@ class Deferred {
3600
3602
  }
3601
3603
  }
3602
3604
 
3605
+ /**
3606
+ * @license
3607
+ * Copyright 2025 Google LLC
3608
+ *
3609
+ * Licensed under the Apache License, Version 2.0 (the "License");
3610
+ * you may not use this file except in compliance with the License.
3611
+ * You may obtain a copy of the License at
3612
+ *
3613
+ * http://www.apache.org/licenses/LICENSE-2.0
3614
+ *
3615
+ * Unless required by applicable law or agreed to in writing, software
3616
+ * distributed under the License is distributed on an "AS IS" BASIS,
3617
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3618
+ * See the License for the specific language governing permissions and
3619
+ * limitations under the License.
3620
+ */
3621
+ /**
3622
+ * Checks whether host is a cloud workstation or not.
3623
+ * @public
3624
+ */
3625
+ function isCloudWorkstation(url) {
3626
+ // `isCloudWorkstation` is called without protocol in certain connect*Emulator functions
3627
+ // In HTTP request builders, it's called with the protocol.
3628
+ // If called with protocol prefix, it's a valid URL, so we extract the hostname
3629
+ // If called without, we assume the string is the hostname.
3630
+ try {
3631
+ const host = url.startsWith('http://') || url.startsWith('https://')
3632
+ ? new URL(url).hostname
3633
+ : url;
3634
+ return host.endsWith('.cloudworkstations.dev');
3635
+ }
3636
+ catch {
3637
+ return false;
3638
+ }
3639
+ }
3640
+ /**
3641
+ * Makes a fetch request to the given server.
3642
+ * Mostly used for forwarding cookies in Firebase Studio.
3643
+ * @public
3644
+ */
3645
+ async function pingServer(endpoint) {
3646
+ const result = await fetch(endpoint, {
3647
+ credentials: 'include'
3648
+ });
3649
+ return result.ok;
3650
+ }
3651
+
3603
3652
  /**
3604
3653
  * @license
3605
3654
  * Copyright 2021 Google LLC
@@ -3631,12 +3680,22 @@ function createMockUserToken(token, projectId) {
3631
3680
  if (!sub) {
3632
3681
  throw new Error("mockUserToken must contain 'sub' or 'user_id' field!");
3633
3682
  }
3634
- const payload = Object.assign({
3683
+ const payload = {
3635
3684
  // Set all required fields to decent defaults
3636
- iss: `https://securetoken.google.com/${project}`, aud: project, iat, exp: iat + 3600, auth_time: iat, sub, user_id: sub, firebase: {
3685
+ iss: `https://securetoken.google.com/${project}`,
3686
+ aud: project,
3687
+ iat,
3688
+ exp: iat + 3600,
3689
+ auth_time: iat,
3690
+ sub,
3691
+ user_id: sub,
3692
+ firebase: {
3637
3693
  sign_in_provider: 'custom',
3638
3694
  identities: {}
3639
- } }, token);
3695
+ },
3696
+ // Override with user options
3697
+ ...token
3698
+ };
3640
3699
  // Unsecured JWTs use the empty string as a signature.
3641
3700
  const signature = '';
3642
3701
  return [
@@ -3645,6 +3704,152 @@ function createMockUserToken(token, projectId) {
3645
3704
  signature
3646
3705
  ].join('.');
3647
3706
  }
3707
+ const emulatorStatus = {};
3708
+ // Checks whether any products are running on an emulator
3709
+ function getEmulatorSummary() {
3710
+ const summary = {
3711
+ prod: [],
3712
+ emulator: []
3713
+ };
3714
+ for (const key of Object.keys(emulatorStatus)) {
3715
+ if (emulatorStatus[key]) {
3716
+ summary.emulator.push(key);
3717
+ }
3718
+ else {
3719
+ summary.prod.push(key);
3720
+ }
3721
+ }
3722
+ return summary;
3723
+ }
3724
+ function getOrCreateEl(id) {
3725
+ let parentDiv = document.getElementById(id);
3726
+ let created = false;
3727
+ if (!parentDiv) {
3728
+ parentDiv = document.createElement('div');
3729
+ parentDiv.setAttribute('id', id);
3730
+ created = true;
3731
+ }
3732
+ return { created, element: parentDiv };
3733
+ }
3734
+ let previouslyDismissed = false;
3735
+ /**
3736
+ * Updates Emulator Banner. Primarily used for Firebase Studio
3737
+ * @param name
3738
+ * @param isRunningEmulator
3739
+ * @public
3740
+ */
3741
+ function updateEmulatorBanner(name, isRunningEmulator) {
3742
+ if (typeof window === 'undefined' ||
3743
+ typeof document === 'undefined' ||
3744
+ !isCloudWorkstation(window.location.host) ||
3745
+ emulatorStatus[name] === isRunningEmulator ||
3746
+ emulatorStatus[name] || // If already set to use emulator, can't go back to prod.
3747
+ previouslyDismissed) {
3748
+ return;
3749
+ }
3750
+ emulatorStatus[name] = isRunningEmulator;
3751
+ function prefixedId(id) {
3752
+ return `__firebase__banner__${id}`;
3753
+ }
3754
+ const bannerId = '__firebase__banner';
3755
+ const summary = getEmulatorSummary();
3756
+ const showError = summary.prod.length > 0;
3757
+ function tearDown() {
3758
+ const element = document.getElementById(bannerId);
3759
+ if (element) {
3760
+ element.remove();
3761
+ }
3762
+ }
3763
+ function setupBannerStyles(bannerEl) {
3764
+ bannerEl.style.display = 'flex';
3765
+ bannerEl.style.background = '#7faaf0';
3766
+ bannerEl.style.position = 'fixed';
3767
+ bannerEl.style.bottom = '5px';
3768
+ bannerEl.style.left = '5px';
3769
+ bannerEl.style.padding = '.5em';
3770
+ bannerEl.style.borderRadius = '5px';
3771
+ bannerEl.style.alignItems = 'center';
3772
+ }
3773
+ function setupIconStyles(prependIcon, iconId) {
3774
+ prependIcon.setAttribute('width', '24');
3775
+ prependIcon.setAttribute('id', iconId);
3776
+ prependIcon.setAttribute('height', '24');
3777
+ prependIcon.setAttribute('viewBox', '0 0 24 24');
3778
+ prependIcon.setAttribute('fill', 'none');
3779
+ prependIcon.style.marginLeft = '-6px';
3780
+ }
3781
+ function setupCloseBtn() {
3782
+ const closeBtn = document.createElement('span');
3783
+ closeBtn.style.cursor = 'pointer';
3784
+ closeBtn.style.marginLeft = '16px';
3785
+ closeBtn.style.fontSize = '24px';
3786
+ closeBtn.innerHTML = ' &times;';
3787
+ closeBtn.onclick = () => {
3788
+ previouslyDismissed = true;
3789
+ tearDown();
3790
+ };
3791
+ return closeBtn;
3792
+ }
3793
+ function setupLinkStyles(learnMoreLink, learnMoreId) {
3794
+ learnMoreLink.setAttribute('id', learnMoreId);
3795
+ learnMoreLink.innerText = 'Learn more';
3796
+ learnMoreLink.href =
3797
+ 'https://firebase.google.com/docs/studio/preview-apps#preview-backend';
3798
+ learnMoreLink.setAttribute('target', '__blank');
3799
+ learnMoreLink.style.paddingLeft = '5px';
3800
+ learnMoreLink.style.textDecoration = 'underline';
3801
+ }
3802
+ function setupDom() {
3803
+ const banner = getOrCreateEl(bannerId);
3804
+ const firebaseTextId = prefixedId('text');
3805
+ const firebaseText = document.getElementById(firebaseTextId) || document.createElement('span');
3806
+ const learnMoreId = prefixedId('learnmore');
3807
+ const learnMoreLink = document.getElementById(learnMoreId) ||
3808
+ document.createElement('a');
3809
+ const prependIconId = prefixedId('preprendIcon');
3810
+ const prependIcon = document.getElementById(prependIconId) ||
3811
+ document.createElementNS('http://www.w3.org/2000/svg', 'svg');
3812
+ if (banner.created) {
3813
+ // update styles
3814
+ const bannerEl = banner.element;
3815
+ setupBannerStyles(bannerEl);
3816
+ setupLinkStyles(learnMoreLink, learnMoreId);
3817
+ const closeBtn = setupCloseBtn();
3818
+ setupIconStyles(prependIcon, prependIconId);
3819
+ bannerEl.append(prependIcon, firebaseText, learnMoreLink, closeBtn);
3820
+ document.body.appendChild(bannerEl);
3821
+ }
3822
+ if (showError) {
3823
+ firebaseText.innerText = `Preview backend disconnected.`;
3824
+ prependIcon.innerHTML = `<g clip-path="url(#clip0_6013_33858)">
3825
+ <path d="M4.8 17.6L12 5.6L19.2 17.6H4.8ZM6.91667 16.4H17.0833L12 7.93333L6.91667 16.4ZM12 15.6C12.1667 15.6 12.3056 15.5444 12.4167 15.4333C12.5389 15.3111 12.6 15.1667 12.6 15C12.6 14.8333 12.5389 14.6944 12.4167 14.5833C12.3056 14.4611 12.1667 14.4 12 14.4C11.8333 14.4 11.6889 14.4611 11.5667 14.5833C11.4556 14.6944 11.4 14.8333 11.4 15C11.4 15.1667 11.4556 15.3111 11.5667 15.4333C11.6889 15.5444 11.8333 15.6 12 15.6ZM11.4 13.6H12.6V10.4H11.4V13.6Z" fill="#212121"/>
3826
+ </g>
3827
+ <defs>
3828
+ <clipPath id="clip0_6013_33858">
3829
+ <rect width="24" height="24" fill="white"/>
3830
+ </clipPath>
3831
+ </defs>`;
3832
+ }
3833
+ else {
3834
+ prependIcon.innerHTML = `<g clip-path="url(#clip0_6083_34804)">
3835
+ <path d="M11.4 15.2H12.6V11.2H11.4V15.2ZM12 10C12.1667 10 12.3056 9.94444 12.4167 9.83333C12.5389 9.71111 12.6 9.56667 12.6 9.4C12.6 9.23333 12.5389 9.09444 12.4167 8.98333C12.3056 8.86111 12.1667 8.8 12 8.8C11.8333 8.8 11.6889 8.86111 11.5667 8.98333C11.4556 9.09444 11.4 9.23333 11.4 9.4C11.4 9.56667 11.4556 9.71111 11.5667 9.83333C11.6889 9.94444 11.8333 10 12 10ZM12 18.4C11.1222 18.4 10.2944 18.2333 9.51667 17.9C8.73889 17.5667 8.05556 17.1111 7.46667 16.5333C6.88889 15.9444 6.43333 15.2611 6.1 14.4833C5.76667 13.7056 5.6 12.8778 5.6 12C5.6 11.1111 5.76667 10.2833 6.1 9.51667C6.43333 8.73889 6.88889 8.06111 7.46667 7.48333C8.05556 6.89444 8.73889 6.43333 9.51667 6.1C10.2944 5.76667 11.1222 5.6 12 5.6C12.8889 5.6 13.7167 5.76667 14.4833 6.1C15.2611 6.43333 15.9389 6.89444 16.5167 7.48333C17.1056 8.06111 17.5667 8.73889 17.9 9.51667C18.2333 10.2833 18.4 11.1111 18.4 12C18.4 12.8778 18.2333 13.7056 17.9 14.4833C17.5667 15.2611 17.1056 15.9444 16.5167 16.5333C15.9389 17.1111 15.2611 17.5667 14.4833 17.9C13.7167 18.2333 12.8889 18.4 12 18.4ZM12 17.2C13.4444 17.2 14.6722 16.6944 15.6833 15.6833C16.6944 14.6722 17.2 13.4444 17.2 12C17.2 10.5556 16.6944 9.32778 15.6833 8.31667C14.6722 7.30555 13.4444 6.8 12 6.8C10.5556 6.8 9.32778 7.30555 8.31667 8.31667C7.30556 9.32778 6.8 10.5556 6.8 12C6.8 13.4444 7.30556 14.6722 8.31667 15.6833C9.32778 16.6944 10.5556 17.2 12 17.2Z" fill="#212121"/>
3836
+ </g>
3837
+ <defs>
3838
+ <clipPath id="clip0_6083_34804">
3839
+ <rect width="24" height="24" fill="white"/>
3840
+ </clipPath>
3841
+ </defs>`;
3842
+ firebaseText.innerText = 'Preview backend running in this workspace.';
3843
+ }
3844
+ firebaseText.setAttribute('id', firebaseTextId);
3845
+ }
3846
+ if (document.readyState === 'loading') {
3847
+ window.addEventListener('DOMContentLoaded', setupDom);
3848
+ }
3849
+ else {
3850
+ setupDom();
3851
+ }
3852
+ }
3648
3853
 
3649
3854
  /**
3650
3855
  * @license
@@ -3762,8 +3967,7 @@ function validateIndexedDBOpenable() {
3762
3967
  preExist = false;
3763
3968
  };
3764
3969
  request.onerror = () => {
3765
- var _a;
3766
- reject(((_a = request.error) === null || _a === void 0 ? void 0 : _a.message) || '');
3970
+ reject(request.error?.message || '');
3767
3971
  };
3768
3972
  }
3769
3973
  catch (error) {
@@ -4865,10 +5069,9 @@ class Provider {
4865
5069
  return this.instancesDeferred.get(normalizedIdentifier).promise;
4866
5070
  }
4867
5071
  getImmediate(options) {
4868
- var _a;
4869
5072
  // if multipleInstances is not supported, use the default name
4870
- const normalizedIdentifier = this.normalizeInstanceIdentifier(options === null || options === void 0 ? void 0 : options.identifier);
4871
- const optional = (_a = options === null || options === void 0 ? void 0 : options.optional) !== null && _a !== void 0 ? _a : false;
5073
+ const normalizedIdentifier = this.normalizeInstanceIdentifier(options?.identifier);
5074
+ const optional = options?.optional ?? false;
4872
5075
  if (this.isInitialized(normalizedIdentifier) ||
4873
5076
  this.shouldAutoInitialize()) {
4874
5077
  try {
@@ -5000,9 +5203,9 @@ class Provider {
5000
5203
  * @returns a function to unregister the callback
5001
5204
  */
5002
5205
  onInit(callback, identifier) {
5003
- var _a;
5004
5206
  const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier);
5005
- const existingCallbacks = (_a = this.onInitCallbacks.get(normalizedIdentifier)) !== null && _a !== void 0 ? _a : new Set();
5207
+ const existingCallbacks = this.onInitCallbacks.get(normalizedIdentifier) ??
5208
+ new Set();
5006
5209
  existingCallbacks.add(callback);
5007
5210
  this.onInitCallbacks.set(normalizedIdentifier, existingCallbacks);
5008
5211
  const existingInstance = this.instances.get(normalizedIdentifier);
@@ -5026,7 +5229,7 @@ class Provider {
5026
5229
  try {
5027
5230
  callback(instance, identifier);
5028
5231
  }
5029
- catch (_a) {
5232
+ catch {
5030
5233
  // ignore errors in the onInit callback
5031
5234
  }
5032
5235
  }
@@ -5055,7 +5258,7 @@ class Provider {
5055
5258
  try {
5056
5259
  this.component.onInstanceCreated(this.container, instanceIdentifier, instance);
5057
5260
  }
5058
- catch (_a) {
5261
+ catch {
5059
5262
  // ignore errors in the onInstanceCreatedCallback
5060
5263
  }
5061
5264
  }
@@ -5622,11 +5825,11 @@ class PlatformLoggerServiceImpl {
5622
5825
  */
5623
5826
  function isVersionServiceProvider(provider) {
5624
5827
  const component = provider.getComponent();
5625
- return (component === null || component === void 0 ? void 0 : component.type) === "VERSION" /* ComponentType.VERSION */;
5828
+ return component?.type === "VERSION" /* ComponentType.VERSION */;
5626
5829
  }
5627
5830
 
5628
5831
  const name$q = "@firebase/app";
5629
- const version$1$1 = "0.10.16";
5832
+ const version$1$1 = "0.14.5";
5630
5833
 
5631
5834
  /**
5632
5835
  * @license
@@ -5692,12 +5895,12 @@ const name$4$1 = "@firebase/storage-compat";
5692
5895
 
5693
5896
  const name$3$1 = "@firebase/firestore";
5694
5897
 
5695
- const name$2$1 = "@firebase/vertexai";
5898
+ const name$2$1 = "@firebase/ai";
5696
5899
 
5697
5900
  const name$1$1 = "@firebase/firestore-compat";
5698
5901
 
5699
5902
  const name$r = "firebase";
5700
- const version$5 = "11.0.2";
5903
+ const version$5 = "12.5.0";
5701
5904
 
5702
5905
  /**
5703
5906
  * @license
@@ -5846,6 +6049,9 @@ function _getProvider(app, name) {
5846
6049
  * @internal
5847
6050
  */
5848
6051
  function _isFirebaseServerApp(obj) {
6052
+ if (obj === null || obj === undefined) {
6053
+ return false;
6054
+ }
5849
6055
  return obj.settings !== undefined;
5850
6056
  }
5851
6057
 
@@ -5904,8 +6110,8 @@ const ERROR_FACTORY$2 = new ErrorFactory('app', 'Firebase', ERRORS$1);
5904
6110
  class FirebaseAppImpl {
5905
6111
  constructor(options, config, container) {
5906
6112
  this._isDeleted = false;
5907
- this._options = Object.assign({}, options);
5908
- this._config = Object.assign({}, config);
6113
+ this._options = { ...options };
6114
+ this._config = { ...config };
5909
6115
  this._name = config.name;
5910
6116
  this._automaticDataCollectionEnabled =
5911
6117
  config.automaticDataCollectionEnabled;
@@ -5980,7 +6186,11 @@ function initializeApp(_options, rawConfig = {}) {
5980
6186
  const name = rawConfig;
5981
6187
  rawConfig = { name };
5982
6188
  }
5983
- const config = Object.assign({ name: DEFAULT_ENTRY_NAME, automaticDataCollectionEnabled: false }, rawConfig);
6189
+ const config = {
6190
+ name: DEFAULT_ENTRY_NAME,
6191
+ automaticDataCollectionEnabled: true,
6192
+ ...rawConfig
6193
+ };
5984
6194
  const name = config.name;
5985
6195
  if (typeof name !== 'string' || !name) {
5986
6196
  throw ERROR_FACTORY$2.create("bad-app-name" /* AppError.BAD_APP_NAME */, {
@@ -6058,10 +6268,9 @@ function getApp(name = DEFAULT_ENTRY_NAME) {
6058
6268
  * @public
6059
6269
  */
6060
6270
  function registerVersion(libraryKeyOrName, version, variant) {
6061
- var _a;
6062
6271
  // TODO: We can use this check to whitelist strings when/if we set up
6063
6272
  // a good whitelist system.
6064
- let library = (_a = PLATFORM_LOG_STRING[libraryKeyOrName]) !== null && _a !== void 0 ? _a : libraryKeyOrName;
6273
+ let library = PLATFORM_LOG_STRING[libraryKeyOrName] ?? libraryKeyOrName;
6065
6274
  if (variant) {
6066
6275
  library += `-${variant}`;
6067
6276
  }
@@ -6152,7 +6361,7 @@ async function readHeartbeatsFromIndexedDB(app) {
6152
6361
  }
6153
6362
  else {
6154
6363
  const idbGetError = ERROR_FACTORY$2.create("idb-get" /* AppError.IDB_GET */, {
6155
- originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
6364
+ originalErrorMessage: e?.message
6156
6365
  });
6157
6366
  logger$2.warn(idbGetError.message);
6158
6367
  }
@@ -6172,7 +6381,7 @@ async function writeHeartbeatsToIndexedDB(app, heartbeatObject) {
6172
6381
  }
6173
6382
  else {
6174
6383
  const idbGetError = ERROR_FACTORY$2.create("idb-set" /* AppError.IDB_WRITE */, {
6175
- originalErrorMessage: e === null || e === void 0 ? void 0 : e.message
6384
+ originalErrorMessage: e?.message
6176
6385
  });
6177
6386
  logger$2.warn(idbGetError.message);
6178
6387
  }
@@ -6199,8 +6408,7 @@ function computeKey(app) {
6199
6408
  * limitations under the License.
6200
6409
  */
6201
6410
  const MAX_HEADER_BYTES = 1024;
6202
- // 30 days
6203
- const STORED_HEARTBEAT_RETENTION_MAX_MILLIS = 30 * 24 * 60 * 60 * 1000;
6411
+ const MAX_NUM_STORED_HEARTBEATS = 30;
6204
6412
  class HeartbeatServiceImpl {
6205
6413
  constructor(container) {
6206
6414
  this.container = container;
@@ -6229,7 +6437,6 @@ class HeartbeatServiceImpl {
6229
6437
  * already logged, subsequent calls to this function in the same day will be ignored.
6230
6438
  */
6231
6439
  async triggerHeartbeat() {
6232
- var _a, _b;
6233
6440
  try {
6234
6441
  const platformLogger = this.container
6235
6442
  .getProvider('platform-logger')
@@ -6238,10 +6445,10 @@ class HeartbeatServiceImpl {
6238
6445
  // service, not the browser user agent.
6239
6446
  const agent = platformLogger.getPlatformInfoString();
6240
6447
  const date = getUTCDateString();
6241
- if (((_a = this._heartbeatsCache) === null || _a === void 0 ? void 0 : _a.heartbeats) == null) {
6448
+ if (this._heartbeatsCache?.heartbeats == null) {
6242
6449
  this._heartbeatsCache = await this._heartbeatsCachePromise;
6243
6450
  // If we failed to construct a heartbeats cache, then return immediately.
6244
- if (((_b = this._heartbeatsCache) === null || _b === void 0 ? void 0 : _b.heartbeats) == null) {
6451
+ if (this._heartbeatsCache?.heartbeats == null) {
6245
6452
  return;
6246
6453
  }
6247
6454
  }
@@ -6254,14 +6461,13 @@ class HeartbeatServiceImpl {
6254
6461
  else {
6255
6462
  // There is no entry for this date. Create one.
6256
6463
  this._heartbeatsCache.heartbeats.push({ date, agent });
6464
+ // If the number of stored heartbeats exceeds the maximum number of stored heartbeats, remove the heartbeat with the earliest date.
6465
+ // Since this is executed each time a heartbeat is pushed, the limit can only be exceeded by one, so only one needs to be removed.
6466
+ if (this._heartbeatsCache.heartbeats.length > MAX_NUM_STORED_HEARTBEATS) {
6467
+ const earliestHeartbeatIdx = getEarliestHeartbeatIdx(this._heartbeatsCache.heartbeats);
6468
+ this._heartbeatsCache.heartbeats.splice(earliestHeartbeatIdx, 1);
6469
+ }
6257
6470
  }
6258
- // Remove entries older than 30 days.
6259
- this._heartbeatsCache.heartbeats =
6260
- this._heartbeatsCache.heartbeats.filter(singleDateHeartbeat => {
6261
- const hbTimestamp = new Date(singleDateHeartbeat.date).valueOf();
6262
- const now = Date.now();
6263
- return now - hbTimestamp <= STORED_HEARTBEAT_RETENTION_MAX_MILLIS;
6264
- });
6265
6471
  return this._storage.overwrite(this._heartbeatsCache);
6266
6472
  }
6267
6473
  catch (e) {
@@ -6276,13 +6482,12 @@ class HeartbeatServiceImpl {
6276
6482
  * returns an empty string.
6277
6483
  */
6278
6484
  async getHeartbeatsHeader() {
6279
- var _a;
6280
6485
  try {
6281
6486
  if (this._heartbeatsCache === null) {
6282
6487
  await this._heartbeatsCachePromise;
6283
6488
  }
6284
6489
  // If it's still null or the array is empty, there is no data to send.
6285
- if (((_a = this._heartbeatsCache) === null || _a === void 0 ? void 0 : _a.heartbeats) == null ||
6490
+ if (this._heartbeatsCache?.heartbeats == null ||
6286
6491
  this._heartbeatsCache.heartbeats.length === 0) {
6287
6492
  return '';
6288
6493
  }
@@ -6383,7 +6588,7 @@ class HeartbeatStorageImpl {
6383
6588
  }
6384
6589
  else {
6385
6590
  const idbHeartbeatObject = await readHeartbeatsFromIndexedDB(this.app);
6386
- if (idbHeartbeatObject === null || idbHeartbeatObject === void 0 ? void 0 : idbHeartbeatObject.heartbeats) {
6591
+ if (idbHeartbeatObject?.heartbeats) {
6387
6592
  return idbHeartbeatObject;
6388
6593
  }
6389
6594
  else {
@@ -6393,7 +6598,6 @@ class HeartbeatStorageImpl {
6393
6598
  }
6394
6599
  // overwrite the storage with the provided heartbeats
6395
6600
  async overwrite(heartbeatsObject) {
6396
- var _a;
6397
6601
  const canUseIndexedDB = await this._canUseIndexedDBPromise;
6398
6602
  if (!canUseIndexedDB) {
6399
6603
  return;
@@ -6401,14 +6605,14 @@ class HeartbeatStorageImpl {
6401
6605
  else {
6402
6606
  const existingHeartbeatsObject = await this.read();
6403
6607
  return writeHeartbeatsToIndexedDB(this.app, {
6404
- lastSentHeartbeatDate: (_a = heartbeatsObject.lastSentHeartbeatDate) !== null && _a !== void 0 ? _a : existingHeartbeatsObject.lastSentHeartbeatDate,
6608
+ lastSentHeartbeatDate: heartbeatsObject.lastSentHeartbeatDate ??
6609
+ existingHeartbeatsObject.lastSentHeartbeatDate,
6405
6610
  heartbeats: heartbeatsObject.heartbeats
6406
6611
  });
6407
6612
  }
6408
6613
  }
6409
6614
  // add heartbeats
6410
6615
  async add(heartbeatsObject) {
6411
- var _a;
6412
6616
  const canUseIndexedDB = await this._canUseIndexedDBPromise;
6413
6617
  if (!canUseIndexedDB) {
6414
6618
  return;
@@ -6416,7 +6620,8 @@ class HeartbeatStorageImpl {
6416
6620
  else {
6417
6621
  const existingHeartbeatsObject = await this.read();
6418
6622
  return writeHeartbeatsToIndexedDB(this.app, {
6419
- lastSentHeartbeatDate: (_a = heartbeatsObject.lastSentHeartbeatDate) !== null && _a !== void 0 ? _a : existingHeartbeatsObject.lastSentHeartbeatDate,
6623
+ lastSentHeartbeatDate: heartbeatsObject.lastSentHeartbeatDate ??
6624
+ existingHeartbeatsObject.lastSentHeartbeatDate,
6420
6625
  heartbeats: [
6421
6626
  ...existingHeartbeatsObject.heartbeats,
6422
6627
  ...heartbeatsObject.heartbeats
@@ -6436,6 +6641,24 @@ function countBytes(heartbeatsCache) {
6436
6641
  // heartbeatsCache wrapper properties
6437
6642
  JSON.stringify({ version: 2, heartbeats: heartbeatsCache })).length;
6438
6643
  }
6644
+ /**
6645
+ * Returns the index of the heartbeat with the earliest date.
6646
+ * If the heartbeats array is empty, -1 is returned.
6647
+ */
6648
+ function getEarliestHeartbeatIdx(heartbeats) {
6649
+ if (heartbeats.length === 0) {
6650
+ return -1;
6651
+ }
6652
+ let earliestHeartbeatIdx = 0;
6653
+ let earliestHeartbeatDate = heartbeats[0].date;
6654
+ for (let i = 1; i < heartbeats.length; i++) {
6655
+ if (heartbeats[i].date < earliestHeartbeatDate) {
6656
+ earliestHeartbeatDate = heartbeats[i].date;
6657
+ earliestHeartbeatIdx = i;
6658
+ }
6659
+ }
6660
+ return earliestHeartbeatIdx;
6661
+ }
6439
6662
 
6440
6663
  /**
6441
6664
  * @license
@@ -6458,8 +6681,8 @@ function registerCoreComponents(variant) {
6458
6681
  _registerComponent(new Component('heartbeat', container => new HeartbeatServiceImpl(container), "PRIVATE" /* ComponentType.PRIVATE */));
6459
6682
  // Register `app` package.
6460
6683
  registerVersion(name$q, version$1$1, variant);
6461
- // BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
6462
- registerVersion(name$q, version$1$1, 'esm2017');
6684
+ // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
6685
+ registerVersion(name$q, version$1$1, 'esm2020');
6463
6686
  // Register platform SDK identifier (no version).
6464
6687
  registerVersion('fire-js', '');
6465
6688
  }
@@ -6473,7 +6696,7 @@ function registerCoreComponents(variant) {
6473
6696
  registerCoreComponents('');
6474
6697
 
6475
6698
  var name$4 = "firebase";
6476
- var version$4 = "11.0.2";
6699
+ var version$4 = "12.5.0";
6477
6700
 
6478
6701
  /**
6479
6702
  * @license
@@ -6494,7 +6717,7 @@ var version$4 = "11.0.2";
6494
6717
  registerVersion(name$4, version$4, 'app');
6495
6718
 
6496
6719
  const name$3 = "@firebase/installations";
6497
- const version$3 = "0.6.11";
6720
+ const version$3 = "0.6.19";
6498
6721
 
6499
6722
  /**
6500
6723
  * @license
@@ -6755,7 +6978,7 @@ function generateFid() {
6755
6978
  const fid = encode(fidByteArray);
6756
6979
  return VALID_FID_PATTERN.test(fid) ? fid : INVALID_FID;
6757
6980
  }
6758
- catch (_a) {
6981
+ catch {
6759
6982
  // FID generation errored
6760
6983
  return INVALID_FID;
6761
6984
  }
@@ -7242,7 +7465,10 @@ function updateAuthTokenRequest(appConfig) {
7242
7465
  }
7243
7466
  const oldAuthToken = oldEntry.authToken;
7244
7467
  if (hasAuthTokenRequestTimedOut(oldAuthToken)) {
7245
- return Object.assign(Object.assign({}, oldEntry), { authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ } });
7468
+ return {
7469
+ ...oldEntry,
7470
+ authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ }
7471
+ };
7246
7472
  }
7247
7473
  return oldEntry;
7248
7474
  });
@@ -7250,7 +7476,10 @@ function updateAuthTokenRequest(appConfig) {
7250
7476
  async function fetchAuthTokenFromServer(installations, installationEntry) {
7251
7477
  try {
7252
7478
  const authToken = await generateAuthTokenRequest(installations, installationEntry);
7253
- const updatedInstallationEntry = Object.assign(Object.assign({}, installationEntry), { authToken });
7479
+ const updatedInstallationEntry = {
7480
+ ...installationEntry,
7481
+ authToken
7482
+ };
7254
7483
  await set$1(installations.appConfig, updatedInstallationEntry);
7255
7484
  return authToken;
7256
7485
  }
@@ -7262,7 +7491,10 @@ async function fetchAuthTokenFromServer(installations, installationEntry) {
7262
7491
  await remove$1(installations.appConfig);
7263
7492
  }
7264
7493
  else {
7265
- const updatedInstallationEntry = Object.assign(Object.assign({}, installationEntry), { authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ } });
7494
+ const updatedInstallationEntry = {
7495
+ ...installationEntry,
7496
+ authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ }
7497
+ };
7266
7498
  await set$1(installations.appConfig, updatedInstallationEntry);
7267
7499
  }
7268
7500
  throw e;
@@ -7287,7 +7519,10 @@ function makeAuthTokenRequestInProgressEntry(oldEntry) {
7287
7519
  requestStatus: 1 /* RequestStatus.IN_PROGRESS */,
7288
7520
  requestTime: Date.now()
7289
7521
  };
7290
- return Object.assign(Object.assign({}, oldEntry), { authToken: inProgressAuthToken });
7522
+ return {
7523
+ ...oldEntry,
7524
+ authToken: inProgressAuthToken
7525
+ };
7291
7526
  }
7292
7527
  function hasAuthTokenRequestTimedOut(authToken) {
7293
7528
  return (authToken.requestStatus === 1 /* RequestStatus.IN_PROGRESS */ &&
@@ -7472,8 +7707,8 @@ function registerInstallations() {
7472
7707
  */
7473
7708
  registerInstallations();
7474
7709
  registerVersion(name$3, version$3);
7475
- // BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
7476
- registerVersion(name$3, version$3, 'esm2017');
7710
+ // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
7711
+ registerVersion(name$3, version$3, 'esm2020');
7477
7712
 
7478
7713
  /**
7479
7714
  * @license
@@ -7633,7 +7868,7 @@ function insertScriptTag(dataLayerName, measurementId) {
7633
7868
  // without fid. We will initialize ga-id using gtag (config) command together with fid.
7634
7869
  const gtagScriptURL = `${GTAG_URL}?l=${dataLayerName}&id=${measurementId}`;
7635
7870
  script.src = trustedTypesPolicy
7636
- ? trustedTypesPolicy === null || trustedTypesPolicy === void 0 ? void 0 : trustedTypesPolicy.createScriptURL(gtagScriptURL)
7871
+ ? trustedTypesPolicy?.createScriptURL(gtagScriptURL)
7637
7872
  : gtagScriptURL;
7638
7873
  script.async = true;
7639
7874
  document.head.appendChild(script);
@@ -7919,7 +8154,6 @@ function getHeaders(apiKey) {
7919
8154
  * @param app Firebase app to fetch config for.
7920
8155
  */
7921
8156
  async function fetchDynamicConfig(appFields) {
7922
- var _a;
7923
8157
  const { appId, apiKey } = appFields;
7924
8158
  const request = {
7925
8159
  method: 'GET',
@@ -7932,7 +8166,7 @@ async function fetchDynamicConfig(appFields) {
7932
8166
  try {
7933
8167
  // Try to get any error message text from server response.
7934
8168
  const jsonResponse = (await response.json());
7935
- if ((_a = jsonResponse.error) === null || _a === void 0 ? void 0 : _a.message) {
8169
+ if (jsonResponse.error?.message) {
7936
8170
  errorMessage = jsonResponse.error.message;
7937
8171
  }
7938
8172
  }
@@ -7983,7 +8217,6 @@ retryData = defaultRetryData, timeoutMillis) {
7983
8217
  */
7984
8218
  async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMillis, backoffCount }, signal, retryData = defaultRetryData // for testing
7985
8219
  ) {
7986
- var _a;
7987
8220
  const { appId, measurementId } = appFields;
7988
8221
  // Starts with a (potentially zero) timeout to support resumption from stored state.
7989
8222
  // Ensures the throttle end time is honored if the last attempt timed out.
@@ -7995,7 +8228,7 @@ async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMi
7995
8228
  if (measurementId) {
7996
8229
  logger$1.warn(`Timed out fetching this Firebase app's measurement ID from the server.` +
7997
8230
  ` Falling back to the measurement ID ${measurementId}` +
7998
- ` provided in the "measurementId" field in the local Firebase config. [${e === null || e === void 0 ? void 0 : e.message}]`);
8231
+ ` provided in the "measurementId" field in the local Firebase config. [${e?.message}]`);
7999
8232
  return { appId, measurementId };
8000
8233
  }
8001
8234
  throw e;
@@ -8013,14 +8246,14 @@ async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMi
8013
8246
  if (measurementId) {
8014
8247
  logger$1.warn(`Failed to fetch this Firebase app's measurement ID from the server.` +
8015
8248
  ` Falling back to the measurement ID ${measurementId}` +
8016
- ` provided in the "measurementId" field in the local Firebase config. [${error === null || error === void 0 ? void 0 : error.message}]`);
8249
+ ` provided in the "measurementId" field in the local Firebase config. [${error?.message}]`);
8017
8250
  return { appId, measurementId };
8018
8251
  }
8019
8252
  else {
8020
8253
  throw e;
8021
8254
  }
8022
8255
  }
8023
- const backoffMillis = Number((_a = error === null || error === void 0 ? void 0 : error.customData) === null || _a === void 0 ? void 0 : _a.httpStatus) === 503
8256
+ const backoffMillis = Number(error?.customData?.httpStatus) === 503
8024
8257
  ? calculateBackoffMillis(backoffCount, retryData.intervalMillis, LONG_RETRY_FACTOR)
8025
8258
  : calculateBackoffMillis(backoffCount, retryData.intervalMillis);
8026
8259
  // Increments backoff state.
@@ -8108,10 +8341,37 @@ async function logEvent$1(gtagFunction, initializationPromise, eventName, eventP
8108
8341
  }
8109
8342
  else {
8110
8343
  const measurementId = await initializationPromise;
8111
- const params = Object.assign(Object.assign({}, eventParams), { 'send_to': measurementId });
8344
+ const params = {
8345
+ ...eventParams,
8346
+ 'send_to': measurementId
8347
+ };
8112
8348
  gtagFunction("event" /* GtagCommand.EVENT */, eventName, params);
8113
8349
  }
8114
8350
  }
8351
+ /**
8352
+ * Set all other user properties other than user_id and screen_name.
8353
+ *
8354
+ * @param gtagFunction Wrapped gtag function that waits for fid to be set before sending an event
8355
+ * @param properties Map of user properties to set
8356
+ */
8357
+ async function setUserProperties$1(gtagFunction, initializationPromise, properties, options) {
8358
+ if (options && options.global) {
8359
+ const flatProperties = {};
8360
+ for (const key of Object.keys(properties)) {
8361
+ // use dot notation for merge behavior in gtag.js
8362
+ flatProperties[`user_properties.${key}`] = properties[key];
8363
+ }
8364
+ gtagFunction("set" /* GtagCommand.SET */, flatProperties);
8365
+ return Promise.resolve();
8366
+ }
8367
+ else {
8368
+ const measurementId = await initializationPromise;
8369
+ gtagFunction("config" /* GtagCommand.CONFIG */, measurementId, {
8370
+ update: true,
8371
+ 'user_properties': properties
8372
+ });
8373
+ }
8374
+ }
8115
8375
 
8116
8376
  /**
8117
8377
  * @license
@@ -8142,7 +8402,7 @@ async function validateIndexedDB() {
8142
8402
  }
8143
8403
  catch (e) {
8144
8404
  logger$1.warn(ERROR_FACTORY.create("indexeddb-unavailable" /* AnalyticsError.INDEXEDDB_UNAVAILABLE */, {
8145
- errorInfo: e === null || e === void 0 ? void 0 : e.toString()
8405
+ errorInfo: e?.toString()
8146
8406
  }).message);
8147
8407
  return false;
8148
8408
  }
@@ -8163,7 +8423,6 @@ async function validateIndexedDB() {
8163
8423
  * @returns Measurement ID.
8164
8424
  */
8165
8425
  async function _initializeAnalytics(app, dynamicConfigPromisesList, measurementIdToAppId, installations, gtagCore, dataLayerName, options) {
8166
- var _a;
8167
8426
  const dynamicConfigPromise = fetchDynamicConfigWithRetry(app);
8168
8427
  // Once fetched, map measurementIds to appId, for ease of lookup in wrapped gtag function.
8169
8428
  dynamicConfigPromise
@@ -8205,7 +8464,7 @@ async function _initializeAnalytics(app, dynamicConfigPromisesList, measurementI
8205
8464
  gtagCore('js', new Date());
8206
8465
  // User config added first. We don't want users to accidentally overwrite
8207
8466
  // base Firebase config properties.
8208
- const configProperties = (_a = options === null || options === void 0 ? void 0 : options.config) !== null && _a !== void 0 ? _a : {};
8467
+ const configProperties = options?.config ?? {};
8209
8468
  // guard against developers accidentally setting properties with prefix `firebase_`
8210
8469
  configProperties[ORIGIN_KEY] = 'firebase';
8211
8470
  configProperties.update = true;
@@ -8393,6 +8652,15 @@ function initializeAnalytics(app, options = {}) {
8393
8652
  const analyticsInstance = analyticsProvider.initialize({ options });
8394
8653
  return analyticsInstance;
8395
8654
  }
8655
+ /**
8656
+ * Use gtag `config` command to set all params specified.
8657
+ *
8658
+ * @public
8659
+ */
8660
+ function setUserProperties(analyticsInstance, properties, options) {
8661
+ analyticsInstance = getModularInstance(analyticsInstance);
8662
+ setUserProperties$1(wrappedGtagFunction, initializationPromisesMap[analyticsInstance.app.options.appId], properties, options).catch(e => logger$1.error(e));
8663
+ }
8396
8664
  /**
8397
8665
  * Sends a Google Analytics event with given `eventParams`. This method
8398
8666
  * automatically associates this logged event with this Firebase web
@@ -8410,7 +8678,7 @@ function logEvent$2(analyticsInstance, eventName, eventParams, options) {
8410
8678
  }
8411
8679
 
8412
8680
  const name$2 = "@firebase/analytics";
8413
- const version$2 = "0.10.10";
8681
+ const version$2 = "0.10.19";
8414
8682
 
8415
8683
  /**
8416
8684
  * The Firebase Analytics Web SDK.
@@ -8429,13 +8697,14 @@ function registerAnalytics() {
8429
8697
  }, "PUBLIC" /* ComponentType.PUBLIC */));
8430
8698
  _registerComponent(new Component('analytics-internal', internalFactory, "PRIVATE" /* ComponentType.PRIVATE */));
8431
8699
  registerVersion(name$2, version$2);
8432
- // BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
8433
- registerVersion(name$2, version$2, 'esm2017');
8700
+ // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
8701
+ registerVersion(name$2, version$2, 'esm2020');
8434
8702
  function internalFactory(container) {
8435
8703
  try {
8436
8704
  const analytics = container.getProvider(ANALYTICS_TYPE).getImmediate();
8437
8705
  return {
8438
- logEvent: (eventName, eventParams, options) => logEvent$2(analytics, eventName, eventParams, options)
8706
+ logEvent: (eventName, eventParams, options) => logEvent$2(analytics, eventName, eventParams, options),
8707
+ setUserProperties: (properties, options) => setUserProperties(analytics, properties, options)
8439
8708
  };
8440
8709
  }
8441
8710
  catch (e) {
@@ -8447,40 +8716,6 @@ function registerAnalytics() {
8447
8716
  }
8448
8717
  registerAnalytics();
8449
8718
 
8450
- /******************************************************************************
8451
- Copyright (c) Microsoft Corporation.
8452
-
8453
- Permission to use, copy, modify, and/or distribute this software for any
8454
- purpose with or without fee is hereby granted.
8455
-
8456
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8457
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
8458
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
8459
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
8460
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
8461
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
8462
- PERFORMANCE OF THIS SOFTWARE.
8463
- ***************************************************************************** */
8464
- /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
8465
-
8466
-
8467
- function __rest(s, e) {
8468
- var t = {};
8469
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
8470
- t[p] = s[p];
8471
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
8472
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8473
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8474
- t[p[i]] = s[p[i]];
8475
- }
8476
- return t;
8477
- }
8478
-
8479
- typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
8480
- var e = new Error(message);
8481
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
8482
- };
8483
-
8484
8719
  function _prodErrorMap() {
8485
8720
  // We will include this one message in the prod error map since by the very
8486
8721
  // nature of this error, developers will never be able to see the message
@@ -8552,7 +8787,10 @@ function _createError(authOrCode, ...rest) {
8552
8787
  return createErrorInternal(authOrCode, ...rest);
8553
8788
  }
8554
8789
  function _errorWithCustomMessage(auth, code, message) {
8555
- const errorMap = Object.assign(Object.assign({}, prodErrorMap()), { [code]: message });
8790
+ const errorMap = {
8791
+ ...prodErrorMap(),
8792
+ [code]: message
8793
+ };
8556
8794
  const factory = new ErrorFactory('auth', 'Firebase', errorMap);
8557
8795
  return factory.create(code, {
8558
8796
  appName: auth.name
@@ -8623,15 +8861,13 @@ function debugAssert(assertion, message) {
8623
8861
  * limitations under the License.
8624
8862
  */
8625
8863
  function _getCurrentUrl() {
8626
- var _a;
8627
- return (typeof self !== 'undefined' && ((_a = self.location) === null || _a === void 0 ? void 0 : _a.href)) || '';
8864
+ return (typeof self !== 'undefined' && self.location?.href) || '';
8628
8865
  }
8629
8866
  function _isHttpOrHttps() {
8630
8867
  return _getCurrentScheme() === 'http:' || _getCurrentScheme() === 'https:';
8631
8868
  }
8632
8869
  function _getCurrentScheme() {
8633
- var _a;
8634
- return (typeof self !== 'undefined' && ((_a = self.location) === null || _a === void 0 ? void 0 : _a.protocol)) || null;
8870
+ return (typeof self !== 'undefined' && self.location?.protocol) || null;
8635
8871
  }
8636
8872
 
8637
8873
  /**
@@ -8934,10 +9170,21 @@ const SERVER_ERROR_MAP = {
8934
9170
  * See the License for the specific language governing permissions and
8935
9171
  * limitations under the License.
8936
9172
  */
9173
+ const CookieAuthProxiedEndpoints = [
9174
+ "/v1/accounts:signInWithCustomToken" /* Endpoint.SIGN_IN_WITH_CUSTOM_TOKEN */,
9175
+ "/v1/accounts:signInWithEmailLink" /* Endpoint.SIGN_IN_WITH_EMAIL_LINK */,
9176
+ "/v1/accounts:signInWithIdp" /* Endpoint.SIGN_IN_WITH_IDP */,
9177
+ "/v1/accounts:signInWithPassword" /* Endpoint.SIGN_IN_WITH_PASSWORD */,
9178
+ "/v1/accounts:signInWithPhoneNumber" /* Endpoint.SIGN_IN_WITH_PHONE_NUMBER */,
9179
+ "/v1/token" /* Endpoint.TOKEN */
9180
+ ];
8937
9181
  const DEFAULT_API_TIMEOUT_MS = new Delay(30000, 60000);
8938
9182
  function _addTidIfNecessary(auth, request) {
8939
9183
  if (auth.tenantId && !request.tenantId) {
8940
- return Object.assign(Object.assign({}, request), { tenantId: auth.tenantId });
9184
+ return {
9185
+ ...request,
9186
+ tenantId: auth.tenantId
9187
+ };
8941
9188
  }
8942
9189
  return request;
8943
9190
  }
@@ -8955,14 +9202,20 @@ async function _performApiRequest(auth, method, path, request, customErrorMap =
8955
9202
  };
8956
9203
  }
8957
9204
  }
8958
- const query = querystring(Object.assign({ key: auth.config.apiKey }, params)).slice(1);
9205
+ const query = querystring({
9206
+ key: auth.config.apiKey,
9207
+ ...params
9208
+ }).slice(1);
8959
9209
  const headers = await auth._getAdditionalHeaders();
8960
9210
  headers["Content-Type" /* HttpHeader.CONTENT_TYPE */] = 'application/json';
8961
9211
  if (auth.languageCode) {
8962
9212
  headers["X-Firebase-Locale" /* HttpHeader.X_FIREBASE_LOCALE */] = auth.languageCode;
8963
9213
  }
8964
- const fetchArgs = Object.assign({ method,
8965
- headers }, body);
9214
+ const fetchArgs = {
9215
+ method,
9216
+ headers,
9217
+ ...body
9218
+ };
8966
9219
  /* Security-conscious server-side frameworks tend to have built in mitigations for referrer
8967
9220
  problems". See the Cloudflare GitHub issue #487: Error: The 'referrerPolicy' field on
8968
9221
  'RequestInitializerDict' is not implemented."
@@ -8970,12 +9223,15 @@ async function _performApiRequest(auth, method, path, request, customErrorMap =
8970
9223
  if (!isCloudflareWorker()) {
8971
9224
  fetchArgs.referrerPolicy = 'no-referrer';
8972
9225
  }
8973
- return FetchProvider.fetch()(_getFinalTarget(auth, auth.config.apiHost, path, query), fetchArgs);
9226
+ if (auth.emulatorConfig && isCloudWorkstation(auth.emulatorConfig.host)) {
9227
+ fetchArgs.credentials = 'include';
9228
+ }
9229
+ return FetchProvider.fetch()(await _getFinalTarget(auth, auth.config.apiHost, path, query), fetchArgs);
8974
9230
  });
8975
9231
  }
8976
9232
  async function _performFetchWithErrorHandling(auth, customErrorMap, fetchFn) {
8977
9233
  auth._canInitEmulator = false;
8978
- const errorMap = Object.assign(Object.assign({}, SERVER_ERROR_MAP), customErrorMap);
9234
+ const errorMap = { ...SERVER_ERROR_MAP, ...customErrorMap };
8979
9235
  try {
8980
9236
  const networkTimeout = new NetworkTimeout(auth);
8981
9237
  const response = await Promise.race([
@@ -9035,12 +9291,25 @@ async function _performSignInRequest(auth, method, path, request, customErrorMap
9035
9291
  }
9036
9292
  return serverResponse;
9037
9293
  }
9038
- function _getFinalTarget(auth, host, path, query) {
9294
+ async function _getFinalTarget(auth, host, path, query) {
9039
9295
  const base = `${host}${path}?${query}`;
9040
- if (!auth.config.emulator) {
9041
- return `${auth.config.apiScheme}://${base}`;
9042
- }
9043
- return _emulatorUrl(auth.config, base);
9296
+ const authInternal = auth;
9297
+ const finalTarget = authInternal.config.emulator
9298
+ ? _emulatorUrl(auth.config, base)
9299
+ : `${auth.config.apiScheme}://${base}`;
9300
+ // Cookie auth works by MiTMing the signIn and token endpoints from the developer's backend,
9301
+ // saving the idToken and refreshToken into cookies, and then redacting the refreshToken
9302
+ // from the response
9303
+ if (CookieAuthProxiedEndpoints.includes(path)) {
9304
+ // Persistence manager is async, we need to await it. We can't just wait for auth initialized
9305
+ // here since auth initialization calls this function.
9306
+ await authInternal._persistenceManagerAvailable;
9307
+ if (authInternal._getPersistenceType() === "COOKIE" /* PersistenceType.COOKIE */) {
9308
+ const cookiePersistence = authInternal._getPersistence();
9309
+ return cookiePersistence._getFinalTarget(finalTarget).toString();
9310
+ }
9311
+ }
9312
+ return finalTarget;
9044
9313
  }
9045
9314
  class NetworkTimeout {
9046
9315
  clearNetworkTimeout() {
@@ -9150,7 +9419,7 @@ async function getIdTokenResult(user, forceRefresh = false) {
9150
9419
  const claims = _parseToken(token);
9151
9420
  _assert(claims && claims.exp && claims.auth_time && claims.iat, userInternal.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
9152
9421
  const firebase = typeof claims.firebase === 'object' ? claims.firebase : undefined;
9153
- const signInProvider = firebase === null || firebase === void 0 ? void 0 : firebase['sign_in_provider'];
9422
+ const signInProvider = firebase?.['sign_in_provider'];
9154
9423
  return {
9155
9424
  claims,
9156
9425
  token,
@@ -9158,7 +9427,7 @@ async function getIdTokenResult(user, forceRefresh = false) {
9158
9427
  issuedAtTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.iat)),
9159
9428
  expirationTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.exp)),
9160
9429
  signInProvider: signInProvider || null,
9161
- signInSecondFactor: (firebase === null || firebase === void 0 ? void 0 : firebase['sign_in_second_factor']) || null
9430
+ signInSecondFactor: firebase?.['sign_in_second_factor'] || null
9162
9431
  };
9163
9432
  }
9164
9433
  function secondsStringToMilliseconds(seconds) {
@@ -9181,7 +9450,7 @@ function _parseToken(token) {
9181
9450
  return JSON.parse(decoded);
9182
9451
  }
9183
9452
  catch (e) {
9184
- _logError('Caught error parsing JWT payload as JSON', e === null || e === void 0 ? void 0 : e.toString());
9453
+ _logError('Caught error parsing JWT payload as JSON', e?.toString());
9185
9454
  return null;
9186
9455
  }
9187
9456
  }
@@ -9277,7 +9546,6 @@ class ProactiveRefresh {
9277
9546
  }
9278
9547
  }
9279
9548
  getInterval(wasError) {
9280
- var _a;
9281
9549
  if (wasError) {
9282
9550
  const interval = this.errorBackoff;
9283
9551
  this.errorBackoff = Math.min(this.errorBackoff * 2, 960000 /* Duration.RETRY_BACKOFF_MAX */);
@@ -9286,7 +9554,7 @@ class ProactiveRefresh {
9286
9554
  else {
9287
9555
  // Reset the error backoff
9288
9556
  this.errorBackoff = 30000 /* Duration.RETRY_BACKOFF_MIN */;
9289
- const expTime = (_a = this.user.stsTokenManager.expirationTime) !== null && _a !== void 0 ? _a : 0;
9557
+ const expTime = this.user.stsTokenManager.expirationTime ?? 0;
9290
9558
  const interval = expTime - Date.now() - 300000 /* Duration.OFFSET */;
9291
9559
  return Math.max(0, interval);
9292
9560
  }
@@ -9307,7 +9575,7 @@ class ProactiveRefresh {
9307
9575
  }
9308
9576
  catch (e) {
9309
9577
  // Only retry on network errors
9310
- if ((e === null || e === void 0 ? void 0 : e.code) ===
9578
+ if (e?.code ===
9311
9579
  `auth/${"network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */}`) {
9312
9580
  this.schedule(/* wasError */ true);
9313
9581
  }
@@ -9373,14 +9641,13 @@ class UserMetadata {
9373
9641
  * limitations under the License.
9374
9642
  */
9375
9643
  async function _reloadWithoutSaving(user) {
9376
- var _a;
9377
9644
  const auth = user.auth;
9378
9645
  const idToken = await user.getIdToken();
9379
9646
  const response = await _logoutIfInvalidated(user, getAccountInfo(auth, { idToken }));
9380
- _assert(response === null || response === void 0 ? void 0 : response.users.length, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
9647
+ _assert(response?.users.length, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
9381
9648
  const coreAccount = response.users[0];
9382
9649
  user._notifyReloadListener(coreAccount);
9383
- const newProviderData = ((_a = coreAccount.providerUserInfo) === null || _a === void 0 ? void 0 : _a.length)
9650
+ const newProviderData = coreAccount.providerUserInfo?.length
9384
9651
  ? extractProviderData(coreAccount.providerUserInfo)
9385
9652
  : [];
9386
9653
  const providerData = mergeProviderData(user.providerData, newProviderData);
@@ -9390,7 +9657,7 @@ async function _reloadWithoutSaving(user) {
9390
9657
  // On the other hand, if it was not anonymous before, it should never be
9391
9658
  // considered anonymous now.
9392
9659
  const oldIsAnonymous = user.isAnonymous;
9393
- const newIsAnonymous = !(user.email && coreAccount.passwordHash) && !(providerData === null || providerData === void 0 ? void 0 : providerData.length);
9660
+ const newIsAnonymous = !(user.email && coreAccount.passwordHash) && !providerData?.length;
9394
9661
  const isAnonymous = !oldIsAnonymous ? false : newIsAnonymous;
9395
9662
  const updates = {
9396
9663
  uid: coreAccount.localId,
@@ -9427,8 +9694,7 @@ function mergeProviderData(original, newData) {
9427
9694
  return [...deduped, ...newData];
9428
9695
  }
9429
9696
  function extractProviderData(providers) {
9430
- return providers.map((_a) => {
9431
- var { providerId } = _a, provider = __rest(_a, ["providerId"]);
9697
+ return providers.map(({ providerId, ...provider }) => {
9432
9698
  return {
9433
9699
  providerId,
9434
9700
  uid: provider.rawId || '',
@@ -9463,14 +9729,19 @@ async function requestStsToken(auth, refreshToken) {
9463
9729
  'refresh_token': refreshToken
9464
9730
  }).slice(1);
9465
9731
  const { tokenApiHost, apiKey } = auth.config;
9466
- const url = _getFinalTarget(auth, tokenApiHost, "/v1/token" /* Endpoint.TOKEN */, `key=${apiKey}`);
9732
+ const url = await _getFinalTarget(auth, tokenApiHost, "/v1/token" /* Endpoint.TOKEN */, `key=${apiKey}`);
9467
9733
  const headers = await auth._getAdditionalHeaders();
9468
9734
  headers["Content-Type" /* HttpHeader.CONTENT_TYPE */] = 'application/x-www-form-urlencoded';
9469
- return FetchProvider.fetch()(url, {
9735
+ const options = {
9470
9736
  method: "POST" /* HttpMethod.POST */,
9471
9737
  headers,
9472
9738
  body
9473
- });
9739
+ };
9740
+ if (auth.emulatorConfig &&
9741
+ isCloudWorkstation(auth.emulatorConfig.host)) {
9742
+ options.credentials = 'include';
9743
+ }
9744
+ return FetchProvider.fetch()(url, options);
9474
9745
  });
9475
9746
  // The response comes back in snake_case. Convert to camel:
9476
9747
  return {
@@ -9615,8 +9886,7 @@ function assertStringOrUndefined(assertion, appName) {
9615
9886
  _assert(typeof assertion === 'string' || typeof assertion === 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */, { appName });
9616
9887
  }
9617
9888
  class UserImpl {
9618
- constructor(_a) {
9619
- var { uid, auth, stsTokenManager } = _a, opt = __rest(_a, ["uid", "auth", "stsTokenManager"]);
9889
+ constructor({ uid, auth, stsTokenManager, ...opt }) {
9620
9890
  // For the user object, provider is always Firebase.
9621
9891
  this.providerId = "firebase" /* ProviderId.FIREBASE */;
9622
9892
  this.proactiveRefresh = new ProactiveRefresh(this);
@@ -9664,12 +9934,16 @@ class UserImpl {
9664
9934
  this.phoneNumber = user.phoneNumber;
9665
9935
  this.isAnonymous = user.isAnonymous;
9666
9936
  this.tenantId = user.tenantId;
9667
- this.providerData = user.providerData.map(userInfo => (Object.assign({}, userInfo)));
9937
+ this.providerData = user.providerData.map(userInfo => ({ ...userInfo }));
9668
9938
  this.metadata._copy(user.metadata);
9669
9939
  this.stsTokenManager._assign(user.stsTokenManager);
9670
9940
  }
9671
9941
  _clone(auth) {
9672
- const newUser = new UserImpl(Object.assign(Object.assign({}, this), { auth, stsTokenManager: this.stsTokenManager._clone() }));
9942
+ const newUser = new UserImpl({
9943
+ ...this,
9944
+ auth,
9945
+ stsTokenManager: this.stsTokenManager._clone()
9946
+ });
9673
9947
  newUser.metadata._copy(this.metadata);
9674
9948
  return newUser;
9675
9949
  }
@@ -9724,26 +9998,40 @@ class UserImpl {
9724
9998
  return this.auth.signOut();
9725
9999
  }
9726
10000
  toJSON() {
9727
- return Object.assign(Object.assign({ uid: this.uid, email: this.email || undefined, emailVerified: this.emailVerified, displayName: this.displayName || undefined, isAnonymous: this.isAnonymous, photoURL: this.photoURL || undefined, phoneNumber: this.phoneNumber || undefined, tenantId: this.tenantId || undefined, providerData: this.providerData.map(userInfo => (Object.assign({}, userInfo))), stsTokenManager: this.stsTokenManager.toJSON(),
10001
+ return {
10002
+ uid: this.uid,
10003
+ email: this.email || undefined,
10004
+ emailVerified: this.emailVerified,
10005
+ displayName: this.displayName || undefined,
10006
+ isAnonymous: this.isAnonymous,
10007
+ photoURL: this.photoURL || undefined,
10008
+ phoneNumber: this.phoneNumber || undefined,
10009
+ tenantId: this.tenantId || undefined,
10010
+ providerData: this.providerData.map(userInfo => ({ ...userInfo })),
10011
+ stsTokenManager: this.stsTokenManager.toJSON(),
9728
10012
  // Redirect event ID must be maintained in case there is a pending
9729
10013
  // redirect event.
9730
- _redirectEventId: this._redirectEventId }, this.metadata.toJSON()), {
10014
+ _redirectEventId: this._redirectEventId,
10015
+ ...this.metadata.toJSON(),
9731
10016
  // Required for compatibility with the legacy SDK (go/firebase-auth-sdk-persistence-parsing):
9732
- apiKey: this.auth.config.apiKey, appName: this.auth.name });
10017
+ apiKey: this.auth.config.apiKey,
10018
+ appName: this.auth.name
10019
+ // Missing authDomain will be tolerated by the legacy SDK.
10020
+ // stsTokenManager.apiKey isn't actually required (despite the legacy SDK persisting it).
10021
+ };
9733
10022
  }
9734
10023
  get refreshToken() {
9735
10024
  return this.stsTokenManager.refreshToken || '';
9736
10025
  }
9737
10026
  static _fromJSON(auth, object) {
9738
- var _a, _b, _c, _d, _e, _f, _g, _h;
9739
- const displayName = (_a = object.displayName) !== null && _a !== void 0 ? _a : undefined;
9740
- const email = (_b = object.email) !== null && _b !== void 0 ? _b : undefined;
9741
- const phoneNumber = (_c = object.phoneNumber) !== null && _c !== void 0 ? _c : undefined;
9742
- const photoURL = (_d = object.photoURL) !== null && _d !== void 0 ? _d : undefined;
9743
- const tenantId = (_e = object.tenantId) !== null && _e !== void 0 ? _e : undefined;
9744
- const _redirectEventId = (_f = object._redirectEventId) !== null && _f !== void 0 ? _f : undefined;
9745
- const createdAt = (_g = object.createdAt) !== null && _g !== void 0 ? _g : undefined;
9746
- const lastLoginAt = (_h = object.lastLoginAt) !== null && _h !== void 0 ? _h : undefined;
10027
+ const displayName = object.displayName ?? undefined;
10028
+ const email = object.email ?? undefined;
10029
+ const phoneNumber = object.phoneNumber ?? undefined;
10030
+ const photoURL = object.photoURL ?? undefined;
10031
+ const tenantId = object.tenantId ?? undefined;
10032
+ const _redirectEventId = object._redirectEventId ?? undefined;
10033
+ const createdAt = object.createdAt ?? undefined;
10034
+ const lastLoginAt = object.lastLoginAt ?? undefined;
9747
10035
  const { uid, emailVerified, isAnonymous, providerData, stsTokenManager: plainObjectTokenManager } = object;
9748
10036
  _assert(uid && plainObjectTokenManager, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
9749
10037
  const stsTokenManager = StsTokenManager.fromJSON(this.name, plainObjectTokenManager);
@@ -9773,7 +10061,7 @@ class UserImpl {
9773
10061
  lastLoginAt
9774
10062
  });
9775
10063
  if (providerData && Array.isArray(providerData)) {
9776
- user.providerData = providerData.map(userInfo => (Object.assign({}, userInfo)));
10064
+ user.providerData = providerData.map(userInfo => ({ ...userInfo }));
9777
10065
  }
9778
10066
  if (_redirectEventId) {
9779
10067
  user._redirectEventId = _redirectEventId;
@@ -9810,7 +10098,7 @@ class UserImpl {
9810
10098
  const providerData = coreAccount.providerUserInfo !== undefined
9811
10099
  ? extractProviderData(coreAccount.providerUserInfo)
9812
10100
  : [];
9813
- const isAnonymous = !(coreAccount.email && coreAccount.passwordHash) && !(providerData === null || providerData === void 0 ? void 0 : providerData.length);
10101
+ const isAnonymous = !(coreAccount.email && coreAccount.passwordHash) && !providerData?.length;
9814
10102
  const stsTokenManager = new StsTokenManager();
9815
10103
  stsTokenManager.updateFromIdToken(idToken);
9816
10104
  // Initialize the Firebase Auth user.
@@ -9832,7 +10120,7 @@ class UserImpl {
9832
10120
  providerData,
9833
10121
  metadata: new UserMetadata(coreAccount.createdAt, coreAccount.lastLoginAt),
9834
10122
  isAnonymous: !(coreAccount.email && coreAccount.passwordHash) &&
9835
- !(providerData === null || providerData === void 0 ? void 0 : providerData.length)
10123
+ !providerData?.length
9836
10124
  };
9837
10125
  Object.assign(user, updates);
9838
10126
  return user;
@@ -9954,7 +10242,17 @@ class PersistenceUserManager {
9954
10242
  }
9955
10243
  async getCurrentUser() {
9956
10244
  const blob = await this.persistence._get(this.fullUserKey);
9957
- return blob ? UserImpl._fromJSON(this.auth, blob) : null;
10245
+ if (!blob) {
10246
+ return null;
10247
+ }
10248
+ if (typeof blob === 'string') {
10249
+ const response = await getAccountInfo(this.auth, { idToken: blob }).catch(() => undefined);
10250
+ if (!response) {
10251
+ return null;
10252
+ }
10253
+ return UserImpl._fromGetAccountInfoResponse(this.auth, response, blob);
10254
+ }
10255
+ return UserImpl._fromJSON(this.auth, blob);
9958
10256
  }
9959
10257
  removeCurrentUser() {
9960
10258
  return this.persistence._remove(this.fullUserKey);
@@ -10001,7 +10299,19 @@ class PersistenceUserManager {
10001
10299
  try {
10002
10300
  const blob = await persistence._get(key);
10003
10301
  if (blob) {
10004
- const user = UserImpl._fromJSON(auth, blob); // throws for unparsable blob (wrong format)
10302
+ let user;
10303
+ if (typeof blob === 'string') {
10304
+ const response = await getAccountInfo(auth, {
10305
+ idToken: blob
10306
+ }).catch(() => undefined);
10307
+ if (!response) {
10308
+ break;
10309
+ }
10310
+ user = await UserImpl._fromGetAccountInfoResponse(auth, response, blob);
10311
+ }
10312
+ else {
10313
+ user = UserImpl._fromJSON(auth, blob); // throws for unparsable blob (wrong format)
10314
+ }
10005
10315
  if (persistence !== selectedPersistence) {
10006
10316
  userToMigrate = user;
10007
10317
  }
@@ -10009,7 +10319,7 @@ class PersistenceUserManager {
10009
10319
  break;
10010
10320
  }
10011
10321
  }
10012
- catch (_a) { }
10322
+ catch { }
10013
10323
  }
10014
10324
  // If we find the user in a persistence that does support migration, use
10015
10325
  // that migration path (of only persistences that support migration)
@@ -10032,7 +10342,7 @@ class PersistenceUserManager {
10032
10342
  try {
10033
10343
  await persistence._remove(key);
10034
10344
  }
10035
- catch (_a) { }
10345
+ catch { }
10036
10346
  }
10037
10347
  }));
10038
10348
  return new PersistenceUserManager(selectedPersistence, auth, userKey);
@@ -10102,7 +10412,7 @@ function _getBrowserName(userAgent) {
10102
10412
  // Most modern browsers have name/version at end of user agent string.
10103
10413
  const re = /([a-zA-Z\d\.]+)\/[a-zA-Z\d\.]*$/;
10104
10414
  const matches = userAgent.match(re);
10105
- if ((matches === null || matches === void 0 ? void 0 : matches.length) === 2) {
10415
+ if (matches?.length === 2) {
10106
10416
  return matches[1];
10107
10417
  }
10108
10418
  }
@@ -10138,8 +10448,7 @@ function _isIOS(ua = getUA()) {
10138
10448
  (/macintosh/i.test(ua) && /mobile/i.test(ua)));
10139
10449
  }
10140
10450
  function _isIOSStandalone(ua = getUA()) {
10141
- var _a;
10142
- return _isIOS(ua) && !!((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.standalone);
10451
+ return _isIOS(ua) && !!window.navigator?.standalone;
10143
10452
  }
10144
10453
  function _isIE10() {
10145
10454
  return isIE() && document.documentMode === 10;
@@ -10270,7 +10579,7 @@ class AuthMiddlewareQueue {
10270
10579
  }
10271
10580
  }
10272
10581
  throw this.auth._errorFactory.create("login-blocked" /* AuthErrorCode.LOGIN_BLOCKED */, {
10273
- originalMessage: e === null || e === void 0 ? void 0 : e.message
10582
+ originalMessage: e?.message
10274
10583
  });
10275
10584
  }
10276
10585
  }
@@ -10328,13 +10637,12 @@ const MINIMUM_MIN_PASSWORD_LENGTH = 6;
10328
10637
  */
10329
10638
  class PasswordPolicyImpl {
10330
10639
  constructor(response) {
10331
- var _a, _b, _c, _d;
10332
10640
  // Only include custom strength options defined in the response.
10333
10641
  const responseOptions = response.customStrengthOptions;
10334
10642
  this.customStrengthOptions = {};
10335
10643
  // TODO: Remove once the backend is updated to include the minimum min password length instead of undefined when there is no minimum length set.
10336
10644
  this.customStrengthOptions.minPasswordLength =
10337
- (_a = responseOptions.minPasswordLength) !== null && _a !== void 0 ? _a : MINIMUM_MIN_PASSWORD_LENGTH;
10645
+ responseOptions.minPasswordLength ?? MINIMUM_MIN_PASSWORD_LENGTH;
10338
10646
  if (responseOptions.maxPasswordLength) {
10339
10647
  this.customStrengthOptions.maxPasswordLength =
10340
10648
  responseOptions.maxPasswordLength;
@@ -10361,12 +10669,11 @@ class PasswordPolicyImpl {
10361
10669
  }
10362
10670
  // Use an empty string if no non-alphanumeric characters are specified in the response.
10363
10671
  this.allowedNonAlphanumericCharacters =
10364
- (_c = (_b = response.allowedNonAlphanumericCharacters) === null || _b === void 0 ? void 0 : _b.join('')) !== null && _c !== void 0 ? _c : '';
10365
- this.forceUpgradeOnSignin = (_d = response.forceUpgradeOnSignin) !== null && _d !== void 0 ? _d : false;
10672
+ response.allowedNonAlphanumericCharacters?.join('') ?? '';
10673
+ this.forceUpgradeOnSignin = response.forceUpgradeOnSignin ?? false;
10366
10674
  this.schemaVersion = response.schemaVersion;
10367
10675
  }
10368
10676
  validatePassword(password) {
10369
- var _a, _b, _c, _d, _e, _f;
10370
10677
  const status = {
10371
10678
  isValid: true,
10372
10679
  passwordPolicy: this
@@ -10375,12 +10682,12 @@ class PasswordPolicyImpl {
10375
10682
  this.validatePasswordLengthOptions(password, status);
10376
10683
  this.validatePasswordCharacterOptions(password, status);
10377
10684
  // Combine the status into single isValid property.
10378
- status.isValid && (status.isValid = (_a = status.meetsMinPasswordLength) !== null && _a !== void 0 ? _a : true);
10379
- status.isValid && (status.isValid = (_b = status.meetsMaxPasswordLength) !== null && _b !== void 0 ? _b : true);
10380
- status.isValid && (status.isValid = (_c = status.containsLowercaseLetter) !== null && _c !== void 0 ? _c : true);
10381
- status.isValid && (status.isValid = (_d = status.containsUppercaseLetter) !== null && _d !== void 0 ? _d : true);
10382
- status.isValid && (status.isValid = (_e = status.containsNumericCharacter) !== null && _e !== void 0 ? _e : true);
10383
- status.isValid && (status.isValid = (_f = status.containsNonAlphanumericCharacter) !== null && _f !== void 0 ? _f : true);
10685
+ status.isValid && (status.isValid = status.meetsMinPasswordLength ?? true);
10686
+ status.isValid && (status.isValid = status.meetsMaxPasswordLength ?? true);
10687
+ status.isValid && (status.isValid = status.containsLowercaseLetter ?? true);
10688
+ status.isValid && (status.isValid = status.containsUppercaseLetter ?? true);
10689
+ status.isValid && (status.isValid = status.containsNumericCharacter ?? true);
10690
+ status.isValid && (status.isValid = status.containsNonAlphanumericCharacter ?? true);
10384
10691
  return status;
10385
10692
  }
10386
10693
  /**
@@ -10495,6 +10802,7 @@ class AuthImpl {
10495
10802
  this._tenantRecaptchaConfigs = {};
10496
10803
  this._projectPasswordPolicy = null;
10497
10804
  this._tenantPasswordPolicies = {};
10805
+ this._resolvePersistenceManagerAvailable = undefined;
10498
10806
  // Tracks the last notified UID for state change listeners to prevent
10499
10807
  // repeated calls to the callbacks. Undefined means it's never been
10500
10808
  // called, whereas null means it's been called with a signed out user
@@ -10505,6 +10813,9 @@ class AuthImpl {
10505
10813
  this.frameworks = [];
10506
10814
  this.name = app.name;
10507
10815
  this.clientVersion = config.sdkClientVersion;
10816
+ // TODO(jamesdaniels) explore less hacky way to do this, cookie authentication needs
10817
+ // persistenceMananger to be available. see _getFinalTarget for more context
10818
+ this._persistenceManagerAvailable = new Promise(resolve => (this._resolvePersistenceManagerAvailable = resolve));
10508
10819
  }
10509
10820
  _initializeWithPersistence(persistenceHierarchy, popupRedirectResolver) {
10510
10821
  if (popupRedirectResolver) {
@@ -10513,17 +10824,17 @@ class AuthImpl {
10513
10824
  // Have to check for app deletion throughout initialization (after each
10514
10825
  // promise resolution)
10515
10826
  this._initializationPromise = this.queue(async () => {
10516
- var _a, _b;
10517
10827
  if (this._deleted) {
10518
10828
  return;
10519
10829
  }
10520
10830
  this.persistenceManager = await PersistenceUserManager.create(this, persistenceHierarchy);
10831
+ this._resolvePersistenceManagerAvailable?.();
10521
10832
  if (this._deleted) {
10522
10833
  return;
10523
10834
  }
10524
10835
  // Initialize the resolver early if necessary (only applicable to web:
10525
10836
  // this will cause the iframe to load immediately in certain cases)
10526
- if ((_a = this._popupRedirectResolver) === null || _a === void 0 ? void 0 : _a._shouldInitProactively) {
10837
+ if (this._popupRedirectResolver?._shouldInitProactively) {
10527
10838
  // If this fails, don't halt auth loading
10528
10839
  try {
10529
10840
  await this._popupRedirectResolver._initialize(this);
@@ -10533,7 +10844,7 @@ class AuthImpl {
10533
10844
  }
10534
10845
  }
10535
10846
  await this.initializeCurrentUser(popupRedirectResolver);
10536
- this.lastNotifiedUid = ((_b = this.currentUser) === null || _b === void 0 ? void 0 : _b.uid) || null;
10847
+ this.lastNotifiedUid = this.currentUser?.uid || null;
10537
10848
  if (this._deleted) {
10538
10849
  return;
10539
10850
  }
@@ -10578,7 +10889,6 @@ class AuthImpl {
10578
10889
  }
10579
10890
  }
10580
10891
  async initializeCurrentUser(popupRedirectResolver) {
10581
- var _a;
10582
10892
  if (_isFirebaseServerApp(this.app)) {
10583
10893
  const idToken = this.app.settings.authIdToken;
10584
10894
  if (idToken) {
@@ -10598,15 +10908,15 @@ class AuthImpl {
10598
10908
  let needsTocheckMiddleware = false;
10599
10909
  if (popupRedirectResolver && this.config.authDomain) {
10600
10910
  await this.getOrInitRedirectPersistenceManager();
10601
- const redirectUserEventId = (_a = this.redirectUser) === null || _a === void 0 ? void 0 : _a._redirectEventId;
10602
- const storedUserEventId = futureCurrentUser === null || futureCurrentUser === void 0 ? void 0 : futureCurrentUser._redirectEventId;
10911
+ const redirectUserEventId = this.redirectUser?._redirectEventId;
10912
+ const storedUserEventId = futureCurrentUser?._redirectEventId;
10603
10913
  const result = await this.tryRedirectSignIn(popupRedirectResolver);
10604
10914
  // If the stored user (i.e. the old "currentUser") has a redirectId that
10605
10915
  // matches the redirect user, then we want to initially sign in with the
10606
10916
  // new user object from result.
10607
10917
  // TODO(samgho): More thoroughly test all of this
10608
10918
  if ((!redirectUserEventId || redirectUserEventId === storedUserEventId) &&
10609
- (result === null || result === void 0 ? void 0 : result.user)) {
10919
+ result?.user) {
10610
10920
  futureCurrentUser = result.user;
10611
10921
  needsTocheckMiddleware = true;
10612
10922
  }
@@ -10681,7 +10991,7 @@ class AuthImpl {
10681
10991
  await _reloadWithoutSaving(user);
10682
10992
  }
10683
10993
  catch (e) {
10684
- if ((e === null || e === void 0 ? void 0 : e.code) !==
10994
+ if (e?.code !==
10685
10995
  `auth/${"network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */}`) {
10686
10996
  // Something's wrong with the user's token. Log them out and remove
10687
10997
  // them from storage
@@ -10787,9 +11097,12 @@ class AuthImpl {
10787
11097
  this._tenantPasswordPolicies[this.tenantId] = passwordPolicy;
10788
11098
  }
10789
11099
  }
10790
- _getPersistence() {
11100
+ _getPersistenceType() {
10791
11101
  return this.assertedPersistence.persistence.type;
10792
11102
  }
11103
+ _getPersistence() {
11104
+ return this.assertedPersistence.persistence;
11105
+ }
10793
11106
  _updateErrorMap(errorMap) {
10794
11107
  this._errorFactory = new ErrorFactory('auth', 'Firebase', errorMap());
10795
11108
  }
@@ -10835,12 +11148,11 @@ class AuthImpl {
10835
11148
  }
10836
11149
  }
10837
11150
  toJSON() {
10838
- var _a;
10839
11151
  return {
10840
11152
  apiKey: this.config.apiKey,
10841
11153
  authDomain: this.config.authDomain,
10842
11154
  appName: this.name,
10843
- currentUser: (_a = this._currentUser) === null || _a === void 0 ? void 0 : _a.toJSON()
11155
+ currentUser: this._currentUser?.toJSON()
10844
11156
  };
10845
11157
  }
10846
11158
  async _setRedirectUser(user, popupRedirectResolver) {
@@ -10861,16 +11173,15 @@ class AuthImpl {
10861
11173
  return this.redirectPersistenceManager;
10862
11174
  }
10863
11175
  async _redirectUserForId(id) {
10864
- var _a, _b;
10865
11176
  // Make sure we've cleared any pending persistence actions if we're not in
10866
11177
  // the initializer
10867
11178
  if (this._isInitialized) {
10868
11179
  await this.queue(async () => { });
10869
11180
  }
10870
- if (((_a = this._currentUser) === null || _a === void 0 ? void 0 : _a._redirectEventId) === id) {
11181
+ if (this._currentUser?._redirectEventId === id) {
10871
11182
  return this._currentUser;
10872
11183
  }
10873
- if (((_b = this.redirectUser) === null || _b === void 0 ? void 0 : _b._redirectEventId) === id) {
11184
+ if (this.redirectUser?._redirectEventId === id) {
10874
11185
  return this.redirectUser;
10875
11186
  }
10876
11187
  return null;
@@ -10906,12 +11217,11 @@ class AuthImpl {
10906
11217
  return this.currentUser;
10907
11218
  }
10908
11219
  notifyAuthListeners() {
10909
- var _a, _b;
10910
11220
  if (!this._isInitialized) {
10911
11221
  return;
10912
11222
  }
10913
11223
  this.idTokenSubscription.next(this.currentUser);
10914
- const currentUid = (_b = (_a = this.currentUser) === null || _a === void 0 ? void 0 : _a.uid) !== null && _b !== void 0 ? _b : null;
11224
+ const currentUid = this.currentUser?.uid ?? null;
10915
11225
  if (this.lastNotifiedUid !== currentUid) {
10916
11226
  this.lastNotifiedUid = currentUid;
10917
11227
  this.authStateSubscription.next(this.currentUser);
@@ -10996,7 +11306,6 @@ class AuthImpl {
10996
11306
  return this.frameworks;
10997
11307
  }
10998
11308
  async _getAdditionalHeaders() {
10999
- var _a;
11000
11309
  // Additional headers on every request
11001
11310
  const headers = {
11002
11311
  ["X-Client-Version" /* HttpHeader.X_CLIENT_VERSION */]: this.clientVersion
@@ -11005,10 +11314,11 @@ class AuthImpl {
11005
11314
  headers["X-Firebase-gmpid" /* HttpHeader.X_FIREBASE_GMPID */] = this.app.options.appId;
11006
11315
  }
11007
11316
  // If the heartbeat service exists, add the heartbeat string
11008
- const heartbeatsHeader = await ((_a = this.heartbeatServiceProvider
11317
+ const heartbeatsHeader = await this.heartbeatServiceProvider
11009
11318
  .getImmediate({
11010
11319
  optional: true
11011
- })) === null || _a === void 0 ? void 0 : _a.getHeartbeatsHeader());
11320
+ })
11321
+ ?.getHeartbeatsHeader();
11012
11322
  if (heartbeatsHeader) {
11013
11323
  headers["X-Firebase-Client" /* HttpHeader.X_FIREBASE_CLIENT */] = heartbeatsHeader;
11014
11324
  }
@@ -11020,17 +11330,20 @@ class AuthImpl {
11020
11330
  return headers;
11021
11331
  }
11022
11332
  async _getAppCheckToken() {
11023
- var _a;
11024
- const appCheckTokenResult = await ((_a = this.appCheckServiceProvider
11025
- .getImmediate({ optional: true })) === null || _a === void 0 ? void 0 : _a.getToken());
11026
- if (appCheckTokenResult === null || appCheckTokenResult === void 0 ? void 0 : appCheckTokenResult.error) {
11333
+ if (_isFirebaseServerApp(this.app) && this.app.settings.appCheckToken) {
11334
+ return this.app.settings.appCheckToken;
11335
+ }
11336
+ const appCheckTokenResult = await this.appCheckServiceProvider
11337
+ .getImmediate({ optional: true })
11338
+ ?.getToken();
11339
+ if (appCheckTokenResult?.error) {
11027
11340
  // Context: appCheck.getToken() will never throw even if an error happened.
11028
11341
  // In the error case, a dummy token will be returned along with an error field describing
11029
11342
  // the error. In general, we shouldn't care about the error condition and just use
11030
11343
  // the token (actual or dummy) to send requests.
11031
11344
  _logWarn(`Error while retrieving App Check token: ${appCheckTokenResult.error}`);
11032
11345
  }
11033
- return appCheckTokenResult === null || appCheckTokenResult === void 0 ? void 0 : appCheckTokenResult.token;
11346
+ return appCheckTokenResult?.token;
11034
11347
  }
11035
11348
  }
11036
11349
  /**
@@ -11138,7 +11451,7 @@ function initializeAuth(app, deps) {
11138
11451
  if (provider.isInitialized()) {
11139
11452
  const auth = provider.getImmediate();
11140
11453
  const initialOptions = provider.getOptions();
11141
- if (deepEqual(initialOptions, deps !== null && deps !== void 0 ? deps : {})) {
11454
+ if (deepEqual(initialOptions, deps ?? {})) {
11142
11455
  return auth;
11143
11456
  }
11144
11457
  else {
@@ -11149,15 +11462,15 @@ function initializeAuth(app, deps) {
11149
11462
  return auth;
11150
11463
  }
11151
11464
  function _initializeAuthInstance(auth, deps) {
11152
- const persistence = (deps === null || deps === void 0 ? void 0 : deps.persistence) || [];
11465
+ const persistence = deps?.persistence || [];
11153
11466
  const hierarchy = (Array.isArray(persistence) ? persistence : [persistence]).map(_getInstance);
11154
- if (deps === null || deps === void 0 ? void 0 : deps.errorMap) {
11467
+ if (deps?.errorMap) {
11155
11468
  auth._updateErrorMap(deps.errorMap);
11156
11469
  }
11157
11470
  // This promise is intended to float; auth initialization happens in the
11158
11471
  // background, meanwhile the auth object may be used by the app.
11159
11472
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
11160
- auth._initializeWithPersistence(hierarchy, deps === null || deps === void 0 ? void 0 : deps.popupRedirectResolver);
11473
+ auth._initializeWithPersistence(hierarchy, deps?.popupRedirectResolver);
11161
11474
  }
11162
11475
 
11163
11476
  /**
@@ -11184,22 +11497,41 @@ function _initializeAuthInstance(auth, deps) {
11184
11497
  */
11185
11498
  function connectAuthEmulator(auth, url, options) {
11186
11499
  const authInternal = _castAuth(auth);
11187
- _assert(authInternal._canInitEmulator, authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
11188
11500
  _assert(/^https?:\/\//.test(url), authInternal, "invalid-emulator-scheme" /* AuthErrorCode.INVALID_EMULATOR_SCHEME */);
11189
- const disableWarnings = !!(void 0 );
11501
+ const disableWarnings = !!options?.disableWarnings;
11190
11502
  const protocol = extractProtocol(url);
11191
11503
  const { host, port } = extractHostAndPort(url);
11192
11504
  const portStr = port === null ? '' : `:${port}`;
11193
11505
  // Always replace path with "/" (even if input url had no path at all, or had a different one).
11194
- authInternal.config.emulator = { url: `${protocol}//${host}${portStr}/` };
11195
- authInternal.settings.appVerificationDisabledForTesting = true;
11196
- authInternal.emulatorConfig = Object.freeze({
11506
+ const emulator = { url: `${protocol}//${host}${portStr}/` };
11507
+ const emulatorConfig = Object.freeze({
11197
11508
  host,
11198
11509
  port,
11199
11510
  protocol: protocol.replace(':', ''),
11200
11511
  options: Object.freeze({ disableWarnings })
11201
11512
  });
11202
- {
11513
+ // There are a few scenarios to guard against if the Auth instance has already started:
11514
+ if (!authInternal._canInitEmulator) {
11515
+ // Applications may not initialize the emulator for the first time if Auth has already started
11516
+ // to make network requests.
11517
+ _assert(authInternal.config.emulator && authInternal.emulatorConfig, authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
11518
+ // Applications may not alter the configuration of the emulator (aka pass a different config)
11519
+ // once Auth has started to make network requests.
11520
+ _assert(deepEqual(emulator, authInternal.config.emulator) &&
11521
+ deepEqual(emulatorConfig, authInternal.emulatorConfig), authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
11522
+ // It's valid, however, to invoke connectAuthEmulator() after Auth has started making
11523
+ // connections, so long as the config matches the existing config. This results in a no-op.
11524
+ return;
11525
+ }
11526
+ authInternal.config.emulator = emulator;
11527
+ authInternal.emulatorConfig = emulatorConfig;
11528
+ authInternal.settings.appVerificationDisabledForTesting = true;
11529
+ // Workaround to get cookies in Firebase Studio
11530
+ if (isCloudWorkstation(host)) {
11531
+ void pingServer(`${protocol}//${host}${portStr}`);
11532
+ updateEmulatorBanner('Auth', true);
11533
+ }
11534
+ else {
11203
11535
  emitEmulatorWarning();
11204
11536
  }
11205
11537
  }
@@ -11438,7 +11770,7 @@ class OAuthCredential extends AuthCredential {
11438
11770
  */
11439
11771
  static fromJSON(json) {
11440
11772
  const obj = typeof json === 'string' ? JSON.parse(json) : json;
11441
- const { providerId, signInMethod } = obj, rest = __rest(obj, ["providerId", "signInMethod"]);
11773
+ const { providerId, signInMethod, ...rest } = obj;
11442
11774
  if (!providerId || !signInMethod) {
11443
11775
  return null;
11444
11776
  }
@@ -11715,7 +12047,7 @@ class FacebookAuthProvider extends BaseOAuthProvider {
11715
12047
  try {
11716
12048
  return FacebookAuthProvider.credential(tokenResponse.oauthAccessToken);
11717
12049
  }
11718
- catch (_a) {
12050
+ catch {
11719
12051
  return null;
11720
12052
  }
11721
12053
  }
@@ -11837,7 +12169,7 @@ class GoogleAuthProvider extends BaseOAuthProvider {
11837
12169
  try {
11838
12170
  return GoogleAuthProvider.credential(oauthIdToken, oauthAccessToken);
11839
12171
  }
11840
- catch (_a) {
12172
+ catch {
11841
12173
  return null;
11842
12174
  }
11843
12175
  }
@@ -11948,7 +12280,7 @@ class GithubAuthProvider extends BaseOAuthProvider {
11948
12280
  try {
11949
12281
  return GithubAuthProvider.credential(tokenResponse.oauthAccessToken);
11950
12282
  }
11951
- catch (_a) {
12283
+ catch {
11952
12284
  return null;
11953
12285
  }
11954
12286
  }
@@ -12059,7 +12391,7 @@ class TwitterAuthProvider extends BaseOAuthProvider {
12059
12391
  try {
12060
12392
  return TwitterAuthProvider.credential(oauthAccessToken, oauthTokenSecret);
12061
12393
  }
12062
- catch (_a) {
12394
+ catch {
12063
12395
  return null;
12064
12396
  }
12065
12397
  }
@@ -12142,7 +12474,6 @@ function providerIdForResponse(response) {
12142
12474
  */
12143
12475
  class MultiFactorError extends FirebaseError {
12144
12476
  constructor(auth, error, operationType, user) {
12145
- var _a;
12146
12477
  super(error.code, error.message);
12147
12478
  this.operationType = operationType;
12148
12479
  this.user = user;
@@ -12150,7 +12481,7 @@ class MultiFactorError extends FirebaseError {
12150
12481
  Object.setPrototypeOf(this, MultiFactorError.prototype);
12151
12482
  this.customData = {
12152
12483
  appName: auth.name,
12153
- tenantId: (_a = auth.tenantId) !== null && _a !== void 0 ? _a : undefined,
12484
+ tenantId: auth.tenantId ?? undefined,
12154
12485
  _serverResponse: error.customData._serverResponse,
12155
12486
  operationType
12156
12487
  };
@@ -12208,7 +12539,7 @@ async function _reauthenticate(user, credential, bypassAuthState = false) {
12208
12539
  }
12209
12540
  catch (e) {
12210
12541
  // Convert user deleted error into user mismatch
12211
- if ((e === null || e === void 0 ? void 0 : e.code) === `auth/${"user-not-found" /* AuthErrorCode.USER_DELETED */}`) {
12542
+ if (e?.code === `auth/${"user-not-found" /* AuthErrorCode.USER_DELETED */}`) {
12212
12543
  _fail(auth, "user-mismatch" /* AuthErrorCode.USER_MISMATCH */);
12213
12544
  }
12214
12545
  throw e;
@@ -12398,7 +12729,7 @@ class BrowserPersistenceClass {
12398
12729
  this.storage.removeItem(STORAGE_AVAILABLE_KEY);
12399
12730
  return Promise.resolve(true);
12400
12731
  }
12401
- catch (_a) {
12732
+ catch {
12402
12733
  return Promise.resolve(false);
12403
12734
  }
12404
12735
  }
@@ -12739,7 +13070,7 @@ class Receiver {
12739
13070
  const messageEvent = event;
12740
13071
  const { eventId, eventType, data } = messageEvent.data;
12741
13072
  const handlers = this.handlersMap[eventType];
12742
- if (!(handlers === null || handlers === void 0 ? void 0 : handlers.size)) {
13073
+ if (!handlers?.size) {
12743
13074
  return;
12744
13075
  }
12745
13076
  messageEvent.ports[0].postMessage({
@@ -12975,20 +13306,19 @@ function _isWorker() {
12975
13306
  typeof _window()['importScripts'] === 'function');
12976
13307
  }
12977
13308
  async function _getActiveServiceWorker() {
12978
- if (!(navigator === null || navigator === void 0 ? void 0 : navigator.serviceWorker)) {
13309
+ if (!navigator?.serviceWorker) {
12979
13310
  return null;
12980
13311
  }
12981
13312
  try {
12982
13313
  const registration = await navigator.serviceWorker.ready;
12983
13314
  return registration.active;
12984
13315
  }
12985
- catch (_a) {
13316
+ catch {
12986
13317
  return null;
12987
13318
  }
12988
13319
  }
12989
13320
  function _getServiceWorkerController() {
12990
- var _a;
12991
- return ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.serviceWorker) === null || _a === void 0 ? void 0 : _a.controller) || null;
13321
+ return navigator?.serviceWorker?.controller || null;
12992
13322
  }
12993
13323
  function _getWorkerGlobalScope() {
12994
13324
  return _isWorker() ? self : null;
@@ -13171,7 +13501,6 @@ class IndexedDBLocalPersistence {
13171
13501
  * may not resolve.
13172
13502
  */
13173
13503
  async initializeSender() {
13174
- var _a, _b;
13175
13504
  // Check to see if there's an active service worker.
13176
13505
  this.activeServiceWorker = await _getActiveServiceWorker();
13177
13506
  if (!this.activeServiceWorker) {
@@ -13183,8 +13512,8 @@ class IndexedDBLocalPersistence {
13183
13512
  if (!results) {
13184
13513
  return;
13185
13514
  }
13186
- if (((_a = results[0]) === null || _a === void 0 ? void 0 : _a.fulfilled) &&
13187
- ((_b = results[0]) === null || _b === void 0 ? void 0 : _b.value.includes("keyChanged" /* _EventType.KEY_CHANGED */))) {
13515
+ if (results[0]?.fulfilled &&
13516
+ results[0]?.value.includes("keyChanged" /* _EventType.KEY_CHANGED */)) {
13188
13517
  this.serviceWorkerReceiverAvailable = true;
13189
13518
  }
13190
13519
  }
@@ -13210,7 +13539,7 @@ class IndexedDBLocalPersistence {
13210
13539
  ? 800 /* _TimeoutDuration.LONG_ACK */
13211
13540
  : 50 /* _TimeoutDuration.ACK */);
13212
13541
  }
13213
- catch (_a) {
13542
+ catch {
13214
13543
  // This is a best effort approach. Ignore errors.
13215
13544
  }
13216
13545
  }
@@ -13224,7 +13553,7 @@ class IndexedDBLocalPersistence {
13224
13553
  await _deleteObject(db, STORAGE_AVAILABLE_KEY);
13225
13554
  return true;
13226
13555
  }
13227
- catch (_a) { }
13556
+ catch { }
13228
13557
  return false;
13229
13558
  }
13230
13559
  async _withPendingWrite(write) {
@@ -13594,8 +13923,7 @@ class PopupOperation extends AbstractPopupRedirectOperation {
13594
13923
  this.pollUserCancellation();
13595
13924
  }
13596
13925
  get eventId() {
13597
- var _a;
13598
- return ((_a = this.authWindow) === null || _a === void 0 ? void 0 : _a.associatedEvent) || null;
13926
+ return this.authWindow?.associatedEvent || null;
13599
13927
  }
13600
13928
  cancel() {
13601
13929
  this.reject(_createError(this.auth, "cancelled-popup-request" /* AuthErrorCode.EXPIRED_POPUP_REQUEST */));
@@ -13613,8 +13941,7 @@ class PopupOperation extends AbstractPopupRedirectOperation {
13613
13941
  }
13614
13942
  pollUserCancellation() {
13615
13943
  const poll = () => {
13616
- var _a, _b;
13617
- if ((_b = (_a = this.authWindow) === null || _a === void 0 ? void 0 : _a.window) === null || _b === void 0 ? void 0 : _b.closed) {
13944
+ if (this.authWindow?.window?.closed) {
13618
13945
  // Make sure that there is sufficient time for whatever action to
13619
13946
  // complete. The window could have closed but the sign in network
13620
13947
  // call could still be in flight. This is specifically true for
@@ -13814,9 +14141,8 @@ class AuthEventManager {
13814
14141
  return handled;
13815
14142
  }
13816
14143
  sendToConsumer(event, consumer) {
13817
- var _a;
13818
14144
  if (event.error && !isNullRedirectEvent(event)) {
13819
- const code = ((_a = event.error.code) === null || _a === void 0 ? void 0 : _a.split('auth/')[1]) ||
14145
+ const code = event.error.code?.split('auth/')[1] ||
13820
14146
  "internal-error" /* AuthErrorCode.INTERNAL_ERROR */;
13821
14147
  consumer.onError(_createError(this.auth, code));
13822
14148
  }
@@ -13846,7 +14172,7 @@ function eventUid(e) {
13846
14172
  }
13847
14173
  function isNullRedirectEvent({ type, error }) {
13848
14174
  return (type === "unknown" /* AuthEventType.UNKNOWN */ &&
13849
- (error === null || error === void 0 ? void 0 : error.code) === `auth/${"no-auth-event" /* AuthErrorCode.NO_AUTH_EVENT */}`);
14175
+ error?.code === `auth/${"no-auth-event" /* AuthErrorCode.NO_AUTH_EVENT */}`);
13850
14176
  }
13851
14177
  function isRedirectEvent(event) {
13852
14178
  switch (event.type) {
@@ -13911,7 +14237,7 @@ async function _validateOrigin(auth) {
13911
14237
  return;
13912
14238
  }
13913
14239
  }
13914
- catch (_a) {
14240
+ catch {
13915
14241
  // Do nothing if there's a URL error; just continue searching
13916
14242
  }
13917
14243
  }
@@ -13974,7 +14300,7 @@ function resetUnloadedGapiModules() {
13974
14300
  // Get gapix.beacon context.
13975
14301
  const beacon = _window().___jsl;
13976
14302
  // Get current hint.
13977
- if (beacon === null || beacon === void 0 ? void 0 : beacon.H) {
14303
+ if (beacon?.H) {
13978
14304
  // Get gapi hint.
13979
14305
  for (const hint of Object.keys(beacon.H)) {
13980
14306
  // Requested modules.
@@ -13995,7 +14321,6 @@ function resetUnloadedGapiModules() {
13995
14321
  }
13996
14322
  function loadGapi(auth) {
13997
14323
  return new Promise((resolve, reject) => {
13998
- var _a, _b, _c;
13999
14324
  // Function to run when gapi.load is ready.
14000
14325
  function loadGapiIframe() {
14001
14326
  // The developer may have tried to previously run gapi.load and failed.
@@ -14018,11 +14343,11 @@ function loadGapi(auth) {
14018
14343
  timeout: NETWORK_TIMEOUT.get()
14019
14344
  });
14020
14345
  }
14021
- if ((_b = (_a = _window().gapi) === null || _a === void 0 ? void 0 : _a.iframes) === null || _b === void 0 ? void 0 : _b.Iframe) {
14346
+ if (_window().gapi?.iframes?.Iframe) {
14022
14347
  // If gapi.iframes.Iframe available, resolve.
14023
14348
  resolve(gapi.iframes.getContext());
14024
14349
  }
14025
- else if (!!((_c = _window().gapi) === null || _c === void 0 ? void 0 : _c.load)) {
14350
+ else if (!!_window().gapi?.load) {
14026
14351
  // Gapi loader ready, load gapi.iframes.
14027
14352
  loadGapiIframe();
14028
14353
  }
@@ -14195,8 +14520,13 @@ function _open(auth, url, name, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT)
14195
14520
  const top = Math.max((window.screen.availHeight - height) / 2, 0).toString();
14196
14521
  const left = Math.max((window.screen.availWidth - width) / 2, 0).toString();
14197
14522
  let target = '';
14198
- const options = Object.assign(Object.assign({}, BASE_POPUP_OPTIONS), { width: width.toString(), height: height.toString(), top,
14199
- left });
14523
+ const options = {
14524
+ ...BASE_POPUP_OPTIONS,
14525
+ width: width.toString(),
14526
+ height: height.toString(),
14527
+ top,
14528
+ left
14529
+ };
14200
14530
  // Chrome iOS 7 and 8 is returning an undefined popup win when target is
14201
14531
  // specified, even though the popup is not necessarily blocked.
14202
14532
  const ua = getUA().toLowerCase();
@@ -14356,8 +14686,7 @@ class BrowserPopupRedirectResolver {
14356
14686
  // Wrapping in async even though we don't await anywhere in order
14357
14687
  // to make sure errors are raised as promise rejections
14358
14688
  async _openPopup(auth, provider, authType, eventId) {
14359
- var _a;
14360
- debugAssert((_a = this.eventManagers[auth._key()]) === null || _a === void 0 ? void 0 : _a.manager, '_initialize() not called before _openPopup()');
14689
+ debugAssert(this.eventManagers[auth._key()]?.manager, '_initialize() not called before _openPopup()');
14361
14690
  const url = await _getRedirectUrl(auth, provider, authType, _getCurrentUrl(), eventId);
14362
14691
  return _open(auth, url, _generateEventId());
14363
14692
  }
@@ -14392,7 +14721,7 @@ class BrowserPopupRedirectResolver {
14392
14721
  const iframe = await _openIframe(auth);
14393
14722
  const manager = new AuthEventManager(auth);
14394
14723
  iframe.register('authEvent', (iframeEvent) => {
14395
- _assert(iframeEvent === null || iframeEvent === void 0 ? void 0 : iframeEvent.authEvent, auth, "invalid-auth-event" /* AuthErrorCode.INVALID_AUTH_EVENT */);
14724
+ _assert(iframeEvent?.authEvent, auth, "invalid-auth-event" /* AuthErrorCode.INVALID_AUTH_EVENT */);
14396
14725
  // TODO: Consider splitting redirect and popup events earlier on
14397
14726
  const handled = manager.onEvent(iframeEvent.authEvent);
14398
14727
  return { status: handled ? "ACK" /* GapiOutcome.ACK */ : "ERROR" /* GapiOutcome.ERROR */ };
@@ -14404,8 +14733,7 @@ class BrowserPopupRedirectResolver {
14404
14733
  _isIframeWebStorageSupported(auth, cb) {
14405
14734
  const iframe = this.iframes[auth._key()];
14406
14735
  iframe.send(WEB_STORAGE_SUPPORT_KEY, { type: WEB_STORAGE_SUPPORT_KEY }, result => {
14407
- var _a;
14408
- const isSupported = (_a = result === null || result === void 0 ? void 0 : result[0]) === null || _a === void 0 ? void 0 : _a[WEB_STORAGE_SUPPORT_KEY];
14736
+ const isSupported = result?.[0]?.[WEB_STORAGE_SUPPORT_KEY];
14409
14737
  if (isSupported !== undefined) {
14410
14738
  cb(!!isSupported);
14411
14739
  }
@@ -14436,7 +14764,7 @@ class BrowserPopupRedirectResolver {
14436
14764
  const browserPopupRedirectResolver = BrowserPopupRedirectResolver;
14437
14765
 
14438
14766
  var name$1 = "@firebase/auth";
14439
- var version$1 = "1.8.1";
14767
+ var version$1 = "1.11.1";
14440
14768
 
14441
14769
  /**
14442
14770
  * @license
@@ -14460,9 +14788,8 @@ class AuthInterop {
14460
14788
  this.internalListeners = new Map();
14461
14789
  }
14462
14790
  getUid() {
14463
- var _a;
14464
14791
  this.assertAuthConfigured();
14465
- return ((_a = this.auth.currentUser) === null || _a === void 0 ? void 0 : _a.uid) || null;
14792
+ return this.auth.currentUser?.uid || null;
14466
14793
  }
14467
14794
  async getToken(forceRefresh) {
14468
14795
  this.assertAuthConfigured();
@@ -14479,7 +14806,7 @@ class AuthInterop {
14479
14806
  return;
14480
14807
  }
14481
14808
  const unsubscribe = this.auth.onIdTokenChanged(user => {
14482
- listener((user === null || user === void 0 ? void 0 : user.stsTokenManager.accessToken) || null);
14809
+ listener(user?.stsTokenManager.accessToken || null);
14483
14810
  });
14484
14811
  this.internalListeners.set(listener, unsubscribe);
14485
14812
  this.updateProactiveRefresh();
@@ -14578,8 +14905,8 @@ function registerAuth(clientPlatform) {
14578
14905
  return (auth => new AuthInterop(auth))(auth);
14579
14906
  }, "PRIVATE" /* ComponentType.PRIVATE */).setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */));
14580
14907
  registerVersion(name$1, version$1, getVersionForPlatform(clientPlatform));
14581
- // BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
14582
- registerVersion(name$1, version$1, 'esm2017');
14908
+ // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
14909
+ registerVersion(name$1, version$1, 'esm2020');
14583
14910
  }
14584
14911
 
14585
14912
  /**
@@ -14609,7 +14936,7 @@ const mintCookieFactory = (url) => async (user) => {
14609
14936
  return;
14610
14937
  }
14611
14938
  // Specifically trip null => undefined when logged out, to delete any existing cookie
14612
- const idToken = idTokenResult === null || idTokenResult === void 0 ? void 0 : idTokenResult.token;
14939
+ const idToken = idTokenResult?.token;
14613
14940
  if (lastPostedIdToken === idToken) {
14614
14941
  return;
14615
14942
  }
@@ -14664,8 +14991,7 @@ function getAuth(app = getApp()) {
14664
14991
  return auth;
14665
14992
  }
14666
14993
  function getScriptParentElement() {
14667
- var _a, _b;
14668
- return (_b = (_a = document.getElementsByTagName('head')) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : document;
14994
+ return document.getElementsByTagName('head')?.[0] ?? document;
14669
14995
  }
14670
14996
  _setExternalJSProvider({
14671
14997
  loadJS(url) {
@@ -14691,7 +15017,7 @@ _setExternalJSProvider({
14691
15017
  registerAuth("Browser" /* ClientPlatform.BROWSER */);
14692
15018
 
14693
15019
  const name = "@firebase/database";
14694
- const version = "1.0.10";
15020
+ const version = "1.1.0";
14695
15021
 
14696
15022
  /**
14697
15023
  * @license
@@ -15388,15 +15714,24 @@ const setTimeoutNonBlocking = function (fn, time) {
15388
15714
  * Abstraction around AppCheck's token fetching capabilities.
15389
15715
  */
15390
15716
  class AppCheckTokenProvider {
15391
- constructor(appName_, appCheckProvider) {
15392
- this.appName_ = appName_;
15717
+ constructor(app, appCheckProvider) {
15393
15718
  this.appCheckProvider = appCheckProvider;
15394
- this.appCheck = appCheckProvider === null || appCheckProvider === void 0 ? void 0 : appCheckProvider.getImmediate({ optional: true });
15719
+ this.appName = app.name;
15720
+ if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
15721
+ this.serverAppAppCheckToken = app.settings.appCheckToken;
15722
+ }
15723
+ this.appCheck = appCheckProvider?.getImmediate({ optional: true });
15395
15724
  if (!this.appCheck) {
15396
- appCheckProvider === null || appCheckProvider === void 0 ? void 0 : appCheckProvider.get().then(appCheck => (this.appCheck = appCheck));
15725
+ appCheckProvider?.get().then(appCheck => (this.appCheck = appCheck));
15397
15726
  }
15398
15727
  }
15399
15728
  getToken(forceRefresh) {
15729
+ if (this.serverAppAppCheckToken) {
15730
+ if (forceRefresh) {
15731
+ throw new Error('Attempted reuse of `FirebaseServerApp.appCheckToken` after previous usage failed.');
15732
+ }
15733
+ return Promise.resolve({ token: this.serverAppAppCheckToken });
15734
+ }
15400
15735
  if (!this.appCheck) {
15401
15736
  return new Promise((resolve, reject) => {
15402
15737
  // Support delayed initialization of FirebaseAppCheck. This allows our
@@ -15416,11 +15751,12 @@ class AppCheckTokenProvider {
15416
15751
  return this.appCheck.getToken(forceRefresh);
15417
15752
  }
15418
15753
  addTokenChangeListener(listener) {
15419
- var _a;
15420
- (_a = this.appCheckProvider) === null || _a === void 0 ? void 0 : _a.get().then(appCheck => appCheck.addTokenListener(listener));
15754
+ this.appCheckProvider
15755
+ ?.get()
15756
+ .then(appCheck => appCheck.addTokenListener(listener));
15421
15757
  }
15422
15758
  notifyForInvalidToken() {
15423
- warn(`Provided AppCheck credentials for the app named "${this.appName_}" ` +
15759
+ warn(`Provided AppCheck credentials for the app named "${this.appName}" ` +
15424
15760
  'are invalid. This usually indicates your app was not initialized correctly.');
15425
15761
  }
15426
15762
  }
@@ -15606,7 +15942,7 @@ class RepoInfo {
15606
15942
  * @param nodeAdmin - Whether this instance uses Admin SDK credentials
15607
15943
  * @param persistenceKey - Override the default session persistence storage key
15608
15944
  */
15609
- constructor(host, secure, namespace, webSocketOnly, nodeAdmin = false, persistenceKey = '', includeNamespaceInQueryParams = false, isUsingEmulator = false) {
15945
+ constructor(host, secure, namespace, webSocketOnly, nodeAdmin = false, persistenceKey = '', includeNamespaceInQueryParams = false, isUsingEmulator = false, emulatorOptions = null) {
15610
15946
  this.secure = secure;
15611
15947
  this.namespace = namespace;
15612
15948
  this.webSocketOnly = webSocketOnly;
@@ -15614,6 +15950,7 @@ class RepoInfo {
15614
15950
  this.persistenceKey = persistenceKey;
15615
15951
  this.includeNamespaceInQueryParams = includeNamespaceInQueryParams;
15616
15952
  this.isUsingEmulator = isUsingEmulator;
15953
+ this.emulatorOptions = emulatorOptions;
15617
15954
  this._host = host.toLowerCase();
15618
15955
  this._domain = this._host.substr(this._host.indexOf('.') + 1);
15619
15956
  this.internalHost =
@@ -17100,7 +17437,9 @@ class Connection {
17100
17437
  if (MESSAGE_DATA in controlData) {
17101
17438
  const payload = controlData[MESSAGE_DATA];
17102
17439
  if (cmd === SERVER_HELLO) {
17103
- const handshakePayload = Object.assign({}, payload);
17440
+ const handshakePayload = {
17441
+ ...payload
17442
+ };
17104
17443
  if (this.repoInfo_.isUsingEmulator) {
17105
17444
  // Upon connecting, the emulator will pass the hostname that it's aware of, but we prefer the user's set hostname via `connectDatabaseEmulator` over what the emulator passes.
17106
17445
  handshakePayload.h = this.repoInfo_.host;
@@ -18366,7 +18705,7 @@ class PersistentConnection extends ServerActions {
18366
18705
  }
18367
18706
  this.lastConnectionEstablishedTime_ = null;
18368
18707
  }
18369
- const timeSinceLastConnectAttempt = new Date().getTime() - this.lastConnectionAttemptTime_;
18708
+ const timeSinceLastConnectAttempt = Math.max(0, new Date().getTime() - this.lastConnectionAttemptTime_);
18370
18709
  let reconnectDelay = Math.max(0, this.reconnectDelay_ - timeSinceLastConnectAttempt);
18371
18710
  reconnectDelay = Math.random() * reconnectDelay;
18372
18711
  this.log_('Trying to reconnect in ' + reconnectDelay + 'ms');
@@ -19908,9 +20247,9 @@ class IndexMap {
19908
20247
  newIndex = fallbackObject;
19909
20248
  }
19910
20249
  const indexName = indexDefinition.toString();
19911
- const newIndexSet = Object.assign({}, this.indexSet_);
20250
+ const newIndexSet = { ...this.indexSet_ };
19912
20251
  newIndexSet[indexName] = indexDefinition;
19913
- const newIndexes = Object.assign({}, this.indexes_);
20252
+ const newIndexes = { ...this.indexes_ };
19914
20253
  newIndexes[indexName] = newIndex;
19915
20254
  return new IndexMap(newIndexes, newIndexSet);
19916
20255
  }
@@ -21639,7 +21978,7 @@ class StatsListener {
21639
21978
  }
21640
21979
  get() {
21641
21980
  const newStats = this.collection_.get();
21642
- const delta = Object.assign({}, newStats);
21981
+ const delta = { ...newStats };
21643
21982
  if (this.last_) {
21644
21983
  each(this.last_, (stat, value) => {
21645
21984
  delta[stat] = delta[stat] - value;
@@ -27123,10 +27462,13 @@ let useRestClient = false;
27123
27462
  /**
27124
27463
  * Update an existing `Repo` in place to point to a new host/port.
27125
27464
  */
27126
- function repoManagerApplyEmulatorSettings(repo, host, port, tokenProvider) {
27127
- repo.repoInfo_ = new RepoInfo(`${host}:${port}`,
27128
- /* secure= */ false, repo.repoInfo_.namespace, repo.repoInfo_.webSocketOnly, repo.repoInfo_.nodeAdmin, repo.repoInfo_.persistenceKey, repo.repoInfo_.includeNamespaceInQueryParams,
27129
- /*isUsingEmulator=*/ true);
27465
+ function repoManagerApplyEmulatorSettings(repo, hostAndPort, emulatorOptions, tokenProvider) {
27466
+ const portIndex = hostAndPort.lastIndexOf(':');
27467
+ const host = hostAndPort.substring(0, portIndex);
27468
+ const useSsl = isCloudWorkstation(host);
27469
+ repo.repoInfo_ = new RepoInfo(hostAndPort,
27470
+ /* secure= */ useSsl, repo.repoInfo_.namespace, repo.repoInfo_.webSocketOnly, repo.repoInfo_.nodeAdmin, repo.repoInfo_.persistenceKey, repo.repoInfo_.includeNamespaceInQueryParams,
27471
+ /*isUsingEmulator=*/ true, emulatorOptions);
27130
27472
  if (tokenProvider) {
27131
27473
  repo.authTokenProvider_ = tokenProvider;
27132
27474
  }
@@ -27165,7 +27507,7 @@ function repoManagerDatabaseFromApp(app, authProvider, appCheckProvider, url, no
27165
27507
  fatal('Database URL must point to the root of a Firebase Database ' +
27166
27508
  '(not including a child path).');
27167
27509
  }
27168
- const repo = repoManagerCreateRepo(repoInfo, app, authTokenProvider, new AppCheckTokenProvider(app.name, appCheckProvider));
27510
+ const repo = repoManagerCreateRepo(repoInfo, app, authTokenProvider, new AppCheckTokenProvider(app, appCheckProvider));
27169
27511
  return new Database(repo, app);
27170
27512
  }
27171
27513
  /**
@@ -27281,10 +27623,17 @@ function getDatabase(app = getApp(), url) {
27281
27623
  function connectDatabaseEmulator(db, host, port, options = {}) {
27282
27624
  db = getModularInstance(db);
27283
27625
  db._checkNotDeleted('useEmulator');
27626
+ const hostAndPort = `${host}:${port}`;
27627
+ const repo = db._repoInternal;
27284
27628
  if (db._instanceStarted) {
27285
- fatal('Cannot call useEmulator() after instance has already been initialized.');
27629
+ // If the instance has already been started, then silenty fail if this function is called again
27630
+ // with the same parameters. If the parameters differ then assert.
27631
+ if (hostAndPort === db._repoInternal.repoInfo_.host &&
27632
+ deepEqual(options, repo.repoInfo_.emulatorOptions)) {
27633
+ return;
27634
+ }
27635
+ fatal('connectDatabaseEmulator() cannot initialize or alter the emulator configuration after the database instance has started.');
27286
27636
  }
27287
- const repo = db._repoInternal;
27288
27637
  let tokenProvider = undefined;
27289
27638
  if (repo.repoInfo_.nodeAdmin) {
27290
27639
  if (options.mockUserToken) {
@@ -27298,8 +27647,13 @@ function connectDatabaseEmulator(db, host, port, options = {}) {
27298
27647
  : createMockUserToken(options.mockUserToken, db.app.options.projectId);
27299
27648
  tokenProvider = new EmulatorTokenProvider(token);
27300
27649
  }
27650
+ // Workaround to get cookies in Firebase Studio
27651
+ if (isCloudWorkstation(host)) {
27652
+ void pingServer(host);
27653
+ updateEmulatorBanner('Database', true);
27654
+ }
27301
27655
  // Modify the repo to apply emulator settings
27302
- repoManagerApplyEmulatorSettings(repo, host, port, tokenProvider);
27656
+ repoManagerApplyEmulatorSettings(repo, hostAndPort, options, tokenProvider);
27303
27657
  }
27304
27658
 
27305
27659
  /**
@@ -27327,8 +27681,8 @@ function registerDatabase(variant) {
27327
27681
  return repoManagerDatabaseFromApp(app, authProvider, appCheckProvider, url);
27328
27682
  }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
27329
27683
  registerVersion(name, version, variant);
27330
- // BUILD_TARGET will be replaced by values like esm2017, cjs2017, etc during the compilation
27331
- registerVersion(name, version, 'esm2017');
27684
+ // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
27685
+ registerVersion(name, version, 'esm2020');
27332
27686
  }
27333
27687
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
27334
27688
  PersistentConnection.prototype.simpleListen = function (pathString, onComplete) {
@@ -27370,12 +27724,11 @@ let databaseReadyQueue = [];
27370
27724
  * Execute all queued callbacks now that database is ready
27371
27725
  */
27372
27726
  function flushDatabaseReadyQueue(state) {
27373
- var _a;
27374
27727
  const queue = [...databaseReadyQueue];
27375
27728
  databaseReadyQueue = []; // Clear queue before executing to avoid re-queuing
27376
27729
  queue.forEach(callback => callback());
27377
27730
  // Also notify the global callback if set
27378
- (_a = state.onFirebaseReady) === null || _a === void 0 ? void 0 : _a.call(state);
27731
+ state.onFirebaseReady?.();
27379
27732
  }
27380
27733
  function initFirebase(state) {
27381
27734
  try {
@@ -27439,7 +27792,7 @@ function isFirebaseAuthenticated(state) {
27439
27792
  async function loginFirebase(state, firebaseToken, onLoginFunc) {
27440
27793
  if (!app && !initFirebase(state))
27441
27794
  return;
27442
- const { userId, locale, projectId, loginMethod } = state;
27795
+ const { netskraflUserId, locale, projectId, loginMethod } = state;
27443
27796
  auth = getAuth(app);
27444
27797
  if (!auth) {
27445
27798
  console.error("Failed to initialize Firebase Auth");
@@ -27454,14 +27807,14 @@ async function loginFirebase(state, firebaseToken, onLoginFunc) {
27454
27807
  logEvent("sign_up", {
27455
27808
  locale,
27456
27809
  method: loginMethod,
27457
- userid: userId
27810
+ userid: netskraflUserId
27458
27811
  });
27459
27812
  }
27460
27813
  // And always log a login event
27461
27814
  logEvent("login", {
27462
27815
  locale,
27463
27816
  method: loginMethod,
27464
- userid: userId
27817
+ userid: netskraflUserId
27465
27818
  });
27466
27819
  }
27467
27820
  });
@@ -27480,7 +27833,7 @@ async function loginFirebase(state, firebaseToken, onLoginFunc) {
27480
27833
  if (!analytics) {
27481
27834
  console.error("Failed to initialize Firebase Analytics");
27482
27835
  }
27483
- initPresence(projectId, userId, locale);
27836
+ initPresence(projectId, netskraflUserId, locale);
27484
27837
  }
27485
27838
  function initPresence(projectId, userId, locale) {
27486
27839
  // Ensure that this user connection is recorded in Firebase
@@ -27643,7 +27996,6 @@ const checkCircuitBreakerReset = () => {
27643
27996
  * - Request deduplication: Only one login attempt at a time
27644
27997
  */
27645
27998
  const ensureAuthenticated = async (state) => {
27646
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
27647
27999
  // Check and potentially reset circuit breaker
27648
28000
  checkCircuitBreakerReset();
27649
28001
  // Circuit breaker check - fail fast if too many recent failures
@@ -27708,22 +28060,22 @@ const ensureAuthenticated = async (state) => {
27708
28060
  // Success! Update global state and persist authentication
27709
28061
  // ========================================================================
27710
28062
  // Update the user's ID to the internal one used by the backend and Firebase
27711
- state.userId = result.user_id || state.userId;
27712
- state.account = result.account || state.userId;
28063
+ state.netskraflUserId = result.user_id || state.netskraflUserId;
28064
+ state.account = result.account || state.netskraflUserId;
27713
28065
  state.userNick = result.nickname || state.userNick;
27714
28066
  state.firebaseApiKey = result.firebase_api_key || state.firebaseApiKey;
27715
28067
  // Load state flags and preferences
27716
- state.beginner = (_b = (_a = result.prefs) === null || _a === void 0 ? void 0 : _a.beginner) !== null && _b !== void 0 ? _b : true;
27717
- state.fairPlay = (_d = (_c = result.prefs) === null || _c === void 0 ? void 0 : _c.fairplay) !== null && _d !== void 0 ? _d : false;
27718
- state.ready = (_f = (_e = result.prefs) === null || _e === void 0 ? void 0 : _e.ready) !== null && _f !== void 0 ? _f : true;
27719
- state.readyTimed = (_h = (_g = result.prefs) === null || _g === void 0 ? void 0 : _g.ready_timed) !== null && _h !== void 0 ? _h : true;
27720
- state.audio = (_k = (_j = result.prefs) === null || _j === void 0 ? void 0 : _j.audio) !== null && _k !== void 0 ? _k : false;
27721
- state.fanfare = (_m = (_l = result.prefs) === null || _l === void 0 ? void 0 : _l.fanfare) !== null && _m !== void 0 ? _m : false;
27722
- state.hasPaid = (_p = (_o = result.prefs) === null || _o === void 0 ? void 0 : _o.haspaid) !== null && _p !== void 0 ? _p : false;
28068
+ state.beginner = result.prefs?.beginner ?? true;
28069
+ state.fairPlay = result.prefs?.fairplay ?? false;
28070
+ state.ready = result.prefs?.ready ?? true;
28071
+ state.readyTimed = result.prefs?.ready_timed ?? true;
28072
+ state.audio = result.prefs?.audio ?? false;
28073
+ state.fanfare = result.prefs?.fanfare ?? false;
28074
+ state.hasPaid = result.prefs?.haspaid ?? false;
27723
28075
  // Save authentication settings to sessionStorage
27724
28076
  saveAuthSettings({
27725
28077
  userEmail: state.userEmail,
27726
- userId: state.userId,
28078
+ netskraflUserId: state.netskraflUserId,
27727
28079
  userNick: state.userNick,
27728
28080
  firebaseApiKey: state.firebaseApiKey,
27729
28081
  beginner: state.beginner,
@@ -27821,7 +28173,7 @@ const requestMoves = (state, options) => {
27821
28173
  const headers = {
27822
28174
  "Content-Type": "application/json; charset=UTF-8",
27823
28175
  Authorization: `Bearer ${state.movesAccessKey}`,
27824
- ...options === null || options === void 0 ? void 0 : options.headers,
28176
+ ...options?.headers,
27825
28177
  };
27826
28178
  return m.request({
27827
28179
  withCredentials: false,
@@ -28057,10 +28409,9 @@ const OnlinePresence = {
28057
28409
  const UserId = {
28058
28410
  // User identifier at top right, opens user preferences
28059
28411
  view: (vnode) => {
28060
- var _a;
28061
28412
  const view = vnode.attrs.view;
28062
28413
  const model = view.model;
28063
- if (!((_a = model.state) === null || _a === void 0 ? void 0 : _a.userId))
28414
+ if (!model.state?.netskraflUserId)
28064
28415
  // Don't show the button if there is no logged-in user
28065
28416
  return "";
28066
28417
  return m(".userid", {
@@ -28073,13 +28424,25 @@ const UserId = {
28073
28424
  }, [glyph("address-book"), nbsp(), model.state.userNick]);
28074
28425
  }
28075
28426
  };
28427
+ const ProModeIndicator = {
28428
+ // Display a Pro Mode (Keppnishamur) indicator using a lightbulb glyph
28429
+ // If header is true, always show grayed with title
28430
+ // Otherwise, the lightbulb is grayed if manual mode is not enabled
28431
+ view: (vnode) => {
28432
+ const { manual, header } = vnode.attrs;
28433
+ const title = ts("Keppnishamur");
28434
+ // Regular case: show grayed if not manual mode
28435
+ const highlight = (manual === true) && !header;
28436
+ const glyphAttrs = (highlight || header) ? { title } : {};
28437
+ return m("span.list-manual", glyphAttrs, glyph("lightbulb", undefined, !highlight));
28438
+ }
28439
+ };
28076
28440
  const MultiSelection = (initialVnode) => {
28077
28441
  // A multiple-selection div where users can click on child nodes
28078
28442
  // to select them, giving them an addional selection class,
28079
28443
  // typically .selected
28080
- var _a, _b;
28081
- let sel = (_a = initialVnode.attrs.initialSelection) !== null && _a !== void 0 ? _a : 0;
28082
- const defaultClass = (_b = initialVnode.attrs.defaultClass) !== null && _b !== void 0 ? _b : "";
28444
+ let sel = initialVnode.attrs.initialSelection ?? 0;
28445
+ const defaultClass = initialVnode.attrs.defaultClass ?? "";
28083
28446
  const selectedClass = initialVnode.attrs.selectedClass || "selected";
28084
28447
  return {
28085
28448
  view: (vnode) => {
@@ -28126,10 +28489,9 @@ const HeaderLogo = {
28126
28489
  };
28127
28490
  const Toggler = () => {
28128
28491
  function doToggle(togglerId, funcToggle) {
28129
- var _a, _b;
28130
28492
  // Perform the toggling, on a mouse click or keyboard input (space bar)
28131
- const cls1 = (_a = document.querySelector("#" + togglerId + " #opt1")) === null || _a === void 0 ? void 0 : _a.classList;
28132
- const cls2 = (_b = document.querySelector("#" + togglerId + " #opt2")) === null || _b === void 0 ? void 0 : _b.classList;
28493
+ const cls1 = document.querySelector("#" + togglerId + " #opt1")?.classList;
28494
+ const cls2 = document.querySelector("#" + togglerId + " #opt2")?.classList;
28133
28495
  cls1 && cls1.toggle("selected");
28134
28496
  cls2 && cls2.toggle("selected");
28135
28497
  if (funcToggle !== undefined && cls2)
@@ -28183,19 +28545,16 @@ const TogglerReady = (initialVnode) => {
28183
28545
  }
28184
28546
  }
28185
28547
  return {
28186
- view: () => {
28187
- var _a, _b;
28188
- return m(Toggler, {
28189
- id: "ready",
28190
- state: (_b = (_a = model.state) === null || _a === void 0 ? void 0 : _a.ready) !== null && _b !== void 0 ? _b : false,
28191
- tabindex: 2,
28192
- opt1: nbsp(),
28193
- opt2: glyph("thumbs-up"),
28194
- funcToggle: toggleFunc,
28195
- small: true,
28196
- title: ts("Tek við áskorunum!"),
28197
- });
28198
- }
28548
+ view: () => m(Toggler, {
28549
+ id: "ready",
28550
+ state: model.state?.ready ?? false,
28551
+ tabindex: 2,
28552
+ opt1: nbsp(),
28553
+ opt2: glyph("thumbs-up"),
28554
+ funcToggle: toggleFunc,
28555
+ small: true,
28556
+ title: ts("Tek við áskorunum!"),
28557
+ })
28199
28558
  };
28200
28559
  };
28201
28560
  const TogglerReadyTimed = (initialVnode) => {
@@ -28210,19 +28569,16 @@ const TogglerReadyTimed = (initialVnode) => {
28210
28569
  }
28211
28570
  }
28212
28571
  return {
28213
- view: () => {
28214
- var _a, _b;
28215
- return m(Toggler, {
28216
- id: "timed",
28217
- state: (_b = (_a = model.state) === null || _a === void 0 ? void 0 : _a.readyTimed) !== null && _b !== void 0 ? _b : false,
28218
- tabindex: 3,
28219
- opt1: nbsp(),
28220
- opt2: glyph("time"),
28221
- funcToggle: toggleFunc,
28222
- small: true,
28223
- title: ts("Til í viðureign með klukku!"),
28224
- });
28225
- }
28572
+ view: () => m(Toggler, {
28573
+ id: "timed",
28574
+ state: model.state?.readyTimed ?? false,
28575
+ tabindex: 3,
28576
+ opt1: nbsp(),
28577
+ opt2: glyph("time"),
28578
+ funcToggle: toggleFunc,
28579
+ small: true,
28580
+ title: ts("Til í viðureign með klukku!"),
28581
+ })
28226
28582
  };
28227
28583
  };
28228
28584
  const TogglerAudio = (initialVnode) => {
@@ -28317,7 +28673,6 @@ const WaitDialog = {
28317
28673
  state.firebasePath = "";
28318
28674
  },
28319
28675
  oncreate: (vnode) => {
28320
- var _a, _b;
28321
28676
  // Note: we must use a two-step initialization here,
28322
28677
  // calling oninit() first and then oncreate(), since calls to
28323
28678
  // m.redraw() - explicit or implicit via m.request() - from within
@@ -28327,7 +28682,7 @@ const WaitDialog = {
28327
28682
  const { view, oppId, challengeKey: key } = attrs;
28328
28683
  const { model } = view;
28329
28684
  const globalState = model.state;
28330
- const userId = (_b = (_a = model.state) === null || _a === void 0 ? void 0 : _a.userId) !== null && _b !== void 0 ? _b : "";
28685
+ const userId = model.state?.netskraflUserId ?? "";
28331
28686
  if (!userId || !oppId)
28332
28687
  return; // Should not happen
28333
28688
  state.firebasePath = `user/${userId}/wait/${oppId}`;
@@ -28366,12 +28721,11 @@ const WaitDialog = {
28366
28721
  });
28367
28722
  },
28368
28723
  view: (vnode) => {
28369
- var _a, _b;
28370
28724
  const { attrs, state } = vnode;
28371
28725
  const { view, oppId, duration, challengeKey: key, oppNick, oppName } = attrs;
28372
28726
  const { model } = view;
28373
28727
  const globalState = model.state;
28374
- const userId = (_b = (_a = model.state) === null || _a === void 0 ? void 0 : _a.userId) !== null && _b !== void 0 ? _b : "";
28728
+ const userId = model.state?.netskraflUserId ?? "";
28375
28729
  if (!globalState)
28376
28730
  return null;
28377
28731
  const cancelWait = async () => {
@@ -28453,7 +28807,7 @@ const AcceptDialog = {
28453
28807
  url: "/waitcheck",
28454
28808
  body: { user: oppId, key }
28455
28809
  });
28456
- if (json === null || json === void 0 ? void 0 : json.waiting) {
28810
+ if (json?.waiting) {
28457
28811
  // Both players are now ready: Start the timed game.
28458
28812
  // The newGame() call switches to a new route (/game),
28459
28813
  // and all open dialogs are thereby closed automatically.
@@ -28516,12 +28870,11 @@ class WordChecker {
28516
28870
  this.wordCheckCache = {};
28517
28871
  }
28518
28872
  ingestTwoLetterWords(locale, twoLetterWords) {
28519
- var _a;
28520
28873
  // Initialize the cache with a list of known two-letter words
28521
28874
  // The two-letter word list contains a list of entries where each
28522
28875
  // entry is an initial letter followed by a list of two-letter words
28523
28876
  // starting with that letter.
28524
- const cache = (_a = this.wordCheckCache[locale]) !== null && _a !== void 0 ? _a : (this.wordCheckCache[locale] = {});
28877
+ const cache = this.wordCheckCache[locale] ?? (this.wordCheckCache[locale] = {});
28525
28878
  for (const [/* firstLetter */ , wordList] of twoLetterWords) {
28526
28879
  for (const word of wordList) {
28527
28880
  cache[word] = true;
@@ -28564,7 +28917,7 @@ class WordChecker {
28564
28917
  words,
28565
28918
  }
28566
28919
  });
28567
- if ((response === null || response === void 0 ? void 0 : response.word) === words[0] && (response === null || response === void 0 ? void 0 : response.valid)) {
28920
+ if (response?.word === words[0] && response?.valid) {
28568
28921
  // Looks like a valid response
28569
28922
  if (!cache) {
28570
28923
  // We didn't already have a cache for this locale
@@ -29207,7 +29560,6 @@ class BaseGame {
29207
29560
  return c ? this.tiles[c] || null : null;
29208
29561
  }
29209
29562
  saveTiles() {
29210
- var _a;
29211
29563
  // Save the current unglued tile configuration to local storage
29212
29564
  let tp = [];
29213
29565
  const tilesPlaced = this.tilesPlaced();
@@ -29222,7 +29574,7 @@ class BaseGame {
29222
29574
  if (sq in this.tiles)
29223
29575
  tp.push({ sq, tile: this.tiles[sq].tile });
29224
29576
  }
29225
- (_a = this.localStorage) === null || _a === void 0 ? void 0 : _a.saveTiles(tp);
29577
+ this.localStorage?.saveTiles(tp);
29226
29578
  }
29227
29579
  ;
29228
29580
  restoreTiles(savedTiles) {
@@ -29326,7 +29678,6 @@ const DEBUG_OVERTIME = 1 * 60.0;
29326
29678
  const GAME_OVER = 99; // Error code corresponding to the Error class in skraflmechanics.py
29327
29679
  class Game extends BaseGame {
29328
29680
  constructor(uuid, srvGame, moveListener, state, maxOvertime) {
29329
- var _a;
29330
29681
  // Call parent constructor
29331
29682
  super(uuid, state); // Default board_type: "standard"
29332
29683
  // A class that represents a Netskrafl game instance on the client
@@ -29389,7 +29740,7 @@ class Game extends BaseGame {
29389
29740
  return;
29390
29741
  }
29391
29742
  // Load previously saved tile positions from local storage, if any
29392
- let savedTiles = ((_a = this.localStorage) === null || _a === void 0 ? void 0 : _a.loadTiles()) || [];
29743
+ let savedTiles = this.localStorage?.loadTiles() || [];
29393
29744
  this.init(srvGame);
29394
29745
  // Put tiles in the same position as they were when the player left the game
29395
29746
  this.restoreTiles(savedTiles);
@@ -29595,7 +29946,7 @@ class Game extends BaseGame {
29595
29946
  url: "/gamestate",
29596
29947
  body: { game: this.uuid } // !!! FIXME: Add delete_zombie parameter
29597
29948
  });
29598
- if (!(result === null || result === void 0 ? void 0 : result.ok)) {
29949
+ if (!result?.ok) {
29599
29950
  // console.log("Game " + uuid + " could not be loaded");
29600
29951
  }
29601
29952
  else {
@@ -29939,8 +30290,7 @@ class Game extends BaseGame {
29939
30290
  this.rack = rack;
29940
30291
  }
29941
30292
  rackAtMove(moveIndex) {
29942
- var _a;
29943
- const numRacks = ((_a = this.racks) === null || _a === void 0 ? void 0 : _a.length) || 0;
30293
+ const numRacks = this.racks?.length || 0;
29944
30294
  if (!numRacks)
29945
30295
  return "";
29946
30296
  return this.racks[moveIndex >= numRacks ? numRacks - 1 : moveIndex];
@@ -30423,8 +30773,8 @@ class Riddle extends BaseGame {
30423
30773
  // Initialize word checker
30424
30774
  wordChecker.ingestTwoLetterWords(this.locale, this.two_letter_words[0]);
30425
30775
  // Load persisted player moves from localStorage
30426
- if (state.userId) {
30427
- const persistedMoves = RiddlePersistence.loadLocalMoves(state.userId, date);
30776
+ if (state.netskraflUserId) {
30777
+ const persistedMoves = RiddlePersistence.loadLocalMoves(state.netskraflUserId, date);
30428
30778
  if (persistedMoves.length > 0) {
30429
30779
  // Convert from IPlayerMove to RiddleWord format, preserving timestamps
30430
30780
  this.playerMoves = persistedMoves.map(move => ({
@@ -30447,7 +30797,7 @@ class Riddle extends BaseGame {
30447
30797
  }
30448
30798
  async submitRiddleWord(move) {
30449
30799
  const { state } = this;
30450
- if (!state || !state.userId)
30800
+ if (!state || !state.netskraflUserId)
30451
30801
  return;
30452
30802
  try {
30453
30803
  await request(state, {
@@ -30456,9 +30806,9 @@ class Riddle extends BaseGame {
30456
30806
  body: {
30457
30807
  date: this.date,
30458
30808
  locale: this.locale,
30459
- userId: state.userId,
30809
+ userId: state.netskraflUserId,
30460
30810
  groupId: state.userGroupId || null,
30461
- userDisplayName: state.userFullname || state.userNick || state.userId,
30811
+ userDisplayName: state.userFullname || state.userNick || "",
30462
30812
  move,
30463
30813
  }
30464
30814
  });
@@ -30508,7 +30858,7 @@ class Riddle extends BaseGame {
30508
30858
  if (!move)
30509
30859
  return;
30510
30860
  const { state } = this;
30511
- if (!state || !state.userId)
30861
+ if (!state || !state.netskraflUserId)
30512
30862
  return;
30513
30863
  // Save all moves to localStorage (local backup/cache)
30514
30864
  // Convert RiddleWord[] to IPlayerMove[] for persistence
@@ -30518,7 +30868,7 @@ class Riddle extends BaseGame {
30518
30868
  coord: m.coord,
30519
30869
  timestamp: m.timestamp
30520
30870
  }));
30521
- RiddlePersistence.saveLocalMoves(state.userId, this.date, movesToSave);
30871
+ RiddlePersistence.saveLocalMoves(state.netskraflUserId, this.date, movesToSave);
30522
30872
  // If the move does not improve the personal best, we're done
30523
30873
  if (move.score <= this.personalBestScore)
30524
30874
  return;
@@ -30848,7 +31198,6 @@ class Model {
30848
31198
  });
30849
31199
  }
30850
31200
  async loadGame(uuid, funcComplete, deleteZombie = false) {
30851
- var _a;
30852
31201
  // Fetch a Netskrafl game state from the server, given the game's UUID.
30853
31202
  // If deleteZombie is true, we are loading a zombie game for
30854
31203
  // inspection, so we tell the server to remove the zombie marker.
@@ -30868,12 +31217,12 @@ class Model {
30868
31217
  game: uuid,
30869
31218
  delete_zombie: deleteZombie
30870
31219
  });
30871
- if (!(result === null || result === void 0 ? void 0 : result.ok)) {
31220
+ if (!result?.ok) {
30872
31221
  // console.log("Game " + uuid + " could not be loaded");
30873
31222
  }
30874
31223
  else {
30875
31224
  // Create a new game instance and load the state into it
30876
- this.game = new Game(uuid, result.game, this, this.state, ((_a = this.state) === null || _a === void 0 ? void 0 : _a.runningLocal) ? DEBUG_OVERTIME : MAX_OVERTIME);
31225
+ this.game = new Game(uuid, result.game, this, this.state, this.state?.runningLocal ? DEBUG_OVERTIME : MAX_OVERTIME);
30877
31226
  // Successfully loaded: call the completion function, if given
30878
31227
  // (this usually attaches the Firebase event listener)
30879
31228
  funcComplete && funcComplete();
@@ -31180,13 +31529,12 @@ class Model {
31180
31529
  }
31181
31530
  }
31182
31531
  async loadHelp() {
31183
- var _a;
31184
31532
  // Load the help screen HTML from the server
31185
31533
  // (this is done the first time the help is displayed)
31186
31534
  if (this.helpHTML !== null)
31187
31535
  return; // Already loaded
31188
31536
  try {
31189
- const locale = ((_a = this.state) === null || _a === void 0 ? void 0 : _a.locale) || "is_IS";
31537
+ const locale = this.state?.locale || "is_IS";
31190
31538
  const result = await this.getText("/rawhelp?version=malstadur&locale=" + locale);
31191
31539
  this.helpHTML = result;
31192
31540
  }
@@ -31231,7 +31579,7 @@ class Model {
31231
31579
  return;
31232
31580
  try {
31233
31581
  const result = await this.post("/saveuserprefs", user);
31234
- if (result === null || result === void 0 ? void 0 : result.ok) {
31582
+ if (result?.ok) {
31235
31583
  // User preferences modified successfully on the server:
31236
31584
  // update the state variables that we're caching
31237
31585
  const state = this.state;
@@ -31263,10 +31611,9 @@ class Model {
31263
31611
  }
31264
31612
  }
31265
31613
  addChatMessage(game, from_userid, msg, ts) {
31266
- var _a, _b;
31267
31614
  // Add a chat message to the game's chat message list
31268
31615
  if (this.game && this.game.uuid == game) {
31269
- const userId = (_b = (_a = this.state) === null || _a === void 0 ? void 0 : _a.userId) !== null && _b !== void 0 ? _b : "";
31616
+ const userId = this.state?.netskraflUserId ?? "";
31270
31617
  this.game.addChatMessage(from_userid, msg, ts, from_userid == userId);
31271
31618
  // Returning true triggers a redraw
31272
31619
  return true;
@@ -31326,10 +31673,9 @@ class Model {
31326
31673
  }
31327
31674
  }
31328
31675
  notifyGameWon() {
31329
- var _a;
31330
31676
  // The user just won a game:
31331
31677
  // play the "you-win" audio if fanfare is enabled
31332
- if ((_a = this.user) === null || _a === void 0 ? void 0 : _a.fanfare) {
31678
+ if (this.user?.fanfare) {
31333
31679
  playAudio(this.state, "you-win");
31334
31680
  }
31335
31681
  }
@@ -31420,7 +31766,7 @@ const FriendPromoteDialog = (initialVnode) => {
31420
31766
  onclick: (ev) => {
31421
31767
  ev.preventDefault();
31422
31768
  logEvent("click_friend", {
31423
- userid: state.userId, locale: state.locale
31769
+ userid: state.netskraflUserId, locale: state.locale
31424
31770
  });
31425
31771
  // Navigate to the container-supplied subscription URL
31426
31772
  window.location.href = state.subscriptionUrl;
@@ -31488,7 +31834,7 @@ const ChallengeDialog = () => {
31488
31834
  m("div", { id: 'chall-15', tabindex: 3 }, [glyph("time"), t("2 x 15 mínútur")]),
31489
31835
  m("div", { id: 'chall-20', tabindex: 4 }, [glyph("time"), t("2 x 20 mínútur")]),
31490
31836
  m("div", { id: 'chall-25', tabindex: 5 }, [glyph("time"), t("2 x 25 mínútur")]),
31491
- (state === null || state === void 0 ? void 0 : state.runningLocal) ? // !!! TODO Debugging aid
31837
+ state?.runningLocal ? // !!! TODO Debugging aid
31492
31838
  m("div", { id: 'chall-3', tabindex: 6 }, [glyph("time"), t("2 x 3 mínútur")])
31493
31839
  :
31494
31840
  m("div", { id: 'chall-30', tabindex: 6 }, [glyph("time"), t("2 x 30 mínútur")])
@@ -31534,9 +31880,8 @@ const ChallengeDialog = () => {
31534
31880
  title: ts("Skora á"),
31535
31881
  tabindex: 9,
31536
31882
  onclick: (ev) => {
31537
- var _a, _b;
31538
31883
  // Issue a new challenge
31539
- let duration = (_b = (_a = document.querySelector("div.chall-time.selected")) === null || _a === void 0 ? void 0 : _a.id.slice(6)) !== null && _b !== void 0 ? _b : "0";
31884
+ let duration = document.querySelector("div.chall-time.selected")?.id.slice(6) ?? "0";
31540
31885
  if (duration === "none")
31541
31886
  duration = 0;
31542
31887
  else
@@ -31579,7 +31924,7 @@ const RecentList = () => {
31579
31924
  function durationDescription() {
31580
31925
  // Format the game duration
31581
31926
  let duration = "";
31582
- if (!item.duration) {
31927
+ if (!item.prefs?.duration) {
31583
31928
  // Regular (non-timed) game
31584
31929
  if (item.days || item.hours || item.minutes) {
31585
31930
  if (item.days > 1)
@@ -31608,7 +31953,7 @@ const RecentList = () => {
31608
31953
  // This was a timed game
31609
31954
  duration = [
31610
31955
  m("span.timed-btn", { title: ts('Viðureign með klukku') }),
31611
- " 2 x " + item.duration + ts(" mínútur")
31956
+ " 2 x " + item.prefs.duration + ts(" mínútur")
31612
31957
  ];
31613
31958
  }
31614
31959
  return duration;
@@ -31658,7 +32003,7 @@ const RecentList = () => {
31658
32003
  m("div.list-elo-adj", eloAdjHuman),
31659
32004
  m("div.list-elo-adj", eloAdj),
31660
32005
  m("span.list-duration", durationDescription()),
31661
- m("span.list-manual", item.manual ? { title: ts("Keppnishamur") } : {}, glyph("lightbulb", undefined, !item.manual))
32006
+ m(ProModeIndicator, { manual: item.prefs?.manual })
31662
32007
  ]));
31663
32008
  }
31664
32009
  return {
@@ -31669,6 +32014,295 @@ const RecentList = () => {
31669
32014
  };
31670
32015
  };
31671
32016
 
32017
+ /*
32018
+
32019
+ Gamelist.ts
32020
+
32021
+ List of games in progress display component
32022
+
32023
+ Copyright (C) 2025 Miðeind ehf.
32024
+ Author: Vilhjalmur Thorsteinsson
32025
+
32026
+ The Creative Commons Attribution-NonCommercial 4.0
32027
+ International Public License (CC-BY-NC 4.0) applies to this software.
32028
+ For further information, see https://github.com/mideind/Netskrafl
32029
+
32030
+ */
32031
+ const Hint = {
32032
+ view: (vnode) => {
32033
+ const { loadingGameList, loadingAllLists, gameList } = vnode.attrs;
32034
+ // Show some help if the user has no games in progress
32035
+ if (loadingGameList || loadingAllLists || (gameList !== null && gameList.length > 0))
32036
+ // Either we have games in progress or the game list is being loaded
32037
+ return "";
32038
+ return m(".hint", { style: { display: "block" } }, [
32039
+ m("p", [
32040
+ "Ef þig vantar einhvern til að skrafla við, veldu flipann ",
32041
+ m(m.route.Link, { href: "/main?tab=2" }, [glyph("user"), nbsp(), "Andstæðingar"]),
32042
+ " og skoraðu á tölvuþjarka - ",
32043
+ glyph("cog"), nbsp(), m("b", "Amlóða"),
32044
+ ", ",
32045
+ glyph("cog"), nbsp(), m("b", "Miðlung"),
32046
+ " eða ",
32047
+ glyph("cog"), nbsp(), m("b", "Fullsterkan"),
32048
+ " - eða veldu þér annan leikmann úr stafrófs\u00ADlistunum " + // Soft hyphen
32049
+ " sem þar er að finna til að skora á."
32050
+ ]),
32051
+ m("p", [
32052
+ "Þú stofnar áskorun með því að smella á bendi-teiknið ",
32053
+ glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
32054
+ " vinstra megin við nafn andstæðingsins."
32055
+ ]),
32056
+ m("p", "Tölvuþjarkarnir eru ætíð reiðubúnir að skrafla og viðureign við þá " +
32057
+ " hefst strax. Aðrir leikmenn þurfa að samþykkja áskorun áður en viðureign hefst."),
32058
+ m("p.no-mobile-block", [
32059
+ m(m.route.Link, { href: "/help" }, "Hjálp"),
32060
+ " má fá með því að smella á bláa ",
32061
+ glyph("info-sign"), nbsp(), "-", nbsp(),
32062
+ "teiknið hér til vinstri."
32063
+ ]),
32064
+ m("p.no-mobile-block", "Þú kemst alltaf aftur í þessa aðalsíðu með því að smella á " +
32065
+ "örvarmerkið efst vinstra megin við skraflborðið.")
32066
+ ]);
32067
+ }
32068
+ };
32069
+ const GameListEntry = () => {
32070
+ // Generate a list item about a game in progress (or recently finished)
32071
+ function vwOpp(item) {
32072
+ const arg = item.oppid === null ? [glyph("cog"), nbsp(), item.opp] : item.opp;
32073
+ return m("span.list-opp", { title: item.fullname }, arg);
32074
+ }
32075
+ function vwTurn(item) {
32076
+ let turnText = "";
32077
+ let flagClass = "";
32078
+ if (item.my_turn) {
32079
+ turnText = ts("Þú átt leik");
32080
+ }
32081
+ else if (item.zombie) {
32082
+ turnText = ts("Viðureign lokið");
32083
+ flagClass = ".zombie";
32084
+ }
32085
+ else {
32086
+ // {opponent}'s move
32087
+ turnText = ts("opp_move", { opponent: item.opp });
32088
+ flagClass = ".grayed";
32089
+ }
32090
+ return m("span.list-myturn", m("span.glyphicon.glyphicon-flag" + flagClass, { title: turnText }));
32091
+ }
32092
+ function vwOverdue(item) {
32093
+ if (item.overdue)
32094
+ return glyph("hourglass", { title: item.my_turn ? "Er að renna út á tíma" : "Getur þvingað fram uppgjöf" });
32095
+ return glyphGrayed("hourglass");
32096
+ }
32097
+ function vwTileCount(item) {
32098
+ const winLose = item.sc0 < item.sc1 ? ".losing" : "";
32099
+ return m(".tilecount", m(".tc" + winLose, { style: { width: item.tile_count.toString() + "%" } }));
32100
+ }
32101
+ return {
32102
+ view: (vnode) => {
32103
+ const { view, item, i } = vnode.attrs;
32104
+ return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
32105
+ m(m.route.Link, { href: gameUrl(item.url) }, [
32106
+ vwTurn(item),
32107
+ m("span.list-overdue", vwOverdue(item)),
32108
+ m("span.list-ts-short", item.ts),
32109
+ vwOpp(item)
32110
+ ]),
32111
+ m(UserInfoButton, {
32112
+ view,
32113
+ item: { userid: item.oppid, nick: item.opp, fullname: item.fullname },
32114
+ }),
32115
+ m("span.list-s0", item.sc0),
32116
+ m("span.list-colon", ":"),
32117
+ m("span.list-s1", item.sc1),
32118
+ m("span.list-tc", vwTileCount(item)),
32119
+ m(ProModeIndicator, { manual: item.prefs?.manual })
32120
+ ]);
32121
+ }
32122
+ };
32123
+ };
32124
+ const GameList = () => {
32125
+ // Shows a list of games in progress, obtained from view.model.gameList
32126
+ return {
32127
+ view: (vnode) => {
32128
+ const { view, id } = vnode.attrs;
32129
+ const { model } = view;
32130
+ const { gameList, loadingGameList, loadingAllLists } = model;
32131
+ // Trigger loading of game list if needed
32132
+ if (gameList === null)
32133
+ model.loadGameList();
32134
+ return [
32135
+ m(".listitem.listheader", [
32136
+ m("span.list-myturn", glyphGrayed("flag", { title: ts('Átt þú leik?') })),
32137
+ m("span.list-overdue", glyphGrayed("hourglass", { title: ts('Langt frá síðasta leik?') })),
32138
+ mt("span.list-ts-short", "Síðasti leikur"),
32139
+ mt("span.list-opp", "Andstæðingur"),
32140
+ mt("span.list-info-hdr", "Ferill"),
32141
+ mt("span.list-scorehdr", "Staða"),
32142
+ mt("span.list-tc", "Framvinda"),
32143
+ m(ProModeIndicator, { header: true }),
32144
+ ]),
32145
+ m("div", { id }, !gameList ? "" : gameList.map((item, i) => m(GameListEntry, { view, item, i }))),
32146
+ m(Hint, { loadingGameList, loadingAllLists, gameList }),
32147
+ ];
32148
+ }
32149
+ };
32150
+ };
32151
+
32152
+ /*
32153
+
32154
+ Challengelist.ts
32155
+
32156
+ List of challenges (received and sent) display component
32157
+
32158
+ Copyright (C) 2025 Miðeind ehf.
32159
+ Author: Vilhjalmur Thorsteinsson
32160
+
32161
+ The Creative Commons Attribution-NonCommercial 4.0
32162
+ International Public License (CC-BY-NC 4.0) applies to this software.
32163
+ For further information, see https://github.com/mideind/Netskrafl
32164
+
32165
+ */
32166
+ const ChallengeListEntry = (initialVnode) => {
32167
+ // Generate a list item about a pending challenge (issued or received)
32168
+ const { view, item, i } = initialVnode.attrs;
32169
+ const { model } = view;
32170
+ function challengeDescription(json) {
32171
+ /* Return a human-readable string describing a challenge
32172
+ according to the enclosed preferences */
32173
+ if (!json || json.duration === undefined || json.duration === 0)
32174
+ /* Normal unbounded (untimed) game */
32175
+ return t("Venjuleg ótímabundin viðureign");
32176
+ return t("with_clock", { duration: json.duration.toString() });
32177
+ }
32178
+ function markChallenge(ev) {
32179
+ // Clicked the icon at the beginning of the line,
32180
+ // to decline a received challenge or retract an issued challenge
32181
+ ev.preventDefault();
32182
+ if (item.received) {
32183
+ view.actions.declineChallenge(item.userid, item.key);
32184
+ }
32185
+ else {
32186
+ view.actions.retractChallenge(item.userid, item.key);
32187
+ }
32188
+ }
32189
+ function clickChallenge(ev) {
32190
+ // Clicked the hotspot area to accept a received challenge
32191
+ ev.preventDefault();
32192
+ if (!model.moreGamesAllowed()) {
32193
+ // User must be a friend to be able to accept more challenges
32194
+ const state = model.state;
32195
+ if (state) {
32196
+ logEvent("hit_game_limit", {
32197
+ userid: state.netskraflUserId,
32198
+ locale: state.locale,
32199
+ limit: model.maxFreeGames
32200
+ });
32201
+ }
32202
+ // Promote a subscription to Netskrafl/Explo
32203
+ view.showFriendPromo();
32204
+ return;
32205
+ }
32206
+ if (item.received) {
32207
+ if (item.prefs && item.prefs.duration !== undefined && item.prefs.duration > 0)
32208
+ // Timed game: display a modal wait dialog
32209
+ view.pushDialog("wait", {
32210
+ oppId: item.userid,
32211
+ oppNick: item.opp,
32212
+ oppName: item.fullname,
32213
+ duration: item.prefs.duration,
32214
+ challengeKey: item.key,
32215
+ });
32216
+ else
32217
+ // Ask the server to create a new game and route to it
32218
+ view.actions.startNewGame(item.userid, false);
32219
+ }
32220
+ else {
32221
+ // Clicking on a sent challenge, i.e. a timed game
32222
+ // where the opponent is waiting and ready to start
32223
+ view.showAcceptDialog(item.userid, item.opp, item.key);
32224
+ }
32225
+ }
32226
+ return {
32227
+ view: () => {
32228
+ const oppReady = !item.received && item.opp_ready &&
32229
+ item.prefs && item.prefs.duration !== undefined &&
32230
+ item.prefs.duration > 0;
32231
+ const clickable = item.received || oppReady;
32232
+ const descr = challengeDescription(item.prefs);
32233
+ return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
32234
+ m("span.list-ch", { onclick: markChallenge }, item.received ?
32235
+ glyph("thumbs-down", { title: ts("Hafna") })
32236
+ :
32237
+ glyph("hand-right", { title: ts("Afturkalla") })),
32238
+ m(clickable ? "a.flex" : "span.flex", clickable ? {
32239
+ href: "#",
32240
+ onclick: clickChallenge,
32241
+ class: oppReady ? "opp-ready" : ""
32242
+ } : {}, [
32243
+ m("span.list-ts", item.ts),
32244
+ m("span.list-nick", { title: item.fullname }, item.opp),
32245
+ m("span.list-chall", [
32246
+ item.prefs.fairplay ? m("span.fairplay-btn", { title: ts("Án hjálpartækja") }) : "",
32247
+ item.prefs.manual ? m("span.manual-btn", { title: ts("Keppnishamur") }) : "",
32248
+ descr
32249
+ ])
32250
+ ]),
32251
+ m(UserInfoButton, {
32252
+ view,
32253
+ item: {
32254
+ userid: item.userid,
32255
+ nick: item.opp,
32256
+ fullname: item.fullname,
32257
+ }
32258
+ }),
32259
+ ]);
32260
+ }
32261
+ };
32262
+ };
32263
+ const ChallengeList = {
32264
+ // Shows a list of challenges (received or sent), obtained from view.model.challengeList
32265
+ view: (vnode) => {
32266
+ const { view, showReceived } = vnode.attrs;
32267
+ const { model } = view;
32268
+ if (!model.state)
32269
+ return [];
32270
+ // Trigger loading of challenge list if needed
32271
+ if (model.challengeList === null)
32272
+ model.loadChallengeList();
32273
+ let cList = [];
32274
+ if (model.challengeList)
32275
+ cList = showReceived ?
32276
+ model.challengeList.filter((item) => item.received) :
32277
+ model.challengeList.filter((item) => !item.received);
32278
+ const listContent = m("div", { id: showReceived ? 'chall-received' : 'chall-sent' }, cList.map((item, i) => m(ChallengeListEntry, { view, item, i })));
32279
+ if (showReceived)
32280
+ // Challenges received
32281
+ return [
32282
+ m(".listitem.listheader", [
32283
+ m("span.list-icon", glyphGrayed("thumbs-down", { title: ts('Hafna') })),
32284
+ mt("span.list-ts", "Hvenær"),
32285
+ mt("span.list-nick", "Áskorandi"),
32286
+ mt("span.list-chall", "Hvernig"),
32287
+ mt("span.list-info-hdr", "Ferill"),
32288
+ ]),
32289
+ listContent
32290
+ ];
32291
+ else
32292
+ // Challenges sent
32293
+ return [
32294
+ m(".listitem.listheader", [
32295
+ m("span.list-icon", glyphGrayed("hand-right", { title: ts('Afturkalla') })),
32296
+ mt("span.list-ts", "Hvenær"),
32297
+ mt("span.list-nick", "Andstæðingur"),
32298
+ mt("span.list-chall", "Hvernig"),
32299
+ mt("span.list-info-hdr", "Ferill"),
32300
+ ]),
32301
+ listContent
32302
+ ];
32303
+ }
32304
+ };
32305
+
31672
32306
  /*
31673
32307
 
31674
32308
  EloPage.ts
@@ -31764,7 +32398,6 @@ const EloList = (initialVnode) => {
31764
32398
  view: (vnode) => {
31765
32399
  function itemize(item, i) {
31766
32400
  // Generate a list item about a user in an Elo ranking table
31767
- var _a;
31768
32401
  const isRobot = item.userid.indexOf("robot-") === 0;
31769
32402
  function rankStr(rank, ref) {
31770
32403
  // Return a rank string or dash if no rank or not meaningful
@@ -31775,7 +32408,7 @@ const EloList = (initialVnode) => {
31775
32408
  }
31776
32409
  let nick = item.nick;
31777
32410
  let ch = m("span.list-ch", nbsp());
31778
- const userId = (_a = state === null || state === void 0 ? void 0 : state.userId) !== null && _a !== void 0 ? _a : "";
32411
+ const userId = state?.netskraflUserId ?? "";
31779
32412
  if (item.userid != userId && !item.inactive) {
31780
32413
  ch = m(ChallengeButton, { view: outerView, item });
31781
32414
  }
@@ -31809,6 +32442,135 @@ const EloList = (initialVnode) => {
31809
32442
  };
31810
32443
  };
31811
32444
 
32445
+ /*
32446
+
32447
+ Userlist.ts
32448
+
32449
+ User list display component
32450
+
32451
+ Copyright (C) 2025 Miðeind ehf.
32452
+ Author: Vilhjalmur Thorsteinsson
32453
+
32454
+ The Creative Commons Attribution-NonCommercial 4.0
32455
+ International Public License (CC-BY-NC 4.0) applies to this software.
32456
+ For further information, see https://github.com/mideind/Netskrafl
32457
+
32458
+ */
32459
+ const UserList = () => {
32460
+ // Shows a list of users, obtained from view.model.userList
32461
+ function itemize(view, item, i) {
32462
+ // Generate a list item about a user
32463
+ const isRobot = item.userid.indexOf("robot-") === 0;
32464
+ let fullname = [];
32465
+ // Online and accepting challenges
32466
+ if (item.ready && !isRobot) {
32467
+ fullname.push(m("span.ready-btn", { title: ts("Álínis og tekur við áskorunum") }));
32468
+ fullname.push(nbsp());
32469
+ }
32470
+ // Willing to accept challenges for timed games
32471
+ if (item.ready_timed) {
32472
+ fullname.push(m("span.timed-btn", { title: ts("Til í viðureign með klukku") }));
32473
+ fullname.push(nbsp());
32474
+ }
32475
+ // Fair play commitment
32476
+ if (item.fairplay) {
32477
+ fullname.push(m("span.fairplay-btn", { title: ts("Skraflar án hjálpartækja") }));
32478
+ fullname.push(nbsp());
32479
+ }
32480
+ fullname.push(item.fullname);
32481
+ function fav() {
32482
+ if (isRobot)
32483
+ return m("span.list-fav", { style: { cursor: "default" } }, glyph("star-empty"));
32484
+ return m("span.list-fav", {
32485
+ title: ts("Uppáhald"),
32486
+ onclick: (ev) => {
32487
+ view.actions.toggleFavorite(item.userid, item.fav);
32488
+ item.fav = !item.fav;
32489
+ ev.preventDefault();
32490
+ }
32491
+ }, glyph(item.fav ? "star" : "star-empty"));
32492
+ }
32493
+ function userLink() {
32494
+ if (isRobot)
32495
+ return m("a", {
32496
+ href: "",
32497
+ onclick: (ev) => {
32498
+ // Start a new game against the robot
32499
+ view.actions.startRobotGame(item.userid);
32500
+ ev.preventDefault();
32501
+ }
32502
+ }, [
32503
+ m("span.list-nick", [glyph("cog"), nbsp(), item.nick]),
32504
+ m("span.list-fullname-robot", fullname)
32505
+ ]);
32506
+ else
32507
+ return [
32508
+ m("span.list-nick", item.nick),
32509
+ m("span.list-fullname", fullname),
32510
+ m("span.list-human-elo", item.human_elo)
32511
+ ];
32512
+ }
32513
+ return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
32514
+ m(ChallengeButton, { view, item }),
32515
+ fav(),
32516
+ userLink(),
32517
+ m(UserInfoButton, { view, item }),
32518
+ ]);
32519
+ }
32520
+ return {
32521
+ view: (vnode) => {
32522
+ const { view, id } = vnode.attrs;
32523
+ const { model } = view;
32524
+ // The type of list to show; by default it's 'robots'
32525
+ const query = model.userListCriteria?.query ?? "";
32526
+ const spec = model.userListCriteria?.spec ?? "";
32527
+ const loadingElo = model.eloRatingSpec === undefined;
32528
+ const listType = query || "robots";
32529
+ if (listType === "elo" || loadingElo)
32530
+ // Show Elo list
32531
+ return [
32532
+ m(EloPage, {
32533
+ id: "elolist",
32534
+ view: view,
32535
+ spec: model.eloRatingSpec ?? null,
32536
+ key: "elopage",
32537
+ })
32538
+ ];
32539
+ // Show normal user list
32540
+ let list = [];
32541
+ if (model.userList === undefined) ;
32542
+ else if (model.userList === null || query !== listType) {
32543
+ view.actions.loadUsersByType(listType, true);
32544
+ }
32545
+ else {
32546
+ list = model.userList;
32547
+ }
32548
+ const nothingFound = list.length === 0 && listType === "search" && spec !== "";
32549
+ const robotList = listType === "robots";
32550
+ return [
32551
+ m(".listitem.listheader", [
32552
+ m("span.list-ch", glyphGrayed("hand-right", { title: ts('Skora á') })),
32553
+ m("span.list-fav", glyph("star-empty", { title: ts('Uppáhald') })),
32554
+ mt("span.list-nick", "Einkenni"),
32555
+ mt("span.list-fullname", "Nafn og merki"),
32556
+ robotList ? "" : mt("span.list-human-elo[id='usr-list-elo']", "Elo"),
32557
+ robotList ? "" : mt("span.list-info-hdr[id='usr-list-info']", "Ferill"),
32558
+ ]),
32559
+ m("div", { id }, list.map((item, i) => itemize(view, item, i))),
32560
+ // Show indicator if search didn't find any users matching the criteria
32561
+ nothingFound ?
32562
+ m("div", { id: "user-no-match", style: { display: "block" } }, [
32563
+ glyph("search"),
32564
+ " ",
32565
+ m("span", { id: "search-prefix" }, spec),
32566
+ t(" finnst ekki")
32567
+ ])
32568
+ : undefined
32569
+ ];
32570
+ }
32571
+ };
32572
+ };
32573
+
31812
32574
  /*
31813
32575
 
31814
32576
  Statsdisplay.ts
@@ -31835,7 +32597,6 @@ const StatsDisplay = () => {
31835
32597
  }
31836
32598
  return {
31837
32599
  view: (vnode) => {
31838
- var _a, _b;
31839
32600
  // Display statistics about this user
31840
32601
  var s = vnode.attrs.ownStats;
31841
32602
  var winRatio = 0, winRatioHuman = 0;
@@ -31860,13 +32621,13 @@ const StatsDisplay = () => {
31860
32621
  m(".option.small" + (sel === 2 ? ".selected" : ""), { id: 'opt2' }, glyph("cog"))
31861
32622
  ]),
31862
32623
  sel === 1 ? m("div", { id: 'own-stats-human', className: 'stats-box', style: { display: "inline-block" } }, [
31863
- m(".stats-fig", { title: ts('Elo-stig') }, s ? vwStat((_a = s.locale_elo) === null || _a === void 0 ? void 0 : _a.human_elo, "crown") : ""),
32624
+ m(".stats-fig", { title: ts('Elo-stig') }, s ? vwStat(s.locale_elo?.human_elo, "crown") : ""),
31864
32625
  m(".stats-fig.stats-games", { title: ts('Fjöldi viðureigna') }, s ? vwStat(s.human_games, "th") : ""),
31865
32626
  m(".stats-fig.stats-win-ratio", { title: ts('Vinningshlutfall') }, vwStat(winRatioHuman, "bookmark", "%")),
31866
32627
  m(".stats-fig.stats-avg-score", { title: ts('Meðalstigafjöldi') }, vwStat(avgScoreHuman, "dashboard"))
31867
32628
  ]) : "",
31868
32629
  sel === 2 ? m("div", { id: 'own-stats-all', className: 'stats-box', style: { display: "inline-block" } }, [
31869
- m(".stats-fig", { title: ts("Elo-stig") }, s ? vwStat((_b = s.locale_elo) === null || _b === void 0 ? void 0 : _b.elo, "crown") : ""),
32630
+ m(".stats-fig", { title: ts("Elo-stig") }, s ? vwStat(s.locale_elo?.elo, "crown") : ""),
31870
32631
  m(".stats-fig.stats-games", { title: ts('Fjöldi viðureigna') }, s ? vwStat(s.games, "th") : ""),
31871
32632
  m(".stats-fig.stats-win-ratio", { title: ts('Vinningshlutfall') }, vwStat(winRatio, "bookmark", "%")),
31872
32633
  m(".stats-fig.stats-avg-score", { title: ts('Meðalstigafjöldi') }, vwStat(avgScore, "dashboard"))
@@ -31898,7 +32659,6 @@ const SearchButton = (initialVnode) => {
31898
32659
  let spec = ""; // The current search pattern
31899
32660
  let promise = undefined;
31900
32661
  function newSearch() {
31901
- var _a, _b;
31902
32662
  // There may have been a change of search parameters: react
31903
32663
  if (promise !== undefined) {
31904
32664
  // There was a previous promise, now obsolete: make it
@@ -31906,13 +32666,13 @@ const SearchButton = (initialVnode) => {
31906
32666
  promise.result = false;
31907
32667
  promise = undefined;
31908
32668
  }
31909
- let sel = ((_a = model.userListCriteria) === null || _a === void 0 ? void 0 : _a.query) || "robots";
32669
+ let sel = model.userListCriteria?.query || "robots";
31910
32670
  if (sel !== "search") {
31911
32671
  // Not already in a search: load the user list immediately
31912
32672
  view.actions.searchUsers(spec, true);
31913
32673
  return;
31914
32674
  }
31915
- if (spec === ((_b = model.userListCriteria) === null || _b === void 0 ? void 0 : _b.spec))
32675
+ if (spec === model.userListCriteria?.spec)
31916
32676
  // We're already looking at the same search spec: done
31917
32677
  return;
31918
32678
  // We're changing the search spec.
@@ -31944,18 +32704,16 @@ const SearchButton = (initialVnode) => {
31944
32704
  }
31945
32705
  return {
31946
32706
  view: () => {
31947
- var _a;
31948
- const sel = ((_a = model.userListCriteria) === null || _a === void 0 ? void 0 : _a.query) || "robots";
32707
+ const sel = model.userListCriteria?.query || "robots";
31949
32708
  return m(".user-cat[id='user-search']", [
31950
32709
  glyph("search", {
31951
32710
  id: 'search',
31952
32711
  className: (sel == "search" ? "shown" : ""),
31953
32712
  onclick: () => {
31954
- var _a;
31955
32713
  // Reset the search pattern when clicking the search icon
31956
32714
  spec = "";
31957
32715
  newSearch();
31958
- (_a = document.getElementById("search-id")) === null || _a === void 0 ? void 0 : _a.focus();
32716
+ document.getElementById("search-id")?.focus();
31959
32717
  }
31960
32718
  }),
31961
32719
  nbsp(),
@@ -32058,8 +32816,7 @@ function makeTabs(view, id, createFunc, wireHrefs, vnode) {
32058
32816
  ev.preventDefault();
32059
32817
  };
32060
32818
  const clickUserPrefs = (ev) => {
32061
- var _a;
32062
- if ((_a = model === null || model === void 0 ? void 0 : model.state) === null || _a === void 0 ? void 0 : _a.userId)
32819
+ if (model?.state?.netskraflUserId)
32063
32820
  // Don't show the userprefs if no user logged in
32064
32821
  view.pushDialog("userprefs");
32065
32822
  ev.preventDefault();
@@ -32109,8 +32866,7 @@ function updateTabVisibility(vnode) {
32109
32866
  const selected = vnode.state.selected;
32110
32867
  const lis = vnode.state.lis;
32111
32868
  vnode.state.ids.map((id, i) => {
32112
- var _a;
32113
- (_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.setAttribute("style", "display: " +
32869
+ document.getElementById(id)?.setAttribute("style", "display: " +
32114
32870
  (i == selected ? "block" : "none"));
32115
32871
  lis[i].classList.toggle("ui-tabs-active", i === selected);
32116
32872
  lis[i].classList.toggle("ui-state-active", i === selected);
@@ -32194,551 +32950,219 @@ const BestDisplay = () => {
32194
32950
  For further information, see https://github.com/mideind/Netskrafl
32195
32951
 
32196
32952
  */
32197
- const Main = () => {
32198
- // Main screen with tabs
32199
- function vwMainTabs(view) {
32200
- const model = view.model;
32201
- function vwMainTabHeader() {
32202
- const numGames = model.numGames;
32203
- const numChallenges = model.numChallenges;
32204
- return [
32205
- m(HeaderLogo, { hidden: true }),
32206
- m("ul", [
32207
- m("li", m("a[href='#tabs-1']", [
32208
- glyph("th"), m("span.tab-legend", t("Viðureignir")),
32209
- m("span", {
32210
- id: 'numgames',
32211
- style: numGames ? 'display: inline-block' : ''
32212
- }, numGames)
32213
- ])),
32214
- m("li", m("a[href='#tabs-2']", [
32215
- glyph("hand-right"), m("span.tab-legend", t("Áskoranir")),
32216
- // Blink if we have timed games where the opponent is ready
32217
- m("span" + (model.oppReady ? ".opp-ready" : ""), {
32218
- id: "numchallenges",
32219
- style: numChallenges ? 'display: inline-block' : ''
32220
- }, numChallenges)
32221
- ])),
32222
- m("li", m("a[href='#tabs-3']", [glyph("user"), m("span.tab-legend", t("Andstæðingar"))])),
32223
- m("li.no-mobile-list", m("a[href='#tabs-4']", [glyph("bookmark"), m("span.tab-legend", t("Ferill"))]))
32224
- ])
32225
- ];
32226
- }
32227
- function vwGamelist() {
32228
- function vwList() {
32229
- function viewGameList() {
32230
- if (!model.gameList)
32231
- return "";
32232
- return model.gameList.map((item, i) => {
32233
- // Show a list item about a game in progress (or recently finished)
32234
- function vwOpp() {
32235
- let arg = item.oppid === null ? [glyph("cog"), nbsp(), item.opp] : item.opp;
32236
- return m("span.list-opp", { title: item.fullname }, arg);
32237
- }
32238
- function vwTurn() {
32239
- let turnText = "";
32240
- let flagClass = "";
32241
- if (item.my_turn) {
32242
- turnText = ts("Þú átt leik");
32243
- }
32244
- else if (item.zombie) {
32245
- turnText = ts("Viðureign lokið");
32246
- flagClass = ".zombie";
32247
- }
32248
- else {
32249
- // {opponent}'s move
32250
- turnText = ts("opp_move", { opponent: item.opp });
32251
- flagClass = ".grayed";
32252
- }
32253
- return m("span.list-myturn", m("span.glyphicon.glyphicon-flag" + flagClass, { title: turnText }));
32254
- }
32255
- function vwOverdue() {
32256
- if (item.overdue)
32257
- return glyph("hourglass", { title: item.my_turn ? "Er að renna út á tíma" : "Getur þvingað fram uppgjöf" });
32258
- return glyphGrayed("hourglass");
32259
- }
32260
- function vwTileCount() {
32261
- const winLose = item.sc0 < item.sc1 ? ".losing" : "";
32262
- return m(".tilecount", m(".tc" + winLose, { style: { width: item.tile_count.toString() + "%" } }));
32263
- }
32264
- return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
32265
- m(m.route.Link, { href: gameUrl(item.url) }, [
32266
- vwTurn(),
32267
- m("span.list-overdue", vwOverdue()),
32268
- m("span.list-ts-short", item.ts),
32269
- vwOpp()
32270
- ]),
32271
- m(UserInfoButton, {
32272
- view,
32273
- item: { userid: item.oppid, nick: item.opp, fullname: item.fullname },
32274
- }),
32275
- m("span.list-s0", item.sc0),
32276
- m("span.list-colon", ":"),
32277
- m("span.list-s1", item.sc1),
32278
- m("span.list-tc", vwTileCount()),
32279
- m("span.list-manual", item.manual
32280
- ? glyph("lightbulb", { title: ts("Keppnishamur") })
32281
- : glyphGrayed("lightbulb"))
32282
- ]);
32283
- });
32284
- }
32285
- if (model.gameList === null)
32286
- model.loadGameList();
32287
- return m("div", { id: 'gamelist' }, viewGameList());
32288
- }
32289
- function vwHint() {
32290
- // Show some help if the user has no games in progress
32291
- if (model.loadingGameList
32292
- || model.loadingAllLists
32293
- || (model.gameList !== null && model.gameList.length > 0))
32294
- // Either we have games in progress or the game list is being loaded
32295
- return "";
32296
- return m(".hint", { style: { display: "block" } }, [
32297
- m("p", [
32298
- "Ef þig vantar einhvern til að skrafla við, veldu flipann ",
32299
- m(m.route.Link, { href: "/main?tab=2" }, [glyph("user"), nbsp(), "Andstæðingar"]),
32300
- " og skoraðu á tölvuþjarka - ",
32301
- glyph("cog"), nbsp(), m("b", "Amlóða"),
32302
- ", ",
32303
- glyph("cog"), nbsp(), m("b", "Miðlung"),
32304
- " eða ",
32305
- glyph("cog"), nbsp(), m("b", "Fullsterkan"),
32306
- " - eða veldu þér annan leikmann úr stafrófs\u00ADlistunum " + // Soft hyphen
32307
- " sem þar er að finna til að skora á."
32308
- ]),
32309
- m("p", [
32310
- "Þú stofnar áskorun með því að smella á bendi-teiknið ",
32311
- glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
32312
- " vinstra megin við nafn andstæðingsins."
32313
- ]),
32314
- m("p", "Tölvuþjarkarnir eru ætíð reiðubúnir að skrafla og viðureign við þá " +
32315
- " hefst strax. Aðrir leikmenn þurfa að samþykkja áskorun áður en viðureign hefst."),
32316
- m("p.no-mobile-block", [
32317
- m(m.route.Link, { href: "/help" }, "Hjálp"),
32318
- " má fá með því að smella á bláa ",
32319
- glyph("info-sign"), nbsp(), "-", nbsp(),
32320
- "teiknið hér til vinstri."
32321
- ]),
32322
- m("p.no-mobile-block", "Þú kemst alltaf aftur í þessa aðalsíðu með því að smella á " +
32323
- "örvarmerkið efst vinstra megin við skraflborðið.")
32324
- ]);
32325
- }
32326
- return [
32327
- m(".listitem.listheader", [
32328
- m("span.list-myturn", glyphGrayed("flag", { title: ts('Átt þú leik?') })),
32329
- m("span.list-overdue", glyphGrayed("hourglass", { title: ts('Langt frá síðasta leik?') })),
32330
- mt("span.list-ts-short", "Síðasti leikur"),
32331
- mt("span.list-opp", "Andstæðingur"),
32332
- mt("span.list-info-hdr", "Ferill"),
32333
- mt("span.list-scorehdr", "Staða"),
32334
- mt("span.list-tc", "Framvinda"),
32335
- m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
32336
- ]),
32337
- vwList(),
32338
- vwHint()
32339
- ];
32340
- }
32341
- function vwChallenges(showReceived) {
32342
- function vwList() {
32343
- if (!model.state)
32344
- return undefined;
32345
- const state = model.state;
32346
- function itemize(item, i) {
32347
- // Generate a list item about a pending challenge (issued or received)
32348
- function challengeDescription(json) {
32349
- /* Return a human-readable string describing a challenge
32350
- according to the enclosed preferences */
32351
- if (!json || json.duration === undefined || json.duration === 0)
32352
- /* Normal unbounded (untimed) game */
32353
- return t("Venjuleg ótímabundin viðureign");
32354
- return t("with_clock", { duration: json.duration.toString() });
32355
- }
32356
- function markChallenge(ev) {
32357
- // Clicked the icon at the beginning of the line,
32358
- // to decline a received challenge or retract an issued challenge
32359
- ev.preventDefault();
32360
- if (item.received) {
32361
- view.actions.declineChallenge(item.userid, item.key);
32362
- }
32363
- else {
32364
- view.actions.retractChallenge(item.userid, item.key);
32365
- }
32366
- }
32367
- function clickChallenge(ev) {
32368
- // Clicked the hotspot area to accept a received challenge
32369
- ev.preventDefault();
32370
- if (!model.moreGamesAllowed()) {
32371
- // User must be a friend to be able to accept more challenges
32372
- logEvent("hit_game_limit", {
32373
- userid: state.userId,
32374
- locale: state.locale,
32375
- limit: model.maxFreeGames
32376
- });
32377
- // Promote a subscription to Netskrafl/Explo
32378
- view.showFriendPromo();
32379
- return;
32380
- }
32381
- if (item.received) {
32382
- if (item.prefs && item.prefs.duration !== undefined && item.prefs.duration > 0)
32383
- // Timed game: display a modal wait dialog
32384
- view.pushDialog("wait", {
32385
- oppId: item.userid,
32386
- oppNick: item.opp,
32387
- oppName: item.fullname,
32388
- duration: item.prefs.duration,
32389
- challengeKey: item.key,
32390
- });
32391
- else
32392
- // Ask the server to create a new game and route to it
32393
- view.actions.startNewGame(item.userid, false);
32394
- }
32395
- else {
32396
- // Clicking on a sent challenge, i.e. a timed game
32397
- // where the opponent is waiting and ready to start
32398
- view.showAcceptDialog(item.userid, item.opp, item.key);
32399
- }
32400
- }
32401
- const oppReady = !item.received && item.opp_ready &&
32402
- item.prefs && item.prefs.duration !== undefined &&
32403
- item.prefs.duration > 0;
32404
- const clickable = item.received || oppReady;
32405
- const descr = challengeDescription(item.prefs);
32406
- return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
32407
- m("span.list-ch", { onclick: markChallenge }, item.received ?
32408
- glyph("thumbs-down", { title: ts("Hafna") })
32409
- :
32410
- glyph("hand-right", { title: ts("Afturkalla") })),
32411
- m(clickable ? "a.flex" : "span.flex", clickable ? {
32412
- href: "#",
32413
- onclick: clickChallenge,
32414
- class: oppReady ? "opp-ready" : ""
32415
- } : {}, [
32416
- m("span.list-ts", item.ts),
32417
- m("span.list-nick", { title: item.fullname }, item.opp),
32418
- m("span.list-chall", [
32419
- item.prefs.fairplay ? m("span.fairplay-btn", { title: ts("Án hjálpartækja") }) : "",
32420
- item.prefs.manual ? m("span.manual-btn", { title: ts("Keppnishamur") }) : "",
32421
- descr
32422
- ])
32423
- ]),
32424
- m(UserInfoButton, {
32425
- view,
32426
- item: {
32427
- userid: item.userid,
32428
- nick: item.opp,
32429
- fullname: item.fullname,
32430
- }
32431
- }),
32432
- ]);
32433
- }
32434
- let cList = [];
32435
- if (model.challengeList)
32436
- cList = showReceived ?
32437
- model.challengeList.filter((item) => item.received) :
32438
- model.challengeList.filter((item) => !item.received);
32439
- return m("div", {
32440
- id: showReceived ? 'chall-received' : 'chall-sent',
32441
- oninit: () => {
32442
- if (model.challengeList === null)
32443
- model.loadChallengeList();
32444
- }
32445
- }, cList.map(itemize));
32446
- }
32447
- const n = vwList();
32448
- if (!n)
32449
- return [];
32450
- if (showReceived)
32451
- // Challenges received
32452
- return [
32453
- m(".listitem.listheader", [
32454
- m("span.list-icon", glyphGrayed("thumbs-down", { title: ts('Hafna') })),
32455
- mt("span.list-ts", "Hvenær"),
32456
- mt("span.list-nick", "Áskorandi"),
32457
- mt("span.list-chall", "Hvernig"),
32458
- mt("span.list-info-hdr", "Ferill"),
32459
- ]),
32460
- n
32461
- ];
32462
- else
32463
- // Challenges sent
32464
- return [
32465
- m(".listitem.listheader", [
32466
- m("span.list-icon", glyphGrayed("hand-right", { title: ts('Afturkalla') })),
32467
- mt("span.list-ts", "Hvenær"),
32468
- mt("span.list-nick", "Andstæðingur"),
32469
- mt("span.list-chall", "Hvernig"),
32470
- mt("span.list-info-hdr", "Ferill"),
32471
- ]),
32472
- n
32473
- ];
32474
- }
32475
- function vwRecentList() {
32476
- function vwList() {
32477
- if (model.recentList === null)
32478
- model.loadRecentList();
32479
- return m(RecentList, {
32480
- view,
32481
- id: "recentlist",
32482
- recentList: model.recentList || [],
32483
- });
32484
- }
32485
- return [
32486
- m(".listitem.listheader", [
32487
- m("span.list-win", glyphGrayed("bookmark", { title: ts('Sigur') })),
32488
- mt("span.list-ts-short", "Viðureign lauk"),
32489
- mt("span.list-nick", "Andstæðingur"),
32490
- mt("span.list-scorehdr", "Úrslit"),
32491
- m("span.list-elo-hdr", [
32492
- m("span.glyphicon.glyphicon-user.elo-hdr-left", { title: ts('Mennskir andstæðingar') }),
32493
- t("Elo"),
32494
- m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
32495
- ]),
32496
- mt("span.list-duration", "Lengd"),
32497
- m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
32953
+ // Local components for MainTabs
32954
+ const MainTabHeader = {
32955
+ // Tab navigation headers with game/challenge counts
32956
+ view: (vnode) => {
32957
+ const { view } = vnode.attrs;
32958
+ const { model } = view;
32959
+ const { numGames, numChallenges } = model;
32960
+ return [
32961
+ m(HeaderLogo, { hidden: true }),
32962
+ m("ul", [
32963
+ m("li", m("a[href='#tabs-1']", [
32964
+ glyph("th"), m("span.tab-legend", t("Viðureignir")),
32965
+ m("span", {
32966
+ id: 'numgames',
32967
+ style: numGames ? 'display: inline-block' : ''
32968
+ }, numGames)
32969
+ ])),
32970
+ m("li", m("a[href='#tabs-2']", [
32971
+ glyph("hand-right"), m("span.tab-legend", t("Áskoranir")),
32972
+ // Blink if we have timed games where the opponent is ready
32973
+ m("span" + (model.oppReady ? ".opp-ready" : ""), {
32974
+ id: "numchallenges",
32975
+ style: numChallenges ? 'display: inline-block' : ''
32976
+ }, numChallenges)
32977
+ ])),
32978
+ m("li", m("a[href='#tabs-3']", [glyph("user"), m("span.tab-legend", t("Andstæðingar"))])),
32979
+ m("li.no-mobile-list", m("a[href='#tabs-4']", [glyph("bookmark"), m("span.tab-legend", t("Ferill"))]))
32980
+ ])
32981
+ ];
32982
+ }
32983
+ };
32984
+ const RecentListWithHeader = {
32985
+ // Recent games list with header
32986
+ view: (vnode) => {
32987
+ const { view } = vnode.attrs;
32988
+ const { model } = view;
32989
+ if (model.recentList === null)
32990
+ model.loadRecentList();
32991
+ return [
32992
+ m(".listitem.listheader", [
32993
+ m("span.list-win", glyphGrayed("bookmark", { title: ts('Sigur') })),
32994
+ mt("span.list-ts-short", "Viðureign lauk"),
32995
+ mt("span.list-nick", "Andstæðingur"),
32996
+ mt("span.list-scorehdr", "Úrslit"),
32997
+ m("span.list-elo-hdr", [
32998
+ m("span.glyphicon.glyphicon-user.elo-hdr-left", { title: ts('Mennskir andstæðingar') }),
32999
+ t("Elo"),
33000
+ m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
32498
33001
  ]),
32499
- vwList()
32500
- ];
32501
- }
32502
- function vwUserButton(id, icon, text) {
32503
- var _a;
32504
- // Select the type of user list (robots, fav, alike, elo)
32505
- const sel = ((_a = model.userListCriteria) === null || _a === void 0 ? void 0 : _a.query) || "robots";
32506
- return m("span", {
32507
- className: (id === sel ? "shown" : ""),
32508
- id: id,
32509
- onclick: (ev) => {
32510
- if (id === "elo") {
32511
- // Load Elo rating list, defaulting to the human-only rating
32512
- model.userListCriteria = { query: "elo", spec: "human" };
32513
- model.userList = null;
32514
- view.actions.withSpinner(() => model.loadEloRatingList("human"));
32515
- }
32516
- else {
32517
- // Load user list
32518
- view.actions.loadUsersByType(id, true);
32519
- model.eloRatingSpec = null;
32520
- model.eloRatingList = null;
32521
- }
32522
- ev.preventDefault();
33002
+ mt("span.list-duration", "Lengd"),
33003
+ m(ProModeIndicator, { header: true })
33004
+ ]),
33005
+ m(RecentList, { view, id: "recentlist", recentList: model.recentList || [] })
33006
+ ];
33007
+ }
33008
+ };
33009
+ const UserButton = {
33010
+ // User list type button (robots, fav, alike, elo)
33011
+ view: (vnode) => {
33012
+ const { view, buttonId, icon, text } = vnode.attrs;
33013
+ const { model } = view;
33014
+ const sel = model.userListCriteria?.query || "robots";
33015
+ return m("span", {
33016
+ className: (buttonId === sel ? "shown" : ""),
33017
+ id: buttonId,
33018
+ onclick: (ev) => {
33019
+ if (buttonId === "elo") {
33020
+ // Load Elo rating list, defaulting to the human-only rating
33021
+ model.userListCriteria = { query: "elo", spec: "human" };
33022
+ model.userList = null;
33023
+ view.actions.withSpinner(() => model.loadEloRatingList("human"));
32523
33024
  }
32524
- }, [glyph(icon, { style: { padding: 0 } }), nbsp(), text]);
32525
- }
32526
- function vwUserList() {
32527
- var _a, _b, _c, _d, _e;
32528
- function vwList(list) {
32529
- function itemize(item, i) {
32530
- // Generate a list item about a user
32531
- const isRobot = item.userid.indexOf("robot-") === 0;
32532
- let fullname = [];
32533
- // Online and accepting challenges
32534
- if (item.ready && !isRobot) {
32535
- fullname.push(m("span.ready-btn", { title: ts("Álínis og tekur við áskorunum") }));
32536
- fullname.push(nbsp());
32537
- }
32538
- // Willing to accept challenges for timed games
32539
- if (item.ready_timed) {
32540
- fullname.push(m("span.timed-btn", { title: ts("Til í viðureign með klukku") }));
32541
- fullname.push(nbsp());
32542
- }
32543
- // Fair play commitment
32544
- if (item.fairplay) {
32545
- fullname.push(m("span.fairplay-btn", { title: ts("Skraflar án hjálpartækja") }));
32546
- fullname.push(nbsp());
32547
- }
32548
- fullname.push(item.fullname);
32549
- function fav() {
32550
- if (isRobot)
32551
- return m("span.list-fav", { style: { cursor: "default" } }, glyph("star-empty"));
32552
- return m("span.list-fav", {
32553
- title: ts("Uppáhald"),
32554
- onclick: (ev) => {
32555
- view.actions.toggleFavorite(item.userid, item.fav);
32556
- item.fav = !item.fav;
32557
- ev.preventDefault();
32558
- }
32559
- }, glyph(item.fav ? "star" : "star-empty"));
32560
- }
32561
- function userLink() {
32562
- if (isRobot)
32563
- return m("a", {
32564
- href: "",
32565
- onclick: (ev) => {
32566
- // Start a new game against the robot
32567
- view.actions.startRobotGame(item.userid);
32568
- ev.preventDefault();
32569
- }
32570
- }, [
32571
- m("span.list-nick", [glyph("cog"), nbsp(), item.nick]),
32572
- m("span.list-fullname-robot", fullname)
32573
- ]);
32574
- else
32575
- return m.fragment({}, [
32576
- m("span.list-nick", item.nick),
32577
- m("span.list-fullname", fullname),
32578
- m("span.list-human-elo", item.human_elo)
32579
- ]);
32580
- }
32581
- return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
32582
- m(ChallengeButton, { view, item }),
32583
- fav(),
32584
- userLink(),
32585
- m(UserInfoButton, { view, item }),
32586
- ]);
33025
+ else {
33026
+ // Load user list
33027
+ view.actions.loadUsersByType(buttonId, true);
33028
+ model.eloRatingSpec = null;
33029
+ model.eloRatingList = null;
32587
33030
  }
32588
- return m("div", { id: "userlist" }, list.map(itemize));
32589
- }
32590
- // The type of list to show; by default it's 'robots'
32591
- const query = (_b = (_a = model.userListCriteria) === null || _a === void 0 ? void 0 : _a.query) !== null && _b !== void 0 ? _b : "";
32592
- const spec = (_d = (_c = model.userListCriteria) === null || _c === void 0 ? void 0 : _c.spec) !== null && _d !== void 0 ? _d : "";
32593
- const loadingElo = model.eloRatingSpec === undefined;
32594
- const listType = query || "robots";
32595
- if (listType === "elo" || loadingElo)
32596
- // Show Elo list
32597
- return [
32598
- m(EloPage, {
32599
- id: "elolist",
32600
- view: view,
32601
- spec: (_e = model.eloRatingSpec) !== null && _e !== void 0 ? _e : null,
32602
- key: "elopage",
32603
- })
32604
- ];
32605
- // Show normal user list
32606
- let list = [];
32607
- if (model.userList === undefined) ;
32608
- else if (model.userList === null || query !== listType) {
32609
- view.actions.loadUsersByType(listType, true);
32610
- }
32611
- else {
32612
- list = model.userList;
33031
+ ev.preventDefault();
32613
33032
  }
32614
- const nothingFound = list.length === 0 && listType === "search" && spec !== "";
32615
- const robotList = listType === "robots";
32616
- return [
32617
- m(".listitem.listheader", [
32618
- m("span.list-ch", glyphGrayed("hand-right", { title: ts('Skora á') })),
32619
- m("span.list-fav", glyph("star-empty", { title: ts('Uppáhald') })),
32620
- mt("span.list-nick", "Einkenni"),
32621
- mt("span.list-fullname", "Nafn og merki"),
32622
- robotList ? "" : mt("span.list-human-elo[id='usr-list-elo']", "Elo"),
32623
- robotList ? "" : mt("span.list-info-hdr[id='usr-list-info']", "Ferill"),
32624
- ]),
32625
- vwList(list),
32626
- // Show indicator if search didn't find any users matching the criteria
32627
- nothingFound ?
32628
- m("div", { id: "user-no-match", style: { display: "block" } }, [
32629
- glyph("search"),
32630
- " ",
32631
- m("span", { id: "search-prefix" }, spec),
32632
- t(" finnst ekki")
32633
- ])
32634
- : undefined
32635
- ];
32636
- }
32637
- function vwStats() {
32638
- // View the user's own statistics summary
32639
- const ownStats = model.ownStats;
32640
- if (ownStats === null)
32641
- model.loadOwnStats();
32642
- return ownStats && m(StatsDisplay, { view, id: 'own-stats', ownStats });
32643
- }
32644
- function vwBest() {
32645
- // View the user's own best game and word scores
32646
- const ownStats = model.ownStats;
32647
- if (ownStats === null)
32648
- model.loadOwnStats();
32649
- return ownStats && m(BestDisplay, { id: 'own-best', ownStats, myself: true });
32650
- }
32651
- function tab1() {
32652
- // Ongoing games
32653
- return m("div", { id: 'tabs-1' }, [
32654
- mt("p.no-mobile-block", [
32655
- mt("strong", "Viðureignir sem standa yfir"),
32656
- "click_on_game", // "Click on a game to view it and make a move if"
32657
- glyph("flag"),
32658
- " þú átt leik"
32659
- ]),
32660
- vwGamelist()
32661
- ]);
32662
- }
32663
- function tab2() {
32664
- // Challenges received and sent
32665
- return m("div", { id: 'tabs-2' }, [
32666
- mt("p.no-mobile-block", [
32667
- mt("strong", "Skorað á þig"),
32668
- "click_on_challenge", // "Click on a challenge to accept it and start a game, or on"
32669
- glyph("thumbs-down", { style: { "margin-left": "6px", "margin-right": "6px" } }),
32670
- " til að hafna henni"
32671
- ]),
32672
- vwChallenges(true),
32673
- mt("p.no-mobile-block", [
32674
- mt("strong", "Þú skorar á aðra"),
32675
- " - smelltu á ",
32676
- glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
32677
- " til afturkalla áskorun"
32678
- ]),
32679
- vwChallenges(false)
32680
- ]);
32681
- }
32682
- function tab3() {
32683
- // Opponent lists
32684
- return m("div", { id: 'tabs-3' }, [
32685
- m("div", { id: 'initials' }, [
32686
- m(".user-cat[id='user-headings']", [
32687
- vwUserButton("robots", "cog", ts("Þjarkar")),
32688
- " ",
32689
- vwUserButton("fav", "star", ts("Uppáhalds")),
32690
- " ",
32691
- vwUserButton("live", "flash", ts("Álínis")),
32692
- " ",
32693
- vwUserButton("alike", "resize-small", ts("Svipaðir")),
32694
- " ",
32695
- vwUserButton("elo", "crown", ts("Topp 100"))
32696
- ]),
32697
- m(SearchButton, { view })
32698
- ]),
32699
- vwUserList()
32700
- ]);
32701
- }
32702
- function tab4() {
32703
- // Recent games and statistics
32704
- return m("div", { id: 'tabs-4' }, [
32705
- vwStats(),
32706
- vwBest(),
32707
- mt("p.no-mobile-block", [
32708
- mt("strong", "Nýlegar viðureignir þínar"),
32709
- "click_to_review" // "Click on a game to review it"
33033
+ }, [glyph(icon, { style: { padding: 0 } }), nbsp(), text]);
33034
+ }
33035
+ };
33036
+ const OwnStats = {
33037
+ // User's own statistics summary
33038
+ view: (vnode) => {
33039
+ const { view } = vnode.attrs;
33040
+ const { model } = view;
33041
+ const { ownStats } = model;
33042
+ if (ownStats === null)
33043
+ model.loadOwnStats();
33044
+ return ownStats ? m(StatsDisplay, { view, id: 'own-stats', ownStats }) : null;
33045
+ }
33046
+ };
33047
+ const OwnBest = {
33048
+ // User's own best game and word scores
33049
+ view: (vnode) => {
33050
+ const { view } = vnode.attrs;
33051
+ const { model } = view;
33052
+ const { ownStats } = model;
33053
+ if (ownStats === null)
33054
+ model.loadOwnStats();
33055
+ return ownStats ? m(BestDisplay, { id: 'own-best', ownStats, myself: true }) : null;
33056
+ }
33057
+ };
33058
+ const Tab1 = {
33059
+ // Ongoing games tab
33060
+ view: (vnode) => {
33061
+ const { view } = vnode.attrs;
33062
+ return m("div", { id: 'tabs-1' }, [
33063
+ mt("p.no-mobile-block", [
33064
+ mt("strong", "Viðureignir sem standa yfir"),
33065
+ "click_on_game", // "Click on a game to view it and make a move if"
33066
+ glyph("flag"),
33067
+ " þú átt leik"
33068
+ ]),
33069
+ m(GameList, { view, id: 'gamelist' })
33070
+ ]);
33071
+ }
33072
+ };
33073
+ const Tab2 = {
33074
+ // Challenges received and sent tab
33075
+ view: (vnode) => {
33076
+ const { view } = vnode.attrs;
33077
+ return m("div", { id: 'tabs-2' }, [
33078
+ mt("p.no-mobile-block", [
33079
+ mt("strong", "Skorað á þig"),
33080
+ "click_on_challenge", // "Click on a challenge to accept it and start a game, or on"
33081
+ glyph("thumbs-down", { style: { "margin-left": "6px", "margin-right": "6px" } }),
33082
+ " til að hafna henni"
33083
+ ]),
33084
+ m(ChallengeList, { view, showReceived: true }),
33085
+ mt("p.no-mobile-block", [
33086
+ mt("strong", "Þú skorar á aðra"),
33087
+ " - smelltu á ",
33088
+ glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
33089
+ " til að afturkalla áskorun"
33090
+ ]),
33091
+ m(ChallengeList, { view, showReceived: false })
33092
+ ]);
33093
+ }
33094
+ };
33095
+ const Tab3 = {
33096
+ // Opponent lists tab
33097
+ view: (vnode) => {
33098
+ const { view } = vnode.attrs;
33099
+ return m("div", { id: 'tabs-3' }, [
33100
+ m("div", { id: 'initials' }, [
33101
+ m(".user-cat[id='user-headings']", [
33102
+ m(UserButton, { view, buttonId: "robots", icon: "cog", text: ts("Þjarkar") }),
33103
+ " ",
33104
+ m(UserButton, { view, buttonId: "fav", icon: "star", text: ts("Uppáhalds") }),
33105
+ " ",
33106
+ m(UserButton, { view, buttonId: "live", icon: "flash", text: ts("Álínis") }),
33107
+ " ",
33108
+ m(UserButton, { view, buttonId: "alike", icon: "resize-small", text: ts("Svipaðir") }),
33109
+ " ",
33110
+ m(UserButton, { view, buttonId: "elo", icon: "crown", text: ts("Topp 100") })
32710
33111
  ]),
32711
- vwRecentList()
32712
- ]);
32713
- }
33112
+ m(SearchButton, { view })
33113
+ ]),
33114
+ m(UserList, { view, id: 'userlist' })
33115
+ ]);
33116
+ }
33117
+ };
33118
+ const Tab4 = {
33119
+ // Recent games and statistics tab
33120
+ view: (vnode) => {
33121
+ const { view } = vnode.attrs;
33122
+ return m("div", { id: 'tabs-4' }, [
33123
+ m(OwnStats, { view }),
33124
+ m(OwnBest, { view }),
33125
+ mt("p.no-mobile-block", [
33126
+ mt("strong", "Nýlegar viðureignir þínar"),
33127
+ "click_to_review" // "Click on a game to review it"
33128
+ ]),
33129
+ m(RecentListWithHeader, { view })
33130
+ ]);
33131
+ }
33132
+ };
33133
+ const MainTabs = {
33134
+ // Main tabs component containing all tabs and their content
33135
+ view: (vnode) => {
33136
+ const { view } = vnode.attrs;
32714
33137
  return m(".tabbed-page", m("div", { id: 'main-tabs' }, [
32715
- vwMainTabHeader(),
33138
+ m(MainTabHeader, { view }),
32716
33139
  m("div.tab-scroll-area", [
32717
- tab1(),
32718
- tab2(),
32719
- tab4(),
32720
- tab3(),
33140
+ m(Tab1, { view }),
33141
+ m(Tab2, { view }),
33142
+ m(Tab4, { view }),
33143
+ m(Tab3, { view }),
32721
33144
  ]),
32722
33145
  ]));
32723
33146
  }
32724
- return {
32725
- view: (vnode) => {
32726
- const view = vnode.attrs.view;
32727
- return m.fragment({}, [
32728
- m(LeftLogo, { hidden: true }), // No legend, scale up by 50%
32729
- m(UserId, { view }),
32730
- m(Info),
32731
- m(TogglerReady, { view }),
32732
- m(TogglerReadyTimed, { view }),
32733
- m("div", {
32734
- oncreate: (vnode) => {
32735
- makeTabs(view, "main-tabs", undefined, false, vnode);
32736
- },
32737
- onupdate: updateSelection
32738
- }, vwMainTabs(view)),
32739
- ]);
32740
- }
32741
- };
33147
+ };
33148
+ const Main = {
33149
+ // Main screen with tabs
33150
+ view: (vnode) => {
33151
+ const { view } = vnode.attrs;
33152
+ return [
33153
+ m(LeftLogo, { hidden: true }), // No legend, scale up by 50%
33154
+ m(UserId, { view }),
33155
+ m(Info),
33156
+ m(TogglerReady, { view }),
33157
+ m(TogglerReadyTimed, { view }),
33158
+ m("div", {
33159
+ oncreate: (vnode) => {
33160
+ makeTabs(view, "main-tabs", undefined, false, vnode);
33161
+ },
33162
+ onupdate: updateSelection
33163
+ }, m(MainTabs, { view })),
33164
+ ];
33165
+ }
32742
33166
  };
32743
33167
 
32744
33168
  /*
@@ -32771,13 +33195,12 @@ const _updateStats = (attrs, state) => {
32771
33195
  });
32772
33196
  };
32773
33197
  const _updateRecentList = (attrs, state) => {
32774
- var _a, _b;
32775
33198
  // Fetch the recent game list of the given user
32776
33199
  if (state.loadingRecentList)
32777
33200
  return;
32778
33201
  state.loadingRecentList = true;
32779
33202
  const { model } = attrs.view;
32780
- model.loadUserRecentList(attrs.userid, state.versusAll ? null : (_b = (_a = model.state) === null || _a === void 0 ? void 0 : _a.userId) !== null && _b !== void 0 ? _b : "", (json) => {
33203
+ model.loadUserRecentList(attrs.userid, state.versusAll ? null : model.state?.netskraflUserId ?? "", (json) => {
32781
33204
  if (json && json.result === 0)
32782
33205
  state.recentList = json.recentlist;
32783
33206
  else
@@ -32832,10 +33255,9 @@ const UserInfoDialog = {
32832
33255
  m(".usr-info-fav", {
32833
33256
  title: ts('Uppáhald'),
32834
33257
  onclick: (ev) => {
32835
- var _a;
32836
33258
  // Toggle the favorite setting
32837
33259
  ev.preventDefault();
32838
- view.actions.toggleFavorite(vnode.attrs.userid, (_a = stats.favorite) !== null && _a !== void 0 ? _a : false);
33260
+ view.actions.toggleFavorite(vnode.attrs.userid, stats.favorite ?? false);
32839
33261
  stats.favorite = !stats.favorite;
32840
33262
  }
32841
33263
  }, stats.favorite ? glyph("star") : glyph("star-empty"))
@@ -32865,7 +33287,7 @@ const UserInfoDialog = {
32865
33287
  m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
32866
33288
  ]),
32867
33289
  mt("span.list-duration", "Lengd"),
32868
- m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
33290
+ m(ProModeIndicator, { header: true })
32869
33291
  ]),
32870
33292
  m(RecentList, { view, id: 'usr-recent', recentList }), // Recent game list
32871
33293
  m(StatsDisplay, { view, id: 'usr-stats', ownStats: stats }),
@@ -32904,8 +33326,7 @@ const UserPrefsDialog = (initialVnode) => {
32904
33326
  m(".errinput", [glyph("arrow-up"), nbsp(), err[propname] || ""]) : "";
32905
33327
  }
32906
33328
  function getToggle(elemId) {
32907
- var _a;
32908
- const cls2 = (_a = document.querySelector("#" + elemId + "-toggler #opt2")) === null || _a === void 0 ? void 0 : _a.classList;
33329
+ const cls2 = document.querySelector("#" + elemId + "-toggler #opt2")?.classList;
32909
33330
  if (!cls2)
32910
33331
  return false;
32911
33332
  return cls2.contains("selected");
@@ -33016,7 +33437,7 @@ const UserPrefsDialog = (initialVnode) => {
33016
33437
  // Open the container's subscription page
33017
33438
  ev.preventDefault();
33018
33439
  logEvent("click_friend", {
33019
- userid: state.userId, locale: state.locale
33440
+ userid: state.netskraflUserId, locale: state.locale
33020
33441
  });
33021
33442
  // Navigate to the container-supplied URL
33022
33443
  window.location.href = state.subscriptionUrl;
@@ -33452,7 +33873,7 @@ const Buttons = {
33452
33873
  sc && r.push(sc);
33453
33874
  }
33454
33875
  // Is the server processing a move?
33455
- if (game === null || game === void 0 ? void 0 : game.moveInProgress) {
33876
+ if (game?.moveInProgress) {
33456
33877
  r.push(m(".waitmove", m(".animated-waitmove", m(AnimatedNetskraflLogo, {
33457
33878
  withCircle: false,
33458
33879
  }))));
@@ -33494,7 +33915,6 @@ class DragManager {
33494
33915
  return (p.x >= rect.left) && (p.x < rect.right) && (p.y >= rect.top) && (p.y < rect.bottom);
33495
33916
  }
33496
33917
  constructor(e, dropHandler) {
33497
- var _a, _b;
33498
33918
  this.parentElement = null;
33499
33919
  this.parentRect = null;
33500
33920
  this.offsetX = 0;
@@ -33515,7 +33935,7 @@ class DragManager {
33515
33935
  this.draggedElement = dragged;
33516
33936
  this.dropHandler = dropHandler;
33517
33937
  this.parentElement = dragged.parentElement;
33518
- this.parentRect = (_b = (_a = this.parentElement) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) !== null && _b !== void 0 ? _b : null;
33938
+ this.parentRect = this.parentElement?.getBoundingClientRect() ?? null;
33519
33939
  // Find out the bounding rectangle of the element
33520
33940
  // before starting to apply modifications
33521
33941
  const rect = dragged.getBoundingClientRect();
@@ -33873,7 +34293,6 @@ const DropTargetSquare = {
33873
34293
  const RackTiles = {
33874
34294
  // A rack of 7 tiles (tiles only, no buttons)
33875
34295
  view: (vnode) => {
33876
- var _a;
33877
34296
  const { view, game, review } = vnode.attrs;
33878
34297
  // If review==true, this is a review rack
33879
34298
  // that is not a drop target and whose color reflects the
@@ -33882,7 +34301,7 @@ const RackTiles = {
33882
34301
  return undefined;
33883
34302
  const { model } = view;
33884
34303
  let r = [];
33885
- const reviewMove = (_a = model.reviewMove) !== null && _a !== void 0 ? _a : 0;
34304
+ const reviewMove = model.reviewMove ?? 0;
33886
34305
  // Avoid flicker when paging through game review screen,
33887
34306
  // as model.reviewMove becomes undefined or 0 while we
33888
34307
  // are fetching the next move to display
@@ -34076,8 +34495,7 @@ const PlayerName = (initialVnode) => {
34076
34495
  const { view } = initialVnode.attrs;
34077
34496
  const model = view.model;
34078
34497
  function lookAtPlayer(game, ev, player, side) {
34079
- var _a;
34080
- if (!((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen))
34498
+ if (!(model.state?.uiFullscreen))
34081
34499
  // Don't do anything on mobile, and allow the click
34082
34500
  // to propagate to the parent
34083
34501
  return;
@@ -34292,14 +34710,13 @@ const Chat = (initialVnode) => {
34292
34710
  return str;
34293
34711
  }
34294
34712
  function chatMessages() {
34295
- var _a, _b;
34296
34713
  let r = [];
34297
- if ((game === null || game === void 0 ? void 0 : game.chatLoading) || !(game === null || game === void 0 ? void 0 : game.messages))
34714
+ if (game?.chatLoading || !game?.messages)
34298
34715
  return r;
34299
34716
  var key = 0;
34300
- const userId = (_b = (_a = model.state) === null || _a === void 0 ? void 0 : _a.userId) !== null && _b !== void 0 ? _b : "";
34717
+ const userId = model.state?.netskraflUserId ?? "";
34301
34718
  for (const msg of game.messages) {
34302
- let p = player !== null && player !== void 0 ? player : 0;
34719
+ let p = player ?? 0;
34303
34720
  if (msg.from_userid != userId)
34304
34721
  p = 1 - p;
34305
34722
  const mTs = makeTimestamp(msg.ts, key);
@@ -34337,13 +34754,13 @@ const Chat = (initialVnode) => {
34337
34754
  setInput("msg", "");
34338
34755
  }
34339
34756
  }
34340
- const numMessages = (game === null || game === void 0 ? void 0 : game.messages) ? game.messages.length : 0;
34757
+ const numMessages = game?.messages ? game.messages.length : 0;
34341
34758
  return {
34342
34759
  view: () => m(".chat-container", {
34343
34760
  style: "z-index: 6" // Appear on top of board on mobile
34344
34761
  // key: uuid
34345
34762
  }, [
34346
- m(".chat-area" + ((game === null || game === void 0 ? void 0 : game.showClock()) ? ".with-clock" : ""), {
34763
+ m(".chat-area" + (game?.showClock() ? ".with-clock" : ""), {
34347
34764
  id: 'chat-area',
34348
34765
  // Make sure that we see the bottom-most chat message
34349
34766
  oncreate: scrollChatToBottom,
@@ -34433,7 +34850,7 @@ const MoveListItem = (initialVnode) => {
34433
34850
  else {
34434
34851
  // Show a friend promotion dialog
34435
34852
  logEvent("click_review", {
34436
- userid: state.userId, locale: state.locale
34853
+ userid: state.netskraflUserId, locale: state.locale
34437
34854
  });
34438
34855
  view.showFriendPromo();
34439
34856
  }
@@ -34617,7 +35034,7 @@ const Movelist = (initialVnode) => {
34617
35034
  onupdate: () => { setTimeout(scrollMovelistToBottom); }
34618
35035
  }, movelist()),
34619
35036
  // Show the bag here on mobile
34620
- (state === null || state === void 0 ? void 0 : state.uiFullscreen) ? "" : m(Bag, { bag, newbag })
35037
+ state?.uiFullscreen ? "" : m(Bag, { bag, newbag })
34621
35038
  ])
34622
35039
  };
34623
35040
  };
@@ -34788,7 +35205,7 @@ const RightColumn = (initialVnode) => {
34788
35205
  break;
34789
35206
  }
34790
35207
  const tabgrp = m(TabGroup, { view });
34791
- return m(".right-area" + ((game === null || game === void 0 ? void 0 : game.showClock()) ? ".with-clock" : ""), component ? [tabgrp, component] : [tabgrp]);
35208
+ return m(".right-area" + (game?.showClock() ? ".with-clock" : ""), component ? [tabgrp, component] : [tabgrp]);
34792
35209
  }
34793
35210
  return {
34794
35211
  view: () => m(".rightcol", [
@@ -34879,14 +35296,14 @@ const GameView = {
34879
35296
  m(RightColumn, { view }),
34880
35297
  m(BoardArea, { view, game }),
34881
35298
  // The bag is visible in fullscreen
34882
- (state === null || state === void 0 ? void 0 : state.uiFullscreen) ? m(Bag, { bag: bag, newbag: newbag }) : "",
35299
+ state?.uiFullscreen ? m(Bag, { bag: bag, newbag: newbag }) : "",
34883
35300
  game.askingForBlank ? m(BlankDialog, { game }) : ""
34884
35301
  ]),
34885
35302
  // The left margin stuff: back button, square color help, info/help button
34886
35303
  // These elements appear after the game container, since we want
34887
35304
  // them to be above it in the z-order
34888
35305
  m(LeftLogo),
34889
- (state === null || state === void 0 ? void 0 : state.beginner) ? m(Beginner, { view, showClose: true }) : "",
35306
+ state?.beginner ? m(Beginner, { view, showClose: true }) : "",
34890
35307
  m(Info),
34891
35308
  ]);
34892
35309
  }
@@ -35097,7 +35514,6 @@ const vwBestMove = (view, moveIndex, bestMoveIndex, move, info) => {
35097
35514
  }
35098
35515
  };
35099
35516
  const vwScoreReview = (view, moveIndex) => {
35100
- var _a;
35101
35517
  // Shows the score of the current move within a game review screen
35102
35518
  const game = view.model.game;
35103
35519
  if (!game)
@@ -35111,7 +35527,7 @@ const vwScoreReview = (view, moveIndex) => {
35111
35527
  return undefined;
35112
35528
  let sc = [".score"];
35113
35529
  if (moveIndex > 0) {
35114
- if (moveIndex % 2 === ((_a = game.player) !== null && _a !== void 0 ? _a : 0))
35530
+ if (moveIndex % 2 === (game.player ?? 0))
35115
35531
  // Opponent's move
35116
35532
  sc.push("opponent");
35117
35533
  else
@@ -35259,12 +35675,11 @@ const vwStatsReview = (view) => {
35259
35675
  ]);
35260
35676
  };
35261
35677
  const vwButtonsReview = (view, moveIndex) => {
35262
- var _a, _b;
35263
35678
  // The navigation buttons below the board on the review screen
35264
35679
  const model = view.model;
35265
35680
  const game = model.game;
35266
- const numMoves = (_a = game === null || game === void 0 ? void 0 : game.moves.length) !== null && _a !== void 0 ? _a : 0;
35267
- const gameUuid = (_b = game === null || game === void 0 ? void 0 : game.uuid) !== null && _b !== void 0 ? _b : "";
35681
+ const numMoves = game?.moves.length ?? 0;
35682
+ const gameUuid = game?.uuid ?? "";
35268
35683
  let r = [];
35269
35684
  if (!gameUuid)
35270
35685
  return r;
@@ -35337,13 +35752,12 @@ const Review = (initialVnode) => {
35337
35752
  }
35338
35753
  return {
35339
35754
  view: () => {
35340
- var _a;
35341
35755
  let r = [];
35342
35756
  const { model } = view;
35343
35757
  const { game } = model;
35344
35758
  if (game) {
35345
35759
  // Create a list of major elements that we're showing
35346
- const moveIndex = (_a = model.reviewMove) !== null && _a !== void 0 ? _a : 0;
35760
+ const moveIndex = model.reviewMove ?? 0;
35347
35761
  const bestMoves = model.bestMoves || [];
35348
35762
  r.push(vwRightColumn(game, moveIndex, bestMoves));
35349
35763
  r.push(m(BoardReview, { view, moveIndex }));
@@ -35498,12 +35912,11 @@ class View {
35498
35912
  this.actions.hideSpinner();
35499
35913
  }
35500
35914
  notifyMediaChange() {
35501
- var _a, _b;
35502
35915
  // The view is changing, between mobile and fullscreen
35503
35916
  // and/or between portrait and landscape: ensure that
35504
35917
  // we don't end up with a selected game tab that is not visible
35505
35918
  const model = this.model;
35506
- if (((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen) || ((_b = model.state) === null || _b === void 0 ? void 0 : _b.uiLandscape)) {
35919
+ if (model.state?.uiFullscreen || model.state?.uiLandscape) {
35507
35920
  // In this case, there is no board tab:
35508
35921
  // show the movelist
35509
35922
  if (this.setSelectedTab("movelist"))
@@ -35540,7 +35953,7 @@ class View {
35540
35953
  }
35541
35954
  this.boardScale = 1.0;
35542
35955
  const boardParent = document.getElementById("board-parent");
35543
- const board = boardParent === null || boardParent === void 0 ? void 0 : boardParent.children[0];
35956
+ const board = boardParent?.children[0];
35544
35957
  if (board) {
35545
35958
  board.style.transition = 'none';
35546
35959
  board.style.transform = `translate(0px, 0px) scale(1)`;
@@ -35549,7 +35962,6 @@ class View {
35549
35962
  boardParent.scrollTo(0, 0);
35550
35963
  }
35551
35964
  updateScale() {
35552
- var _a;
35553
35965
  const model = this.model;
35554
35966
  // Use either the regular game or the riddle (Gáta Dagsins)
35555
35967
  const game = model.game || model.riddle;
@@ -35560,7 +35972,7 @@ class View {
35560
35972
  // taking clamping into account to ensure that the board always fills
35561
35973
  // the viewport.
35562
35974
  const boardParent = document.getElementById("board-parent");
35563
- const board = boardParent === null || boardParent === void 0 ? void 0 : boardParent.children[0];
35975
+ const board = boardParent?.children[0];
35564
35976
  if (!board || !boardParent)
35565
35977
  return;
35566
35978
  const offset = 3;
@@ -35570,7 +35982,7 @@ class View {
35570
35982
  const c = coord(row, col);
35571
35983
  // board.style.transform = `translate(0, 0) scale(1.0)`;
35572
35984
  const el = document.getElementById("sq_" + c);
35573
- const elRect = el === null || el === void 0 ? void 0 : el.getBoundingClientRect();
35985
+ const elRect = el?.getBoundingClientRect();
35574
35986
  const boardRect = boardParent.getBoundingClientRect();
35575
35987
  if (elRect && boardRect) {
35576
35988
  // Get the dimensions of the scrollable area
@@ -35612,7 +36024,7 @@ class View {
35612
36024
  }, 350);
35613
36025
  }
35614
36026
  };
35615
- if (!game || ((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen) || game.moveInProgress) {
36027
+ if (!game || model.state?.uiFullscreen || game.moveInProgress) {
35616
36028
  // No game or we're in full screen mode: always 100% scale
35617
36029
  // Also, as soon as a move is being processed by the server, we zoom out
35618
36030
  this.boardScale = 1.0; // Needs to be done before setTimeout() call
@@ -35669,12 +36081,10 @@ class View {
35669
36081
  function wireQuestions(vnode) {
35670
36082
  // Clicking on a question brings the corresponding answer into view
35671
36083
  // This is achieved by wiring up all contained a[href="#faq-*"] links
35672
- var _a;
35673
36084
  function showAnswer(ev, href) {
35674
- var _a;
35675
36085
  // this points to the vnode
35676
36086
  vnode.state.selected = 1; // FAQ tab
35677
- (_a = vnode.dom.querySelector(href)) === null || _a === void 0 ? void 0 : _a.scrollIntoView();
36087
+ vnode.dom.querySelector(href)?.scrollIntoView();
35678
36088
  ev.preventDefault();
35679
36089
  }
35680
36090
  const anchors = vnode.dom.querySelectorAll("a");
@@ -35688,7 +36098,7 @@ class View {
35688
36098
  // Go to the FAQ tab and scroll the requested question into view
35689
36099
  selectTab(vnode, 1);
35690
36100
  vnode.state.selected = 1; // FAQ tab
35691
- (_a = vnode.dom.querySelector("#faq-" + faqNumber.toString())) === null || _a === void 0 ? void 0 : _a.scrollIntoView();
36101
+ vnode.dom.querySelector("#faq-" + faqNumber.toString())?.scrollIntoView();
35692
36102
  }
35693
36103
  }
35694
36104
  // Output literal HTML obtained from rawhelp.html on the server
@@ -35766,14 +36176,13 @@ class Actions {
35766
36176
  // when view is available
35767
36177
  }
35768
36178
  onNavigateTo(routeName, params, view) {
35769
- var _a, _b;
35770
36179
  // We have navigated to a new route
35771
36180
  // If navigating to something other than help,
35772
36181
  // we need to have a logged-in user
35773
36182
  const model = this.model;
35774
36183
  model.routeName = routeName;
35775
36184
  model.params = params;
35776
- const uuid = (_a = params.uuid) !== null && _a !== void 0 ? _a : "";
36185
+ const uuid = params.uuid ?? "";
35777
36186
  if (routeName == "game") {
35778
36187
  // New game route: initiate loading of the game into the model
35779
36188
  if (model.game !== null) {
@@ -35783,10 +36192,9 @@ class Actions {
35783
36192
  const deleteZombie = params.zombie === "1";
35784
36193
  // Load the game, and attach it to the Firebase listener once it's loaded
35785
36194
  model.loadGame(uuid, () => {
35786
- var _a;
35787
36195
  this.attachListenerToGame(uuid, view);
35788
36196
  setTimeout(scrollMovelistToBottom);
35789
- if (!((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen))
36197
+ if (!model.state?.uiFullscreen)
35790
36198
  // Mobile UI: show board tab
35791
36199
  view.setSelectedTab("board");
35792
36200
  }, deleteZombie);
@@ -35814,10 +36222,9 @@ class Actions {
35814
36222
  // Different game than we had before: load it, and then
35815
36223
  // fetch the best moves
35816
36224
  model.loadGame(uuid, () => {
35817
- var _a;
35818
36225
  model.loadBestMoves(move);
35819
36226
  setTimeout(scrollMovelistToBottom);
35820
- if (!((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen))
36227
+ if (!model.state?.uiFullscreen)
35821
36228
  // Mobile UI: show board tab
35822
36229
  view.setSelectedTab("board");
35823
36230
  });
@@ -35835,7 +36242,7 @@ class Actions {
35835
36242
  model.game.cleanup();
35836
36243
  model.game = null;
35837
36244
  }
35838
- const locale = ((_b = model.state) === null || _b === void 0 ? void 0 : _b.locale) || "is_IS";
36245
+ const locale = model.state?.locale || "is_IS";
35839
36246
  if (routeName == "help") {
35840
36247
  // Make sure that the help HTML is loaded upon first use
35841
36248
  model.loadHelp();
@@ -35867,7 +36274,6 @@ class Actions {
35867
36274
  this.model.handleUserMoveMessage(json, firstAttach);
35868
36275
  }
35869
36276
  onChatMessage(json, firstAttach, view) {
35870
- var _a;
35871
36277
  // Handle an incoming chat message
35872
36278
  if (firstAttach) ;
35873
36279
  else {
@@ -35879,7 +36285,7 @@ class Actions {
35879
36285
  // - User has audio enabled
35880
36286
  // - Message is from opponent (not from current user)
35881
36287
  const { state } = this.model;
35882
- const userId = (_a = state.userId) !== null && _a !== void 0 ? _a : "";
36288
+ const userId = state.netskraflUserId ?? "";
35883
36289
  if (state.audio && json.from_userid !== userId) {
35884
36290
  playAudio(state, "new-msg");
35885
36291
  }
@@ -35983,26 +36389,26 @@ class Actions {
35983
36389
  }
35984
36390
  attachListenerToUser() {
35985
36391
  const state = this.model.state;
35986
- // console.log(`attachListenerToUser(): userId=${state?.userId}`);
35987
- if (!state || !state.userId)
36392
+ // console.log(`attachListenerToUser(): netskraflUserId=${state?.netskraflUserId}`);
36393
+ if (!state || !state.netskraflUserId)
35988
36394
  return;
35989
36395
  // Listen to challenge and move notifications separately
35990
- attachFirebaseListener(`user/${state.userId}/challenge`, (json, firstAttach) => this.onUserChallengeMessage(json, firstAttach));
35991
- attachFirebaseListener(`user/${state.userId}/move`, (json, firstAttach) => this.onUserMoveMessage(json, firstAttach));
36396
+ attachFirebaseListener(`user/${state.netskraflUserId}/challenge`, (json, firstAttach) => this.onUserChallengeMessage(json, firstAttach));
36397
+ attachFirebaseListener(`user/${state.netskraflUserId}/move`, (json, firstAttach) => this.onUserMoveMessage(json, firstAttach));
35992
36398
  }
35993
36399
  detachListenerFromUser() {
35994
36400
  // Stop listening to Firebase notifications for the current user
35995
36401
  const state = this.model.state;
35996
- // console.log(`detachListenerFromUser(): userId=${state?.userId}`);
35997
- if (state && state.userId)
35998
- detachFirebaseListener('user/' + state.userId);
36402
+ // console.log(`detachListenerFromUser(): netskraflUserId=${state?.netskraflUserId}`);
36403
+ if (state && state.netskraflUserId)
36404
+ detachFirebaseListener('user/' + state.netskraflUserId);
35999
36405
  }
36000
36406
  attachListenerToGame(uuid, view) {
36001
36407
  // Listen to Firebase events on the /game/[gameId]/[userId] path
36002
36408
  const state = this.model.state;
36003
36409
  if (!uuid || !state)
36004
36410
  return;
36005
- const basepath = `game/${uuid}/${state.userId}/`;
36411
+ const basepath = `game/${uuid}/${state.netskraflUserId}/`;
36006
36412
  // New moves
36007
36413
  attachFirebaseListener(basepath + "move", (json, firstAttach) => this.onMoveMessage(json, firstAttach));
36008
36414
  // New chat messages
@@ -36013,7 +36419,7 @@ class Actions {
36013
36419
  const state = this.model.state;
36014
36420
  if (!uuid || !state)
36015
36421
  return;
36016
- const basepath = `game/${uuid}/${state.userId}/`;
36422
+ const basepath = `game/${uuid}/${state.netskraflUserId}/`;
36017
36423
  detachFirebaseListener(basepath + "move");
36018
36424
  detachFirebaseListener(basepath + "chat");
36019
36425
  }
@@ -36038,7 +36444,6 @@ class Actions {
36038
36444
  }
36039
36445
  // Challenge Management Actions
36040
36446
  async handleChallenge(parameters) {
36041
- var _a;
36042
36447
  // Reject or retract a challenge
36043
36448
  if (!this.model.state)
36044
36449
  return;
@@ -36048,9 +36453,9 @@ class Actions {
36048
36453
  url: "/challenge",
36049
36454
  body: parameters
36050
36455
  });
36051
- if ((json === null || json === void 0 ? void 0 : json.result) === 0) {
36456
+ if (json?.result === 0) {
36052
36457
  // Log the change of challenge status (issue/decline/retract/accept)
36053
- const locale = ((_a = this.model.state) === null || _a === void 0 ? void 0 : _a.locale) || "is_IS";
36458
+ const locale = this.model.state?.locale || "is_IS";
36054
36459
  var p = { locale };
36055
36460
  if (parameters.duration !== undefined)
36056
36461
  p.duration = parameters.duration;
@@ -36096,7 +36501,6 @@ class Actions {
36096
36501
  }
36097
36502
  // Game Management Actions
36098
36503
  async startNewGame(oppid, reverse = false) {
36099
- var _a;
36100
36504
  // Ask the server to initiate a new game against the given opponent
36101
36505
  if (!this.model.state)
36102
36506
  return;
@@ -36113,9 +36517,9 @@ class Actions {
36113
36517
  body: rqBody
36114
36518
  };
36115
36519
  const json = await request(this.model.state, rq);
36116
- if (json === null || json === void 0 ? void 0 : json.ok) {
36520
+ if (json?.ok) {
36117
36521
  // Log the new game event
36118
- const locale = ((_a = this.model.state) === null || _a === void 0 ? void 0 : _a.locale) || "is_IS";
36522
+ const locale = this.model.state?.locale || "is_IS";
36119
36523
  logEvent("new_game", {
36120
36524
  uuid: json.uuid,
36121
36525
  timed: reverse,
@@ -36205,14 +36609,14 @@ class Actions {
36205
36609
  url: "/cancelplan",
36206
36610
  body: {}
36207
36611
  });
36208
- if (json === null || json === void 0 ? void 0 : json.ok) {
36612
+ if (json?.ok) {
36209
36613
  // Successfully cancelled: immediately update the friend and hasPaid state
36210
36614
  user.friend = false;
36211
36615
  state.hasPaid = false;
36212
36616
  state.plan = "";
36213
36617
  // Log a friendship cancellation event
36214
36618
  logEvent("cancel_plan", {
36215
- userid: state.userId,
36619
+ userid: state.netskraflUserId,
36216
36620
  locale: state.locale,
36217
36621
  // Add plan identifiers here
36218
36622
  plan: "friend"
@@ -36231,29 +36635,29 @@ class Actions {
36231
36635
  // Listen to global best score
36232
36636
  attachFirebaseListener(basePath + "best", (json, firstAttach) => this.onRiddleGlobalScoreUpdate(json, firstAttach));
36233
36637
  // Listen to group scores (if user has a group)
36234
- if (state === null || state === void 0 ? void 0 : state.userGroupId) {
36638
+ if (state?.userGroupId) {
36235
36639
  attachFirebaseListener(basePath + `group/${state.userGroupId}/best`, (json, firstAttach) => this.onRiddleGroupScoreUpdate(json, firstAttach));
36236
36640
  }
36237
36641
  // Listen to global leaderboard
36238
36642
  attachFirebaseListener(basePath + "leaders", (json, firstAttach) => this.onLeaderboardUpdate(json, firstAttach));
36239
36643
  // Listen to user stats (if user is logged in)
36240
- if (state === null || state === void 0 ? void 0 : state.userId) {
36241
- attachFirebaseListener(`gatadagsins/users/${locale}/${state.userId}/stats`, (json, firstAttach) => this.onUserStatsUpdate(json, firstAttach));
36644
+ if (state?.netskraflUserId) {
36645
+ attachFirebaseListener(`gatadagsins/users/${locale}/${state.netskraflUserId}/stats`, (json, firstAttach) => this.onUserStatsUpdate(json, firstAttach));
36242
36646
  // Listen to personal best move (entire achievement object)
36243
- attachFirebaseListener(basePath + `achievements/${state.userId}`, (json, firstAttach) => this.onPersonalBestScoreUpdate(json, firstAttach));
36647
+ attachFirebaseListener(basePath + `achievements/${state.netskraflUserId}`, (json, firstAttach) => this.onPersonalBestScoreUpdate(json, firstAttach));
36244
36648
  }
36245
36649
  }
36246
36650
  detachListenerFromRiddle(date, locale) {
36247
36651
  const { state } = this.model;
36248
36652
  const basePath = `gatadagsins/${date}/${locale}/`;
36249
36653
  detachFirebaseListener(basePath + "best");
36250
- if (state === null || state === void 0 ? void 0 : state.userGroupId) {
36654
+ if (state?.userGroupId) {
36251
36655
  detachFirebaseListener(basePath + `group/${state.userGroupId}/best`);
36252
36656
  }
36253
36657
  detachFirebaseListener(basePath + "leaders");
36254
- if (state === null || state === void 0 ? void 0 : state.userId) {
36255
- detachFirebaseListener(`gatadagsins/users/${locale}/${state.userId}/stats`);
36256
- detachFirebaseListener(basePath + `achievements/${state.userId}`);
36658
+ if (state?.netskraflUserId) {
36659
+ detachFirebaseListener(`gatadagsins/users/${locale}/${state.netskraflUserId}/stats`);
36660
+ detachFirebaseListener(basePath + `achievements/${state.netskraflUserId}`);
36257
36661
  }
36258
36662
  }
36259
36663
  onRiddleGlobalScoreUpdate(json, firstAttach) {
@@ -36461,7 +36865,6 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
36461
36865
  }, []);
36462
36866
  */
36463
36867
  useEffect(() => {
36464
- var _a;
36465
36868
  // Load the Netskrafl (Mithril) UI for a new user
36466
36869
  if (!userEmail)
36467
36870
  return;
@@ -36471,7 +36874,7 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
36471
36874
  return;
36472
36875
  }
36473
36876
  const elemId = `netskrafl-user-${userEmail}`;
36474
- if (((_a = container.firstElementChild) === null || _a === void 0 ? void 0 : _a.id) === elemId) {
36877
+ if (container.firstElementChild?.id === elemId) {
36475
36878
  // The Netskrafl UI is already correctly mounted
36476
36879
  return;
36477
36880
  }
@@ -36497,8 +36900,8 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
36497
36900
  // when the component is unmounted
36498
36901
  // console.log("Dismounting Netskrafl UI for user", userEmail);
36499
36902
  const container = document.getElementById("netskrafl-container");
36500
- const div = container === null || container === void 0 ? void 0 : container.firstElementChild;
36501
- if ((div === null || div === void 0 ? void 0 : div.id) === elemId) {
36903
+ const div = container?.firstElementChild;
36904
+ if (div?.id === elemId) {
36502
36905
  document.body.appendChild(div);
36503
36906
  }
36504
36907
  };
@@ -37139,7 +37542,7 @@ const RightSideTabs = () => {
37139
37542
  return m(".gatadagsins-right-side-tabs", "");
37140
37543
  }
37141
37544
  // Check if current user is on the leaderboard
37142
- const currentUserId = (state === null || state === void 0 ? void 0 : state.userId) || "";
37545
+ const currentUserId = state?.netskraflUserId || "";
37143
37546
  const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
37144
37547
  const tabs = [
37145
37548
  { id: "performance", label: ts("Frammistaða"), iconGlyph: "dashboard" },
@@ -37178,7 +37581,7 @@ const RightSideTabs = () => {
37178
37581
  // Leaderboard tab
37179
37582
  activeTab === "leaderboard" ? m(LeaderboardView, {
37180
37583
  leaderboard: view.model.leaderboard || [],
37181
- currentUserId: (state === null || state === void 0 ? void 0 : state.userId) || "",
37584
+ currentUserId: state?.netskraflUserId || "",
37182
37585
  date: riddle.date,
37183
37586
  loading: false
37184
37587
  }) : null
@@ -37355,7 +37758,7 @@ const StatsModal = () => {
37355
37758
  return null;
37356
37759
  }
37357
37760
  // Check if current user is on the leaderboard
37358
- const currentUserId = (state === null || state === void 0 ? void 0 : state.userId) || "";
37761
+ const currentUserId = state?.netskraflUserId || "";
37359
37762
  const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
37360
37763
  const tabs = [
37361
37764
  { id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
@@ -37401,7 +37804,7 @@ const StatsModal = () => {
37401
37804
  }) : null,
37402
37805
  activeTab === "leaderboard" ? m(LeaderboardView, {
37403
37806
  leaderboard: view.model.leaderboard || [],
37404
- currentUserId: (state === null || state === void 0 ? void 0 : state.userId) || "",
37807
+ currentUserId: state?.netskraflUserId || "",
37405
37808
  date: riddle.date,
37406
37809
  loading: false
37407
37810
  }) : null
@@ -37429,8 +37832,7 @@ const StatsModal = () => {
37429
37832
  */
37430
37833
  const MAX_MOVES_TO_DISPLAY = 10;
37431
37834
  const currentMoveState = (riddle) => {
37432
- var _a, _b, _c;
37433
- const thisPlayer = ((_a = riddle.model.state) === null || _a === void 0 ? void 0 : _a.userId) || "";
37835
+ const thisPlayer = riddle.model.state?.netskraflUserId || "";
37434
37836
  const { bestPossibleScore, // The highest score achievable for this riddle
37435
37837
  globalBestScore, // The best score achieved by any player
37436
37838
  groupBestScore, // The best score achieved within the player's group
@@ -37451,7 +37853,7 @@ const currentMoveState = (riddle) => {
37451
37853
  // Check whether we need to add or annotate the global best score
37452
37854
  if (globalBestScore && globalBestScore.score > 0) {
37453
37855
  const { score, word, coord } = globalBestScore;
37454
- if (((_c = (_b = selectedMoves[0]) === null || _b === void 0 ? void 0 : _b.score) !== null && _c !== void 0 ? _c : 0) >= score) {
37856
+ if ((selectedMoves[0]?.score ?? 0) >= score) {
37455
37857
  // This player has made a move that scores the same
37456
37858
  // or better as the top score: mark the move
37457
37859
  selectedMoves[0].isGlobalBestScore = true;
@@ -37493,7 +37895,6 @@ const GataDagsins$1 = () => {
37493
37895
  showStatsModal = false;
37494
37896
  },
37495
37897
  view: (vnode) => {
37496
- var _a;
37497
37898
  const { view } = vnode.attrs;
37498
37899
  const { model } = view;
37499
37900
  const { riddle } = model;
@@ -37528,7 +37929,7 @@ const GataDagsins$1 = () => {
37528
37929
  // These elements appear after the main container for proper z-order
37529
37930
  // m(LeftLogo), // Currently no need for the logo for Gáta Dagsins
37530
37931
  // Show the Beginner component if the user is a beginner
37531
- ((_a = model.state) === null || _a === void 0 ? void 0 : _a.beginner) ? m(Beginner, { view, showClose: false }) : "",
37932
+ model.state?.beginner ? m(Beginner, { view, showClose: false }) : "",
37532
37933
  // Custom Info button for GataDagsins that shows help dialog
37533
37934
  m(".info", { title: ts("Upplýsingar og hjálp") }, m("a.iconlink", { href: "#", onclick: (e) => { e.preventDefault(); toggleHelp(); } }, glyph("info-sign"))),
37534
37935
  // Help dialog and backdrop
@@ -37637,7 +38038,6 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
37637
38038
  const completeState = makeGlobalState({ ...state, tokenExpired });
37638
38039
  const { userEmail } = completeState;
37639
38040
  useEffect(() => {
37640
- var _a;
37641
38041
  // Load the Gáta Dagsins (Mithril) UI for a new user
37642
38042
  if (!userEmail)
37643
38043
  return;
@@ -37647,7 +38047,7 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
37647
38047
  return;
37648
38048
  }
37649
38049
  const elemId = `gatadagsins-user-${userEmail}`;
37650
- if (((_a = container.firstElementChild) === null || _a === void 0 ? void 0 : _a.id) === elemId) {
38050
+ if (container.firstElementChild?.id === elemId) {
37651
38051
  // The Gáta Dagsins UI is already correctly mounted
37652
38052
  return;
37653
38053
  }
@@ -37673,8 +38073,8 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
37673
38073
  // when the component is unmounted
37674
38074
  // console.log("Dismounting Gáta Dagsins UI for user", userEmail);
37675
38075
  const container = document.getElementById("gatadagsins-container");
37676
- const div = container === null || container === void 0 ? void 0 : container.firstElementChild;
37677
- if ((div === null || div === void 0 ? void 0 : div.id) === elemId) {
38076
+ const div = container?.firstElementChild;
38077
+ if (div?.id === elemId) {
37678
38078
  document.body.appendChild(div);
37679
38079
  }
37680
38080
  };