tg-prepare 1.1.0__py3-none-any.whl → 2.1.0b2__py3-none-any.whl

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.

Potentially problematic release.


This version of tg-prepare might be problematic. Click here for more details.

Files changed (70) hide show
  1. {tg_prepare-1.1.0.dist-info → tg_prepare-2.1.0b2.dist-info}/METADATA +3 -3
  2. tg_prepare-2.1.0b2.dist-info/RECORD +78 -0
  3. {tg_prepare-1.1.0.dist-info → tg_prepare-2.1.0b2.dist-info}/WHEEL +1 -1
  4. tg_prepare-2.1.0b2.dist-info/projects/.secret_key +1 -0
  5. tgp_backend/__init__.py +3 -4
  6. tgp_backend/config.py +31 -0
  7. tgp_backend/directories.py +6 -8
  8. tgp_backend/nextcloud.py +40 -19
  9. tgp_backend/project.py +172 -45
  10. tgp_backend/session_manager.py +47 -0
  11. tgp_backend/util.py +73 -25
  12. tgp_ui/app.py +43 -335
  13. tgp_ui/routes/__init__.py +0 -0
  14. tgp_ui/routes/collection.py +272 -0
  15. tgp_ui/routes/data.py +228 -0
  16. tgp_ui/routes/project.py +102 -0
  17. tgp_ui/routes/publication.py +129 -0
  18. tgp_ui/routes/tabs.py +34 -0
  19. tgp_ui/routes/views.py +62 -0
  20. tgp_ui/static/css/bootstrap.min.css.map +1 -0
  21. tgp_ui/static/css/navbar.css +92 -0
  22. tgp_ui/static/img/favicon.ico +0 -0
  23. tgp_ui/static/img/textgrid-logo.svg +1 -0
  24. tgp_ui/static/js/collectionManager.js +60 -0
  25. tgp_ui/static/js/fileManager.js +186 -0
  26. tgp_ui/static/js/main.js +32 -485
  27. tgp_ui/static/js/modalManager.js +105 -0
  28. tgp_ui/static/js/navbarManager.js +151 -0
  29. tgp_ui/static/js/projectManager.js +60 -0
  30. tgp_ui/static/js/require.js +5 -0
  31. tgp_ui/static/js/sidebarManager.js +32 -0
  32. tgp_ui/static/js/tabManager.js +79 -0
  33. tgp_ui/templates/details/empty_container.html +16 -0
  34. tgp_ui/templates/details/manage_collection.html +205 -0
  35. tgp_ui/templates/includes/set_session_id_form.html +16 -0
  36. tgp_ui/templates/includes/upload_form.html +131 -0
  37. tgp_ui/templates/layout.html +9 -48
  38. tgp_ui/templates/macros.html +79 -72
  39. tgp_ui/templates/modal/delete_project.html +25 -0
  40. tgp_ui/templates/modal/empty_container.html +9 -0
  41. tgp_ui/templates/modal/file_explorer_content.html +34 -0
  42. tgp_ui/templates/modal/file_explorer_main.html +22 -0
  43. tgp_ui/templates/modal/file_explorer_nextcloud.html +27 -0
  44. tgp_ui/templates/modal/github_modal.html +29 -0
  45. tgp_ui/templates/modal/nextcloud_login.html +48 -0
  46. tgp_ui/templates/modal/tei_explorer.html +58 -0
  47. tgp_ui/templates/modal/xpath_parser.html +52 -0
  48. tgp_ui/templates/project_main.html +36 -0
  49. tgp_ui/templates/project_navbar.html +81 -0
  50. tgp_ui/templates/{projects.html → projects_main.html} +13 -28
  51. tgp_ui/templates/tab_final_upload.html +29 -0
  52. tgp_ui/templates/tabs/check_result.html +90 -0
  53. tgp_ui/templates/tabs/edit_project.html +113 -0
  54. tgp_ui/templates/tabs/empty_container.html +12 -0
  55. tgp_ui/templates/tabs/import_data.html +138 -0
  56. tgp_ui/templates/tabs/manage_collections.html +88 -0
  57. tgp_ui/templates/tabs/select_directories.html +41 -0
  58. tgp_ui/templates/tabs/upload.html +42 -0
  59. tgp_ui/templates/tabs/validate_metadata.html +227 -0
  60. tg_prepare-1.1.0.dist-info/RECORD +0 -39
  61. tgp_ui/templates/collection.html +0 -194
  62. tgp_ui/templates/file_upload.html +0 -24
  63. tgp_ui/templates/nxc_file_tree.html +0 -33
  64. tgp_ui/templates/project.html +0 -26
  65. tgp_ui/templates/storage.html +0 -49
  66. tgp_ui/templates/tei_explorer.html +0 -48
  67. tgp_ui/templates/xpath_parser_modal_content.html +0 -37
  68. {tg_prepare-1.1.0.dist-info → tg_prepare-2.1.0b2.dist-info}/entry_points.txt +0 -0
  69. {tg_prepare-1.1.0.dist-info → tg_prepare-2.1.0b2.dist-info}/licenses/LICENSE +0 -0
  70. {tg_prepare-1.1.0.dist-info → tg_prepare-2.1.0b2.dist-info}/top_level.txt +0 -0
