@oat-sa/tao-core-ui 3.23.0 → 3.24.1

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 (31) hide show
  1. package/dist/figure/FigureStateActive.js +64 -32
  2. package/dist/image/ImgStateActive/initHelper.js +64 -32
  3. package/dist/image/ImgStateActive.js +64 -32
  4. package/dist/previewer.js +1 -1
  5. package/dist/resourcemgr/assetSearch.js +793 -0
  6. package/dist/resourcemgr/assetSearchContract.js +477 -0
  7. package/dist/resourcemgr/css/resourcemgr.css +1230 -97
  8. package/dist/resourcemgr/css/resourcemgr.css.map +1 -1
  9. package/dist/resourcemgr/fileBrowser.js +815 -83
  10. package/dist/resourcemgr/filePreview.js +82 -7
  11. package/dist/resourcemgr/fileSelector.js +377 -92
  12. package/dist/resourcemgr.js +314 -57
  13. package/dist/searchModal/advancedSearch.js +233 -31
  14. package/package.json +15 -6
  15. package/src/image/ImgStateActive/initHelper.js +69 -31
  16. package/src/previewer.js +1 -1
  17. package/src/resourcemgr/assetSearch.js +847 -0
  18. package/src/resourcemgr/assetSearchContract.js +489 -0
  19. package/src/resourcemgr/css/resourcemgr.css +1230 -97
  20. package/src/resourcemgr/css/resourcemgr.css.map +1 -1
  21. package/src/resourcemgr/fileBrowser.js +872 -92
  22. package/src/resourcemgr/filePreview.js +89 -8
  23. package/src/resourcemgr/fileSelector.js +356 -46
  24. package/src/resourcemgr/scss/resourcemgr.scss +1450 -89
  25. package/src/resourcemgr/tpl/advanced-search.tpl +10 -0
  26. package/src/resourcemgr/tpl/fileSelect.tpl +25 -28
  27. package/src/resourcemgr/tpl/folder.tpl +4 -1
  28. package/src/resourcemgr/tpl/layout.tpl +125 -54
  29. package/src/resourcemgr/tpl/rootFolder.tpl +3 -1
  30. package/src/resourcemgr.js +294 -47
  31. package/src/searchModal/advancedSearch.js +242 -31
