bf41876d16d13e2b0df621d032f18611ff4a9240
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Resources / Public / JavaScript / Main.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13 /**
14 * Module: TYPO3/CMS/Extensionmanager/Main
15 * main logic holding everything together, consists of multiple parts
16 * ExtensionManager => Various functions for displaying the extension list / sorting
17 * Repository => Various AJAX functions for TER downloads
18 * ExtensionManager.Update => Various AJAX functions to display updates
19 * ExtensionManager.uploadForm => helper to show the upload form
20 */
21 define([
22 'jquery',
23 'nprogress',
24 'TYPO3/CMS/Backend/Modal',
25 'TYPO3/CMS/Backend/SplitButtons',
26 'TYPO3/CMS/Backend/Tooltip',
27 'TYPO3/CMS/Backend/Notification',
28 'TYPO3/CMS/Backend/Severity',
29 'datatables',
30 'TYPO3/CMS/Backend/jquery.clearable'
31 ], function($, NProgress, Modal, SplitButtons, Tooltip, Notification, Severity) {
32
33 /**
34 *
35 * @type {{identifier: {extensionlist: string, searchField: string, extensionManager: string}}}
36 * @exports TYPO3/CMS/Extensionmanager/Main
37 */
38 var ExtensionManager = {
39 identifier: {
40 extensionlist: '#typo3-extension-list',
41 searchField: '#Tx_Extensionmanager_extensionkey'
42 }
43 };
44
45 /**
46 *
47 * @returns {Object}
48 */
49 ExtensionManager.manageExtensionListing = function() {
50 var $searchField = $(this.identifier.searchField),
51 dataTable = $(this.identifier.extensionlist).DataTable({
52 paging: false,
53 dom: 'lrtip',
54 lengthChange: false,
55 pageLength: 15,
56 stateSave: true,
57 drawCallback: this.bindExtensionListActions,
58 columns: [
59 null,
60 null,
61 {
62 type: 'extension'
63 },
64 null,
65 {
66 type: 'version'
67 }, {
68 orderable: false
69 },
70 null
71 ]
72 });
73
74 $searchField.parents('form').on('submit', function() {
75 return false;
76 });
77
78 var getVars = ExtensionManager.getUrlVars();
79
80 // restore filter
81 var currentSearch = (getVars['search'] ? getVars['search'] : dataTable.search());
82 $searchField.val(currentSearch);
83
84 $searchField.on('input', function(e) {
85 dataTable.search($(this).val()).draw();
86 });
87
88 return dataTable;
89 };
90
91 /**
92 *
93 */
94 ExtensionManager.bindExtensionListActions = function() {
95 $('.removeExtension').not('.transformed').each(function() {
96 var $me = $(this);
97 $me.data('href', $me.attr('href'));
98 $me.attr('href', '#');
99 $me.addClass('transformed');
100 $me.click(function() {
101 Modal.confirm(
102 TYPO3.lang['extensionList.removalConfirmation.title'],
103 TYPO3.lang['extensionList.removalConfirmation.question'],
104 Severity.error,
105 [
106 {
107 text: TYPO3.lang['button.cancel'],
108 active: true,
109 btnClass: 'btn-default',
110 trigger: function() {
111 Modal.dismiss();
112 }
113 }, {
114 text: TYPO3.lang['button.remove'],
115 btnClass: 'btn-danger',
116 trigger: function() {
117 ExtensionManager.removeExtensionFromDisk($me);
118 Modal.dismiss();
119 }
120 }
121 ]
122 );
123 });
124 });
125 };
126
127 /**
128 *
129 * @param {Object} $extension
130 */
131 ExtensionManager.removeExtensionFromDisk = function($extension) {
132 $.ajax({
133 url: $extension.data('href'),
134 beforeSend: function() {
135 NProgress.start();
136 },
137 success: function() {
138 location.reload();
139 },
140 complete: function() {
141 NProgress.done();
142 }
143 });
144 };
145
146 /**
147 *
148 * @returns {Array}
149 */
150 ExtensionManager.getUrlVars = function() {
151 var vars = [], hash;
152 var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
153 for (var i = 0; i < hashes.length; i++) {
154 hash = hashes[i].split('=');
155 vars.push(hash[0]);
156 vars[hash[0]] = hash[1];
157 }
158 return vars;
159 };
160
161 $.fn.dataTableExt.oSort['extension-asc'] = function(a, b) {
162 return ExtensionManager.extensionCompare(a, b);
163 };
164
165 $.fn.dataTableExt.oSort['extension-desc'] = function(a, b) {
166 var result = ExtensionManager.extensionCompare(a, b);
167 return result * -1;
168 };
169
170 $.fn.dataTableExt.oSort['version-asc'] = function(a, b) {
171 var result = ExtensionManager.versionCompare(a, b);
172 return result * -1;
173 };
174
175 $.fn.dataTableExt.oSort['version-desc'] = function(a, b) {
176 return ExtensionManager.versionCompare(a, b);
177 };
178
179 /**
180 * Special sorting for the extension version column
181 *
182 * @param {String} a
183 * @param {String} b
184 * @returns {Number}
185 */
186 ExtensionManager.versionCompare = function(a, b) {
187 if (a === b) {
188 return 0;
189 }
190
191 var a_components = a.split(".");
192 var b_components = b.split(".");
193
194 var len = Math.min(a_components.length, b_components.length);
195
196 // loop while the components are equal
197 for (var i = 0; i < len; i++) {
198 // A bigger than B
199 if (parseInt(a_components[i]) > parseInt(b_components[i])) {
200 return 1;
201 }
202
203 // B bigger than A
204 if (parseInt(a_components[i]) < parseInt(b_components[i])) {
205 return -1;
206 }
207 }
208
209 // If one's a prefix of the other, the longer one is greaRepository.
210 if (a_components.length > b_components.length) {
211 return 1;
212 }
213
214 if (a_components.length < b_components.length) {
215 return -1;
216 }
217 // Otherwise they are the same.
218 return 0;
219 };
220
221 /**
222 * The extension name column can contain various forms of HTML that
223 * break a direct comparison of values
224 *
225 * @param {String} a
226 * @param {String} b
227 * @returns {Number}
228 */
229 ExtensionManager.extensionCompare = function(a, b) {
230 var div = document.createElement("div");
231 div.innerHTML = a;
232 var aStr = div.textContent || div.innerText || a;
233
234 div.innerHTML = b;
235 var bStr = div.textContent || div.innerText || b;
236
237 return aStr.trim().localeCompare(bStr.trim());
238 }
239
240 /**
241 *
242 * @param {Object} data
243 */
244 ExtensionManager.updateExtension = function(data) {
245 var message = '<h1>' + TYPO3.lang['extensionList.updateConfirmation.title'] + '</h1>';
246 message += '<h2>' + TYPO3.lang['extensionList.updateConfirmation.message'] + '</h2>';
247 message += '<form>';
248 var i = 0;
249 $.each(data.updateComments, function(version, comment) {
250 comment = comment.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1<br />$2');
251 message += '<h3><input type="radio" ' + (i === 0 ? 'checked="checked" ' : '') + 'name="version" value="' + version + '" /> ' + version + '</h3>';
252 message += '<div>' + comment + '</div>';
253 i++;
254 });
255 message += '</form>';
256
257 NProgress.done();
258
259 Modal.confirm(
260 TYPO3.lang['extensionList.updateConfirmation.questionVersionComments'],
261 message,
262 Severity.warning,
263 [
264 {
265 text: TYPO3.lang['button.cancel'],
266 active: true,
267 btnClass: 'btn-default',
268 trigger: function() {
269 Modal.dismiss();
270 }
271 }, {
272 text: TYPO3.lang['button.updateExtension'],
273 btnClass: 'btn-warning',
274 trigger: function() {
275 $.ajax({
276 url: data.url,
277 data: {
278 tx_extensionmanager_tools_extensionmanagerextensionmanager: {
279 version: $('input:radio[name=version]:checked', Modal.currentModal).val()
280 }
281 },
282 dataType: 'json',
283 beforeSend: function() {
284 NProgress.start();
285 },
286 complete: function() {
287 location.reload();
288 }
289 });
290 Modal.dismiss();
291 }
292 }
293 ]
294 );
295 };
296
297 /**
298 * configuration properties
299 */
300 ExtensionManager.configurationFieldSupport = function() {
301 $('.t3js-emconf-offset').each(function() {
302 var $me = $(this),
303 $parent = $me.parent(),
304 id = $me.attr('id'),
305 val = $me.attr('value'),
306 valArr = val.split(',');
307
308 $me.attr('data-offsetfield-x', '#' + id + '_offset_x')
309 .attr('data-offsetfield-y', '#' + id + '_offset_y')
310 .wrap('<div class="hidden"></div>');
311
312 var elementX = '' +
313 '<div class="form-multigroup-item">' +
314 '<div class="input-group">' +
315 '<div class="input-group-addon">x</div>' +
316 '<input id="' + id + '_offset_x" class="form-control t3js-emconf-offsetfield" data-target="#' + id + '" value="' + $.trim(valArr[0]) + '">' +
317 '</div>' +
318 '</div>';
319 var elementY = '' +
320 '<div class="form-multigroup-item">' +
321 '<div class="input-group">' +
322 '<div class="input-group-addon">y</div>' +
323 '<input id="' + id + '_offset_y" class="form-control t3js-emconf-offsetfield" data-target="#' + id + '" value="' + $.trim(valArr[1]) + '">' +
324 '</div>' +
325 '</div>';
326
327 var offsetGroup = '<div class="form-multigroup-wrap">' + elementX + elementY + '</div>';
328 $parent.append(offsetGroup);
329 $parent.find('.t3js-emconf-offset').keyup(function() {
330 var $target = $($(this).data('target'));
331 $target.attr(
332 'value',
333 $($target.data('offsetfield-x')).val() + ',' + $($target.data('offsetfield-y')).val()
334 );
335 });
336 });
337
338 $('.t3js-emconf-wrap').each(function() {
339 var $me = $(this),
340 $parent = $me.parent(),
341 id = $me.attr('id'),
342 val = $me.attr('value'),
343 valArr = val.split('|');
344
345 $me.attr('data-wrapfield-start', '#' + id + '_wrap_start')
346 .attr('data-wrapfield-end', '#' + id + '_wrap_end')
347 .wrap('<div class="hidden"></div>');
348
349 var elementStart = '' +
350 '<div class="form-multigroup-item">' +
351 '<input id="' + id + '_wrap_start" class="form-control t3js-emconf-wrapfield" data-target="#' + id + '" value="' + $.trim(valArr[0]) + '">' +
352 '</div>';
353 var elementEnd = '' +
354 '<div class="form-multigroup-item">' +
355 '<input id="' + id + '_wrap_end" class="form-control t3js-emconf-wrapfield" data-target="#' + id + '" value="' + $.trim(valArr[1]) + '">' +
356 '</div>';
357
358 var wrapGroup = '<div class="form-multigroup-wrap">' + elementStart + elementEnd + '</div>';
359 $parent.append(wrapGroup);
360 $parent.find('.t3js-emconf-wrapfield').keyup(function() {
361 var $target = $($(this).data('target'));
362 $target.attr(
363 'value',
364 $($target.data('wrapfield-start')).val() + '|' + $($target.data('wrapfield-end')).val()
365 );
366 });
367 });
368 };
369
370 /**
371 *
372 * @type {{downloadPath: string}}
373 */
374 var Repository = {
375 downloadPath: ''
376 };
377
378 /**
379 *
380 */
381 Repository.initDom = function() {
382 NProgress.configure({parent: '.module-loading-indicator', showSpinner: false});
383
384 $('#terTable').DataTable({
385 lengthChange: false,
386 pageLength: 15,
387 stateSave: false,
388 info: false,
389 paging: false,
390 searching: false,
391 ordering: false,
392 drawCallback: Repository.bindDownload
393 });
394
395 $('#terVersionTable').DataTable({
396 lengthChange: false,
397 pageLength: 15,
398 stateSave: false,
399 info: false,
400 paging: false,
401 searching: false,
402 drawCallback: Repository.bindDownload,
403 order: [
404 [2, 'asc']
405 ],
406 columns: [
407 {orderable: false},
408 null,
409 {type: 'version'},
410 null,
411 null,
412 null
413 ]
414 });
415
416 $('#terSearchTable').DataTable({
417 paging: false,
418 lengthChange: false,
419 stateSave: false,
420 searching: false,
421 language: {
422 search: 'Filter results:'
423 },
424 ordering: false,
425 drawCallback: Repository.bindDownload
426 });
427
428 Repository.bindDownload();
429 Repository.bindSearchFieldResetter();
430 };
431
432 /**
433 *
434 */
435 Repository.bindDownload = function() {
436 var installButtons = $('.downloadFromTer form.download button[type=submit]');
437 installButtons.off('click');
438 installButtons.on('click', function(event) {
439 event.preventDefault();
440 var url = $(event.currentTarget.form).attr('data-href');
441 Repository.downloadPath = $(event.currentTarget.form).find('input.downloadPath:checked').val();
442 $.ajax({
443 url: url,
444 dataType: 'json',
445 beforeSend: function() {
446 NProgress.start();
447 },
448 success: Repository.getDependencies
449 });
450 });
451 };
452
453 /**
454 *
455 * @param {Object} data
456 * @returns {Boolean}
457 */
458 Repository.getDependencies = function(data) {
459 NProgress.done();
460 if (data.hasDependencies) {
461 Modal.confirm(data.title, data.message, Severity.info, [
462 {
463 text: TYPO3.lang['button.cancel'],
464 active: true,
465 btnClass: 'btn-default',
466 trigger: function() {
467 Modal.dismiss();
468 }
469 }, {
470 text: TYPO3.lang['button.resolveDependencies'],
471 btnClass: 'btn-info',
472 trigger: function() {
473 Repository.getResolveDependenciesAndInstallResult(data.url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + Repository.downloadPath);
474 Modal.dismiss();
475 }
476 }
477 ]);
478 } else {
479 if(data.hasErrors) {
480 Notification.error(data.title, data.message, 15);
481 } else {
482 Repository.getResolveDependenciesAndInstallResult(data.url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + Repository.downloadPath);
483 }
484 }
485 return false;
486 };
487
488 /**
489 *
490 * @param {String} url
491 */
492 Repository.getResolveDependenciesAndInstallResult = function(url) {
493 $.ajax({
494 url: url,
495 dataType: 'json',
496 beforeSend: function() {
497 NProgress.start();
498 },
499 success: function (data) {
500 if (data.errorCount > 0) {
501 Modal.confirm(data.errorTitle, data.errorMessage, Severity.error, [
502 {
503 text: TYPO3.lang['button.cancel'],
504 active: true,
505 btnClass: 'btn-default',
506 trigger: function() {
507 Modal.dismiss();
508 }
509 }, {
510 text: TYPO3.lang['button.resolveDependenciesIgnore'],
511 btnClass: 'btn-danger disabled t3js-dependencies',
512 trigger: function() {
513 if (!$(this).hasClass('disabled')) {
514 Repository.getResolveDependenciesAndInstallResult(data.skipDependencyUri);
515 Modal.dismiss();
516 }
517 }
518 }
519 ]);
520 Modal.currentModal.on('shown.bs.modal', function() {
521 var $actionButton = Modal.currentModal.find('.t3js-dependencies');
522 $('input[name="unlockDependencyIgnoreButton"]', Modal.currentModal).on('change', function() {
523 $actionButton.toggleClass('disabled', !$(this).prop('checked'));
524 });
525 });
526 } else {
527 var successMessage = TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.message' + data.installationTypeLanguageKey].replace(/\{0\}/g, data.extension);
528
529 successMessage += '\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.header'] + ': ';
530 $.each(data.result, function(index, value) {
531 successMessage += '\n\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.item'] + ' ' + index + ': ';
532 $.each(value, function(extkey) {
533 successMessage += '\n* ' + extkey
534 });
535 });
536 Notification.info(TYPO3.lang['extensionList.dependenciesResolveFlashMessage.title' + data.installationTypeLanguageKey].replace(/\{0\}/g, data.extension), successMessage, 15);
537 top.TYPO3.ModuleMenu.App.refreshMenu();
538 }
539 },
540 complete: function() {
541 NProgress.done();
542 }
543 });
544 };
545
546 /**
547 *
548 */
549 Repository.bindSearchFieldResetter = function() {
550 var $searchFields = $('.typo3-extensionmanager-searchTerForm input[type="text"]');
551 var searchResultShown = ('' !== $searchFields.first().val());
552
553 $searchFields.clearable(
554 {
555 onClear: function() {
556 if (searchResultShown) {
557 $(this).closest('form').submit();
558 }
559 }
560 }
561 );
562 };
563
564 /**
565 *
566 * @type {{identifier: {extensionTable: string, terUpdateAction: string, pagination: string, splashscreen: string, terTableWrapper: string, terTableDataTableWrapper: string}}}
567 */
568 ExtensionManager.Update = {
569 identifier: {
570 extensionTable: '#terTable',
571 terUpdateAction: '.update-from-ter',
572 pagination: '.pagination-wrap',
573 splashscreen: '.splash-receivedata',
574 terTableWrapper: '#terTableWrapper',
575 terTableDataTableWrapper: '#terTableWrapper .dataTables_wrapper'
576 }
577 };
578
579 /**
580 * Register "update from ter" action
581 */
582 ExtensionManager.Update.initializeEvents = function() {
583 $(ExtensionManager.Update.identifier.terUpdateAction).each(function() {
584
585 // "this" is the form which updates the extension list from
586 // TER on submit
587 var $me = $(this),
588 updateURL = $(this).attr('action');
589
590 $me.attr('action', '#');
591 $me.submit(function() {
592 // Force update on click.
593 ExtensionManager.Update.updateFromTer(updateURL, true);
594
595 // Prevent normal submit action.
596 return false;
597 });
598
599 // This might give problems when there are more "update"-buttons,
600 // each one would trigger a TER-ExtensionManager.Update.
601 ExtensionManager.Update.updateFromTer(updateURL, false);
602 });
603 };
604
605 /**
606 *
607 * @param {String} url
608 * @param {Boolean} forceUpdate
609 */
610 ExtensionManager.Update.updateFromTer = function(url, forceUpdate) {
611 if (forceUpdate) {
612 url = url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager%5BforceUpdateCheck%5D=1';
613 }
614
615 // Hide triggers for TER update
616 $(ExtensionManager.Update.identifier.terUpdateAction).addClass('is-hidden');
617
618 // Hide extension table
619 $(ExtensionManager.Update.identifier.extensionTable).hide();
620
621 // Show loaders
622 $(ExtensionManager.Update.identifier.splashscreen).addClass('is-shown');
623 $(ExtensionManager.Update.identifier.terTableDataTableWrapper).addClass('is-loading');
624 $(ExtensionManager.Update.identifier.pagination).addClass('is-loading');
625
626 var reload = false;
627
628 $.ajax({
629 url: url,
630 dataType: 'json',
631 cache: false,
632 beforeSend: function() {
633 NProgress.start();
634 },
635 success: function(data) {
636 // Something went wrong, show message
637 if (data.errorMessage.length) {
638 Notification.error(TYPO3.lang['extensionList.updateFromTerFlashMessage.title'], data.errorMessage, 10);
639 }
640
641 // Message with latest updates
642 var $lastUpdate = $(ExtensionManager.Update.identifier.terUpdateAction + ' .time-since-last-update');
643 $lastUpdate.text(data.timeSinceLastUpdate);
644 $lastUpdate.attr(
645 'title',
646 TYPO3.lang['extensionList.updateFromTer.lastUpdate.timeOfLastUpdate'] + data.lastUpdateTime
647 );
648
649 if (data.updated) {
650 // Reload page
651 reload = true;
652 window.location.replace(window.location.href);
653 }
654 },
655 error: function(jqXHR, textStatus, errorThrown) {
656 // Create an error message with diagnosis info.
657 var errorMessage = textStatus + '(' + errorThrown + '): ' + jqXHR.responseText;
658
659 Notification.warning(
660 TYPO3.lang['extensionList.updateFromTerFlashMessage.title'],
661 errorMessage,
662 10
663 );
664 },
665 complete: function() {
666 NProgress.done();
667
668 if (!reload) {
669 // Hide loaders
670 $(ExtensionManager.Update.identifier.splashscreen).removeClass('is-shown');
671 $(ExtensionManager.Update.identifier.terTableDataTableWrapper).removeClass('is-loading');
672 $(ExtensionManager.Update.identifier.pagination).removeClass('is-loading');
673
674 // Show triggers for TER-update
675 $(ExtensionManager.Update.identifier.terUpdateAction).removeClass('is-hidden');
676
677 // Show extension table
678 $(ExtensionManager.Update.identifier.extensionTable).show();
679 }
680 }
681 });
682 };
683
684 /**
685 *
686 */
687 ExtensionManager.Update.transformPaginatorToAjax = function () {
688 $(ExtensionManager.Update.identifier.pagination + ' a').each(function() {
689 var $me = $(this);
690 $me.data('href', $(this).attr('href'));
691 $me.attr('href', '#');
692 $me.click(function() {
693 var $terTableWrapper = $(ExtensionManager.Update.identifier.terTableWrapper);
694 NProgress.start();
695 $.ajax({
696 url: $(this).data('href'),
697 dataType: 'json',
698 success: function(data) {
699 $terTableWrapper.html(data);
700 ExtensionManager.Update.transformPaginatorToAjax();
701 },
702 complete: function() {
703 NProgress.done();
704 }
705 });
706 });
707 });
708 };
709
710 /**
711 * show the uploading form
712 */
713 ExtensionManager.UploadForm = {
714 expandedUploadFormClass: 'transformed'
715 };
716
717 /**
718 *
719 */
720 ExtensionManager.UploadForm.initializeEvents = function() {
721 // Show upload form
722 $(document).on('click', '.t3js-upload', function(event) {
723 var $me = $(this),
724 $uploadForm = $('.uploadForm');
725
726 event.preventDefault();
727 if($me.hasClass(ExtensionManager.UploadForm.expandedUploadFormClass)) {
728 $uploadForm.stop().slideUp();
729 $me.removeClass(ExtensionManager.UploadForm.expandedUploadFormClass);
730 } else {
731 $me.addClass(ExtensionManager.UploadForm.expandedUploadFormClass);
732 $uploadForm.stop().slideDown();
733
734 $.ajax({
735 url: $me.attr('href'),
736 dataType: 'html',
737 success: function (data) {
738 $uploadForm.html(data);
739 }
740 });
741 }
742 });
743 };
744
745 $(function() {
746 var dataTable = ExtensionManager.manageExtensionListing();
747
748 $(document).on('click', '.onClickMaskExtensionManager', function() {
749 NProgress.start();
750 }).on('click', 'a[data-action=update-extension]', function(e) {
751 e.preventDefault();
752 $.ajax({
753 url: $(this).attr('href'),
754 dataType: 'json',
755 beforeSend: function() {
756 NProgress.start();
757 },
758 success: ExtensionManager.updateExtension
759 });
760 }).on('change', 'input[name=unlockDependencyIgnoreButton]', function() {
761 var $actionButton = $('.t3js-dependencies');
762 $actionButton.toggleClass('disabled', !$(this).prop('checked'));
763 });
764
765 $(ExtensionManager.identifier.searchField).clearable({
766 onClear: function() {
767 dataTable.search('').draw();
768 }
769 });
770
771 $(document).on('click', '.t3-button-action-installdistribution', function() {
772 NProgress.start();
773 });
774
775 ExtensionManager.configurationFieldSupport();
776
777 SplitButtons.addPreSubmitCallback(function(e) {
778 if ($(e.target).hasClass('t3js-save-close')) {
779 $('#configurationform').append($('<input />', {type: 'hidden', name: 'tx_extensionmanager_tools_extensionmanagerextensionmanager[action]', value: 'saveAndClose'}));
780 }
781 });
782
783 // initialize the repository
784 Repository.initDom();
785
786 ExtensionManager.Update.initializeEvents();
787 ExtensionManager.UploadForm.initializeEvents();
788
789 Tooltip.initialize('#typo3-extension-list [title]', {
790 delay: {
791 show: 500,
792 hide: 100
793 },
794 trigger: 'hover',
795 container: 'body'
796 });
797 });
798
799 if (typeof TYPO3.ExtensionManager === 'undefined') {
800 TYPO3.ExtensionManager = ExtensionManager;
801 }
802
803 return ExtensionManager;
804 });