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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +1137 -97
  8. package/dist/resourcemgr/css/resourcemgr.css.map +1 -1
  9. package/dist/resourcemgr/fileBrowser.js +456 -79
  10. package/dist/resourcemgr/filePreview.js +40 -4
  11. package/dist/resourcemgr/fileSelector.js +362 -92
  12. package/dist/resourcemgr.js +309 -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 +1137 -97
  20. package/src/resourcemgr/css/resourcemgr.css.map +1 -1
  21. package/src/resourcemgr/fileBrowser.js +470 -91
  22. package/src/resourcemgr/filePreview.js +46 -4
  23. package/src/resourcemgr/fileSelector.js +338 -46
  24. package/src/resourcemgr/scss/resourcemgr.scss +1351 -88
  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 +113 -54
  29. package/src/resourcemgr/tpl/rootFolder.tpl +3 -1
  30. package/src/resourcemgr.js +289 -47
  31. package/src/searchModal/advancedSearch.js +242 -31
@@ -1,10 +1,14 @@
1
1
  import $ from 'jquery';
2
2
  import _ from 'lodash';
3
3
  import Pluginifier from 'core/pluginifier';
4
+ import assetSearch from 'ui/resourcemgr/assetSearch';
4
5
  import fileBrowser from 'ui/resourcemgr/fileBrowser';
5
6
  import filePreview from 'ui/resourcemgr/filePreview';
6
7
  import fileSelector from 'ui/resourcemgr/fileSelector';
8
+ import feedback from 'ui/feedback';
7
9
  import layout from 'ui/resourcemgr/tpl/layout';
10
+ import urlUtil from 'util/url';
11
+ import __ from 'i18n';
8
12
  import 'ui/modal';
9
13
  import 'ui/resourcemgr/css/resourcemgr.css';
10
14
 
@@ -15,7 +19,13 @@ var defaults = {
15
19
  mediaSources: [{ root: 'local', path: '/' }],
16
20
  open: true,
17
21
  appendContainer: '.tao-scope:first',
18
- title: ''
22
+ title: '',
23
+ pathParam: 'path',
24
+ // Advanced Search metadata filters (same endpoints as ui/searchModal)
25
+ rootClassUri: 'http://www.tao.lu/Ontologies/TAOMedia.rdf#Media',
26
+ classMappingUrl: urlUtil.route('getWithMapping', 'ClassMetadata', 'tao'),
27
+ statusUrl: urlUtil.route('status', 'AdvancedSearch', 'tao'),
28
+ maxListSize: 5
19
29
  };
20
30
 
21
31
  /**
@@ -34,7 +44,18 @@ var resourceMgr = {
34
44
  * @constructor
35
45
  * @param {Object} options - the plugin options
36
46
  * @param {Sring|Boolean} [options.bindEvent = 'click'] - the event that trigger the toggling
37
- * @param {String} options.url - the URL of the service used to retrieve the resources.
47
+ * @param {String} [options.browseUrl] - folder browse endpoint (legacy alias: options.url)
48
+ * @param {String} [options.url] - legacy alias for browseUrl
49
+ * @param {String} [options.searchUrl] - scoped asset-search endpoint; omit for browse-only
50
+ * @param {Boolean} [options.browseSearchFallback=false] - client filter when searchUrl returns browse-shaped `{ children }`
51
+ * @param {String} [options.initialPath] - folder to open on start (within the media source tree)
52
+ * @param {String} [options.initialSelection] - asset URI to preselect when present in results
53
+ * @param {String} [options.currentAsset] - asset URI/path to resolve to parent folder + selection
54
+ * @param {String} [options.pathParam='path'] - query parameter name for folder/asset path
55
+ * @param {String} [options.rootClassUri=TAOMedia#Media] - Assets class URI for Advanced Search criteria (metadata filters)
56
+ * @param {String} [options.classMappingUrl] - ClassMetadata mapping endpoint for criteria list
57
+ * @param {String} [options.statusUrl] - Advanced Search status endpoint
58
+ * @param {Number} [options.maxListSize=5] - max list values per criterion in filters UI
38
59
  * @fires ResourceMgr#create.resourcemgr
39
60
  * @returns {jQueryElement} for chaining
40
61
  */