@@ -1,13 +1,17 @@
1
- define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'ui/resourcemgr/filePreview', 'ui/resourcemgr/fileSelector', 'handlebars', 'lib/handlebars/helpers', 'ui/modal', 'css!ui/resourcemgr/css/resourcemgr.css'], function ($$1, _, Pluginifier, fileBrowser, filePreview, fileSelector, Handlebars, Helpers0, modal, resourcemgr_css) { 'use strict';
1
+ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/assetSearch', 'ui/resourcemgr/fileBrowser', 'ui/resourcemgr/filePreview', 'ui/resourcemgr/fileSelector', 'ui/feedback', 'handlebars', 'lib/handlebars/helpers', 'util/url', 'i18n', 'ui/modal', 'css!ui/resourcemgr/css/resourcemgr.css'], function ($$1, _, Pluginifier, assetSearch, fileBrowser, filePreview, fileSelector, feedback, Handlebars, Helpers0, urlUtil, __, modal, resourcemgr_css) { 'use strict';
2
2
 
3
3
  $$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
4
4
  _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
5
5
  Pluginifier = Pluginifier && Object.prototype.hasOwnProperty.call(Pluginifier, 'default') ? Pluginifier['default'] : Pluginifier;
6
+ assetSearch = assetSearch && Object.prototype.hasOwnProperty.call(assetSearch, 'default') ? assetSearch['default'] : assetSearch;
6
7
  fileBrowser = fileBrowser && Object.prototype.hasOwnProperty.call(fileBrowser, 'default') ? fileBrowser['default'] : fileBrowser;
7
8
  filePreview = filePreview && Object.prototype.hasOwnProperty.call(filePreview, 'default') ? filePreview['default'] : filePreview;
8
9
  fileSelector = fileSelector && Object.prototype.hasOwnProperty.call(fileSelector, 'default') ? fileSelector['default'] : fileSelector;
10
+ feedback = feedback && Object.prototype.hasOwnProperty.call(feedback, 'default') ? feedback['default'] : feedback;
9
11
  Handlebars = Handlebars && Object.prototype.hasOwnProperty.call(Handlebars, 'default') ? Handlebars['default'] : Handlebars;
10
12
  Helpers0 = Helpers0 && Object.prototype.hasOwnProperty.call(Helpers0, 'default') ? Helpers0['default'] : Helpers0;
13
+ urlUtil = urlUtil && Object.prototype.hasOwnProperty.call(urlUtil, 'default') ? urlUtil['default'] : urlUtil;
14
+ __ = __ && Object.prototype.hasOwnProperty.call(__, 'default') ? __['default'] : __;
11
15
 
12
16
  if (!Helpers0.__initialized) {
13
17
  Helpers0(Handlebars);
@@ -33,31 +37,71 @@ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'u
33
37
  if (helper = helpers.title) { stack1 = helper.call(depth0, {hash:{},data:data}); }
34
38
  else { helper = (depth0 && depth0.title); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
35
39
  if(stack1 || stack1 === 0) { buffer += stack1; }
36
- buffer += "</h2>\n\n <div class=\"file-wrapper\">\n\n <!-- left section: items selection -->\n <section class=\"file-browser\">\n <h1>"
37
- + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Browse resources", options) : helperMissing.call(depth0, "__", "Browse resources", options)))
38
- + "</h1>\n <div class=\"file-browser-wrapper\"></div>\n </section>\n\n <!-- test editor -->\n <section class=\"file-selector\">\n\n <h1>\n <div class=\"title lft\"></div>\n <div class=\"upload-switcher rgt\">\n <a href=\"#\" class=\"btn-info small upload hidden\"><span class=\"icon-add\"></span>"
40
+ buffer += "</h2>\n\n <div class=\"file-wrapper\">\n\n <div class=\"file-panels\">\n <!-- left section: search, filters, folder tree -->\n <section class=\"file-browser\">\n <div class=\"asset-search hidden\" hidden>\n <div class=\"asset-search-header\">\n <button\n type=\"button\"\n class=\"asset-search-toggle\"\n aria-expanded=\"true\"\n aria-controls=\"";
41
+ if (helper = helpers.assetSearchBodyId) { stack1 = helper.call(depth0, {hash:{},data:data}); }
42
+ else { helper = (depth0 && depth0.assetSearchBodyId); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
43
+ buffer += escapeExpression(stack1)
44
+ + "\"\n >\n <span class=\"asset-search-title\">"
45
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Search", options) : helperMissing.call(depth0, "__", "Search", options)))
46
+ + "</span>\n <span class=\"asset-search-applied-count hidden\" hidden></span>\n <span class=\"asset-search-scope\" aria-live=\"polite\">\n <span class=\"asset-search-scope-prefix\">"
47
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Searching in:", options) : helperMissing.call(depth0, "__", "Searching in:", options)))
48
+ + "</span>\n <span class=\"asset-search-scope-name\"></span>\n <span class=\"asset-search-scope-subfolders hidden\" hidden>"
49
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "and its subfolders", options) : helperMissing.call(depth0, "__", "and its subfolders", options)))
50
+ + "</span>\n </span>\n <span class=\"asset-search-chevron icon-up\" aria-hidden=\"true\"></span>\n </button>\n </div>\n <div class=\"asset-search-body\" id=\"";
51
+ if (helper = helpers.assetSearchBodyId) { stack1 = helper.call(depth0, {hash:{},data:data}); }
52
+ else { helper = (depth0 && depth0.assetSearchBodyId); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
53
+ buffer += escapeExpression(stack1)
54
+ + "\">\n <div class=\"asset-search-field\">\n <span class=\"icon-find\" aria-hidden=\"true\"></span>\n <label class=\"asset-search-label\" for=\"";
55
+ if (helper = helpers.assetSearchInputId) { stack1 = helper.call(depth0, {hash:{},data:data}); }
56
+ else { helper = (depth0 && depth0.assetSearchInputId); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
57
+ buffer += escapeExpression(stack1)
58
+ + "\">"
59
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Search by name or label", options) : helperMissing.call(depth0, "__", "Search by name or label", options)))
60
+ + "</label>\n <input\n id=\"";
61
+ if (helper = helpers.assetSearchInputId) { stack1 = helper.call(depth0, {hash:{},data:data}); }
62
+ else { helper = (depth0 && depth0.assetSearchInputId); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
63
+ buffer += escapeExpression(stack1)
64
+ + "\"\n type=\"search\"\n class=\"asset-search-input\"\n placeholder=\""
65
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Search by name or label", options) : helperMissing.call(depth0, "__", "Search by name or label", options)))
66
+ + "\"\n autocomplete=\"off\"\n />\n </div>\n <div class=\"asset-search-filters\"></div>\n <div class=\"asset-search-actions\">\n <button type=\"button\" class=\"asset-search-clear hidden\" hidden>"
67
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Clear all", options) : helperMissing.call(depth0, "__", "Clear all", options)))
68
+ + "</button>\n <button type=\"button\" class=\"btn-info small asset-search-submit\" disabled>\n <span class=\"asset-search-submit-spinner icon-loop hidden\" aria-hidden=\"true\"></span>\n <span class=\"asset-search-submit-label\">"
69
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Search", options) : helperMissing.call(depth0, "__", "Search", options)))
70
+ + "</span>\n </button>\n </div>\n </div>\n </div>\n\n <h1 class=\"resources-title\">"
71
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Resources", options) : helperMissing.call(depth0, "__", "Resources", options)))
72
+ + "</h1>\n <div class=\"file-browser-wrapper\" aria-busy=\"false\">\n <div class=\"file-browser-tree-loading hidden\" aria-hidden=\"true\">\n <span class=\"icon-loop\" aria-hidden=\"true\"></span>\n <span class=\"file-browser-tree-loading-status\" aria-live=\"polite\" aria-atomic=\"true\" aria-busy=\"false\">"
73
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Loading", options) : helperMissing.call(depth0, "__", "Loading", options)))
74
+ + "</span>\n </div>\n <div class=\"file-browser-scroll\"></div>\n </div>\n </section>\n\n <!-- center: asset listing -->\n <section class=\"file-selector\">\n\n <h1>\n <div class=\"title lft\">"
75
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Assets", options) : helperMissing.call(depth0, "__", "Assets", options)))
76
+ + "</div>\n <div class=\"upload-switcher rgt\">\n <a href=\"#\" class=\"btn-info small upload hidden\"><span class=\"icon-add\"></span>"
39
77
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Add file(s)", options) : helperMissing.call(depth0, "__", "Add file(s)", options)))
40
- + "</a>\n <a href=\"#\" class=\"btn-info small listing\"><span class=\"icon-undo\"></span>"
78
+ + "</a>\n <a href=\"#\" class=\"btn-info small listing\"><span class=\"icon-undo\"></span>"
41
79
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Back to listing", options) : helperMissing.call(depth0, "__", "Back to listing", options)))
42
- + "</a>\n </div>\n </h1>\n\n <div class=\"empty\">\n "
80
+ + "</a>\n </div>\n </h1>\n\n <div class=\"asset-search-error hidden\" hidden role=\"alert\">\n <p class=\"asset-search-error-message\"></p>\n <button type=\"button\" class=\"btn-info small asset-search-retry\">"
81
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Retry", options) : helperMissing.call(depth0, "__", "Retry", options)))
82
+ + "</button>\n </div>\n\n <div class=\"empty\">\n "
43
83
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "No files", options) : helperMissing.call(depth0, "__", "No files", options)))
44
- + "\n </div>\n\n <ul class=\"files\"></ul>\n\n <div class=\"file-upload-container\"></div>\n\n <div class=\"pagination-bottom\"></div>\n\n </section>\n\n <section class=\"file-preview\">\n\n <h1>"
84
+ + "\n </div>\n\n <div class=\"files-wrapper\" aria-busy=\"false\">\n <div class=\"files-list-loading hidden\" aria-hidden=\"true\">\n <span class=\"icon-loop\" aria-hidden=\"true\"></span>\n <span class=\"files-list-loading-status\" aria-live=\"polite\" aria-atomic=\"true\" aria-busy=\"false\">"
85
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Loading", options) : helperMissing.call(depth0, "__", "Loading", options)))
86
+ + "</span>\n </div>\n <div class=\"files-scroll\">\n <table class=\"files\">\n <thead>\n <tr>\n <th class=\"files-label sortable sorted sorted_asc\" data-sort-by=\"label\" aria-sort=\"ascending\" tabindex=\"0\">\n <span class=\"sort-label\">"
87
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Label", options) : helperMissing.call(depth0, "__", "Label", options)))
88
+ + "</span>\n </th>\n <th class=\"files-location sortable\" data-sort-by=\"location\" aria-sort=\"none\" tabindex=\"0\">\n <span class=\"sort-label\">"
89
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Location", options) : helperMissing.call(depth0, "__", "Location", options)))
90
+ + "</span>\n </th>\n <th class=\"files-updated sortable\" data-sort-by=\"updatedAt\" aria-sort=\"none\" tabindex=\"0\">\n <span class=\"sort-label\">"
91
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Last modified on", options) : helperMissing.call(depth0, "__", "Last modified on", options)))
92
+ + "</span>\n </th>\n </tr>\n </thead>\n <tbody class=\"files-list\"></tbody>\n </table>\n </div>\n </div>\n\n <div class=\"file-upload-container\"></div>\n\n <div class=\"pagination-bottom\"></div>\n\n </section>\n\n <section class=\"file-preview\">\n\n <h1>"
45
93
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Preview", options) : helperMissing.call(depth0, "__", "Preview", options)))
46
- + "</h1>\n\n <div class=\"previewer\">\n <p class=\"nopreview\"></p>\n </div>\n\n <h2 class=\"toggler\" data-toggle=\"~ .file-properties\">"
47
- + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "File Properties", options) : helperMissing.call(depth0, "__", "File Properties", options)))
48
- + "</h2>\n\n <div class=\"file-properties\">\n\n <div class=\"grid-row\">\n <div class=\"col-2\">\n "
94
+ + "</h1>\n\n <div class=\"previewer\">\n <p class=\"nopreview\"></p>\n </div>\n\n <div class=\"file-properties\">\n\n <div class=\"grid-row\">\n <div class=\"col-2\">\n "
49
95
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Type", options) : helperMissing.call(depth0, "__", "Type", options)))
50
- + "\n </div>\n <div class=\"col-10 prop-type\"></div>\n </div>\n\n <div class=\"grid-row\">\n <div class=\"col-2\">\n "
96
+ + "\n </div>\n <div class=\"col-10 prop-type\">—</div>\n </div>\n\n <div class=\"grid-row\">\n <div class=\"col-2\">\n "
51
97
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Size", options) : helperMissing.call(depth0, "__", "Size", options)))
52
- + "\n </div>\n <div class=\"col-10 prop-size\"></div>\n </div>\n\n <div class=\"grid-row prop-url\">\n <div class=\"actions\">\n <a href=\"#\" download=\"\" target=\"_blank\" class=\"tlb-button-off download hidden\" title=\""
98
+ + "\n </div>\n <div class=\"col-10 prop-size\">—</div>\n </div>\n\n <div class=\"grid-row prop-url\">\n <div class=\"actions\">\n <a href=\"#\" download=\"\" target=\"_blank\" class=\"tlb-button-off download hidden\" title=\""
53
99
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Download this file", options) : helperMissing.call(depth0, "__", "Download this file", options)))
54
- + "\">\n <button class=\"btn-info small\">\n <span class=\"icon-download\"></span>"
100
+ + "\">\n <span class=\"icon-download\"></span>"
55
101
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Download this file", options) : helperMissing.call(depth0, "__", "Download this file", options)))
56
- + "\n </button>\n </a>\n </div>\n </div>\n </div>\n\n <h2 class=\"toggler\" data-toggle=\"~ .actions\">"
57
- + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Actions", options) : helperMissing.call(depth0, "__", "Actions", options)))
58
- + "</h2>\n\n <div class=\"actions\">\n <button class=\"btn-success select-action small\" disabled>\n <span class=\"icon-move-item\"></span>"
102
+ + "\n </a>\n </div>\n </div>\n </div>\n\n <div class=\"actions select-actions\">\n <button class=\"btn-success select-action small\" disabled>\n <span class=\"icon-move-item\"></span>"
59
103
  + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Select", options) : helperMissing.call(depth0, "__", "Select", options)))
