@oat-sa/tao-core-ui 3.24.0 → 3.24.2

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.
Files changed (146) hide show
  1. package/dist/animable/pulsable/pulsable.js +0 -1
  2. package/dist/autocomplete/css/autocomplete.css +1 -1
  3. package/dist/autocomplete/css/autocomplete.css.map +1 -1
  4. package/dist/badge/css/badge.css +6 -6
  5. package/dist/badge/css/badge.css.map +1 -1
  6. package/dist/breadcrumbs/css/breadcrumbs.css +2 -2
  7. package/dist/breadcrumbs/css/breadcrumbs.css.map +1 -1
  8. package/dist/calculator/css/calculator.css +8 -6
  9. package/dist/calculator/css/calculator.css.map +1 -1
  10. package/dist/class/css/selector.css +4 -4
  11. package/dist/class/css/selector.css.map +1 -1
  12. package/dist/component/css/components.css +5 -5
  13. package/dist/component/css/components.css.map +1 -1
  14. package/dist/component/css/windowComponent.css +1 -1
  15. package/dist/component/css/windowComponent.css.map +1 -1
  16. package/dist/component/draggable.js +0 -1
  17. package/dist/component/resizable.js +0 -1
  18. package/dist/contextualPopup/css/contextualPopup.css +1 -1
  19. package/dist/contextualPopup/css/contextualPopup.css.map +1 -1
  20. package/dist/datatable/css/datatable.css +3 -3
  21. package/dist/datatable/css/datatable.css.map +1 -1
  22. package/dist/datatable.js +1 -2
  23. package/dist/datetime/css/picker.css +13 -13
  24. package/dist/datetime/css/picker.css.map +1 -1
  25. package/dist/documentViewer/css/documentViewer.css +10 -10
  26. package/dist/documentViewer/css/documentViewer.css.map +1 -1
  27. package/dist/documentViewer/providers/pdfViewer/pdfjs/searchEngine.js +0 -1
  28. package/dist/dynamicComponent/css/dynamicComponent.css +1 -1
  29. package/dist/dynamicComponent/css/dynamicComponent.css.map +1 -1
  30. package/dist/dynamicComponent.js +1 -4
  31. package/dist/figure/FigureStateActive.js +20 -23
  32. package/dist/form/css/dropdownForm.css +1 -1
  33. package/dist/form/css/dropdownForm.css.map +1 -1
  34. package/dist/form/widget/widget.js +1 -4
  35. package/dist/highlighter.js +0 -2
  36. package/dist/image/ImgStateActive.js +13 -15
  37. package/dist/interactUtils.js +7 -8
  38. package/dist/itemButtonList/css/item-button-list.css +9 -3
  39. package/dist/itemButtonList/css/item-button-list.css.map +1 -1
  40. package/dist/itemButtonList.js +1 -2
  41. package/dist/keyNavigation/navigableDomElement.js +3 -4
  42. package/dist/keyNavigation/navigator.js +1 -2
  43. package/dist/listbox/css/listbox.css +1 -1
  44. package/dist/listbox/css/listbox.css.map +1 -1
  45. package/dist/maths/calculator/core/board.js +58 -127
  46. package/dist/maths/calculator/core/labels.js +1 -6
  47. package/dist/maths/calculator/css/calculator.css +18 -18
  48. package/dist/maths/calculator/css/calculator.css.map +1 -1
  49. package/dist/maths/calculator/plugins/screen/simpleScreen/simpleScreen.js +1 -6
  50. package/dist/mediaplayer/css/player.css +7 -7
  51. package/dist/mediaplayer/css/player.css.map +1 -1
  52. package/dist/mediaplayer/players/html5.js +3 -14
  53. package/dist/mediaplayer/players/youtube.js +3 -12
  54. package/dist/mediaplayer/utils/crossOrigin.js +1 -1
  55. package/dist/mediaplayer/utils/timeObserver.js +1 -2
  56. package/dist/mediaplayer/youtubeManager.js +1 -2
  57. package/dist/mediaplayer.js +2 -8
  58. package/dist/mediasizer.js +0 -1
  59. package/dist/resizeObserver.js +1 -3
  60. package/dist/resource/css/selector.css +31 -23
  61. package/dist/resource/css/selector.css.map +1 -1
  62. package/dist/resource/list.js +239 -203
  63. package/dist/resource/selector.js +0 -1
  64. package/dist/resource/tree.js +239 -203
  65. package/dist/resourcemgr/assetSearch.js +3 -3
  66. package/dist/resourcemgr/assetSearchContract.js +40 -12
  67. package/dist/resourcemgr/css/resourcemgr.css +113 -21
  68. package/dist/resourcemgr/css/resourcemgr.css.map +1 -1
  69. package/dist/resourcemgr/fileBrowser.js +370 -17
  70. package/dist/resourcemgr/filePreview.js +42 -3
  71. package/dist/resourcemgr/fileSelector.js +15 -0
  72. package/dist/resourcemgr.js +15 -10
  73. package/dist/searchModal/advancedSearch.js +3 -4
  74. package/dist/searchModal/css/searchModal.css +14 -14
  75. package/dist/searchModal/css/searchModal.css.map +1 -1
  76. package/dist/searchModal.js +42 -53
  77. package/dist/switch/css/switch.css +2 -2
  78. package/dist/switch/css/switch.css.map +1 -1
  79. package/dist/tabs/css/tabs.css +2 -2
  80. package/dist/tabs/css/tabs.css.map +1 -1
  81. package/dist/taskQueue/css/taskQueue.css +4 -4
  82. package/dist/taskQueue/css/taskQueue.css.map +1 -1
  83. package/dist/taskQueue/taskQueueModel.js +1 -2
  84. package/dist/taskQueueButton/taskable.js +1 -2
  85. package/dist/validator/validators.js +0 -1
  86. package/dist/waitingDialog/css/waitingDialog.css +2 -2
  87. package/dist/waitingDialog/css/waitingDialog.css.map +1 -1
  88. package/package.json +1 -1
  89. package/src/autocomplete/css/autocomplete.css +1 -1
  90. package/src/autocomplete/css/autocomplete.css.map +1 -1
  91. package/src/badge/css/badge.css +6 -6
  92. package/src/badge/css/badge.css.map +1 -1
  93. package/src/breadcrumbs/css/breadcrumbs.css +2 -2
  94. package/src/breadcrumbs/css/breadcrumbs.css.map +1 -1
  95. package/src/calculator/css/calculator.css +8 -6
  96. package/src/calculator/css/calculator.css.map +1 -1
  97. package/src/class/css/selector.css +4 -4
  98. package/src/class/css/selector.css.map +1 -1
  99. package/src/component/css/components.css +5 -5
  100. package/src/component/css/components.css.map +1 -1
  101. package/src/component/css/windowComponent.css +1 -1
  102. package/src/component/css/windowComponent.css.map +1 -1
  103. package/src/contextualPopup/css/contextualPopup.css +1 -1
  104. package/src/contextualPopup/css/contextualPopup.css.map +1 -1
  105. package/src/css/basic.css +123 -113
  106. package/src/css/basic.css.map +1 -1
  107. package/src/datatable/css/datatable.css +3 -3
  108. package/src/datatable/css/datatable.css.map +1 -1
  109. package/src/datetime/css/picker.css +13 -13
  110. package/src/datetime/css/picker.css.map +1 -1
  111. package/src/documentViewer/css/documentViewer.css +10 -10
  112. package/src/documentViewer/css/documentViewer.css.map +1 -1
  113. package/src/dynamicComponent/css/dynamicComponent.css +1 -1
  114. package/src/dynamicComponent/css/dynamicComponent.css.map +1 -1
  115. package/src/form/css/dropdownForm.css +1 -1
  116. package/src/form/css/dropdownForm.css.map +1 -1
  117. package/src/itemButtonList/css/item-button-list.css +9 -3
  118. package/src/itemButtonList/css/item-button-list.css.map +1 -1
  119. package/src/listbox/css/listbox.css +1 -1
  120. package/src/listbox/css/listbox.css.map +1 -1
  121. package/src/maths/calculator/css/calculator.css +18 -18
  122. package/src/maths/calculator/css/calculator.css.map +1 -1
  123. package/src/mediaplayer/css/player.css +7 -7
  124. package/src/mediaplayer/css/player.css.map +1 -1
  125. package/src/resource/css/selector.css +31 -23
  126. package/src/resource/css/selector.css.map +1 -1
  127. package/src/resourcemgr/assetSearch.js +3 -3
  128. package/src/resourcemgr/assetSearchContract.js +40 -18
  129. package/src/resourcemgr/css/resourcemgr.css +113 -21
  130. package/src/resourcemgr/css/resourcemgr.css.map +1 -1
  131. package/src/resourcemgr/fileBrowser.js +434 -33
  132. package/src/resourcemgr/filePreview.js +43 -4
  133. package/src/resourcemgr/fileSelector.js +18 -0
  134. package/src/resourcemgr/scss/resourcemgr.scss +103 -5
  135. package/src/resourcemgr/tpl/layout.tpl +30 -18
  136. package/src/resourcemgr.js +9 -4
  137. package/src/searchModal/css/searchModal.css +14 -14
  138. package/src/searchModal/css/searchModal.css.map +1 -1
  139. package/src/switch/css/switch.css +2 -2
  140. package/src/switch/css/switch.css.map +1 -1
  141. package/src/tabs/css/tabs.css +2 -2
  142. package/src/tabs/css/tabs.css.map +1 -1
  143. package/src/taskQueue/css/taskQueue.css +4 -4
  144. package/src/taskQueue/css/taskQueue.css.map +1 -1
  145. package/src/waitingDialog/css/waitingDialog.css +2 -2
  146. package/src/waitingDialog/css/waitingDialog.css.map +1 -1
