gionuibk commited on
Commit
f1742ad
·
verified ·
1 Parent(s): 251af83

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. hf_config.json +3 -0
  2. src/gui/src/index.js +1 -0
  3. src/gui/src/initgui.js +160 -174
hf_config.json CHANGED
@@ -22,6 +22,9 @@
22
  "disk_max_size": 16384,
23
  "precache_size": 16384,
24
  "path": "./file-cache"
 
 
 
25
  }
26
  }
27
  }
 
22
  "disk_max_size": 16384,
23
  "precache_size": 16384,
24
  "path": "./file-cache"
25
+ },
26
+ "cloudflare-turnstile": {
27
+ "enabled": false
28
  }
29
  }
30
  }
src/gui/src/index.js CHANGED
@@ -72,6 +72,7 @@ window.gui = async (options) => {
72
  // note: the order of the bundles is important
73
  // note: Build script will prepend `window.gui_env="prod"` to the top of the file
74
  else if (window.gui_env === 'prod') {
 
75
  await window.loadScript('/puter.js/v2');
76
  // Load the minified bundles
77
  await window.loadCSS('/dist/bundle.min.css');
 
72
  // note: the order of the bundles is important
73
  // note: Build script will prepend `window.gui_env="prod"` to the top of the file
74
  else if (window.gui_env === 'prod') {
75
+ window.PUTER_API_ORIGIN = window.api_origin;
76
  await window.loadScript('/puter.js/v2');
77
  // Load the minified bundles
78
  await window.loadCSS('/dist/bundle.min.css');
src/gui/src/initgui.js CHANGED
@@ -56,7 +56,7 @@ const launch_services = async function (options) {
56
  globalThis.services = {
57
  get: (name) => services_m_[name],
58
  emit: (id, args) => {
59
- for ( const [_, instance] of services_l_ ) {
60
  instance.__on(id, args ?? []);
61
  }
62
  },
@@ -94,24 +94,24 @@ const launch_services = async function (options) {
94
  register('__launch-on-init', new LaunchOnInitService());
95
 
96
  // === Service-Script Services ===
97
- for ( const [name, script] of service_script_deferred.services ) {
98
  register(name, script);
99
  }
100
 
101
- for ( const [_, instance] of services_l_ ) {
102
  await instance.construct({
103
  gui_params: options,
104
  });
105
  }
106
 
107
- for ( const [_, instance] of services_l_ ) {
108
  await instance.init({
109
  services: globalThis.services,
110
  });
111
  }
112
 
113
  // === Service-Script Ready ===
114
- for ( const fn of service_script_deferred.on_ready ) {
115
  await fn();
116
  }
117
 
@@ -128,31 +128,31 @@ const launch_services = async function (options) {
128
  // By setting the 'passive' option appropriately, it ensures that default browser
129
  // behavior is prevented when necessary, thereby improving page scroll performance.
130
  // More info: https://stackoverflow.com/a/62177358
131
- if ( jQuery ) {
132
  jQuery.event.special.touchstart = {
133
- setup: function ( _, ns, handle ) {
134
  this.addEventListener('touchstart', handle, { passive: !ns.includes('noPreventDefault') });
135
  },
136
  };
137
  jQuery.event.special.touchmove = {
138
- setup: function ( _, ns, handle ) {
139
  this.addEventListener('touchmove', handle, { passive: !ns.includes('noPreventDefault') });
140
  },
141
  };
142
  jQuery.event.special.wheel = {
143
- setup: function ( _, ns, handle ) {
144
  this.addEventListener('wheel', handle, { passive: true });
145
  },
146
  };
147
  jQuery.event.special.mousewheel = {
148
- setup: function ( _, ns, handle ) {
149
  this.addEventListener('mousewheel', handle, { passive: true });
150
  },
151
  };
152
  }
153
 
154
  // are we in dashboard mode?
155
- if(window.location.pathname === '/dashboard' || window.location.pathname === '/dashboard/'){
156
  window.is_dashboard_mode = true;
157
  }
158
 
@@ -167,7 +167,7 @@ window.showTurnstileChallenge = function (options) {
167
  const modalId = 'turnstile-challenge-modal';
168
  const siteKey = window.gui_params?.turnstileSiteKey;
169
 
170
- if ( ! siteKey ) {
171
  options.onError('Turnstile site key not configured');
172
  return resolve();
173
  }
@@ -204,7 +204,7 @@ window.showTurnstileChallenge = function (options) {
204
 
205
  // Initialize Turnstile widget
206
  const initTurnstile = () => {
207
- if ( ! window.turnstile ) {
208
  setTimeout(initTurnstile, 100);
209
  return;
210
  }
@@ -233,7 +233,7 @@ window.showTurnstileChallenge = function (options) {
233
  options.onError('Turnstile verification failed');
234
  },
235
  });
236
- } catch ( error ) {
237
  console.error('Failed to initialize Turnstile:', error);
238
  showError('Failed to load security verification. Please refresh the page.');
239
  options.onError(error);
@@ -250,11 +250,11 @@ window.showTurnstileChallenge = function (options) {
250
 
251
  // Prevent modal from closing by clicking outside
252
  modal.addEventListener('click', (e) => {
253
- if ( e.target === modal ) {
254
  // Don't close - force users to complete verification
255
  turnstileContainer.style.transform = 'scale(1.05)';
256
  setTimeout(() => {
257
- if ( turnstileContainer ) {
258
  turnstileContainer.style.transform = 'scale(1)';
259
  }
260
  }, 200);
@@ -280,16 +280,14 @@ window.initgui = async function (options) {
280
 
281
  let picked_a_user_for_sdk_login = false;
282
 
283
- // update SDK if auth_token is different from the one in the SDK
284
- if ( window.auth_token && puter.authToken !== window.auth_token )
285
- {
286
- puter.setAuthToken(window.auth_token);
287
- }
288
  // update SDK if api_origin is different from the one in the SDK
289
- if ( window.api_origin && puter.APIOrigin !== window.api_origin )
290
- {
291
  puter.setAPIOrigin(window.api_origin);
292
  }
 
 
 
 
293
 
294
  // Print the version to the console
295
  puter.os.version()
@@ -305,11 +303,11 @@ window.initgui = async function (options) {
305
  // Depending on the device type, it sets a class attribute on the body tag
306
  // to style or script the page differently for each device type.
307
 
308
- if ( isMobile.phone ) {
309
  $('body').attr('class', 'device-phone');
310
- } else if ( isMobile.tablet ) {
311
  // This is our new, smarter check for tablets
312
- if ( window.matchMedia && typeof window.matchMedia === 'function' && window.matchMedia('(hover: hover)').matches ) {
313
  // The user has a mouse/trackpad, so give them the desktop UI
314
  $('body').attr('class', 'device-desktop');
315
  } else {
@@ -338,7 +336,7 @@ window.initgui = async function (options) {
338
  // Extract 'action' from URL
339
  //--------------------------------------------------------------------------------------
340
  let action;
341
- if ( window.url_paths[0]?.toLocaleLowerCase() === 'action' && window.url_paths[1] ) {
342
  action = window.url_paths[1].toLowerCase();
343
  }
344
 
@@ -346,9 +344,9 @@ window.initgui = async function (options) {
346
  // Determine if we are in full-page mode
347
  // i.e. https://puter.com/app/<app_name>/?puter.fullpage=true
348
  //--------------------------------------------------------------------------------------
349
- if ( window.url_query_params.has('puter.fullpage') && (window.url_query_params.get('puter.fullpage') === 'false' || window.url_query_params.get('puter.fullpage') === '0') ) {
350
  window.is_fullpage_mode = false;
351
- } else if ( window.url_query_params.has('puter.fullpage') && (window.url_query_params.get('puter.fullpage') === 'true' || window.url_query_params.get('puter.fullpage') === '1') ) {
352
  // In fullpage mode, we want to hide the taskbar for better UX
353
  window.taskbar_height = 0;
354
 
@@ -365,7 +363,7 @@ window.initgui = async function (options) {
365
  // Is attempt_temp_user_creation?
366
  // i.e. https://puter.com/?attempt_temp_user_creation=true
367
  //--------------------------------------------------------------------------------------
368
- if ( window.url_query_params.has('attempt_temp_user_creation') && (window.url_query_params.get('attempt_temp_user_creation') === 'true' || window.url_query_params.get('attempt_temp_user_creation') === '1') ) {
369
  window.attempt_temp_user_creation = true;
370
  }
371
 
@@ -373,7 +371,7 @@ window.initgui = async function (options) {
373
  // Is GUI embedded in a popup?
374
  // i.e. https://puter.com/?embedded_in_popup=true
375
  //--------------------------------------------------------------------------------------
376
- if ( window.url_query_params.has('embedded_in_popup') && (window.url_query_params.get('embedded_in_popup') === 'true' || window.url_query_params.get('embedded_in_popup') === '1') ) {
377
  window.embedded_in_popup = true;
378
  $('body').addClass('embedded-in-popup');
379
 
@@ -381,7 +379,7 @@ window.initgui = async function (options) {
381
  window.openerOrigin = document.referrer;
382
 
383
  // if no referrer, request it from the opener via messaging
384
- if ( ! document.referrer ) {
385
  try {
386
  window.openerOrigin = await requestOpenerOrigin();
387
  } catch (e) {
@@ -392,9 +390,9 @@ window.initgui = async function (options) {
392
  // this is the referrer in terms of user acquisition
393
  window.referrerStr = window.openerOrigin;
394
 
395
- if ( action === 'sign-in' && !window.is_auth() && !(window.attempt_temp_user_creation && window.first_visit_ever) ) {
396
  // show signup window
397
- if ( await UIWindowSignup({
398
  reload_on_success: false,
399
  send_confirmation_code: false,
400
  show_close_button: false,
@@ -402,12 +400,11 @@ window.initgui = async function (options) {
402
  has_head: false,
403
  cover_page: true,
404
  },
405
- }) )
406
- {
407
  await window.getUserAppToken(window.openerOrigin);
408
  }
409
  }
410
- else if ( action === 'sign-in' && window.is_auth() && !(window.attempt_temp_user_creation && window.first_visit_ever) ) {
411
  picked_a_user_for_sdk_login = await UIWindowSessionList({
412
  reload_on_success: false,
413
  draggable_body: false,
@@ -415,7 +412,7 @@ window.initgui = async function (options) {
415
  cover_page: true,
416
  });
417
 
418
- if ( picked_a_user_for_sdk_login ) {
419
  await window.getUserAppToken(window.openerOrigin);
420
  }
421
 
@@ -425,7 +422,7 @@ window.initgui = async function (options) {
425
  //--------------------------------------------------------------------------------------
426
  // Display an error if the query parameters have an error
427
  //--------------------------------------------------------------------------------------
428
- if ( window.url_query_params.has('error') ) {
429
  // TODO: i18n
430
  await UIAlert({
431
  message: window.url_query_params.get('message'),
@@ -436,12 +433,12 @@ window.initgui = async function (options) {
436
  // Get user referral code from URL query params
437
  // i.e. https://puter.com/?r=123456
438
  //--------------------------------------------------------------------------------------
439
- if ( window.url_query_params.has('r') ) {
440
  window.referral_code = window.url_query_params.get('r');
441
  // remove 'r' from URL
442
  window.history.pushState(null, document.title, '/');
443
  // show referral notice, this will be used later if Desktop is loaded
444
- if ( window.first_visit_ever ) {
445
  window.show_referral_notice = true;
446
  }
447
  }
@@ -449,7 +446,7 @@ window.initgui = async function (options) {
449
  //--------------------------------------------------------------------------------------
450
  // Action: Request Permission
451
  //--------------------------------------------------------------------------------------
452
- if ( action === 'request-permission' ) {
453
  let app_uid = window.url_query_params.get('app_uid');
454
  let origin = window.openerOrigin ?? window.url_query_params.get('origin');
455
  let permission = window.url_query_params.get('permission');
@@ -469,7 +466,7 @@ window.initgui = async function (options) {
469
  //--------------------------------------------------------------------------------------
470
  // Action: Password recovery
471
  //--------------------------------------------------------------------------------------
472
- else if ( action === 'set-new-password' ) {
473
  let user = window.url_query_params.get('user');
474
  let token = window.url_query_params.get('token');
475
 
@@ -481,19 +478,19 @@ window.initgui = async function (options) {
481
  //--------------------------------------------------------------------------------------
482
  // Action: Change Username
483
  //--------------------------------------------------------------------------------------
484
- else if ( action === 'change-username' ) {
485
  await UIWindowChangeUsername();
486
  }
487
  //--------------------------------------------------------------------------------------
488
  // Action: Login
489
  //--------------------------------------------------------------------------------------
490
- else if ( action === 'login' ) {
491
  await UIWindowLogin();
492
  }
493
  //--------------------------------------------------------------------------------------
494
  // Action: Signup
495
  //--------------------------------------------------------------------------------------
496
- else if ( action === 'signup' ) {
497
  await UIWindowSignup();
498
  }
499
 
@@ -502,11 +499,11 @@ window.initgui = async function (options) {
502
  // if yes, we need to get the user app token and send it to the opener
503
  // if not, we need to ask the user for confirmation before proceeding BUT only if the action is a file-picker action
504
  // -------------------------------------------------------------------------------------
505
- if ( window.embedded_in_popup && window.openerOrigin ) {
506
  let response = await window.checkUserSiteRelationship(window.openerOrigin);
507
  window.userAppToken = response.token;
508
 
509
- if ( !picked_a_user_for_sdk_login && window.logged_in_users.length > 1 && (!window.userAppToken || window.url_query_params.get('request_auth') ) ) {
510
  picked_a_user_for_sdk_login = await UIWindowSessionList({
511
  reload_on_success: false,
512
  draggable_body: false,
@@ -518,7 +515,7 @@ window.initgui = async function (options) {
518
  // -------------------------------------------------------------------------------------
519
  // `auth_token` provided in URL, use it to log in
520
  // -------------------------------------------------------------------------------------
521
- else if ( window.url_query_params.has('auth_token') ) {
522
  let query_param_auth_token = window.url_query_params.get('auth_token');
523
 
524
  puter.setAuthToken(query_param_auth_token);
@@ -526,14 +523,14 @@ window.initgui = async function (options) {
526
  try {
527
  whoami = await puter.os.user({ query: 'icon_size=64' });
528
  } catch (e) {
529
- if ( e.status === 401 ) {
530
  window.logout();
531
  return;
532
  }
533
  }
534
 
535
- if ( whoami ) {
536
- if ( whoami.requires_email_confirmation ) {
537
  let is_verified;
538
  do {
539
  is_verified = await UIWindowEmailConfirmationRequired({
@@ -541,7 +538,7 @@ window.initgui = async function (options) {
541
  has_head: false,
542
  });
543
  }
544
- while ( !is_verified );
545
  }
546
  // if user is logging in using an auth token that means it's not their first ever visit to Puter.com
547
  // it might be their first visit to Puter on this specific device but it's not their first time ever visiting Puter.
@@ -581,22 +578,22 @@ window.initgui = async function (options) {
581
  // -------------------------------------------------------------------------------------
582
  // Authed
583
  // -------------------------------------------------------------------------------------
584
- if ( window.is_auth() ) {
585
  // try to get user data using /whoami, only if that data is missing
586
- if ( ! whoami ) {
587
  try {
588
  whoami = await puter.os.user({ query: 'icon_size=64' });
589
  } catch (e) {
590
- if ( e.status === 401 ) {
591
  bad_session_logout();
592
  return;
593
  }
594
  }
595
  }
596
  // update local user data
597
- if ( whoami ) {
598
  // is email confirmation required?
599
- if ( whoami.requires_email_confirmation ) {
600
  let is_verified;
601
  do {
602
  is_verified = await UIWindowEmailConfirmationRequired({
@@ -604,14 +601,14 @@ window.initgui = async function (options) {
604
  has_head: false,
605
  });
606
  }
607
- while ( !is_verified );
608
  }
609
  window.update_auth_data(whoami.token || window.auth_token, whoami);
610
 
611
  // -------------------------------------------------------------------------------------
612
  // Load desktop, only if we're not embedded in a popup and not on the dashboard page
613
  // -------------------------------------------------------------------------------------
614
- if ( ! window.embedded_in_popup && ! window.is_dashboard_mode ) {
615
  await window.get_auto_arrange_data();
616
  puter.fs.stat({ path: window.desktop_path, consistency: 'eventual' }).then(desktop_fsentry => {
617
  UIDesktop({ desktop_fsentry: desktop_fsentry });
@@ -620,7 +617,7 @@ window.initgui = async function (options) {
620
  // -------------------------------------------------------------------------------------
621
  // Dashboard mode
622
  // -------------------------------------------------------------------------------------
623
- else if ( window.is_dashboard_mode ) {
624
  UIDashboard();
625
  }
626
  // -------------------------------------------------------------------------------------
@@ -643,11 +640,11 @@ window.initgui = async function (options) {
643
  msg_id: msg_id,
644
  }, window.openerOrigin);
645
  // close popup
646
- if ( !action || action === 'sign-in' ) {
647
  window.close();
648
  window.open('', '_self').close();
649
  }
650
- } catch ( err ) {
651
  // send error to parent
652
  window.opener.postMessage({
653
  msg: 'puter.token',
@@ -662,12 +659,12 @@ window.initgui = async function (options) {
662
 
663
  let app_uid;
664
 
665
- if ( window.openerOrigin ) {
666
  app_uid = await window.getAppUIDFromOrigin(window.openerOrigin);
667
  window.host_app_uid = app_uid;
668
  }
669
 
670
- if ( action === 'show-open-file-picker' ) {
671
  let options = window.url_query_params.get('options');
672
  options = JSON.parse(options ?? '{}');
673
 
@@ -675,7 +672,7 @@ window.initgui = async function (options) {
675
  UIWindow({
676
  allowed_file_types: options?.accept,
677
  selectable_body: options?.multiple,
678
- path: `/${ window.user.username }/Desktop`,
679
  // this is the uuid of the window to which this dialog will return
680
  return_to_parent_window: true,
681
  show_maximize_button: false,
@@ -701,10 +698,10 @@ window.initgui = async function (options) {
701
  //--------------------------------------------------------------------------------------
702
  // Action: Show Directory Picker
703
  //--------------------------------------------------------------------------------------
704
- else if ( action === 'show-directory-picker' ) {
705
  // open directory picker dialog
706
  UIWindow({
707
- path: `/${ window.user.username }/Desktop`,
708
  // this is the uuid of the window to which this dialog will return
709
  // parent_uuid: event.data.appInstanceID,
710
  return_to_parent_window: true,
@@ -731,7 +728,7 @@ window.initgui = async function (options) {
731
  //--------------------------------------------------------------------------------------
732
  // Action: Show Save File Dialog
733
  //--------------------------------------------------------------------------------------
734
- else if ( action === 'show-save-file-picker' ) {
735
  let allowed_file_types = window.url_query_params.get('allowed_file_types');
736
 
737
  // send 'sendMeFileData' event to parent
@@ -741,15 +738,14 @@ window.initgui = async function (options) {
741
 
742
  // listen for 'showSaveFilePickerPopup' event from parent
743
  window.addEventListener('message', async (event) => {
744
- if ( event.data.msg !== 'showSaveFilePickerPopup' )
745
- {
746
  return;
747
  }
748
 
749
  // Open dialog
750
  UIWindow({
751
  allowed_file_types: allowed_file_types,
752
- path: `/${ window.user.username }/Desktop`,
753
  // this is the uuid of the window to which this dialog will return
754
  return_to_parent_window: true,
755
  show_maximize_button: false,
@@ -777,20 +773,19 @@ window.initgui = async function (options) {
777
  let overwrite = false;
778
  let file_to_upload = new File([event.data.content], path.basename(target_path));
779
  let item_with_same_name_already_exists = true;
780
- while ( item_with_same_name_already_exists ) {
781
  // overwrite?
782
- if ( overwrite )
783
- {
784
  item_with_same_name_already_exists = false;
785
  }
786
  // upload
787
  try {
788
  const res = await puter.fs.write(target_path,
789
- file_to_upload,
790
- {
791
- dedupeName: false,
792
- overwrite: overwrite,
793
- });
794
 
795
  let file_signature = await puter.fs.sign(app_uid, { uid: res.uid, action: 'write' });
796
  file_signature = file_signature.items;
@@ -814,9 +809,9 @@ window.initgui = async function (options) {
814
  window.close();
815
  window.open('', '_self').close();
816
  }
817
- catch ( err ) {
818
  // item with same name exists
819
- if ( err.code === 'item_with_same_name_exists' ) {
820
  const alert_resp = await UIAlert({
821
  message: `<strong>${html_encode(err.entry_name)}</strong> already exists.`,
822
  buttons: [
@@ -832,9 +827,9 @@ window.initgui = async function (options) {
832
  ],
833
  parent_uuid: $(el_filedialog_window).attr('data-element_uuid'),
834
  });
835
- if ( alert_resp === 'replace' ) {
836
  overwrite = true;
837
- } else if ( alert_resp === 'cancel' ) {
838
  // enable parent window
839
  $(el_filedialog_window).find('.window-disable-mask, .busy-indicator').hide();
840
  return;
@@ -856,7 +851,7 @@ window.initgui = async function (options) {
856
 
857
  // done
858
  let busy_duration = (Date.now() - busy_init_ts);
859
- if ( busy_duration >= window.busy_indicator_hide_delay ) {
860
  $(el_filedialog_window).close();
861
  } else {
862
  setTimeout(() => {
@@ -880,7 +875,7 @@ window.initgui = async function (options) {
880
  // `share_token` provided
881
  // i.e. https://puter.com/?share_token=<share_token>
882
  //--------------------------------------------------------------------------------------
883
- if ( window.url_query_params.has('share_token') ) {
884
  let share_token = window.url_query_params.get('share_token');
885
 
886
  fetch(`${puter.APIOrigin}/sharelink/check`, {
@@ -894,8 +889,8 @@ window.initgui = async function (options) {
894
  'method': 'POST',
895
  }).then(response => response.json())
896
  .then(async data => {
897
- // Show register screen
898
- if ( data.email && data.email !== window.user?.email ) {
899
  await UIWindowSignup({
900
  reload_on_success: true,
901
  email: data.email,
@@ -906,8 +901,8 @@ window.initgui = async function (options) {
906
  });
907
  }
908
  // Show email confirmation screen
909
- else if ( data.email && data.email === window.user.email && !window.user.email_confirmed ) {
910
- // todo show email confirmation window
911
  await UIWindowEmailConfirmationRequired({
912
  stay_on_top: true,
913
  has_head: false,
@@ -932,24 +927,24 @@ window.initgui = async function (options) {
932
  // If we're in fullpage/emebedded/Auth Popup mode, we don't want to load the custom background
933
  // because it's not visible anyway and it's a waste of bandwidth
934
  // -------------------------------------------------------------------------------------
935
- if ( !window.is_fullpage_mode && !window.embedded_in_popup ) {
936
  window.refresh_desktop_background();
937
  }
938
  // -------------------------------------------------------------------------------------
939
  // Un-authed but not first visit -> try to log in/sign up
940
  // -------------------------------------------------------------------------------------
941
- if ( !window.is_auth() && (!window.first_visit_ever || window.disable_temp_users) ) {
942
- if ( window.logged_in_users.length > 0 ) {
943
  UIWindowSessionList();
944
  }
945
  else {
946
- const resp = await fetch(`${window.gui_origin }/whoarewe`);
947
  const whoarewe = await resp.json();
948
  await UIWindowLogin({
949
  // show_signup_button:
950
  reload_on_success: true,
951
  send_confirmation_code: false,
952
- show_signup_button: ( !whoarewe.disable_user_signup ),
953
  window_options: {
954
  has_head: false,
955
  },
@@ -960,7 +955,7 @@ window.initgui = async function (options) {
960
  // -------------------------------------------------------------------------------------
961
  // Un-authed and first visit ever -> create temp user with Turnstile challenge
962
  // -------------------------------------------------------------------------------------
963
- else if ( !window.is_auth() && window.first_visit_ever && !window.disable_temp_users ) {
964
  let referrer;
965
  try {
966
  referrer = new URL(window.location.href).pathname;
@@ -974,17 +969,15 @@ window.initgui = async function (options) {
974
  window.referrerStr = referrer;
975
 
976
  // in case there is also a referrer query param, add it to the referrer URL
977
- if ( window.url_query_params.has('ref') ) {
978
- if ( ! referrer )
979
- {
980
  referrer = '/';
981
  }
982
- referrer += `?ref=${ html_encode(window.url_query_params.get('ref'))}`;
983
  }
984
 
985
  let headers = {};
986
- if ( window.custom_headers )
987
- {
988
  headers = window.custom_headers;
989
  }
990
 
@@ -992,7 +985,7 @@ window.initgui = async function (options) {
992
  const createTempUser = (turnstileToken) => {
993
  // if this is a popup, show a spinner
994
  let spinner_init_ts = Date.now();
995
- if ( window.embedded_in_popup ) {
996
  puter.ui.showSpinner('<span style="-webkit-font-smoothing: antialiased;">Setting up your <a href="https://puter.com" target="_blank">Puter.com</a> account for secure AI and Cloud features</span>');
997
  }
998
 
@@ -1003,12 +996,12 @@ window.initgui = async function (options) {
1003
  };
1004
 
1005
  // Add Turnstile token if available
1006
- if ( turnstileToken ) {
1007
  requestData['cf-turnstile-response'] = turnstileToken;
1008
  }
1009
 
1010
  $.ajax({
1011
- url: `${window.gui_origin }/signup`,
1012
  type: 'POST',
1013
  async: true,
1014
  headers: headers,
@@ -1023,7 +1016,7 @@ window.initgui = async function (options) {
1023
  }
1024
 
1025
  const $captchaModal = $('.captcha-modal');
1026
- if ( $captchaModal.length > 0 ) await new Promise(async resolve => {
1027
  // The callback operand for fadeOut could be called
1028
  // more than once if there are multiple `.captcha-modal`
1029
  // elements, but only the first call to `resolve()` will
@@ -1032,7 +1025,7 @@ window.initgui = async function (options) {
1032
  $(this).remove();
1033
  resolve();
1034
  });
1035
-
1036
  // Just in case anything fails, also resolve after 500ms
1037
  await window.sleep(500);
1038
  resolve();
@@ -1041,7 +1034,7 @@ window.initgui = async function (options) {
1041
  window.update_auth_data(data.token, data.user);
1042
 
1043
  // if this is a popup, hide the spinner, make sure it was visible for at least 2 seconds
1044
- if(window.embedded_in_popup) await new Promise(async resolve => {
1045
  let spinner_duration = (Date.now() - spinner_init_ts);
1046
 
1047
  (async () => {
@@ -1060,7 +1053,7 @@ window.initgui = async function (options) {
1060
  app_uid: data.app_uid,
1061
  }, window.openerOrigin);
1062
  // close popup
1063
- if ( !action || action === 'sign-in' ) {
1064
  window.close();
1065
  window.open('', '_self').close();
1066
  }
@@ -1087,7 +1080,7 @@ window.initgui = async function (options) {
1087
  };
1088
 
1089
  // Check if Turnstile is enabled and show challenge
1090
- if ( window.gui_params?.turnstileSiteKey ) {
1091
  window.showTurnstileChallenge({
1092
  onSuccess: createTempUser,
1093
  onError: (error) => {
@@ -1104,10 +1097,9 @@ window.initgui = async function (options) {
1104
  }
1105
 
1106
  // if there is at least one window open (only non-Explorer windows), ask user for confirmation when navigating away from puter
1107
- if ( window.feature_flags.prompt_user_when_navigation_away_from_puter ) {
1108
  window.onbeforeunload = function () {
1109
- if ( $('.window:not(.window[data-app="explorer"])').length > 0 )
1110
- {
1111
  return true;
1112
  }
1113
  };
@@ -1123,7 +1115,7 @@ window.initgui = async function (options) {
1123
  // -------------------------------------------------------------------------------------
1124
  // Load desktop, if not embedded in a popup and not on the dashboard page
1125
  // -------------------------------------------------------------------------------------
1126
- if ( ! window.embedded_in_popup && ! window.is_dashboard_mode ) {
1127
  await window.get_auto_arrange_data();
1128
  puter.fs.stat({ path: window.desktop_path, consistency: 'eventual' }).then(desktop_fsentry => {
1129
  UIDesktop({ desktop_fsentry: desktop_fsentry });
@@ -1132,7 +1124,7 @@ window.initgui = async function (options) {
1132
  // -------------------------------------------------------------------------------------
1133
  // Dashboard mode: open explorer pointing to home directory
1134
  // -------------------------------------------------------------------------------------
1135
- else if ( window.is_dashboard_mode ) {
1136
  UIDashboard();
1137
  }
1138
  // -------------------------------------------------------------------------------------
@@ -1156,11 +1148,11 @@ window.initgui = async function (options) {
1156
  app_uid: data.app_uid,
1157
  }, window.openerOrigin);
1158
  // close popup
1159
- if ( !action || action === 'sign-in' ) {
1160
  window.close();
1161
  window.open('', '_self').close();
1162
  }
1163
- } catch ( err ) {
1164
  // send error to parent
1165
  window.opener.postMessage({
1166
  msg: 'puter.token',
@@ -1175,7 +1167,7 @@ window.initgui = async function (options) {
1175
 
1176
  let app_uid;
1177
 
1178
- if ( window.openerOrigin ) {
1179
  app_uid = await window.getAppUIDFromOrigin(window.openerOrigin);
1180
  window.host_app_uid = app_uid;
1181
  }
@@ -1183,7 +1175,7 @@ window.initgui = async function (options) {
1183
  //--------------------------------------------------------------------------------------
1184
  // Action: Show Open File Picker
1185
  //--------------------------------------------------------------------------------------
1186
- if ( action === 'show-open-file-picker' ) {
1187
  let options = window.url_query_params.get('options');
1188
  options = JSON.parse(options ?? '{}');
1189
 
@@ -1191,7 +1183,7 @@ window.initgui = async function (options) {
1191
  UIWindow({
1192
  allowed_file_types: options?.accept,
1193
  selectable_body: options?.multiple,
1194
- path: `/${ window.user.username }/Desktop`,
1195
  return_to_parent_window: true,
1196
  show_maximize_button: false,
1197
  show_minimize_button: false,
@@ -1215,10 +1207,10 @@ window.initgui = async function (options) {
1215
  //--------------------------------------------------------------------------------------
1216
  // Action: Show Directory Picker
1217
  //--------------------------------------------------------------------------------------
1218
- else if ( action === 'show-directory-picker' ) {
1219
  // open directory picker dialog
1220
  UIWindow({
1221
- path: `/${ window.user.username }/Desktop`,
1222
  // this is the uuid of the window to which this dialog will return
1223
  // parent_uuid: event.data.appInstanceID,
1224
  return_to_parent_window: true,
@@ -1246,7 +1238,7 @@ window.initgui = async function (options) {
1246
  //--------------------------------------------------------------------------------------
1247
  // Action: Show Save File Dialog
1248
  //--------------------------------------------------------------------------------------
1249
- else if ( action === 'show-save-file-picker' ) {
1250
  let allowed_file_types = window.url_query_params.get('allowed_file_types');
1251
 
1252
  // send 'sendMeFileData' event to parent
@@ -1256,15 +1248,14 @@ window.initgui = async function (options) {
1256
 
1257
  // listen for 'showSaveFilePickerPopup' event from parent
1258
  window.addEventListener('message', async (event) => {
1259
- if ( event.data.msg !== 'showSaveFilePickerPopup' )
1260
- {
1261
  return;
1262
  }
1263
 
1264
  // Open dialog
1265
  UIWindow({
1266
  allowed_file_types: allowed_file_types,
1267
- path: `/${ window.user.username }/Desktop`,
1268
  // this is the uuid of the window to which this dialog will return
1269
  return_to_parent_window: true,
1270
  show_maximize_button: false,
@@ -1292,20 +1283,19 @@ window.initgui = async function (options) {
1292
  let overwrite = false;
1293
  let file_to_upload = new File([event.data.content], path.basename(target_path));
1294
  let item_with_same_name_already_exists = true;
1295
- while ( item_with_same_name_already_exists ) {
1296
  // overwrite?
1297
- if ( overwrite )
1298
- {
1299
  item_with_same_name_already_exists = false;
1300
  }
1301
  // upload
1302
  try {
1303
  const res = await puter.fs.write(target_path,
1304
- file_to_upload,
1305
- {
1306
- dedupeName: false,
1307
- overwrite: overwrite,
1308
- });
1309
 
1310
  let file_signature = await puter.fs.sign(app_uid, { uid: res.uid, action: 'write' });
1311
  file_signature = file_signature.items;
@@ -1330,9 +1320,9 @@ window.initgui = async function (options) {
1330
  window.open('', '_self').close();
1331
  // show_save_account_notice_if_needed();
1332
  }
1333
- catch ( err ) {
1334
  // item with same name exists
1335
- if ( err.code === 'item_with_same_name_exists' ) {
1336
  const alert_resp = await UIAlert({
1337
  message: `<strong>${html_encode(err.entry_name)}</strong> already exists.`,
1338
  buttons: [
@@ -1348,9 +1338,9 @@ window.initgui = async function (options) {
1348
  ],
1349
  parent_uuid: $(el_filedialog_window).attr('data-element_uuid'),
1350
  });
1351
- if ( alert_resp === 'replace' ) {
1352
  overwrite = true;
1353
- } else if ( alert_resp === 'cancel' ) {
1354
  // enable parent window
1355
  $(el_filedialog_window).find('.window-disable-mask, .busy-indicator').hide();
1356
  return;
@@ -1372,7 +1362,7 @@ window.initgui = async function (options) {
1372
 
1373
  // done
1374
  let busy_duration = (Date.now() - busy_init_ts);
1375
- if ( busy_duration >= window.busy_indicator_hide_delay ) {
1376
  $(el_filedialog_window).close();
1377
  } else {
1378
  setTimeout(() => {
@@ -1400,32 +1390,31 @@ window.initgui = async function (options) {
1400
  update_last_touch_coordinates(e);
1401
 
1402
  // dismiss touchstart on regular devices
1403
- if ( e.type === 'touchstart' && !isMobile.phone && !isMobile.tablet )
1404
- {
1405
  return;
1406
  }
1407
 
1408
  // If .item-container clicked, unselect all its item children
1409
- if ( $(e.target).hasClass('item-container') && !e.ctrlKey && !e.metaKey ) {
1410
  $(e.target).children('.item-selected').removeClass('item-selected');
1411
  window.update_explorer_footer_selected_items_count(e.target);
1412
  }
1413
 
1414
  // If the clicked element is not a context menu, remove all context menus
1415
- if ( $(e.target).parents('.context-menu').length === 0 ) {
1416
  $('.context-menu').fadeOut(200, function () {
1417
  $(this).remove();
1418
  });
1419
  }
1420
 
1421
  // click on anything will close all popovers, but there are some exceptions
1422
- if ( !$(e.target).hasClass('start-app')
1423
  && !$(e.target).hasClass('launch-search')
1424
  && !$(e.target).hasClass('launch-search-clear')
1425
  && $(e.target).closest('.start-app').length === 0
1426
  && !isMobile.phone && !isMobile.tablet
1427
  && !$(e.target).hasClass('popover')
1428
- && $(e.target).parents('.popover').length === 0 ) {
1429
 
1430
  $('.popover').fadeOut(200, function () {
1431
  $('.popover').remove();
@@ -1436,21 +1425,21 @@ window.initgui = async function (options) {
1436
  $('.ui-tooltip').remove();
1437
 
1438
  // rename items whose names were being edited
1439
- if ( ! $(e.target).hasClass('item-name-editor') ) {
1440
  // blurring an Item Name Editor will automatically trigger renaming the item
1441
  $('.item-name-editor-active').blur();
1442
  }
1443
 
1444
  // update active_item_container
1445
- if ( $(e.target).hasClass('item-container') ) {
1446
  window.active_item_container = e.target;
1447
  } else {
1448
  let ic = $(e.target).closest('.item-container');
1449
- if ( ic.length > 0 ) {
1450
  window.active_item_container = ic.get(0);
1451
  } else {
1452
  let pp = $(e.target).find('.item-container');
1453
- if ( pp.length > 0 ) {
1454
  window.active_item_container = pp.get(0);
1455
  }
1456
  }
@@ -1470,25 +1459,24 @@ window.initgui = async function (options) {
1470
  //--------------------------------------------------------
1471
  $(document).on('mousedown', function (e) {
1472
  // if taskbar or any parts of it is clicked, drop the event
1473
- if ( $(e.target).hasClass('taskbar') || $(e.target).closest('.taskbar').length > 0 ) {
1474
  return;
1475
  }
1476
  // if toolbar or any parts of it is clicked, drop the event
1477
- if ( $(e.target).hasClass('toolbar') || $(e.target).closest('.toolbar').length > 0 ) {
1478
  return;
1479
  }
1480
 
1481
  // if close or minimize button clicked, drop the event
1482
- if ( document.elementFromPoint(e.clientX, e.clientY).closest('.window-close-btn, .window-minimize-btn') ) {
1483
  return;
1484
  }
1485
 
1486
  // if mouse is clicked on a window, activate it
1487
- if ( window.mouseover_window !== undefined ) {
1488
  // if popover clicked on, don't activate window. This is because if an app
1489
  // is using the popover API to show a popover, the popover will be closed if the window is activated
1490
- if ( $(e.target).hasClass('popover') || $(e.target).parents('.popover').length > 0 )
1491
- {
1492
  return;
1493
  }
1494
  $(window.mouseover_window).focusWindow(e);
@@ -1509,12 +1497,12 @@ window.initgui = async function (options) {
1509
  });
1510
 
1511
  document.addEventListener('visibilitychange', (event) => {
1512
- if ( document.visibilityState !== 'visible' ) {
1513
  window.doc_title_before_blur = document.title;
1514
- if ( ! _.isEmpty(window.active_uploads) ) {
1515
  update_title_based_on_uploads();
1516
  }
1517
- } else if ( window.active_uploads ) {
1518
  document.title = window.doc_title_before_blur ?? 'Puter';
1519
  }
1520
  });
@@ -1532,7 +1520,7 @@ window.initgui = async function (options) {
1532
  */
1533
  $(document).on('logout', async function (event) {
1534
  // is temp user?
1535
- if ( window.user && window.user.is_temp && !window.user.deleted ) {
1536
  const alert_resp = await UIAlert({
1537
  message: '<strong>Save account before logging out!</strong><p>You are using a temporary account and logging out will erase all your data.</p>',
1538
  buttons: [
@@ -1551,16 +1539,15 @@ window.initgui = async function (options) {
1551
  },
1552
  ],
1553
  });
1554
- if ( alert_resp === 'save_account' ) {
1555
  let saved = await UIWindowSaveAccount({
1556
  send_confirmation_code: false,
1557
  default_username: window.user.username,
1558
  });
1559
- if ( saved )
1560
- {
1561
  window.logout();
1562
  }
1563
- } else if ( alert_resp === 'log_out' ) {
1564
  window.logout();
1565
  }
1566
  else {
@@ -1573,12 +1560,12 @@ window.initgui = async function (options) {
1573
  const resp = await fetch(`${window.gui_origin}/get-anticsrf-token`);
1574
  const { token } = await resp.json();
1575
  await $.ajax({
1576
- url: `${window.gui_origin }/logout`,
1577
  type: 'POST',
1578
  async: true,
1579
  contentType: 'application/json',
1580
  headers: {
1581
- 'Authorization': `Bearer ${ window.auth_token}`,
1582
  },
1583
  data: JSON.stringify({ anti_csrf: token }),
1584
  statusCode: {
@@ -1591,8 +1578,8 @@ window.initgui = async function (options) {
1591
  }
1592
 
1593
  // remove this user from the array of logged_in_users
1594
- for ( let i = 0; i < window.logged_in_users.length; i++ ) {
1595
- if ( window.logged_in_users[i].uuid === window.user.uuid ) {
1596
  window.logged_in_users.splice(i, 1);
1597
  break;
1598
  }
@@ -1622,9 +1609,9 @@ window.initgui = async function (options) {
1622
  });
1623
  };
1624
 
1625
- function requestOpenerOrigin () {
1626
  return new Promise((resolve, reject) => {
1627
- if ( ! window.opener ) {
1628
  reject(new Error('No window.opener available'));
1629
  return;
1630
  }
@@ -1632,7 +1619,7 @@ function requestOpenerOrigin () {
1632
  // Function to handle the message event
1633
  const handleMessage = (event) => {
1634
  // Check if the message is the expected response
1635
- if ( event.data.msg === 'originResponse' ) {
1636
  // Clean up by removing the event listener
1637
  window.removeEventListener('message', handleMessage);
1638
  resolve(event.origin);
@@ -1658,7 +1645,7 @@ $(document).on('click', '.generic-close-window-button', function (e) {
1658
  });
1659
 
1660
  $(document).on('click', function (e) {
1661
- if ( !$(e.target).hasClass('window-search') && $(e.target).closest('.window-search').length === 0 && !$(e.target).is('.toolbar-btn.search-btn') ) {
1662
  $('.window-search').close();
1663
  }
1664
  });
@@ -1666,8 +1653,7 @@ $(document).on('click', function (e) {
1666
  // Re-calculate desktop height and width on window resize and re-position the login and signup windows
1667
  $(window).on('resize', function () {
1668
  // If host env is popup, don't continue because the popup window has its own resize requirements.
1669
- if ( window.embedded_in_popup )
1670
- {
1671
  return;
1672
  }
1673
 
@@ -1694,7 +1680,7 @@ $(window).on('resize', function () {
1694
  });
1695
 
1696
  $(document).on('contextmenu', '.disable-context-menu', function (e) {
1697
- if ( $(e.target).hasClass('disable-context-menu') ) {
1698
  e.preventDefault();
1699
  return false;
1700
  }
 
56
  globalThis.services = {
57
  get: (name) => services_m_[name],
58
  emit: (id, args) => {
59
+ for (const [_, instance] of services_l_) {
60
  instance.__on(id, args ?? []);
61
  }
62
  },
 
94
  register('__launch-on-init', new LaunchOnInitService());
95
 
96
  // === Service-Script Services ===
97
+ for (const [name, script] of service_script_deferred.services) {
98
  register(name, script);
99
  }
100
 
101
+ for (const [_, instance] of services_l_) {
102
  await instance.construct({
103
  gui_params: options,
104
  });
105
  }
106
 
107
+ for (const [_, instance] of services_l_) {
108
  await instance.init({
109
  services: globalThis.services,
110
  });
111
  }
112
 
113
  // === Service-Script Ready ===
114
+ for (const fn of service_script_deferred.on_ready) {
115
  await fn();
116
  }
117
 
 
128
  // By setting the 'passive' option appropriately, it ensures that default browser
129
  // behavior is prevented when necessary, thereby improving page scroll performance.
130
  // More info: https://stackoverflow.com/a/62177358
131
+ if (jQuery) {
132
  jQuery.event.special.touchstart = {
133
+ setup: function (_, ns, handle) {
134
  this.addEventListener('touchstart', handle, { passive: !ns.includes('noPreventDefault') });
135
  },
136
  };
137
  jQuery.event.special.touchmove = {
138
+ setup: function (_, ns, handle) {
139
  this.addEventListener('touchmove', handle, { passive: !ns.includes('noPreventDefault') });
140
  },
141
  };
142
  jQuery.event.special.wheel = {
143
+ setup: function (_, ns, handle) {
144
  this.addEventListener('wheel', handle, { passive: true });
145
  },
146
  };
147
  jQuery.event.special.mousewheel = {
148
+ setup: function (_, ns, handle) {
149
  this.addEventListener('mousewheel', handle, { passive: true });
150
  },
151
  };
152
  }
153
 
154
  // are we in dashboard mode?
155
+ if (window.location.pathname === '/dashboard' || window.location.pathname === '/dashboard/') {
156
  window.is_dashboard_mode = true;
157
  }
158
 
 
167
  const modalId = 'turnstile-challenge-modal';
168
  const siteKey = window.gui_params?.turnstileSiteKey;
169
 
170
+ if (!siteKey) {
171
  options.onError('Turnstile site key not configured');
172
  return resolve();
173
  }
 
204
 
205
  // Initialize Turnstile widget
206
  const initTurnstile = () => {
207
+ if (!window.turnstile) {
208
  setTimeout(initTurnstile, 100);
209
  return;
210
  }
 
233
  options.onError('Turnstile verification failed');
234
  },
235
  });
236
+ } catch (error) {
237
  console.error('Failed to initialize Turnstile:', error);
238
  showError('Failed to load security verification. Please refresh the page.');
239
  options.onError(error);
 
250
 
251
  // Prevent modal from closing by clicking outside
252
  modal.addEventListener('click', (e) => {
253
+ if (e.target === modal) {
254
  // Don't close - force users to complete verification
255
  turnstileContainer.style.transform = 'scale(1.05)';
256
  setTimeout(() => {
257
+ if (turnstileContainer) {
258
  turnstileContainer.style.transform = 'scale(1)';
259
  }
260
  }, 200);
 
280
 
281
  let picked_a_user_for_sdk_login = false;
282
 
 
 
 
 
 
283
  // update SDK if api_origin is different from the one in the SDK
284
+ if (window.api_origin && puter.APIOrigin !== window.api_origin) {
 
285
  puter.setAPIOrigin(window.api_origin);
286
  }
287
+ // update SDK if auth_token is different from the one in the SDK
288
+ if (window.auth_token && puter.authToken !== window.auth_token) {
289
+ puter.setAuthToken(window.auth_token);
290
+ }
291
 
292
  // Print the version to the console
293
  puter.os.version()
 
303
  // Depending on the device type, it sets a class attribute on the body tag
304
  // to style or script the page differently for each device type.
305
 
306
+ if (isMobile.phone) {
307
  $('body').attr('class', 'device-phone');
308
+ } else if (isMobile.tablet) {
309
  // This is our new, smarter check for tablets
310
+ if (window.matchMedia && typeof window.matchMedia === 'function' && window.matchMedia('(hover: hover)').matches) {
311
  // The user has a mouse/trackpad, so give them the desktop UI
312
  $('body').attr('class', 'device-desktop');
313
  } else {
 
336
  // Extract 'action' from URL
337
  //--------------------------------------------------------------------------------------
338
  let action;
339
+ if (window.url_paths[0]?.toLocaleLowerCase() === 'action' && window.url_paths[1]) {
340
  action = window.url_paths[1].toLowerCase();
341
  }
342
 
 
344
  // Determine if we are in full-page mode
345
  // i.e. https://puter.com/app/<app_name>/?puter.fullpage=true
346
  //--------------------------------------------------------------------------------------
347
+ if (window.url_query_params.has('puter.fullpage') && (window.url_query_params.get('puter.fullpage') === 'false' || window.url_query_params.get('puter.fullpage') === '0')) {
348
  window.is_fullpage_mode = false;
349
+ } else if (window.url_query_params.has('puter.fullpage') && (window.url_query_params.get('puter.fullpage') === 'true' || window.url_query_params.get('puter.fullpage') === '1')) {
350
  // In fullpage mode, we want to hide the taskbar for better UX
351
  window.taskbar_height = 0;
352
 
 
363
  // Is attempt_temp_user_creation?
364
  // i.e. https://puter.com/?attempt_temp_user_creation=true
365
  //--------------------------------------------------------------------------------------
366
+ if (window.url_query_params.has('attempt_temp_user_creation') && (window.url_query_params.get('attempt_temp_user_creation') === 'true' || window.url_query_params.get('attempt_temp_user_creation') === '1')) {
367
  window.attempt_temp_user_creation = true;
368
  }
369
 
 
371
  // Is GUI embedded in a popup?
372
  // i.e. https://puter.com/?embedded_in_popup=true
373
  //--------------------------------------------------------------------------------------
374
+ if (window.url_query_params.has('embedded_in_popup') && (window.url_query_params.get('embedded_in_popup') === 'true' || window.url_query_params.get('embedded_in_popup') === '1')) {
375
  window.embedded_in_popup = true;
376
  $('body').addClass('embedded-in-popup');
377
 
 
379
  window.openerOrigin = document.referrer;
380
 
381
  // if no referrer, request it from the opener via messaging
382
+ if (!document.referrer) {
383
  try {
384
  window.openerOrigin = await requestOpenerOrigin();
385
  } catch (e) {
 
390
  // this is the referrer in terms of user acquisition
391
  window.referrerStr = window.openerOrigin;
392
 
393
+ if (action === 'sign-in' && !window.is_auth() && !(window.attempt_temp_user_creation && window.first_visit_ever)) {
394
  // show signup window
395
+ if (await UIWindowSignup({
396
  reload_on_success: false,
397
  send_confirmation_code: false,
398
  show_close_button: false,
 
400
  has_head: false,
401
  cover_page: true,
402
  },
403
+ })) {
 
404
  await window.getUserAppToken(window.openerOrigin);
405
  }
406
  }
407
+ else if (action === 'sign-in' && window.is_auth() && !(window.attempt_temp_user_creation && window.first_visit_ever)) {
408
  picked_a_user_for_sdk_login = await UIWindowSessionList({
409
  reload_on_success: false,
410
  draggable_body: false,
 
412
  cover_page: true,
413
  });
414
 
415
+ if (picked_a_user_for_sdk_login) {
416
  await window.getUserAppToken(window.openerOrigin);
417
  }
418
 
 
422
  //--------------------------------------------------------------------------------------
423
  // Display an error if the query parameters have an error
424
  //--------------------------------------------------------------------------------------
425
+ if (window.url_query_params.has('error')) {
426
  // TODO: i18n
427
  await UIAlert({
428
  message: window.url_query_params.get('message'),
 
433
  // Get user referral code from URL query params
434
  // i.e. https://puter.com/?r=123456
435
  //--------------------------------------------------------------------------------------
436
+ if (window.url_query_params.has('r')) {
437
  window.referral_code = window.url_query_params.get('r');
438
  // remove 'r' from URL
439
  window.history.pushState(null, document.title, '/');
440
  // show referral notice, this will be used later if Desktop is loaded
441
+ if (window.first_visit_ever) {
442
  window.show_referral_notice = true;
443
  }
444
  }
 
446
  //--------------------------------------------------------------------------------------
447
  // Action: Request Permission
448
  //--------------------------------------------------------------------------------------
449
+ if (action === 'request-permission') {
450
  let app_uid = window.url_query_params.get('app_uid');
451
  let origin = window.openerOrigin ?? window.url_query_params.get('origin');
452
  let permission = window.url_query_params.get('permission');
 
466
  //--------------------------------------------------------------------------------------
467
  // Action: Password recovery
468
  //--------------------------------------------------------------------------------------
469
+ else if (action === 'set-new-password') {
470
  let user = window.url_query_params.get('user');
471
  let token = window.url_query_params.get('token');
472
 
 
478
  //--------------------------------------------------------------------------------------
479
  // Action: Change Username
480
  //--------------------------------------------------------------------------------------
481
+ else if (action === 'change-username') {
482
  await UIWindowChangeUsername();
483
  }
484
  //--------------------------------------------------------------------------------------
485
  // Action: Login
486
  //--------------------------------------------------------------------------------------
487
+ else if (action === 'login') {
488
  await UIWindowLogin();
489
  }
490
  //--------------------------------------------------------------------------------------
491
  // Action: Signup
492
  //--------------------------------------------------------------------------------------
493
+ else if (action === 'signup') {
494
  await UIWindowSignup();
495
  }
496
 
 
499
  // if yes, we need to get the user app token and send it to the opener
500
  // if not, we need to ask the user for confirmation before proceeding BUT only if the action is a file-picker action
501
  // -------------------------------------------------------------------------------------
502
+ if (window.embedded_in_popup && window.openerOrigin) {
503
  let response = await window.checkUserSiteRelationship(window.openerOrigin);
504
  window.userAppToken = response.token;
505
 
506
+ if (!picked_a_user_for_sdk_login && window.logged_in_users.length > 1 && (!window.userAppToken || window.url_query_params.get('request_auth'))) {
507
  picked_a_user_for_sdk_login = await UIWindowSessionList({
508
  reload_on_success: false,
509
  draggable_body: false,
 
515
  // -------------------------------------------------------------------------------------
516
  // `auth_token` provided in URL, use it to log in
517
  // -------------------------------------------------------------------------------------
518
+ else if (window.url_query_params.has('auth_token')) {
519
  let query_param_auth_token = window.url_query_params.get('auth_token');
520
 
521
  puter.setAuthToken(query_param_auth_token);
 
523
  try {
524
  whoami = await puter.os.user({ query: 'icon_size=64' });
525
  } catch (e) {
526
+ if (e.status === 401) {
527
  window.logout();
528
  return;
529
  }
530
  }
531
 
532
+ if (whoami) {
533
+ if (whoami.requires_email_confirmation) {
534
  let is_verified;
535
  do {
536
  is_verified = await UIWindowEmailConfirmationRequired({
 
538
  has_head: false,
539
  });
540
  }
541
+ while (!is_verified);
542
  }
543
  // if user is logging in using an auth token that means it's not their first ever visit to Puter.com
544
  // it might be their first visit to Puter on this specific device but it's not their first time ever visiting Puter.
 
578
  // -------------------------------------------------------------------------------------
579
  // Authed
580
  // -------------------------------------------------------------------------------------
581
+ if (window.is_auth()) {
582
  // try to get user data using /whoami, only if that data is missing
583
+ if (!whoami) {
584
  try {
585
  whoami = await puter.os.user({ query: 'icon_size=64' });
586
  } catch (e) {
587
+ if (e.status === 401) {
588
  bad_session_logout();
589
  return;
590
  }
591
  }
592
  }
593
  // update local user data
594
+ if (whoami) {
595
  // is email confirmation required?
596
+ if (whoami.requires_email_confirmation) {
597
  let is_verified;
598
  do {
599
  is_verified = await UIWindowEmailConfirmationRequired({
 
601
  has_head: false,
602
  });
603
  }
604
+ while (!is_verified);
605
  }
606
  window.update_auth_data(whoami.token || window.auth_token, whoami);
607
 
608
  // -------------------------------------------------------------------------------------
609
  // Load desktop, only if we're not embedded in a popup and not on the dashboard page
610
  // -------------------------------------------------------------------------------------
611
+ if (!window.embedded_in_popup && !window.is_dashboard_mode) {
612
  await window.get_auto_arrange_data();
613
  puter.fs.stat({ path: window.desktop_path, consistency: 'eventual' }).then(desktop_fsentry => {
614
  UIDesktop({ desktop_fsentry: desktop_fsentry });
 
617
  // -------------------------------------------------------------------------------------
618
  // Dashboard mode
619
  // -------------------------------------------------------------------------------------
620
+ else if (window.is_dashboard_mode) {
621
  UIDashboard();
622
  }
623
  // -------------------------------------------------------------------------------------
 
640
  msg_id: msg_id,
641
  }, window.openerOrigin);
642
  // close popup
643
+ if (!action || action === 'sign-in') {
644
  window.close();
645
  window.open('', '_self').close();
646
  }
647
+ } catch (err) {
648
  // send error to parent
649
  window.opener.postMessage({
650
  msg: 'puter.token',
 
659
 
660
  let app_uid;
661
 
662
+ if (window.openerOrigin) {
663
  app_uid = await window.getAppUIDFromOrigin(window.openerOrigin);
664
  window.host_app_uid = app_uid;
665
  }
666
 
667
+ if (action === 'show-open-file-picker') {
668
  let options = window.url_query_params.get('options');
669
  options = JSON.parse(options ?? '{}');
670
 
 
672
  UIWindow({
673
  allowed_file_types: options?.accept,
674
  selectable_body: options?.multiple,
675
+ path: `/${window.user.username}/Desktop`,
676
  // this is the uuid of the window to which this dialog will return
677
  return_to_parent_window: true,
678
  show_maximize_button: false,
 
698
  //--------------------------------------------------------------------------------------
699
  // Action: Show Directory Picker
700
  //--------------------------------------------------------------------------------------
701
+ else if (action === 'show-directory-picker') {
702
  // open directory picker dialog
703
  UIWindow({
704
+ path: `/${window.user.username}/Desktop`,
705
  // this is the uuid of the window to which this dialog will return
706
  // parent_uuid: event.data.appInstanceID,
707
  return_to_parent_window: true,
 
728
  //--------------------------------------------------------------------------------------
729
  // Action: Show Save File Dialog
730
  //--------------------------------------------------------------------------------------
731
+ else if (action === 'show-save-file-picker') {
732
  let allowed_file_types = window.url_query_params.get('allowed_file_types');
733
 
734
  // send 'sendMeFileData' event to parent
 
738
 
739
  // listen for 'showSaveFilePickerPopup' event from parent
740
  window.addEventListener('message', async (event) => {
741
+ if (event.data.msg !== 'showSaveFilePickerPopup') {
 
742
  return;
743
  }
744
 
745
  // Open dialog
746
  UIWindow({
747
  allowed_file_types: allowed_file_types,
748
+ path: `/${window.user.username}/Desktop`,
749
  // this is the uuid of the window to which this dialog will return
750
  return_to_parent_window: true,
751
  show_maximize_button: false,
 
773
  let overwrite = false;
774
  let file_to_upload = new File([event.data.content], path.basename(target_path));
775
  let item_with_same_name_already_exists = true;
776
+ while (item_with_same_name_already_exists) {
777
  // overwrite?
778
+ if (overwrite) {
 
779
  item_with_same_name_already_exists = false;
780
  }
781
  // upload
782
  try {
783
  const res = await puter.fs.write(target_path,
784
+ file_to_upload,
785
+ {
786
+ dedupeName: false,
787
+ overwrite: overwrite,
788
+ });
789
 
790
  let file_signature = await puter.fs.sign(app_uid, { uid: res.uid, action: 'write' });
791
  file_signature = file_signature.items;
 
809
  window.close();
810
  window.open('', '_self').close();
811
  }
812
+ catch (err) {
813
  // item with same name exists
814
+ if (err.code === 'item_with_same_name_exists') {
815
  const alert_resp = await UIAlert({
816
  message: `<strong>${html_encode(err.entry_name)}</strong> already exists.`,
817
  buttons: [
 
827
  ],
828
  parent_uuid: $(el_filedialog_window).attr('data-element_uuid'),
829
  });
830
+ if (alert_resp === 'replace') {
831
  overwrite = true;
832
+ } else if (alert_resp === 'cancel') {
833
  // enable parent window
834
  $(el_filedialog_window).find('.window-disable-mask, .busy-indicator').hide();
835
  return;
 
851
 
852
  // done
853
  let busy_duration = (Date.now() - busy_init_ts);
854
+ if (busy_duration >= window.busy_indicator_hide_delay) {
855
  $(el_filedialog_window).close();
856
  } else {
857
  setTimeout(() => {
 
875
  // `share_token` provided
876
  // i.e. https://puter.com/?share_token=<share_token>
877
  //--------------------------------------------------------------------------------------
878
+ if (window.url_query_params.has('share_token')) {
879
  let share_token = window.url_query_params.get('share_token');
880
 
881
  fetch(`${puter.APIOrigin}/sharelink/check`, {
 
889
  'method': 'POST',
890
  }).then(response => response.json())
891
  .then(async data => {
892
+ // Show register screen
893
+ if (data.email && data.email !== window.user?.email) {
894
  await UIWindowSignup({
895
  reload_on_success: true,
896
  email: data.email,
 
901
  });
902
  }
903
  // Show email confirmation screen
904
+ else if (data.email && data.email === window.user.email && !window.user.email_confirmed) {
905
+ // todo show email confirmation window
906
  await UIWindowEmailConfirmationRequired({
907
  stay_on_top: true,
908
  has_head: false,
 
927
  // If we're in fullpage/emebedded/Auth Popup mode, we don't want to load the custom background
928
  // because it's not visible anyway and it's a waste of bandwidth
929
  // -------------------------------------------------------------------------------------
930
+ if (!window.is_fullpage_mode && !window.embedded_in_popup) {
931
  window.refresh_desktop_background();
932
  }
933
  // -------------------------------------------------------------------------------------
934
  // Un-authed but not first visit -> try to log in/sign up
935
  // -------------------------------------------------------------------------------------
936
+ if (!window.is_auth() && (!window.first_visit_ever || window.disable_temp_users)) {
937
+ if (window.logged_in_users.length > 0) {
938
  UIWindowSessionList();
939
  }
940
  else {
941
+ const resp = await fetch(`${window.gui_origin}/whoarewe`);
942
  const whoarewe = await resp.json();
943
  await UIWindowLogin({
944
  // show_signup_button:
945
  reload_on_success: true,
946
  send_confirmation_code: false,
947
+ show_signup_button: (!whoarewe.disable_user_signup),
948
  window_options: {
949
  has_head: false,
950
  },
 
955
  // -------------------------------------------------------------------------------------
956
  // Un-authed and first visit ever -> create temp user with Turnstile challenge
957
  // -------------------------------------------------------------------------------------
958
+ else if (!window.is_auth() && window.first_visit_ever && !window.disable_temp_users) {
959
  let referrer;
960
  try {
961
  referrer = new URL(window.location.href).pathname;
 
969
  window.referrerStr = referrer;
970
 
971
  // in case there is also a referrer query param, add it to the referrer URL
972
+ if (window.url_query_params.has('ref')) {
973
+ if (!referrer) {
 
974
  referrer = '/';
975
  }
976
+ referrer += `?ref=${html_encode(window.url_query_params.get('ref'))}`;
977
  }
978
 
979
  let headers = {};
980
+ if (window.custom_headers) {
 
981
  headers = window.custom_headers;
982
  }
983
 
 
985
  const createTempUser = (turnstileToken) => {
986
  // if this is a popup, show a spinner
987
  let spinner_init_ts = Date.now();
988
+ if (window.embedded_in_popup) {
989
  puter.ui.showSpinner('<span style="-webkit-font-smoothing: antialiased;">Setting up your <a href="https://puter.com" target="_blank">Puter.com</a> account for secure AI and Cloud features</span>');
990
  }
991
 
 
996
  };
997
 
998
  // Add Turnstile token if available
999
+ if (turnstileToken) {
1000
  requestData['cf-turnstile-response'] = turnstileToken;
1001
  }
1002
 
1003
  $.ajax({
1004
+ url: `${window.gui_origin}/signup`,
1005
  type: 'POST',
1006
  async: true,
1007
  headers: headers,
 
1016
  }
1017
 
1018
  const $captchaModal = $('.captcha-modal');
1019
+ if ($captchaModal.length > 0) await new Promise(async resolve => {
1020
  // The callback operand for fadeOut could be called
1021
  // more than once if there are multiple `.captcha-modal`
1022
  // elements, but only the first call to `resolve()` will
 
1025
  $(this).remove();
1026
  resolve();
1027
  });
1028
+
1029
  // Just in case anything fails, also resolve after 500ms
1030
  await window.sleep(500);
1031
  resolve();
 
1034
  window.update_auth_data(data.token, data.user);
1035
 
1036
  // if this is a popup, hide the spinner, make sure it was visible for at least 2 seconds
1037
+ if (window.embedded_in_popup) await new Promise(async resolve => {
1038
  let spinner_duration = (Date.now() - spinner_init_ts);
1039
 
1040
  (async () => {
 
1053
  app_uid: data.app_uid,
1054
  }, window.openerOrigin);
1055
  // close popup
1056
+ if (!action || action === 'sign-in') {
1057
  window.close();
1058
  window.open('', '_self').close();
1059
  }
 
1080
  };
1081
 
1082
  // Check if Turnstile is enabled and show challenge
1083
+ if (window.gui_params?.turnstileSiteKey) {
1084
  window.showTurnstileChallenge({
1085
  onSuccess: createTempUser,
1086
  onError: (error) => {
 
1097
  }
1098
 
1099
  // if there is at least one window open (only non-Explorer windows), ask user for confirmation when navigating away from puter
1100
+ if (window.feature_flags.prompt_user_when_navigation_away_from_puter) {
1101
  window.onbeforeunload = function () {
1102
+ if ($('.window:not(.window[data-app="explorer"])').length > 0) {
 
1103
  return true;
1104
  }
1105
  };
 
1115
  // -------------------------------------------------------------------------------------
1116
  // Load desktop, if not embedded in a popup and not on the dashboard page
1117
  // -------------------------------------------------------------------------------------
1118
+ if (!window.embedded_in_popup && !window.is_dashboard_mode) {
1119
  await window.get_auto_arrange_data();
1120
  puter.fs.stat({ path: window.desktop_path, consistency: 'eventual' }).then(desktop_fsentry => {
1121
  UIDesktop({ desktop_fsentry: desktop_fsentry });
 
1124
  // -------------------------------------------------------------------------------------
1125
  // Dashboard mode: open explorer pointing to home directory
1126
  // -------------------------------------------------------------------------------------
1127
+ else if (window.is_dashboard_mode) {
1128
  UIDashboard();
1129
  }
1130
  // -------------------------------------------------------------------------------------
 
1148
  app_uid: data.app_uid,
1149
  }, window.openerOrigin);
1150
  // close popup
1151
+ if (!action || action === 'sign-in') {
1152
  window.close();
1153
  window.open('', '_self').close();
1154
  }
1155
+ } catch (err) {
1156
  // send error to parent
1157
  window.opener.postMessage({
1158
  msg: 'puter.token',
 
1167
 
1168
  let app_uid;
1169
 
1170
+ if (window.openerOrigin) {
1171
  app_uid = await window.getAppUIDFromOrigin(window.openerOrigin);
1172
  window.host_app_uid = app_uid;
1173
  }
 
1175
  //--------------------------------------------------------------------------------------
1176
  // Action: Show Open File Picker
1177
  //--------------------------------------------------------------------------------------
1178
+ if (action === 'show-open-file-picker') {
1179
  let options = window.url_query_params.get('options');
1180
  options = JSON.parse(options ?? '{}');
1181
 
 
1183
  UIWindow({
1184
  allowed_file_types: options?.accept,
1185
  selectable_body: options?.multiple,
1186
+ path: `/${window.user.username}/Desktop`,
1187
  return_to_parent_window: true,
1188
  show_maximize_button: false,
1189
  show_minimize_button: false,
 
1207
  //--------------------------------------------------------------------------------------
1208
  // Action: Show Directory Picker
1209
  //--------------------------------------------------------------------------------------
1210
+ else if (action === 'show-directory-picker') {
1211
  // open directory picker dialog
1212
  UIWindow({
1213
+ path: `/${window.user.username}/Desktop`,
1214
  // this is the uuid of the window to which this dialog will return
1215
  // parent_uuid: event.data.appInstanceID,
1216
  return_to_parent_window: true,
 
1238
  //--------------------------------------------------------------------------------------
1239
  // Action: Show Save File Dialog
1240
  //--------------------------------------------------------------------------------------
1241
+ else if (action === 'show-save-file-picker') {
1242
  let allowed_file_types = window.url_query_params.get('allowed_file_types');
1243
 
1244
  // send 'sendMeFileData' event to parent
 
1248
 
1249
  // listen for 'showSaveFilePickerPopup' event from parent
1250
  window.addEventListener('message', async (event) => {
1251
+ if (event.data.msg !== 'showSaveFilePickerPopup') {
 
1252
  return;
1253
  }
1254
 
1255
  // Open dialog
1256
  UIWindow({
1257
  allowed_file_types: allowed_file_types,
1258
+ path: `/${window.user.username}/Desktop`,
1259
  // this is the uuid of the window to which this dialog will return
1260
  return_to_parent_window: true,
1261
  show_maximize_button: false,
 
1283
  let overwrite = false;
1284
  let file_to_upload = new File([event.data.content], path.basename(target_path));
1285
  let item_with_same_name_already_exists = true;
1286
+ while (item_with_same_name_already_exists) {
1287
  // overwrite?
1288
+ if (overwrite) {
 
1289
  item_with_same_name_already_exists = false;
1290
  }
1291
  // upload
1292
  try {
1293
  const res = await puter.fs.write(target_path,
1294
+ file_to_upload,
1295
+ {
1296
+ dedupeName: false,
1297
+ overwrite: overwrite,
1298
+ });
1299
 
1300
  let file_signature = await puter.fs.sign(app_uid, { uid: res.uid, action: 'write' });
1301
  file_signature = file_signature.items;
 
1320
  window.open('', '_self').close();
1321
  // show_save_account_notice_if_needed();
1322
  }
1323
+ catch (err) {
1324
  // item with same name exists
1325
+ if (err.code === 'item_with_same_name_exists') {
1326
  const alert_resp = await UIAlert({
1327
  message: `<strong>${html_encode(err.entry_name)}</strong> already exists.`,
1328
  buttons: [
 
1338
  ],
1339
  parent_uuid: $(el_filedialog_window).attr('data-element_uuid'),
1340
  });
1341
+ if (alert_resp === 'replace') {
1342
  overwrite = true;
1343
+ } else if (alert_resp === 'cancel') {
1344
  // enable parent window
1345
  $(el_filedialog_window).find('.window-disable-mask, .busy-indicator').hide();
1346
  return;
 
1362
 
1363
  // done
1364
  let busy_duration = (Date.now() - busy_init_ts);
1365
+ if (busy_duration >= window.busy_indicator_hide_delay) {
1366
  $(el_filedialog_window).close();
1367
  } else {
1368
  setTimeout(() => {
 
1390
  update_last_touch_coordinates(e);
1391
 
1392
  // dismiss touchstart on regular devices
1393
+ if (e.type === 'touchstart' && !isMobile.phone && !isMobile.tablet) {
 
1394
  return;
1395
  }
1396
 
1397
  // If .item-container clicked, unselect all its item children
1398
+ if ($(e.target).hasClass('item-container') && !e.ctrlKey && !e.metaKey) {
1399
  $(e.target).children('.item-selected').removeClass('item-selected');
1400
  window.update_explorer_footer_selected_items_count(e.target);
1401
  }
1402
 
1403
  // If the clicked element is not a context menu, remove all context menus
1404
+ if ($(e.target).parents('.context-menu').length === 0) {
1405
  $('.context-menu').fadeOut(200, function () {
1406
  $(this).remove();
1407
  });
1408
  }
1409
 
1410
  // click on anything will close all popovers, but there are some exceptions
1411
+ if (!$(e.target).hasClass('start-app')
1412
  && !$(e.target).hasClass('launch-search')
1413
  && !$(e.target).hasClass('launch-search-clear')
1414
  && $(e.target).closest('.start-app').length === 0
1415
  && !isMobile.phone && !isMobile.tablet
1416
  && !$(e.target).hasClass('popover')
1417
+ && $(e.target).parents('.popover').length === 0) {
1418
 
1419
  $('.popover').fadeOut(200, function () {
1420
  $('.popover').remove();
 
1425
  $('.ui-tooltip').remove();
1426
 
1427
  // rename items whose names were being edited
1428
+ if (!$(e.target).hasClass('item-name-editor')) {
1429
  // blurring an Item Name Editor will automatically trigger renaming the item
1430
  $('.item-name-editor-active').blur();
1431
  }
1432
 
1433
  // update active_item_container
1434
+ if ($(e.target).hasClass('item-container')) {
1435
  window.active_item_container = e.target;
1436
  } else {
1437
  let ic = $(e.target).closest('.item-container');
1438
+ if (ic.length > 0) {
1439
  window.active_item_container = ic.get(0);
1440
  } else {
1441
  let pp = $(e.target).find('.item-container');
1442
+ if (pp.length > 0) {
1443
  window.active_item_container = pp.get(0);
1444
  }
1445
  }
 
1459
  //--------------------------------------------------------
1460
  $(document).on('mousedown', function (e) {
1461
  // if taskbar or any parts of it is clicked, drop the event
1462
+ if ($(e.target).hasClass('taskbar') || $(e.target).closest('.taskbar').length > 0) {
1463
  return;
1464
  }
1465
  // if toolbar or any parts of it is clicked, drop the event
1466
+ if ($(e.target).hasClass('toolbar') || $(e.target).closest('.toolbar').length > 0) {
1467
  return;
1468
  }
1469
 
1470
  // if close or minimize button clicked, drop the event
1471
+ if (document.elementFromPoint(e.clientX, e.clientY).closest('.window-close-btn, .window-minimize-btn')) {
1472
  return;
1473
  }
1474
 
1475
  // if mouse is clicked on a window, activate it
1476
+ if (window.mouseover_window !== undefined) {
1477
  // if popover clicked on, don't activate window. This is because if an app
1478
  // is using the popover API to show a popover, the popover will be closed if the window is activated
1479
+ if ($(e.target).hasClass('popover') || $(e.target).parents('.popover').length > 0) {
 
1480
  return;
1481
  }
1482
  $(window.mouseover_window).focusWindow(e);
 
1497
  });
1498
 
1499
  document.addEventListener('visibilitychange', (event) => {
1500
+ if (document.visibilityState !== 'visible') {
1501
  window.doc_title_before_blur = document.title;
1502
+ if (!_.isEmpty(window.active_uploads)) {
1503
  update_title_based_on_uploads();
1504
  }
1505
+ } else if (window.active_uploads) {
1506
  document.title = window.doc_title_before_blur ?? 'Puter';
1507
  }
1508
  });
 
1520
  */
1521
  $(document).on('logout', async function (event) {
1522
  // is temp user?
1523
+ if (window.user && window.user.is_temp && !window.user.deleted) {
1524
  const alert_resp = await UIAlert({
1525
  message: '<strong>Save account before logging out!</strong><p>You are using a temporary account and logging out will erase all your data.</p>',
1526
  buttons: [
 
1539
  },
1540
  ],
1541
  });
1542
+ if (alert_resp === 'save_account') {
1543
  let saved = await UIWindowSaveAccount({
1544
  send_confirmation_code: false,
1545
  default_username: window.user.username,
1546
  });
1547
+ if (saved) {
 
1548
  window.logout();
1549
  }
1550
+ } else if (alert_resp === 'log_out') {
1551
  window.logout();
1552
  }
1553
  else {
 
1560
  const resp = await fetch(`${window.gui_origin}/get-anticsrf-token`);
1561
  const { token } = await resp.json();
1562
  await $.ajax({
1563
+ url: `${window.gui_origin}/logout`,
1564
  type: 'POST',
1565
  async: true,
1566
  contentType: 'application/json',
1567
  headers: {
1568
+ 'Authorization': `Bearer ${window.auth_token}`,
1569
  },
1570
  data: JSON.stringify({ anti_csrf: token }),
1571
  statusCode: {
 
1578
  }
1579
 
1580
  // remove this user from the array of logged_in_users
1581
+ for (let i = 0; i < window.logged_in_users.length; i++) {
1582
+ if (window.logged_in_users[i].uuid === window.user.uuid) {
1583
  window.logged_in_users.splice(i, 1);
1584
  break;
1585
  }
 
1609
  });
1610
  };
1611
 
1612
+ function requestOpenerOrigin() {
1613
  return new Promise((resolve, reject) => {
1614
+ if (!window.opener) {
1615
  reject(new Error('No window.opener available'));
1616
  return;
1617
  }
 
1619
  // Function to handle the message event
1620
  const handleMessage = (event) => {
1621
  // Check if the message is the expected response
1622
+ if (event.data.msg === 'originResponse') {
1623
  // Clean up by removing the event listener
1624
  window.removeEventListener('message', handleMessage);
1625
  resolve(event.origin);
 
1645
  });
1646
 
1647
  $(document).on('click', function (e) {
1648
+ if (!$(e.target).hasClass('window-search') && $(e.target).closest('.window-search').length === 0 && !$(e.target).is('.toolbar-btn.search-btn')) {
1649
  $('.window-search').close();
1650
  }
1651
  });
 
1653
  // Re-calculate desktop height and width on window resize and re-position the login and signup windows
1654
  $(window).on('resize', function () {
1655
  // If host env is popup, don't continue because the popup window has its own resize requirements.
1656
+ if (window.embedded_in_popup) {
 
1657
  return;
1658
  }
1659
 
 
1680
  });
1681
 
1682
  $(document).on('contextmenu', '.disable-context-menu', function (e) {
1683
+ if ($(e.target).hasClass('disable-context-menu')) {
1684
  e.preventDefault();
1685
  return false;
1686
  }