60
- + "\n </button>\n </div>\n\n </section>\n\n </div>\n</div>\n";
104
+ + "\n </button>\n </div>\n\n </section>\n </div>\n\n </div>\n</div>\n";
61
105
  return buffer;
62
106
  });
63
107
  function layout(data, options, asString) {
@@ -74,7 +118,14 @@ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'u
74
118
  }],
75
119
  open: true,
76
120
  appendContainer: '.tao-scope:first',
77
- title: ''
121
+ title: '',
122
+ pathParam: 'path',
123
+ // Advanced Search metadata filters (same endpoints as ui/searchModal)
124
+ rootClassUri: 'http://www.tao.lu/Ontologies/TAOMedia.rdf#Media',
125
+ classMappingUrl: urlUtil.route('getWithMapping', 'ClassMetadata', 'tao'),
126
+ statusUrl: urlUtil.route('status', 'AdvancedSearch', 'tao'),
127
+ maxListSize: 5,
128
+ ajaxTimeoutMs: 30000
78
129
  };
79
130
 
80
131
  /**
@@ -93,7 +144,18 @@ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'u
93
144
  * @constructor
94
145
  * @param {Object} options - the plugin options
95
146
  * @param {Sring|Boolean} [options.bindEvent = 'click'] - the event that trigger the toggling
96
- * @param {String} options.url - the URL of the service used to retrieve the resources.
147
+ * @param {String} [options.browseUrl] - folder browse endpoint (legacy alias: options.url)
148
+ * @param {String} [options.url] - legacy alias for browseUrl
149
+ * @param {String} [options.searchUrl] - scoped asset-search endpoint; omit for browse-only
150
+ * @param {Boolean} [options.browseSearchFallback=false] - client filter when searchUrl returns browse-shaped `{ children }`
151
+ * @param {String} [options.initialPath] - folder to open on start (within the media source tree)
152
+ * @param {String} [options.initialSelection] - asset URI to preselect when present in results
153
+ * @param {String} [options.currentAsset] - asset URI/path to resolve to parent folder + selection
154
+ * @param {String} [options.pathParam='path'] - query parameter name for folder/asset path
155
+ * @param {String} [options.rootClassUri=TAOMedia#Media] - Assets class URI for Advanced Search criteria (metadata filters)
156
+ * @param {String} [options.classMappingUrl] - ClassMetadata mapping endpoint for criteria list
157
+ * @param {String} [options.statusUrl] - Advanced Search status endpoint
158
+ * @param {Number} [options.maxListSize=5] - max list values per criterion in filters UI
97
159
  * @fires ResourceMgr#create.resourcemgr
98
160
  * @returns {jQueryElement} for chaining
99
161
  */