@@ -204,7 +204,18 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
204
204
  */
205
205
  const NS = 'resourcemgr';
206
206
  const LOGGER = loggerFactory(`ui/${NS}`);
207
- const DEFAULT_AJAX_TIMEOUT_MS = 10000;
207
+ const DEFAULT_AJAX_TIMEOUT_MS = 30000;
208
+ const TREE_LOADING_REF_KEY = `${NS}TreeLoadingRefCount`;
209
+
210
+ /**
211
+ * @param {Object} data
212
+ * @returns {Array}
213
+ */
214
+ function folderChildrenOnly(data) {
215
+ return _.filter(data && data.children, function (child) {
216
+ return child && child.path && !child.uri;
217
+ });
218
+ }
208
219
  function fileBrowser (options) {
209
220
  if (!options.browseUrl && options.url) {
210
221
  options.browseUrl = options.url;
@@ -221,15 +232,50 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
221
232
  // for pagination
222
233
  let selectedClass = {
223
234
  path: rootPath,
224
- childrenLimit: 10,
235
+ childrenLimit: 11,
225
236
  total: 0,
226
237
  page: 1
227
238
  };
228
239
  let searchMode = false;
229
240
  let sort = Object.assign({}, assetSearchContract.DEFAULT_SORT);
241
+ const splitBrowse = options.splitBrowse !== false;
230
242
  const ajaxTimeoutMs = Number.isFinite(Number(options.ajaxTimeoutMs)) && Number(options.ajaxTimeoutMs) > 0 ? Number(options.ajaxTimeoutMs) : DEFAULT_AJAX_TIMEOUT_MS;
243
+ const $treeLoadingOverlay = $$1('.file-browser-tree-loading', $fileBrowser);
244
+ const $treeLoadingStatus = $$1('.file-browser-tree-loading-status', $treeLoadingOverlay);
245
+
246
+ /**
247
+ * Ref-count in-flight browse requests and toggle the tree loading overlay.
248
+ * @param {Boolean} increment - true when a load starts, false when it finishes
249
+ */
250
+ function setTreeLoading(increment) {
251
+ let treeLoadingRefCount = Number($fileBrowser.data(TREE_LOADING_REF_KEY)) || 0;
252
+ treeLoadingRefCount += increment ? 1 : -1;
253
+ if (treeLoadingRefCount < 0) {
254
+ treeLoadingRefCount = 0;
255
+ }
256
+ $fileBrowser.data(TREE_LOADING_REF_KEY, treeLoadingRefCount);
257
+ const loading = treeLoadingRefCount > 0;
258
+ $fileBrowser.toggleClass('is-tree-loading', loading);
259
+ $fileBrowser.attr('aria-busy', loading ? 'true' : 'false');
260
+ $treeLoadingOverlay.toggleClass('hidden', !loading).attr('aria-hidden', loading ? 'false' : 'true');
261
+ $treeLoadingStatus.attr('aria-busy', loading ? 'true' : 'false');
262
+ }
263
+
264
+ /**
265
+ * Show or hide the asset table loading overlay (browse mode only).
266
+ * @param {Boolean} loading
267
+ */
268
+ function setFilesLoading(loading) {
269
+ if (searchMode) {
270
+ return;
271
+ }
272
+ $container.trigger(`filesloading.${NS}`, [!!loading]);
273
+ }
231
274
  $container.on(`searchmode.${NS}`, function (e, enabled) {
232
275
  searchMode = !!enabled;
276
+ if (searchMode) {
277
+ $container.trigger(`filesloading.${NS}`, [false]);
278
+ }
233
279
  });
234
280
  $container.on(`sortchange.${NS}`, function (e, nextSort) {
235
281
  sort = Object.assign({}, assetSearchContract.DEFAULT_SORT, nextSort || {});
@@ -248,9 +294,14 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
248
294
  return;
249
295
  }
250
296
  const subTree = getByExactPath(fileTree, targetPath) || getByPath(fileTree, targetPath) || fileTree;
297
+ if (!searchMode) {
298
+ setFilesLoading(true);
299
+ }
251
300
  getFolderContent(subTree, targetPath, function (content) {
252
301
  if (content) {
253
302
  selectFolder(content, targetPath);
303
+ } else {
304
+ setFilesLoading(false);
254
305
  }
255
306
  });
256
307
  });
@@ -274,8 +325,15 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
274
325
  });
