[BUGFIX] Prevent loading jsfunc.inline.js twice
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Private / TypeScript / PageActions.ts
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 import {KeyTypesEnum} from './Enum/KeyTypes';
15 import * as $ from 'jquery';
16 import PersistentStorage = require('./Storage/Persistent');
17 import NewContentElement = require('./Wizard/NewContentElement');
18
19 enum IdentifierEnum {
20   pageTitle = '.t3js-title-inlineedit',
21   hiddenElements = '.t3js-hidden-record',
22   newButton = '.t3js-toggle-new-content-element-wizard'
23 }
24
25 /**
26  * Module: TYPO3/CMS/Backend/PageActions
27  * JavaScript implementations for page actions
28  */
29 class PageActions {
30   private pageId: number = 0;
31   private pageOverlayId: number = 0;
32   private $pageTitle: JQuery = null;
33   private $showHiddenElementsCheckbox: JQuery = null;
34
35   constructor() {
36     $((): void => {
37       this.initializeElements();
38       this.initializeEvents();
39       this.initializeNewContentElementWizard();
40       this.initializePageTitleRenaming();
41     });
42   }
43
44   /**
45    * Set the page id (used in the RequireJS callback)
46    *
47    * @param {number} pageId
48    */
49   public setPageId(pageId: number): void {
50     this.pageId = pageId;
51   }
52
53   /**
54    * Set the overlay id
55    *
56    * @param {number} overlayId
57    */
58   public setLanguageOverlayId(overlayId: number): void {
59     this.pageOverlayId = overlayId;
60   }
61
62   /**
63    * Initialize page title renaming
64    */
65   public initializePageTitleRenaming(): void {
66     if (!$.isReady) {
67       $((): void => {
68         this.initializePageTitleRenaming();
69       });
70       return;
71     }
72     if (this.pageId <= 0) {
73       return;
74     }
75
76     const $editActionLink = $('<a class="hidden" href="#" data-action="edit"><span class="t3-icon fa fa-pencil"></span></a>');
77     $editActionLink.on('click', (e: JQueryEventObject): void => {
78       e.preventDefault();
79       this.editPageTitle();
80     });
81     this.$pageTitle
82       .on('dblclick',  (): void => {
83         this.editPageTitle();
84       })
85       .on('mouseover', (): void => {
86         $editActionLink.removeClass('hidden');
87       })
88       .on('mouseout', (): void => {
89         $editActionLink.addClass('hidden');
90       })
91       .append($editActionLink);
92   }
93
94   /**
95    * Initialize elements
96    */
97   private initializeElements(): void {
98     this.$pageTitle = $(IdentifierEnum.pageTitle + ':first');
99     this.$showHiddenElementsCheckbox = $('#checkTt_content_showHidden');
100   }
101
102   /**
103    * Initialize events
104    */
105   private initializeEvents(): void {
106     this.$showHiddenElementsCheckbox.on('change', this.toggleContentElementVisibility);
107   }
108
109   /**
110    * Toggles the "Show hidden content elements" checkbox
111    */
112   private toggleContentElementVisibility(e: JQueryEventObject): void {
113     const $me = $(e.currentTarget);
114     const $hiddenElements = $(IdentifierEnum.hiddenElements);
115
116     // show a spinner to show activity
117     const $spinner = $('<span />', {class: 'checkbox-spinner fa fa-circle-o-notch fa-spin'});
118     $me.hide().after($spinner);
119
120     if ($me.prop('checked')) {
121       $hiddenElements.slideDown();
122     } else {
123       $hiddenElements.slideUp();
124     }
125
126     PersistentStorage.set('moduleData.web_layout.tt_content_showHidden', $me.prop('checked') ? '1' : '0').done((): void => {
127       $spinner.remove();
128       $me.show();
129     });
130   }
131
132   /**
133    * Changes the h1 to an edit form
134    */
135   private editPageTitle(): void {
136     const $inputFieldWrap = $(
137       '<form>' +
138       '<div class="form-group">' +
139       '<div class="input-group input-group-lg">' +
140       '<input class="form-control">' +
141       '<span class="input-group-btn">' +
142       '<button class="btn btn-default" type="button" data-action="submit"><span class="t3-icon fa fa-floppy-o"></span></button> ' +
143       '</span>' +
144       '<span class="input-group-btn">' +
145       '<button class="btn btn-default" type="button" data-action="cancel"><span class="t3-icon fa fa-times"></span></button> ' +
146       '</span>' +
147       '</div>' +
148       '</div>' +
149       '</form>'
150       ),
151       $inputField = $inputFieldWrap.find('input');
152
153     $inputFieldWrap.find('[data-action=cancel]').on('click', (): void => {
154       $inputFieldWrap.replaceWith(this.$pageTitle);
155       this.initializePageTitleRenaming();
156     });
157
158     $inputFieldWrap.find('[data-action=submit]').on('click', (): void => {
159       const newPageTitle = $.trim($inputField.val());
160       if (newPageTitle !== '' && this.$pageTitle.text() !== newPageTitle) {
161         this.saveChanges($inputField);
162       } else {
163         $inputFieldWrap.find('[data-action=cancel]').trigger('click');
164       }
165     });
166
167     // the form stuff is a wacky workaround to prevent the submission of the docheader form
168     $inputField.parents('form').on('submit', (e: JQueryEventObject): boolean => {
169       e.preventDefault();
170       return false;
171     });
172
173     const $h1 = this.$pageTitle;
174     $h1.children().last().remove();
175     $h1.replaceWith($inputFieldWrap);
176     $inputField.val($h1.text()).focus();
177
178     $inputField.on('keyup', (e: JQueryEventObject): void => {
179       switch (e.which) {
180         case KeyTypesEnum.ENTER:
181           $inputFieldWrap.find('[data-action="submit"]').trigger('click');
182           break;
183         case KeyTypesEnum.ESCAPE:
184           $inputFieldWrap.find('[data-action="cancel"]').trigger('click');
185           break;
186         default:
187       }
188     });
189   }
190
191   /**
192    * Save the changes and reload the page tree
193    *
194    * @param {JQuery} $field
195    */
196   private saveChanges($field: JQuery): void {
197     const $inputFieldWrap = $field.parents('form');
198     $inputFieldWrap.find('button').addClass('disabled');
199     $field.attr('disabled', 'disabled');
200
201     let parameters: {[k: string]: any} = {};
202     let recordUid;
203
204     if (this.pageOverlayId > 0) {
205       recordUid = this.pageOverlayId;
206     } else {
207       recordUid = this.pageId;
208     }
209
210     parameters.data = {};
211     parameters.data.pages = {};
212     parameters.data.pages[recordUid] = {title: $field.val()};
213
214     require(['TYPO3/CMS/Backend/AjaxDataHandler'], (DataHandler: any): void => {
215       DataHandler.process(parameters).done((): void => {
216         $inputFieldWrap.find('[data-action=cancel]').trigger('click');
217         this.$pageTitle.text($field.val());
218         this.initializePageTitleRenaming();
219         top.TYPO3.Backend.NavigationContainer.PageTree.refreshTree();
220       }).fail((): void => {
221         $inputFieldWrap.find('[data-action=cancel]').trigger('click');
222       });
223     });
224   }
225
226   /**
227    * Activate New Content Element Wizard
228    */
229   private initializeNewContentElementWizard(): void {
230     $(IdentifierEnum.newButton).click((e: JQueryEventObject): void => {
231       const $me = $(e.currentTarget);
232       NewContentElement.wizard($me.data('url'), $me.data('title'));
233     });
234   }
235 }
236
237 export = new PageActions();