@@ -102,6 +164,9 @@ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'u
102
164
 
103
165
  //get options using default
104
166
  options = _.defaults(options, defaults);
167
+ if (!options.browseUrl && options.url) {
168
+ options.browseUrl = options.url;
169
+ }
105
170
  return this.each(function () {
106
171
  var $elt = $$1(this);
107
172
  var $target;
@@ -112,7 +177,10 @@ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'u
112
177
  //auto bind events configured in options
113
178
  _.functions(options).forEach(function (eventName) {
114
179
  $elt.on(eventName + '.' + ns, function () {
115
- options[eventName].apply($elt, arguments);
180
+ const live = $elt.data(dataNs);
181
+ if (live && typeof live[eventName] === 'function') {
182
+ live[eventName].apply($elt, arguments);
183
+ }
116
184
  });
117
185
  });
118
186
  $target = options.$target || that._createTarget($elt);
@@ -129,49 +197,236 @@ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'u
129
197
  $target.on('closed.modal', function () {
130
198
  $elt.trigger('close.' + ns);
131
199
  });
132
- //initialize the components
133
- const $fileBrowser = $$1('.file-browser .file-browser-wrapper', $target);
134
- if (options.mediaSourcesUrl) {
135
- $$1.getJSON(options.mediaSourcesUrl).done(function (data) {
136
- const mediaSources = data || defaults.mediaSources;
137
- for (let i = 0; i < mediaSources.length; i++) {
138
- options.root = mediaSources[i].root;
139
- options.path = mediaSources[i].path;
140
- $fileBrowser.append('<div class="' + options.root + '"><ul class="folders"></ul></div>');
141
- fileBrowser(options);
200
+ options.contextToken = 1;
201
+ const initToken = options.contextToken;
202
+ $elt.data(dataNs, options);
203
+ that._resolveCurrentAssetContext(options, initToken).always(function (resolvedOptions) {
204
+ const current = $elt.data(dataNs);
205
+ if (!current) {
206
+ return;
207
+ }
208
+ const ownsOpen = current.contextToken === initToken;
209
+ if (!ownsOpen) {
210
+ return;
211
+ }
212
+ $elt.data(dataNs, resolvedOptions);
213
+ that._ensureBrowsersStarted($elt, resolvedOptions).always(function () {
214
+ const latest = $elt.data(dataNs) || resolvedOptions;
215
+ if (!latest._createEventEmitted) {
216
+ latest._createEventEmitted = true;
217
+ $elt.data(dataNs, latest);
218
+ /**
219
+ * The plugin have been created.
220
+ * @event ResourceMgr#create.resourcemgr
221
+ */
222
+ $elt.trigger('create.' + ns, [latest.$target[0]]);
142
223
  }
143
- }).fail(function () {
144
- for (let i = 0; i < defaults.mediaSources.length; i++) {
145
- options.root = defaults.mediaSources[i].root;
146
- options.path = defaults.mediaSources[i].path;
147
- $fileBrowser.append('<div class="' + options.root + '"><ul class="folders"></ul></div>');
148
- fileBrowser(options);
224
+ if (ownsOpen && latest.open) {
225
+ that._open($elt);
149
226
  }
150
227
  });
151
- } else if (options.path && options.root) {
152
- $fileBrowser.append('<div class="' + options.root + '"><ul class="folders"></ul></div>');
153
- fileBrowser(options);
154
- }
155
- $fileBrowser.find('li.root:last').addClass('active');
156
- fileSelector(options);
157
- filePreview(options);
158
-
159
- /**
160
- * The plugin have been created.
161
- * @event ResourceMgr#create.resourcemgr
162
- */
163
- $elt.trigger('create.' + ns, [$target[0]]);
164
- if (options.open) {
165
- that._open($elt);
166
- }
228
+ });
167
229
  } else {
168
- options = $elt.data(dataNs);
169
- if (options.open) {
170
- that._open($elt);
171
- }
230
+ // Reopen: consumer may pass a new currentAsset (edit/change after create).
231
+ that._reopenWithContext($elt, options);
172
232
  }
173
233
  });
174
234
  },
235
+ /**
236
+ * Merge reopen options, re-resolve currentAsset, re-apply folder/selection, then open.
237
+ * @param {jQuery} $elt
238
+ * @param {Object} incoming - options from the latest .resourcemgr(…) call
239
+ */
240
+ _reopenWithContext: function ($elt, incoming) {
241
+ const that = resourceMgr;
242
+ const stored = $elt.data(dataNs);
243
+ if (!stored) {
244
+ return;
245
+ }
246
+ if (Object.prototype.hasOwnProperty.call(incoming, 'currentAsset')) {
247
+ stored.currentAsset = incoming.currentAsset;
248
+ stored.initialPath = null;
249
+ stored.initialSelection = null;
250
+ stored.currentAssetItem = null;
251
+ }
252
+ if (incoming.params && typeof incoming.params === 'object') {
253
+ stored.params = Object.assign({}, stored.params || {}, incoming.params);
254
+ }
255
+ ['select', 'open', 'close'].forEach(function (key) {
256
+ if (typeof incoming[key] === 'function') {
257
+ stored[key] = incoming[key];
258
+ }
259
+ });
260
+ stored.contextToken = (stored.contextToken || 0) + 1;
261
+ const token = stored.contextToken;
262
+ $elt.data(dataNs, stored);
263
+ that._resolveCurrentAssetContext(stored, token).always(function (resolved) {
264
+ const current = $elt.data(dataNs);
265
+ if (!current || current.contextToken !== token) {
266
+ return;
267
+ }
268
+ $elt.data(dataNs, resolved);
269
+ that._ensureBrowsersStarted($elt, resolved).always(function () {
270
+ that._applyResolvedContext($elt, resolved);
271
+ that._open($elt);
272
+ });
273
+ });
274
+ },
275
+ /**
276
+ * Notify mounted browser/selector to open parent folder and (re)preselect.
277
+ * @param {jQuery} $elt
278
+ * @param {Object} options
279
+ */
280
+ _applyResolvedContext: function ($elt, options) {
281
+ if (!options || !options.$target) {
282
+ return;
283
+ }
284
+ options.$target.trigger('applycontext.' + ns, [{
285
+ path: options.initialPath || options.path || '/',
286
+ selection: options.initialSelection || null,
287
+ currentAssetItem: options.currentAssetItem || null
288
+ }]);
289
+ },
290
+ /**
291
+ * Resolve currentAsset via browse/search endpoint into initialPath/initialSelection.
292
+ * @param {Object} options
293
+ * @param {number} [expectedToken] - contextToken that must still match before mutating options
294
+ * @returns {Promise}
295
+ */
296
+ _resolveCurrentAssetContext: function (options, expectedToken) {
297
+ const deferred = $$1.Deferred();
298
+ const isStale = function () {
299
+ return typeof expectedToken !== 'undefined' && expectedToken !== null && options.contextToken !== expectedToken;
300
+ };
301
+ if (!options.currentAsset || !options.browseUrl && !options.searchUrl) {
302
+ deferred.resolve(options);
303
+ return deferred.promise();
304
+ }
305
+ const endpoint = options.browseUrl || options.searchUrl;
306
+ const pathParam = options.pathParam || 'path';
307
+ const params = Object.assign({}, options.params || {}, {
308
+ currentAsset: options.currentAsset
309
+ });
310
+ params[pathParam] = options.path || '/';
311
+ $$1.ajax({
312
+ url: endpoint,
313
+ method: 'GET',
314
+ dataType: 'json',
315
+ timeout: Number(options.ajaxTimeoutMs) > 0 ? Number(options.ajaxTimeoutMs) : defaults.ajaxTimeoutMs,
316
+ data: params
317
+ }).done(function (response) {
318
+ if (isStale()) {
319
+ deferred.resolve(options);
320
+ return;
321
+ }
322
+ const payload = response && response.data ? response.data : response;
323
+ if (payload && payload.parentPath) {
324
+ options.initialPath = payload.parentPath;
325
+ }
326
+ if (payload && payload.currentAsset && payload.currentAsset.uri) {
327
+ options.initialSelection = payload.currentAsset.uri;
328
+ options.currentAssetItem = payload.currentAsset;
329
+ } else {
330
+ options.initialSelection = null;
331
+ options.currentAssetItem = null;
332
+ }
333
+ deferred.resolve(options);
334
+ }).fail(function () {
335
+ if (isStale()) {
336
+ deferred.resolve(options);
337
+ return;
338
+ }
339
+ // AC6: keep RM usable without selection; surface recoverable feedback.
340
+ options.initialSelection = null;
341
+ options.currentAssetItem = null;
342
+ if (options.$target && options.$target.length) {
343
+ feedback(options.$target).warning(__('Unable to resolve the current asset. You can still browse and select another file.'));
344
+ }
345
+ deferred.resolve(options);
346
+ });
347
+ return deferred.promise();
348
+ },
349
+ /**
350
+ * Start browsers once and resolve when the tree wrapper is mounted.
351
+ * @param {jQuery} $elt
352
+ * @param {Object} options
353
+ * @returns {Promise}
354
+ */
355
+ _ensureBrowsersStarted: function ($elt, options) {
356
+ const stored = $elt.data(dataNs) || options;
357
+ if (stored._browsersStartPromise) {
358
+ return stored._browsersStartPromise;
359
+ }
360
+ stored._browsersStartPromise = this._startBrowsers($elt, stored);
361
+ $elt.data(dataNs, stored);
362
+ return stored._browsersStartPromise;
363
+ },
364
+ /**
365
+ * Mount file browser / selector / search after options are ready.
366
+ * @param {jQuery} $elt
367
+ * @param {Object} options
368
+ */
369
+ _startBrowsers: function ($elt, options) {
370
+ const $fileBrowser = $$1('.file-browser .file-browser-wrapper', options.$target);
371
+ const waitForTreeWrapper = function () {
372
+ const deferred = $$1.Deferred();
373
+ const isMounted = function () {
374
+ return $fileBrowser.children().length > 0;
375
+ };
376
+ if (isMounted()) {
377
+ deferred.resolve();
378
+ return deferred.promise();
379
+ }
380
+ let attempts = 200;
381
+ const poll = function () {
382
+ if (isMounted()) {
383
+ deferred.resolve();
384
+ return;
385
+ }
386
+ attempts -= 1;
387
+ if (attempts <= 0) {
388
+ deferred.reject();
389
+ return;
390
+ }
391
+ window.setTimeout(poll, 50);
392
+ };
393
+ poll();
394
+ return deferred.promise();
395
+ };
396
+ const finishMount = function () {
397
+ $fileBrowser.find('li.root:last').addClass('active');
398
+ fileSelector(options);
399
+ filePreview(options);
400
+ assetSearch(options);
401
+ };
402
+ const mountFromSources = function (mediaSources) {
403
+ const sources = mediaSources || defaults.mediaSources;
404
+ for (let i = 0; i < sources.length; i++) {
405
+ options.root = sources[i].root;
406
+ options.path = sources[i].path;
407
+ $fileBrowser.children('.file-browser-scroll').append('<div class="' + options.root + '"><ul class="folders"></ul></div>');
408
+ fileBrowser(options);
409
+ }
410
+ };
411
+ const afterComponentsMounted = function () {
412
+ finishMount();
413
+ return waitForTreeWrapper();
414
+ };
415
+ if (options.mediaSourcesUrl) {
416
+ return $$1.getJSON(options.mediaSourcesUrl).then(function (data) {
417
+ mountFromSources(data || defaults.mediaSources);
418
+ return afterComponentsMounted();
419
+ }, function () {
420
+ mountFromSources(defaults.mediaSources);
421
+ return afterComponentsMounted();
422
+ });
423
+ }
424
+ if (options.path && options.root) {
425
+ $fileBrowser.children('.file-browser-scroll').append('<div class="' + options.root + '"><ul class="folders"></ul></div>');
426
+ fileBrowser(options);
427
+ }
428
+ return afterComponentsMounted();
429
+ },
175
430
  _createTarget: function ($elt) {
176
431
  var options = $elt.data(dataNs);
177
432
  if (options) {
@@ -181,7 +436,9 @@ define(['jquery', 'lodash', 'core/pluginifier', 'ui/resourcemgr/fileBrowser', 'u
181
436
  //generate
182
437
  options.$target = $$1(layout({
183
438
  title: options.title || '',
184
- className: options.className || ''
439
+ className: options.className || '',
440
+ assetSearchInputId: options.targetId + '-asset-search',
441
+ assetSearchBodyId: options.targetId + '-asset-search-body'
185
442
  }));
186
443
  options.$target.attr('id', options.targetId).css('display', 'none').appendTo(options.appendContainer);
187
444
  $elt.data(dataNs, options);