@@ -43,6 +64,9 @@ var resourceMgr = {
43
64
 
44
65
  //get options using default
45
66
  options = _.defaults(options, defaults);
67
+ if (!options.browseUrl && options.url) {
68
+ options.browseUrl = options.url;
69
+ }
46
70
 
47
71
  return this.each(function() {
48
72
  var $elt = $(this);
@@ -55,7 +79,10 @@ var resourceMgr = {
55
79
  //auto bind events configured in options
56
80
  _.functions(options).forEach(function(eventName) {
57
81
  $elt.on(eventName + '.' + ns, function() {
58
- options[eventName].apply($elt, arguments);
82
+ const live = $elt.data(dataNs);
83
+ if (live && typeof live[eventName] === 'function') {
84
+ live[eventName].apply($elt, arguments);
85
+ }
59
86
  });
60
87
  });
61
88
 
@@ -74,56 +101,269 @@ var resourceMgr = {
74
101
  $target.on('closed.modal', function() {
75
102
  $elt.trigger('close.' + ns);
76
103
  });
77
- //initialize the components
78
- const $fileBrowser = $('.file-browser .file-browser-wrapper', $target);
79
- if (options.mediaSourcesUrl) {
80
- $.getJSON(options.mediaSourcesUrl)
81
- .done(function(data) {
82
- const mediaSources = data || defaults.mediaSources;
83
- for (let i = 0; i < mediaSources.length; i++) {
84
- options.root = mediaSources[i].root;
85
- options.path = mediaSources[i].path;
86
- $fileBrowser.append(
87
- '<div class="' + options.root + '"><ul class="folders"></ul></div>'
88
- );
89
- fileBrowser(options);
90
- }
91
- })
92
- .fail(function() {
93
- for (let i = 0; i < defaults.mediaSources.length; i++) {
94
- options.root = defaults.mediaSources[i].root;
95
- options.path = defaults.mediaSources[i].path;
96
- $fileBrowser.append(
97
- '<div class="' + options.root + '"><ul class="folders"></ul></div>'
98
- );
99
- fileBrowser(options);
100
- }
101
- });
102
- } else if (options.path && options.root) {
103
- $fileBrowser.append('<div class="' + options.root + '"><ul class="folders"></ul></div>');
104
- fileBrowser(options);
104
+
105
+ options.contextToken = 1;
106
+ const initToken = options.contextToken;
107
+ $elt.data(dataNs, options);
108
+
109
+ that._resolveCurrentAssetContext(options, initToken).always(function(resolvedOptions) {
110
+ const current = $elt.data(dataNs);
111
+ if (!current) {
112
+ return;
113
+ }
114
+ const ownsOpen = current.contextToken === initToken;
115
+ if (!ownsOpen) {
116
+ return;
117
+ }
118
+ $elt.data(dataNs, resolvedOptions);
119
+
120
+ that._ensureBrowsersStarted($elt, resolvedOptions).always(function() {
121
+ const latest = $elt.data(dataNs) || resolvedOptions;
122
+ if (!latest._createEventEmitted) {
123
+ latest._createEventEmitted = true;
124
+ $elt.data(dataNs, latest);
125
+ /**
126
+ * The plugin have been created.
127
+ * @event ResourceMgr#create.resourcemgr
128
+ */
129
+ $elt.trigger('create.' + ns, [latest.$target[0]]);
130
+ }
131
+
132
+ if (ownsOpen && latest.open) {
133
+ that._open($elt);
134
+ }
135
+ });
136
+ });
137
+ } else {
138
+ // Reopen: consumer may pass a new currentAsset (edit/change after create).
139
+ that._reopenWithContext($elt, options);
140
+ }
141
+ });
142
+ },
143
+
144
+ /**
145
+ * Merge reopen options, re-resolve currentAsset, re-apply folder/selection, then open.
146
+ * @param {jQuery} $elt
147
+ * @param {Object} incoming - options from the latest .resourcemgr(…) call
148
+ */
149
+ _reopenWithContext: function($elt, incoming) {
150
+ const that = resourceMgr;
151
+ const stored = $elt.data(dataNs);
152
+ if (!stored) {
153
+ return;
154
+ }
155
+
156
+ if (Object.prototype.hasOwnProperty.call(incoming, 'currentAsset')) {
157
+ stored.currentAsset = incoming.currentAsset;
158
+ stored.initialPath = null;
159
+ stored.initialSelection = null;
160
+ stored.currentAssetItem = null;
161
+ }
162
+ if (incoming.params && typeof incoming.params === 'object') {
163
+ stored.params = Object.assign({}, stored.params || {}, incoming.params);
164
+ }
165
+ ['select', 'open', 'close'].forEach(function(key) {
166
+ if (typeof incoming[key] === 'function') {
167
+ stored[key] = incoming[key];
168
+ }
169
+ });
170
+
171
+ stored.contextToken = (stored.contextToken || 0) + 1;
172
+ const token = stored.contextToken;
173
+ $elt.data(dataNs, stored);
174
+
175
+ that._resolveCurrentAssetContext(stored, token).always(function(resolved) {
176
+ const current = $elt.data(dataNs);
177
+ if (!current || current.contextToken !== token) {
178
+ return;
179
+ }
180
+ $elt.data(dataNs, resolved);
181
+ that._ensureBrowsersStarted($elt, resolved).always(function() {
182
+ that._applyResolvedContext($elt, resolved);
183
+ that._open($elt);
184
+ });
185
+ });
186
+ },
187
+
188
+ /**
189
+ * Notify mounted browser/selector to open parent folder and (re)preselect.
190
+ * @param {jQuery} $elt
191
+ * @param {Object} options
192
+ */
193
+ _applyResolvedContext: function($elt, options) {
194
+ if (!options || !options.$target) {
195
+ return;
196
+ }
197
+ options.$target.trigger('applycontext.' + ns, [
198
+ {
199
+ path: options.initialPath || options.path || '/',
200
+ selection: options.initialSelection || null,
201
+ currentAssetItem: options.currentAssetItem || null
202
+ }
203
+ ]);
204
+ },
205
+
206
+ /**
207
+ * Resolve currentAsset via browse/search endpoint into initialPath/initialSelection.
208
+ * @param {Object} options
209
+ * @param {number} [expectedToken] - contextToken that must still match before mutating options
210
+ * @returns {Promise}
211
+ */
212
+ _resolveCurrentAssetContext: function(options, expectedToken) {
213
+ const deferred = $.Deferred();
214
+ const isStale = function() {
215
+ return typeof expectedToken !== 'undefined' && expectedToken !== null && options.contextToken !== expectedToken;
216
+ };
217
+ if (!options.currentAsset || (!options.browseUrl && !options.searchUrl)) {
218
+ deferred.resolve(options);
219
+ return deferred.promise();
220
+ }
221
+
222
+ const endpoint = options.browseUrl || options.searchUrl;
223
+ const pathParam = options.pathParam || 'path';
224
+ const params = Object.assign({}, options.params || {}, {
225
+ currentAsset: options.currentAsset
226
+ });
227
+ params[pathParam] = options.path || '/';
228
+
229
+ $.ajax({
230
+ url: endpoint,
231
+ method: 'GET',
232
+ dataType: 'json',
233
+ timeout: Number(options.ajaxTimeoutMs) > 0 ? Number(options.ajaxTimeoutMs) : 10000,
234
+ data: params
235
+ })
236
+ .done(function(response) {
237
+ if (isStale()) {
238
+ deferred.resolve(options);
239
+ return;
240
+ }
241
+ const payload = response && response.data ? response.data : response;
242
+ if (payload && payload.parentPath) {
243
+ options.initialPath = payload.parentPath;
105
244
  }
245
+ if (payload && payload.currentAsset && payload.currentAsset.uri) {
246
+ options.initialSelection = payload.currentAsset.uri;
247
+ options.currentAssetItem = payload.currentAsset;
248
+ } else {
249
+ options.initialSelection = null;
250
+ options.currentAssetItem = null;
251
+ }
252
+ deferred.resolve(options);
253
+ })
254
+ .fail(function() {
255
+ if (isStale()) {
256
+ deferred.resolve(options);
257
+ return;
258
+ }
259
+ // AC6: keep RM usable without selection; surface recoverable feedback.
260
+ options.initialSelection = null;
261
+ options.currentAssetItem = null;
262
+ if (options.$target && options.$target.length) {
263
+ feedback(options.$target).warning(
264
+ __('Unable to resolve the current asset. You can still browse and select another file.')
265
+ );
266
+ }
267
+ deferred.resolve(options);
268
+ });
106
269
 
107
- $fileBrowser.find('li.root:last').addClass('active');
108
- fileSelector(options);
109
- filePreview(options);
270
+ return deferred.promise();
271
+ },
110
272
 
111
- /**
112
- * The plugin have been created.
113
- * @event ResourceMgr#create.resourcemgr
114
- */
115
- $elt.trigger('create.' + ns, [$target[0]]);
273
+ /**
274
+ * Start browsers once and resolve when the tree wrapper is mounted.
275
+ * @param {jQuery} $elt
276
+ * @param {Object} options
277
+ * @returns {Promise}
278
+ */
279
+ _ensureBrowsersStarted: function($elt, options) {
280
+ const stored = $elt.data(dataNs) || options;
281
+ if (stored._browsersStartPromise) {
282
+ return stored._browsersStartPromise;
283
+ }
284
+
285
+ stored._browsersStartPromise = this._startBrowsers($elt, stored);
286
+ $elt.data(dataNs, stored);
287
+ return stored._browsersStartPromise;
288
+ },
289
+
290
+ /**
291
+ * Mount file browser / selector / search after options are ready.
292
+ * @param {jQuery} $elt
293
+ * @param {Object} options
294
+ */
295
+ _startBrowsers: function($elt, options) {
296
+ const $fileBrowser = $('.file-browser .file-browser-wrapper', options.$target);
297
+
298
+ const waitForTreeWrapper = function() {
299
+ const deferred = $.Deferred();
300
+ const isMounted = function() {
301
+ return $fileBrowser.children().length > 0;
302
+ };
303
+
304
+ if (isMounted()) {
305
+ deferred.resolve();
306
+ return deferred.promise();
307
+ }
116
308
 
117
- if (options.open) {
118
- that._open($elt);
309
+ let attempts = 200;
310
+ const poll = function() {
311
+ if (isMounted()) {
312
+ deferred.resolve();
313
+ return;
119
314
  }
120
- } else {
121
- options = $elt.data(dataNs);
122
- if (options.open) {
123
- that._open($elt);
315
+ attempts -= 1;
316
+ if (attempts <= 0) {
317
+ deferred.reject();
318
+ return;
124
319
  }
320
+ window.setTimeout(poll, 50);
321
+ };
322
+ poll();
323
+ return deferred.promise();
324
+ };
325
+
326
+ const finishMount = function() {
327
+ $fileBrowser.find('li.root:last').addClass('active');
328
+ fileSelector(options);
329
+ filePreview(options);
330
+ assetSearch(options);
331
+ };
332
+
333
+ const mountFromSources = function(mediaSources) {
334
+ const sources = mediaSources || defaults.mediaSources;
335
+ for (let i = 0; i < sources.length; i++) {
336
+ options.root = sources[i].root;
337
+ options.path = sources[i].path;
338
+ $fileBrowser.append('<div class="' + options.root + '"><ul class="folders"></ul></div>');
339
+ fileBrowser(options);
125
340
  }
126
- });
341
+ };
342
+
343
+ const afterComponentsMounted = function() {
344
+ finishMount();
345
+ return waitForTreeWrapper();
346
+ };
347
+
348
+ if (options.mediaSourcesUrl) {
349
+ return $.getJSON(options.mediaSourcesUrl).then(
350
+ function(data) {
351
+ mountFromSources(data || defaults.mediaSources);
352
+ return afterComponentsMounted();
353
+ },
354
+ function() {
355
+ mountFromSources(defaults.mediaSources);
356
+ return afterComponentsMounted();
357
+ }
358
+ );
359
+ }
360
+
361
+ if (options.path && options.root) {
362
+ $fileBrowser.append('<div class="' + options.root + '"><ul class="folders"></ul></div>');
363
+ fileBrowser(options);
364
+ }
365
+
366
+ return afterComponentsMounted();
127
367
  },
128
368
 
129
369
  _createTarget: function($elt) {
@@ -136,7 +376,9 @@ var resourceMgr = {
136
376
  options.$target = $(
137
377
  layout({
138
378
  title: options.title || '',
139
- className: options.className || ''
379
+ className: options.className || '',
380
+ assetSearchInputId: options.targetId + '-asset-search',
381
+ assetSearchBodyId: options.targetId + '-asset-search-body'
140
382
  })
141
383
  );
142
384