tgp_ui/static/js/main.js CHANGED
@@ -1,489 +1,36 @@
1
- function loadTEIContent(e) {
2
- e.preventDefault();
3
- const self = $(this);
4
- const tab = self.closest('.tab-pane');
5
-
6
- // toggle classes to highlight selected row and XML element
7
- tab.find('.row.bg-light').removeClass('bg-light')
8
- tab.find('.bg-info').removeClass('bg-info')
9
- self.closest('.row').addClass('bg-light')
10
- self.addClass('bg-info')
11
-
12
- $.ajax({
13
- url: self.data('url'),
14
- method: 'GET',
15
- dataType: 'json',
16
- data: self.data(),
17
- success: function (response) {
18
- tab.find(".output").empty();
19
- var col = $('<div class="col"></div>');
20
- col.append('<h3>' + self.data('heading') + '</h3>');
21
- col.append('<span></span>');
22
- tab.find(".output").append(col);
23
- col.find('span').simpleXML({ xmlString: response.content });
24
- }
25
- });
26
- };
27
-
28
- const loadTEIExplorerTab = function (e) {
29
- const self = $(this);
30
- const tab = $(self.data('bs-target'));
31
- if (!tab.hasClass('loaded')) {
32
- tab.load(self.data('url'), function () {
33
- tab.addClass('loaded');
34
- })
35
- }
36
- }
37
-
38
- const showXPathInTEIExplorer = function (e) {
39
- const self = $(this);
40
- const form = self.closest('form');
41
- // fill xpath value into input field
42
- form.find('input[name=xpath]').val(self.data('xpath'));
43
- // trigger search for xpath
44
- form.find('.check-xpath').trigger('click');
45
- // open tei_explorer tab
46
- form.find('.tei_explorer').trigger('click');
47
- }
48
-
49
- const checkXPath = function (e) {
50
- e.preventDefault();
51
- const self = $(this);
52
- const tab = self.closest('form').find(
53
- '.tab-pane.tei-explorer-content.loaded');
54
- $.ajax({
55
- url: self.data('url'),
56
- method: 'GET',
57
- dataType: 'json',
58
- data: { 'xpath': self.closest('.row').find('input[name=xpath]').val() },
59
- success: function (response) {
60
- // set '-' as default value (in case no result is found)
61
- tab.find('.xpath-result').text('-');
62
- response.results.forEach(function (d) {
63
- tab.find('[data-name="' + d.filename + '"]').text(d.result);
64
- })
65
- }
66
- });
67
- }
68
-
69
- const cloneFromGit = function (e) {
70
- e.preventDefault();
71
- var self = $(this);
72
- self.find('button')
73
- .removeClass('btn-danger')
74
- .addClass('btn-primary')
75
- .prop('disabled', true);
76
- self.find('button span').toggle();
77
- $.ajax({
78
- url: self.data('url'),
79
- method: 'POST',
80
- dataType: 'json',
81
- data: self.serialize(),
82
- success: function (response) {
83
- self.find('button').prop('disabled', false);
84
- self.find('button span').toggle();
85
- if (response.value == 'OK') {
86
- location.reload();
87
- }
1
+ require.config({
2
+ baseUrl: '/static/js',
3
+ paths: {
4
+ bootstrap: 'bootstrap.bundle.min',
5
+ jquery: 'jquery.min',
6
+ simpleXML: 'simpleXML',
7
+ },
8
+ shim: {
9
+ jquery: {
10
+ exports: '$'
88
11
  },
89
- error: function (response) {
90
- self.find('button').prop('disabled', false);
91
- self.find('button')
92
- .removeClass('btn-primary')
93
- .addClass('btn-danger');
94
- self.find('button span').toggle();
95
- }
96
-
97
- });
98
- };
99
-
100
- const showFiles = function (e) {
101
- e.preventDefault();
102
- $(this).closest('.folder').find('.file').toggle();
103
- $(this).find('span')
104
- .toggleClass('bi-eye-slash-fill bi-eye-fill');
105
- };
106
-
107
- const saveCollectionSettings = function (e) {
108
- e.preventDefault();
109
- var self = $(this);
110
- let data = {};
111
-
112
- self.find(':not(.no-serialization)').serializeArray().forEach(function (d) {
113
- data[d.name] = d.value;
114
- })
115
-
116
- self.find('.multi-input-xpath').each(function (i, mix) {
117
- let multi_input = $(mix);
118
- if (data[multi_input.data('name')] === undefined) {
119
- data[multi_input.data('name')] = [];
120
- }
121
- // get all keys and values for each multi-input
122
- let item = {};
123
- multi_input.find('input').each(function (i, mix_input) {
124
- const key = $(mix_input).data('key');
125
- const type = $(mix_input).data('type');
126
- if (item[key] === undefined) {
127
- item[key] = {};
128
- }
129
- item[key][type] = $(mix_input).val();
130
- });
131
- console.table(item)
132
- data[multi_input.data('name')].push(JSON.stringify(item))
133
-
134
- })
135
- // loop through multi-inputs with fixed values
136
- self.find('.multi-input-fixed').each(function (i, mif) {
137
- let multi_input = $(mif);
138
- if (data[multi_input.data('name')] === undefined) {
139
- data[multi_input.data('name')] = [];
140
- }
141
- // get all keys and values for each multi-input
142
- let item = {};
143
- multi_input.find('input').each(function (i, mif_input) {
144
- item[$(mif_input).data('key')] = $(mif_input).val();
145
- });
146
- data[multi_input.data('name')].push(JSON.stringify(item))
147
- })
148
-
149
- // loop throuh xpath-or-value-input
150
- let _data = {}
151
- self.find('.xpath-or-value-input input').each(function (i, d) {
152
- const xv_input = $(d);
153
- const xv_data = xv_input.data();
154
- const xv_name = xv_data['name'];
155
- if (_data[xv_name] === undefined) {
156
- _data[xv_name] = {};
12
+ simpleXML: {
13
+ deps: ['jquery'],
14
+ exports: 'simpleXML'
157
15
  }
158
- _data[xv_name][xv_data['type']] = xv_input.val();
159
- })
160
- for (const [key, value] of Object.entries(_data)) {
161
- data[key] = JSON.stringify(value);
162
16
  }
163
-
164
- $.ajax({
165
- url: self.attr('action'),
166
- method: 'POST',
167
- dataType: 'json',
168
- data: data,
169
- success: function (response) {
170
- if (response.response == 'OK') {
171
- // trigger reload of tab content
172
- const target = $('.collection-tab.active').attr('href')
173
- $(target).attr('reload', true)
174
- loadCollectionTab.call($('.collection-tab.active')[0]);
175
- }
176
- }
177
- });
178
- }
179
-
180
- // NEXTCLOUD
181
- // #######################
182
-
183
- const selectNextcloudFolder = function (e) {
184
- e.preventDefault();
185
- const self = $(this);
186
- self.find('span.bi')
187
- .toggleClass('bi-circle-fill bi-circle');
188
- }
189
-
190
- const loginNextcloud = function (e) {
191
- e.preventDefault();
192
- const spinner = document.querySelector(".spinner-login");
193
- const error = document.getElementById("login-error");
194
- spinner.hidden = false;
195
- error.hidden = true;
196
-
197
- fetch('/nextcloud_login', {
198
- method: 'POST',
199
- headers: { 'Content-Type': 'application/json' },
200
- body: JSON.stringify({ data: Object.fromEntries(new FormData(document.querySelector(".login"))) }),
201
- })
202
- .then(response => response.json())
203
- .then(() => _loadNextcloudTab('/nextcloud_tab'))
204
- .catch(() => (error.hidden = false))
205
- .finally(() => (spinner.hidden = true));
206
- };
207
-
208
- const processSelectionCloud = function (e) {
209
- const spinner = document.querySelector(".spinner-submit");
210
- spinner.hidden = false;
211
-
212
- const filePaths = Array.from(document.querySelectorAll('.nxc_select-folder > .bi.bi-circle-fill'))
213
- .map(path => path.dataset.path);
214
- const projectname = document.getElementById('tab_nextcloud').dataset.projectname;
215
-
216
- fetch('/nextcloud_download', {
217
- method: 'POST',
218
- headers: { 'Content-Type': 'application/json' },
219
- body: JSON.stringify({ file_paths: filePaths, projectname }),
220
- })
221
- .then(response => response.json())
222
- .finally(() => (spinner.hidden = true));
223
- };
224
-
225
- // Function to handle logout from Nextcloud
226
- const logoutNextcloud = function (e) {
227
- var self = $(this);
228
- // Send a POST request to the server to log out
229
- fetch('/nextcloud_logout', {
230
- method: 'POST',
231
- headers: {
232
- 'Content-Type': 'application/json',
233
- }
234
- })
235
- .then(response => response.json())
236
- .then(data => {
237
- if (data.response === 'OK') {
238
- // If logout is successful, mark the Nextcloud tab as not loaded
239
- $('#tab_nextcloud').removeClass('loaded');
240
- // Reload the Nextcloud tab with the specified project name
241
- _loadNextcloudTab('/nextcloud_tab');
242
- }
17
+ });
18
+
19
+ require([
20
+ 'bootstrap', 'jquery', 'simpleXML', 'tabManager', 'modalManager', 'fileManager', 'sidebarManager', 'navbarManager', 'collectionManager', 'projectManager'],
21
+ function (bootstrap, $, simpleXML, TabManager, ModalManager, FileManager, SidebarManager, NavbarManager, CollectionManager, ProjectManager) {
22
+ $(document).ready(function () {
23
+ // Initialize all generic managers
24
+ TabManager.init();
25
+ ModalManager.init();
26
+ FileManager.init();
27
+ SidebarManager.init();
28
+ NavbarManager.init();
29
+
30
+ // Initialize all specific managers
31
+ CollectionManager.init();
32
+ ProjectManager.init();
33
+
34
+ console.log('Modules loaded successfully!');
243
35
  });
244
- }
245
-
246
- // Helper function to load the Nextcloud tab content
247
- const _loadNextcloudTab = function (url) {
248
- // Check if the Nextcloud tab is already loaded
249
- if (!$('#tab_nextcloud').hasClass('loaded')) {
250
- // Show a loading spinner while the content is being fetched
251
- $('#tab_nextcloud').html('<div class="d-flex justify-content-center p-5"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div></div>')
252
- // Send an AJAX request to load the tab content
253
- $.ajax({
254
- url: url,
255
- method: 'POST',
256
- dataType: 'html',
257
- success: function (response) {
258
- // Populate the tab with the response and mark it as loaded
259
- $('#tab_nextcloud')
260
- .html(response)
261
- .addClass('active')
262
- .show();
263
- if ($('#tab_nextcloud').find('#nextcloud_login').length == 0) {
264
- $('#tab_nextcloud').addClass('loaded');
265
- }
266
- }
267
- });
268
- } else {
269
- // If the tab is already loaded, simply make it active and visible
270
- $('#tab_nextcloud').addClass('active').show();
271
- }
272
- }
273
-
274
- // Function to load the Nextcloud tab when triggered
275
- const loadNextcloudTab = function (e) {
276
- const self = $(this);
277
- // Call the helper function to load the tab content
278
- _loadNextcloudTab(self.data('url'));
279
- }
280
-
281
- // #######################
282
-
283
- const selectFolder = function (e) {
284
- e.preventDefault();
285
- const self = $(this);
286
- self.closest('.folder').toggleClass('bg-warning');
287
- self.find('span.bi')
288
- .toggleClass('bi-circle-fill bi-circle');
289
- const tab = self.closest('.tab-pane');
290
- const spinner = self.closest('.folder').find('.spinner-border');
291
- spinner.toggle();
292
-
293
- const data = tab.find('span.bi-circle-fill').map((i, d) => (
294
- { name: 'tei_directories', value: $(d).data('path') })).get();
295
- $.post({
296
- url: self.data('url'),
297
- dataType: 'json',
298
- data: data,
299
- success: (response) => {
300
- spinner.toggle();
301
- if (response.response === 'OK') {
302
- location.reload();
303
- }
304
- }
305
- });
306
- };
307
-
308
- const addMultiInput = function (e) {
309
- e.preventDefault();
310
- const self = $(this);
311
- const this_row = self.closest('.row')
312
- this_row.next().clone()
313
- .insertAfter(this_row)
314
- .find('input').val('');
315
- }
316
- const removeMultiInput = function (e) {
317
- e.preventDefault();
318
- const self = $(this);
319
- const this_multi_input = self.closest('.multi-input')
320
- this_multi_input.remove();
321
- }
322
-
323
- const loadCollectionTab = function (e) {
324
- const self = $(this);
325
- const tab = $(self.attr('href'));
326
- if (tab.find('form.collection-form').length === 0 || tab.attr('reload') === 'true') {
327
- tab.css('opacity', 0.5);
328
- tab.attr('reload', false)
329
- self.find('i').toggle();
330
- $('#tab_' + self.data('name')).load(
331
- self.data('url'),
332
- self.data(), function () {
333
- self.find('i').toggle();
334
- tab.css('opacity', 1);
335
- })
336
- }
337
- }
338
- // Helper function to load the publication tab for a specific project
339
- const _loadPublicationTab = function (projectname) {
340
- // Select the tab element for the given project
341
- const tab = $('#tab_publish_' + projectname);
342
- tab.show(); // Make the tab visible
343
- $('.tab-pane.active').hide(); // Hide the currently active tab
344
- // Load the tab content via AJAX with the project name as data
345
- tab.load(tab.data('url'), { projectname: projectname });
346
- }
347
-
348
- // Event handler to load the publication tab when triggered
349
- const loadPublicationTab = function (e) {
350
- const self = $(this); // Reference the clicked element
351
- // Call the helper function with the project name from the data attribute
352
- _loadPublicationTab(self.data('projectname'));
353
- }
354
-
355
- // this function replaces (kind of) the normal submit
356
- // by sending the form data via ajax and reloading the tab content
357
- const tabSubmit = function (e) {
358
- e.preventDefault();
359
- const self = $(this);
360
- const submit_icons = self.find('button[type=submit] span');
361
- submit_icons.toggle();
362
-
363
- $.post(self.attr('action'), self.serialize(), function (response) {
364
- if (response.response === 'OK') {
365
- _loadPublicationTab(self.data('projectname'));
366
- }
367
- })
368
- }
369
-
370
- const selectTGProject = function (e) {
371
- e.preventDefault();
372
- const self = $(this);
373
- self.closest('.row').find('.bi-circle-fill').toggleClass('bi-circle-fill bi-circle');
374
- self.find('span')
375
- .addClass('bi-circle-fill')
376
- .removeClass('bi-circle');
377
- $.post(self.data('url'), self.data(), function (response) {
378
- if (response.response === 'OK') {
379
- _loadPublicationTab(self.data('projectname'));
380
- }
381
- })
382
- }
383
-
384
- // get the hits of a project (recursively) until the spinner is no more visible
385
- const getTGProjectHits = function (url, content, spinner) {
386
- // wait 10 seconds before sending the next request
387
- setTimeout(function () {
388
- $.get(url, function (response) {
389
- const fs = content.css('font-size');
390
- // show hits and (slightly) highlight the text
391
- content
392
- .animate({ fontSize: '18px' }, 'slow')
393
- .text(response.hits)
394
- .animate({ fontSize: fs }, 'slow');
395
- // check if spinner is still visible
396
- if (spinner.is(':visible') > 0) {
397
- // if so, trigger this function again
398
- getTGProjectHits(url, content, spinner);
399
- }
400
- })
401
- }, 10000);
402
- }
403
-
404
- const deleteTGProject = function (e) {
405
- e.preventDefault();
406
- const self = $(this);
407
- const spans = self.find('span');
408
- // show spinner & hide trash icon
409
- spans.toggle();
410
- // send delete request to server
411
- $.post(self.data('url'), function () {
412
- _loadPublicationTab(self.data('projectname'));
413
- });
414
- // get the hits of the project
415
- getTGProjectHits(
416
- self.data('hits_url'), // url to get the hits
417
- self.closest('.tg-project-row').find('.tgProjectHits'), // content to show the hits
418
- self.find('.spinner-border') // spinner to check if it is still visible
419
- );
420
- }
421
-
422
- $(document).ready(function () {
423
-
424
- // ##SIDEBAR##
425
- document.querySelector(".toggle-btn").addEventListener("click", function () {
426
- document.querySelector("#sidebar").classList.toggle("expand");
427
- });
428
-
429
- // ##FILES VIEW##
430
- $('li.folder a.show-files').on('click', showFiles);
431
- $('li.folder a.select-folder').on('click', selectFolder);
432
- $(document).on('submit', 'form#cloneFromGit', cloneFromGit);
433
- $(document).on('submit', 'form#nextcloud_login', loginNextcloud);
434
- $(document).on('click', '#tab_nextcloud #nextcloud_logout', logoutNextcloud);
435
- $(document).on(
436
- 'show.bs.tab', 'a[data-bs-toggle="tab"][aria-controls="tab_nextcloud"]',
437
- loadNextcloudTab);
438
- $(document).on('click', 'li.folder a.nxc_select-folder', selectNextcloudFolder);
439
- $(document).on('click', 'button.submit-selection', processSelectionCloud);
440
-
441
- // ##COLLECTION VIEW##
442
- // load TEI content into the output div
443
- $(document).on('click', '.load-tei-content', loadTEIContent)
444
-
445
- // add a new multi input-field (e.g. 'Basic classification', 'Rights Holder')
446
- $(document).on('click', '.add-multi-input', addMultiInput);
447
- // remove a new multi input-field (e.g. 'Basic classification', 'Rights Holder')
448
- $(document).on('click', '.remove-multi-field', removeMultiInput);
449
- // save collection settings
450
- $(document).on('submit', '.collection-form', saveCollectionSettings);
451
-
452
- // load collection tab content
453
- // Option1: after clicking on the tab
454
- $(document).on('click', '.collection-tab', loadCollectionTab);
455
- // Option2: when the page loads (by opening collection in sidebar)
456
- if ($('.collection-tab.active').length > 0) {
457
- loadCollectionTab.call($('.collection-tab.active')[0]);
458
- }
459
-
460
- // ###TEI EXPLORER###
461
- // load TEI Explorer tab content
462
- $(document).on(
463
- 'show.bs.tab', 'button[data-bs-toggle="tab"].tei_explorer',
464
- loadTEIExplorerTab);
465
- // trigger xpath search
466
- $(document).on('click', '.check-xpath', checkXPath);
467
- // show xpath (of a 'dynamic attribute') in TEI Explorer
468
- $(document).on('click', '.show-xpath-in-tei-explorer', showXPathInTEIExplorer);
469
-
470
- // ##PUBLISH##
471
- // load publication tab content
472
- $(document).on('click', '.publication-tab', loadPublicationTab);
473
-
474
- // this is generic form-submit for all forms with class 'tab-submit'
475
- $(document).on('submit', 'form.tab-submit', tabSubmit);
476
- // select a listed TextGrid-Project
477
- $(document).on('click', 'a.select-tg-project', selectTGProject);
478
- // delete a listed TextGrid-Project
479
- $(document).on('click', 'a.delete-tg-project', deleteTGProject);
480
-
481
- // ###PROJECTS###
482
- $('#deleteProject').on('show.bs.modal', function (event) {
483
- const pressed_button = $(event.relatedTarget);
484
- const target_input = $(this).find('input[name=projectname]');
485
- target_input.val(pressed_button.data('projectname'));
486
- })
487
-
488
- console.log('main.js has been loaded! ');
489
- })
36
+ });
@@ -0,0 +1,105 @@
1
+ define(['bootstrap'], function (bootstrap) {
2
+ const ModalManager = {
3
+ formSubmit: function (event) {
4
+ event.preventDefault();
5
+ const form = $(this);
6
+ const submitButton = form.find('button[type="submit"]');
7
+ const originalButtonText = submitButton.html();
8
+
9
+ // show spinner and disable button
10
+ submitButton.html('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
11
+ .prop('disabled', true);
12
+
13
+ $.ajax({
14
+ url: form.attr('action'),
15
+ type: form.attr("method") || 'GET',
16
+ data: form.serialize(),
17
+ success: function (response) {
18
+ if (form.data('reload-location')) {
19
+ location.reload();
20
+ } else {
21
+ form.closest('.modal-dialog').html(response);
22
+ }
23
+ },
24
+ error: function () {
25
+ alert('An error occurred. Please try again.');
26
+ },
27
+ complete: function () {
28
+ // restore original button text and remove spinner
29
+ submitButton.html(originalButtonText).prop('disabled', false);
30
+ }
31
+ });
32
+ },
33
+
34
+ loadModal: function (e) {
35
+ e.preventDefault();
36
+
37
+ const self = $(this);
38
+ const url = self.data('url');
39
+ const modalContainer = $('#genericModalContainer');
40
+ const argsKeys = Object.keys(self.data()).filter(key => key.startsWith('args_'));
41
+
42
+ modalContainer.load("/modal/container .modal-dialog");
43
+ const modal = new bootstrap.Modal(modalContainer[0]);
44
+ if (!modalContainer.is(':visible')) {
45
+ modal.show();
46
+ };
47
+
48
+ let args = {};
49
+ if (argsKeys.length > 0) {
50
+ for (const key of argsKeys) {
51
+ const value = self.data(key);
52
+ const paramName = key.replace('args_', '');
53
+ args[paramName] = value;
54
+ }
55
+ }
56
+
57
+ modalContainer.load(url + " .modal-dialog", args);
58
+ },
59
+
60
+ loadTEIContent: function (e) {
61
+ e.preventDefault();
62
+ const self = $(this);
63
+ const modal = self.closest('.modal');
64
+
65
+ modal.find('.bg-primary, .bg-light')
66
+ .removeClass('bg-primary')
67
+ .removeClass('bg-light')
68
+ .removeClass('text-white');
69
+ self.addClass('bg-primary')
70
+ .addClass('text-white')
71
+ .closest('.list-group-item').addClass('bg-light');
72
+
73
+ $.ajax({
74
+ url: self.data('url'),
75
+ method: 'GET',
76
+ dataType: 'json',
77
+ data: self.data(),
78
+ success: function (response) {
79
+ const teiContentOutput = modal.find('#teiContentOutput');
80
+ teiContentOutput.empty();
81
+ const col = $('<div class="col"></div>');
82
+ col.append('<h3>' + self.data('heading') + '</h3>');
83
+ col.append('<span></span>');
84
+ teiContentOutput.append(col);
85
+ col.find('span').simpleXML({ xmlString: response.content });
86
+ modal.find('#contentTab').trigger('click');
87
+ }
88
+ });
89
+ },
90
+
91
+ focusInputProjectname: function () {
92
+ const projectInput = document.getElementById('projectNameInput');
93
+ projectInput.focus();
94
+ },
95
+
96
+ init: function () {
97
+ $(document).on('submit', 'form.js-modal-submit', this.formSubmit);
98
+ $(document).on('click', '.load-modal', this.loadModal);
99
+ $(document).on('click', '.load-tei-content', this.loadTEIContent);
100
+ $(document).on('shown.bs.modal', '#newProject', this.focusInputProjectname);
101
+ }
102
+ };
103
+
104
+ return ModalManager;
105
+ });