275
326
 
276
327
  //load the content of the ROOT
328
+ const showInitialListLoading = !searchMode && root === 'local';
329
+ if (showInitialListLoading) {
330
+ setFilesLoading(true);
331
+ }
277
332
  getFolderContent(fileTree, rootPath, function (content) {
278
333
  if (!content) {
334
+ if (showInitialListLoading) {
335
+ setFilesLoading(false);
336
+ }
279
337
  return;
280
338
  }
281
339
  indexTree(content);
@@ -303,6 +361,8 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
303
361
  if (root !== 'local') {
304
362
  options.hasAlreadySelected = true;
305
363
  }
364
+ } else if (showInitialListLoading) {
365
+ setFilesLoading(false);
306
366
  }
307
367
  });
308
368
 
@@ -312,6 +372,14 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
312
372
  const $selected = $$1(this);
313
373
  const fullPath = $selected.data('path');
314
374
  const subTree = getByExactPath(fileTree, fullPath);
375
+ const openingFolder = !searchMode && fullPath !== selectedClass.path;
376
+ if (openingFolder) {
377
+ selectedClass.page = 1;
378
+ }
379
+ if (!searchMode) {
380
+ setFilesLoading(true);
381
+ }
382
+ invalidateFolderFiles(fullPath);
315
383
 
