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