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