316
384
  //get the folder content
317
385
  getFolderContent(subTree, fullPath, function (content) {
@@ -341,6 +409,8 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
341
409
 
342
410
  //internal event to set the file-selector content
343
411
  selectFolder(content, fullPath);
412
+ } else if (!searchMode) {
413
+ setFilesLoading(false);
344
414
  }
345
415
  });
346
416
  });
@@ -361,10 +431,11 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
361
431
  }
362
432
  if (targetPath !== selectedClass.path) {
363
433
  selectedClass.path = targetPath;
364
- selectedClass.page = 1;
365
434
  }
435
+ selectedClass.page = 1;
366
436
  invalidateFolderFiles(targetPath);
367
- reloadSortedFolder();
437
+ const pendingUpload = file && !file.error && (file.uri || file.name) ? file : null;
438
+ reloadSortedFolder(pendingUpload ? [pendingUpload] : []);
368
439
  });
369
440
  $container.on(`filedelete.${NS}`, function (e, path) {
370
441
  if (searchMode) {
@@ -494,13 +565,16 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
494
565
  $container.data('activeFileBrowserRoot', root);
495
566
  if (searchMode) {
496
567
  $container.trigger(`folderpath.${NS}`, [path, content.label]);
497
- $container.trigger(`folderselect.${NS}`, [content.label, getPage(content.children || []), path, content]);
568
+ $container.trigger(`folderselect.${NS}`, [content.label, getFilesForDisplay(content), path, content]);
498
569
  return;
499
570
  }
500
571
  updateSelectedClass(path, content.total, content.childrenLimit);
501
572
  $container.trigger(`folderpath.${NS}`, [path, content.label]);
502
- $container.trigger(`folderselect.${NS}`, [content.label, getPage(content.children || []), path, content]);
573
+ $container.trigger(`folderselect.${NS}`, [content.label, getFilesForDisplay(content), path, content]);
503
574
  renderPagination();
575
+ if (!searchMode) {
576
+ setFilesLoading(false);
577
+ }
504
578
  }
505
579
 
506
580
  /**
@@ -517,6 +591,172 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
517
591
  }
518
592
  return files;
519
593
  }
594
+
595
+ /**
596
+ * Files for the table (split list payload or legacy children slice).
597
+ * @param {Object} content
598
+ * @returns {Array}
599
+ */
600
+ function getFilesForDisplay(content) {
601
+ if (!content) {
602
+ return [];
603
+ }
604
+ if (splitBrowse) {
605
+ return content.listItems || [];
606
+ }
607
+ return getPage(content.children || []);
608
+ }
609
+ function listCacheKey() {
610
+ return `${sort.field}:${sort.direction}:${selectedClass.page}`;
611
+ }
612
+
613
+ /**
614
+ * Snapshot list request identity (folder + page/sort) when a browse starts.
615
+ * @returns {{path: String, cacheKey: String}}
616
+ */
617
+ function captureListRequestContext() {
618
+ return {
619
+ path: selectedClass.path,
620
+ cacheKey: listCacheKey()
621
+ };
622
+ }
623
+
624
+ /**
625
+ * Whether navigation changed before an in-flight list response should apply.
626
+ * @param {{path: String, cacheKey: String}} captured
627
+ * @returns {Boolean}
628
+ */
629
+ function isListRequestStale(captured) {
630
+ return !captured || captured.path !== selectedClass.path || captured.cacheKey !== listCacheKey();
631
+ }
632
+
633
+ /**
634
+ * @param {Object} response
635
+ * @returns {Object}
636
+ */
637
+ function unwrapBrowseResponse(response) {
638
+ if (response && response.success === false) {
639
+ return Promise.reject(response);
640
+ }
641
+ let data = response && response.data ? response.data : response;
642
+ data = updatePermissions(data);
643
+ if (data.children && data.children.length > 0) {
644
+ data.children = data.children.map(function (child) {
645
+ return updatePermissions(child);
646
+ });
647
+ }
648
+ if (data.items && data.items.length > 0) {
649
+ data.items = data.items.map(function (item) {
650
+ return updatePermissions(item);
651
+ });
652
+ }
653
+ return data;
654
+ }
655
+
656
+ /**
657
+ * @param {String} path
658
+ * @param {Object} extraData
659
+ * @returns {Promise}
660
+ */
661
+ function browseRequest(path, extraData) {
662
+ const parameters = {};
663
+ parameters[options.pathParam || 'path'] = path;
664
+ return Promise.resolve($$1.ajax({
665
+ url: options.browseUrl,
666
+ method: 'GET',
667
+ dataType: 'json',
668
+ timeout: ajaxTimeoutMs,
669
+ data: _.merge(parameters, options.params, extraData)
670
+ })).then(unwrapBrowseResponse);
671
+ }
672
+
673
+ /**
674
+ * @param {Object} tree
675
+ * @param {String} path
676
+ * @param {Object} data
677
+ */
678
+ function applyTreePayload(tree, path, data) {
679
+ const folderData = Object.assign({}, data, {
680
+ children: folderChildrenOnly(data)
681
+ });
682
+ if (!tree.path) {
683
+ _.merge(tree, folderData);
684
+ } else if (folderData.children) {
685
+ setToPath(tree, path, folderData);
686
+ if (!_.find(folderData.children, 'path')) {
687
+ markFolderEmptyAtPath(tree, path);
688
+ }
689
+ } else {
690
+ setToPath(tree, path, folderData);
691
+ markFolderEmptyAtPath(tree, path);
692
+ }
693
+ }
694
+
695
+ /**
696
+ * @param {Object} tree
697
+ * @param {String} path
698
+ * @param {Object} listData
699
+ * @param {String} cacheKey
700
+ */
701
+ function applyListPayload(tree, path, listData, cacheKey) {
702
+ const node = getByExactPath(tree, path) || getByPath(tree, path);
703
+ if (!node || !listData) {
704
+ return;
705
+ }
706
+ node.listItems = listData.items || [];
707
+ node.total = listData.total;
708
+ node.childrenLimit = listData.pageSize || listData.childrenLimit || selectedClass.childrenLimit;
709
+ node.listPage = listData.page;
710
+ node.totalIsApproximate = listData.totalIsApproximate;
711
+ node.listCacheKey = cacheKey;
712
+ }
713
+
714
+ /**
715
+ * Ensure upload responses appear in the table before Elasticsearch catches up.
716
+ * @param {String} path
717
+ * @param {Array<Object>} uploads
718
+ */
719
+ function mergePendingUploads(path, uploads) {
720
+ if (!uploads || !uploads.length) {
721
+ return;
722
+ }
723
+ const node = getByExactPath(fileTree, path) || getByPath(fileTree, path);
724
+ if (!node) {
725
+ return;
726
+ }
727
+ if (!Array.isArray(node.listItems)) {
728
+ node.listItems = [];
729
+ }
730
+ let added = 0;
731
+ uploads.forEach(function (uploaded) {
732
+ if (!uploaded || uploaded.error) {
733
+ return;
734
+ }
735
+ const normalized = updatePermissions(uploaded);
736
+ if (!normalized.uri && !normalized.name) {
737
+ return;
738
+ }
739
+ const exists = _.some(node.listItems, function (item) {
740
+ return normalized.uri && item.uri === normalized.uri || normalized.name && item.name === normalized.name;
741
+ });
742
+ if (exists) {
743
+ return;
744
+ }
745
+ node.listItems.push(normalized);
746
+ added++;
747
+ });
748
+ if (!added) {
749
+ return;
750
+ }
751
+ if (splitBrowse) {
752
+ node.listItems = assetSearchContract.sortAssetItems(node.listItems, sort);
753
+ }
754
+ const total = Number(node.total);
755
+ if (Number.isFinite(total) && total >= 0) {
756
+ node.total = total + added;
757
+ }
758
+ }
759
+
520
760
  /**
521
761
  * Get the content of a folder, either in the model or load it
522
762
  * @param {Object} tree - the tree model
@@ -530,6 +770,43 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
530
770
  }
531
771
  }
532
772
  function getFolderContent(tree, path, cb) {
773
+ if (splitBrowse) {
774
+ const existing = getByExactPath(tree, path) || getByPath(tree, path);
775
+ const needTree = !existing || !existing.children && !existing.empty;
776
+ const cacheKey = listCacheKey();
777
+ const needList = !existing || existing.listCacheKey !== cacheKey;
778
+ const treePromise = needTree ? (setTreeLoading(true), browseRequest(path, {
779
+ part: 'tree',
780
+ depth: 1,
781
+ sortBy: sort.field,
782
+ sortDir: sort.direction
783
+ }).then(function (data) {
784
+ setTreeLoading(false);
785
+ return data;
786
+ }, function (error) {
787
+ setTreeLoading(false);
788
+ return Promise.reject(error);
789
+ })) : Promise.resolve(null);
790
+ const listPromise = needList ? browseRequest(path, {
791
+ part: 'list',
792
+ page: selectedClass.page,
793
+ pageSize: selectedClass.childrenLimit || 11,
794
+ sortBy: sort.field,
795
+ sortDir: sort.direction
796
+ }) : Promise.resolve(null);
797
+ Promise.all([treePromise, listPromise]).then(function ([treeData, listData]) {
798
+ if (treeData) {
799
+ applyTreePayload(tree, path, treeData);
800
+ }
801
+ if (listData) {
802
+ applyListPayload(tree, path, listData, cacheKey);
803
+ }
804
+ cb(getByExactPath(tree, path) || getByPath(tree, path));
805
+ }).catch(function () {
806
+ cb(null);
807
+ });
808
+ return;
809
+ }
533
810
  let content = getByPath(tree, path);
534
811
  if (!content || !content.children && !content.empty) {
535
812
  loadContent(path).then(function (data) {
@@ -555,7 +832,7 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
555
832
  });
556
833
  // Use folder total (not selectedClass): openInitialPath runs before selectFolder.
557
834
  const expectedTotal = Number(content.total);
558
- const pageSize = Number(content.childrenLimit) || selectedClass.childrenLimit || 10;
835
+ const pageSize = Number(content.childrenLimit) || selectedClass.childrenLimit || 11;
559
836
  const page = selectedClass.page || 1;
560
837
  // Missing/NaN total → unknown size; refetch so invalidated cache is not treated as complete.
561
838
  if (!Number.isFinite(expectedTotal) || files.length < expectedTotal && files.length < page * pageSize) {
@@ -592,8 +869,7 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
592
869
  * @param {object} tree - the tree model
593
870
  * @param {number} level - the root level
594
871
  */
595
- function indexTree(tree) {
596
- let level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
872
+ function indexTree(tree, level = 0) {
597
873
  if (!tree) {
598
874
  return;
599
875
  }
@@ -715,7 +991,16 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
715
991
  */
716
992
  function invalidateFolderFiles(path) {
717
993
  const content = getByExactPath(fileTree, path);
718
- if (content && Array.isArray(content.children)) {
994
+ if (!content) {
995
+ return;
996
+ }
997
+ if (splitBrowse) {
998
+ delete content.listItems;
999
+ delete content.listCacheKey;
1000
+ delete content.listPage;
1001
+ return;
1002
+ }
1003
+ if (Array.isArray(content.children)) {
719
1004
  content.children = content.children.filter(function (child) {
720
1005
  return child.path && !child.uri;
721
1006
  });
@@ -727,11 +1012,15 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
727
1012
  * @param {String} path
728
1013
  * @param {Object} data
729
1014
  */
730
- function replaceFolderContent(path, data) {
1015
+ function replaceFolderContent(path, data, cacheKey) {
731
1016
  const content = getByExactPath(fileTree, path);
732
1017
  if (!content || !data) {
733
1018
  return;
734
1019
  }
1020
+ if (splitBrowse && Object.prototype.hasOwnProperty.call(data, 'items')) {
1021
+ applyListPayload(fileTree, path, data, cacheKey || listCacheKey());
1022
+ return;
1023
+ }
735
1024
  if (Object.prototype.hasOwnProperty.call(data, 'children')) {
736
1025
  content.children = data.children;
737
1026
  } else {
@@ -761,6 +1050,7 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
761
1050
  function loadContent(path) {
762
1051
  const parameters = {};
763
1052
  parameters[options.pathParam || 'path'] = path;
1053
+ setTreeLoading(true);
764
1054
  return Promise.resolve($$1.ajax({
765
1055
  url: options.browseUrl,
766
1056
  method: 'GET',
@@ -784,6 +1074,12 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
784
1074
  data.children.map(responseChildren => updatePermissions(responseChildren));
785
1075
  }
786
1076
  return data;
1077
+ }).then(function (data) {
1078
+ setTreeLoading(false);
1079
+ return data;
1080
+ }, function (error) {
1081
+ setTreeLoading(false);
1082
+ return Promise.reject(error);
787
1083
  });
788
1084
  }
789
1085
 
@@ -853,7 +1149,7 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
853
1149
  selectedClass = {
854
1150
  path,
855
1151
  total: Number.isFinite(normalizedTotal) && normalizedTotal >= 0 ? normalizedTotal : 0,
856
- childrenLimit: Number.isFinite(normalizedChildrenLimit) && normalizedChildrenLimit > 0 ? normalizedChildrenLimit : selectedClass.childrenLimit || 10,
1152
+ childrenLimit: Number.isFinite(normalizedChildrenLimit) && normalizedChildrenLimit > 0 ? normalizedChildrenLimit : selectedClass.childrenLimit || 11,
857
1153
  page: 1
858
1154
  };
859
1155
  }
@@ -890,17 +1186,35 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
890
1186
  /**
891
1187
  * Re-fetch the current folder with the active sort and publish files.
892
1188
  */
893
- function reloadSortedFolder() {
894
- const path = selectedClass.path;
895
- loadContent(path).then(function (data) {
1189
+ function reloadSortedFolder(pendingUploads) {
1190
+ const listRequest = captureListRequestContext();
1191
+ const path = listRequest.path;
1192
+ if (!searchMode && isActiveBrowser()) {
1193
+ setFilesLoading(true);
1194
+ }
1195
+ const loadPromise = splitBrowse ? browseRequest(path, {
1196
+ part: 'list',
1197
+ page: selectedClass.page,
1198
+ pageSize: selectedClass.childrenLimit || 11,
1199
+ sortBy: sort.field,
1200
+ sortDir: sort.direction
1201
+ }) : loadContent(path);
1202
+ loadPromise.then(function (data) {
896
1203
  if (!data) {
1204
+ setFilesLoading(false);
897
1205
  return;
898
1206
  }
899
- replaceFolderContent(path, data);
1207
+ if (isListRequestStale(listRequest)) {
1208
+ setFilesLoading(false);
1209
+ return;
1210
+ }
1211
+ replaceFolderContent(path, data, listRequest.cacheKey);
1212
+ mergePendingUploads(path, pendingUploads);
900
1213
  const content = getByExactPath(fileTree, path) || data;
901
1214
  selectFolder(content, content.path || path);
902
1215
  }).catch(function (error) {
903
1216
  LOGGER.error(error);
1217
+ setFilesLoading(false);
904
1218
  });
905
1219
  }
906
1220
 
@@ -908,14 +1222,53 @@ define(['jquery', 'lodash', 'ui/pagination', 'handlebars', 'lib/handlebars/helpe
908
1222
  * Load page
909
1223
  */
910
1224
  function loadPage() {
1225
+ const listRequest = captureListRequestContext();
1226
+ if (!searchMode && isActiveBrowser()) {
1227
+ setFilesLoading(true);
1228
+ }
1229
+ if (splitBrowse) {
1230
+ const path = listRequest.path;
1231
+ browseRequest(path, {
1232
+ part: 'list',
1233
+ page: selectedClass.page,
1234
+ pageSize: selectedClass.childrenLimit || 11,
1235
+ sortBy: sort.field,
1236
+ sortDir: sort.direction
1237
+ }).then(function (data) {
1238
+ if (isListRequestStale(listRequest)) {
1239
+ setFilesLoading(false);
1240
+ return;
1241
+ }
1242
+ applyListPayload(fileTree, path, data, listRequest.cacheKey);
1243
+ const content = getByExactPath(fileTree, path);
1244
+ if (content) {
1245
+ $container.trigger(`folderselect.${NS}`, [content.label, getFilesForDisplay(content), content.path, content]);
1246
+ renderPagination();
1247
+ }
1248
+ setFilesLoading(false);
1249
+ }).catch(function (error) {
1250
+ LOGGER.error(error);
1251
+ setFilesLoading(false);
1252
+ });
1253
+ return;
1254
+ }
911
1255
  const subTree = getByPath(fileTree, selectedClass.path) || fileTree;
912
1256
 
913
1257
  //get the folder content
914
1258
  getFolderContent(subTree, selectedClass.path, function (content) {
1259
+ if (isListRequestStale(listRequest)) {
1260
+ if (!searchMode) {
1261
+ setFilesLoading(false);
1262
+ }
1263
+ return;
1264
+ }
915
1265
  indexTree(fileTree);
916
1266
  if (content) {
917
1267
  //internal event to set the file-selector content
918
- $container.trigger(`folderselect.${NS}`, [content.label, getPage(content.children), content.path, content]);
1268
+ $container.trigger(`folderselect.${NS}`, [content.label, getFilesForDisplay(content), content.path, content]);
1269
+ }
1270
+ if (!searchMode) {
1271
+ setFilesLoading(false);
919
1272
  }
920
1273
  });
921
1274
  }
@@ -67,6 +67,39 @@ define(['jquery', 'lodash', 'util/bytes', 'context', 'ui/previewer'], function (
67
67
  }
68
68
  return String(value);
69
69
  }
70
+
71
+ /**
72
+ * Row markup uses data-download for permission flag (boolean), not the URL.
73
+ * @param {*} file
74
+ * @returns {string}
75
+ */
76
+ function downloadHref(file) {
77
+ if (typeof file.downloadUrl === 'string' && file.downloadUrl) {
78
+ return file.downloadUrl;
79
+ }
80
+ if (typeof file.url === 'string' && file.url) {
81
+ const query = file.url.split('#')[0].split('?')[1] || '';
82
+ const hasSvgzSupport = query.split('&').some(param => param.split('=')[0] === 'svgzsupport');
83
+ return hasSvgzSupport ? file.url : `${file.url}&svgzsupport=true`;
84
+ }
85
+ if (typeof file.download === 'string' && file.download) {
86
+ return file.download;
87
+ }
88
+ return '';
89
+ }
90
+
91
+ /**
92
+ * @param {*} file
93
+ * @returns {string}
94
+ */
95
+ function downloadFilename(file) {
96
+ if (typeof file.display === 'string' && file.display) {
97
+ return file.display;
98
+ }
99
+ const path = file.file || '';
100
+ const idx = path.lastIndexOf('/');
101
+ return idx >= 0 ? path.slice(idx + 1) : path;
102
+ }
70
103
  function startPreview(file, preview, download, select) {
71
104
  if (preview) {
72
105
  $previewer.previewer(file);
@@ -81,9 +114,15 @@ define(['jquery', 'lodash', 'util/bytes', 'context', 'ui/previewer'], function (
81
114
  $propSize.text(EMPTY_META);
82
115
  }
83
116
  if (download) {
84
- $link.attr('href', file.download).attr('download', file.file);
85
- if ($link.hasClass('hidden')) {
86
- $link.removeClass('hidden');
117
+ const href = downloadHref(file);
118
+ if (href) {
119
+ $link.attr('href', href).attr('download', downloadFilename(file));
120
+ if ($link.hasClass('hidden')) {
121
+ $link.removeClass('hidden');
122
+ }
123
+ } else {
124
+ $link.attr('href', '#').removeAttr('download');
125
+ $link.addClass('hidden');
87
126
  }
88
127
  } else {
89
128
  $link.attr('href', '#').attr('download', '#');
@@ -312,6 +312,8 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
312
312
  let $container = options.$target;
313
313
  let $fileSelector = $$1('.file-selector', $container);
314
314
  let $filesWrapper = $$1('.files-wrapper', $fileSelector);
315
+ let $filesListLoading = $$1('.files-list-loading', $filesWrapper);
316
+ let $filesListLoadingStatus = $$1('.files-list-loading-status', $filesListLoading);
315
317
  let $fileContainer = $$1('.files-list', $fileSelector);
316
318
  let $placeholder = $$1('.empty', $fileSelector);
317
319
  let $uploader = $$1('.file-upload-container', $fileSelector);
@@ -338,6 +340,18 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
338
340
  searchMode = !!enabled;
339
341
  $fileSelector.toggleClass('search-mode', searchMode);
340
342
  });
343
+ function applyFilesListLoading(loading) {
344
+ $filesWrapper.toggleClass('is-files-loading', !!loading);
345
+ $filesWrapper.attr('aria-busy', loading ? 'true' : 'false');
346
+ $filesListLoading.toggleClass('hidden', !loading).attr('aria-hidden', loading ? 'false' : 'true');
347
+ $filesListLoadingStatus.attr('aria-busy', loading ? 'true' : 'false');
348
+ }
349
+ $container.on(`filesloading.${ns}`, function (e, loading) {
350
+ if (searchMode) {
351
+ return;
352
+ }
353
+ applyFilesListLoading(loading);
354
+ });
341
355
 
342
356
  //update current folder
343
357
  $container.on(`folderselect.${ns}`, function (e, fullPath, data, activePath, content) {
@@ -345,6 +359,7 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
345
359
  if (searchMode) {
346
360
  return;
347
361
  }
362
+ applyFilesListLoading(false);
348
363
 
349
364
  //update title
350
365
  if ($container[0].querySelector('.upload')) {