458b3d1e5166c0de573939a89028b8b3476bc6b2
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / FormEngineFlexForm.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 /**
15 * Module: TYPO3/CMS/Backend/FormEngineFlexForm
16 * Contains all JS functions related to TYPO3 Flexforms
17 * available under the latest jQuery version
18 * can be used by $('myflexform').t3FormEngineFlexFormElement({options});, all .t3-flex-form containers will be called on load
19 *
20 * currently TYPO3.FormEngine.FlexFormElement represents one Flexform element
21 * which can contain one ore more sections
22 */
23 define(['jquery',
24 'TYPO3/CMS/Backend/Modal',
25 'TYPO3/CMS/Backend/FormEngine'
26 ], function ($, Modal) {
27
28 /**
29 *
30 * @param {HTMLElement} el
31 * @param {Object} options
32 * @constructor
33 * @exports TYPO3/CMS/Backend/FormEngineFlexForm
34 */
35 TYPO3.FormEngine.FlexFormElement = function(el, options) {
36 var me = this; // avoid scope issues
37 var opts; // shorthand options notation
38
39 // initialization function; private
40 me.initialize = function() {
41 // store DOM element and jQuery object for later use
42 me.el = el;
43 me.$el = $(el);
44
45 // remove any existing backups
46 var old_me = me.$el.data('TYPO3.FormEngine.FlexFormElement');
47 if (typeof old_me !== 'undefined') {
48 me.$el.removeData('TYPO3.FormEngine.FlexFormElement');
49 }
50
51 // add a reverse reference to the DOM element
52 me.$el.data('TYPO3.FormEngine.FlexFormElement', me);
53
54 if (!options) {
55 options = {};
56 }
57
58 // set some values from existing properties
59 options.allowRestructure = me.$el.data('t3-flex-allow-restructure');
60 options.flexformId = me.$el.attr('id');
61
62 // store options and merge with default options
63 opts = me.options = $.extend({}, TYPO3.FormEngine.FlexFormElement.defaults, options);
64
65 // initialize events
66 me.initializeEvents();
67
68 // generate the preview text if a section is hidden on load
69 me.$el.find(opts.sectionSelector).each(function() {
70 me.generateSectionPreview($(this));
71 });
72
73 return me;
74 };
75
76 /**
77 * init all events related to the flexform. As this method is called multiple times,
78 * some handlers need to be off'ed first to prevent event stacking.
79 */
80 me.initializeEvents = function() {
81 // Toggling all sections on/off by clicking all toggle buttons of each section
82 me.$el.prev(opts.flexFormToggleAllSectionsSelector).off('click').on('click', function() {
83 me.$el.find(opts.sectionToggleButtonSelector).trigger('click');
84 });
85
86 if (opts.allowRestructure) {
87 // create a sortable when dragging on the header of a section
88 me.createSortable();
89
90 // allow delete of a single section
91 me.$el.off('click').on('click', opts.deleteIconSelector, function(evt) {
92 evt.preventDefault();
93
94 var confirmTitle = TYPO3.lang['flexform.section.delete.title'] || 'Are you sure?';
95 var confirmMessage = TYPO3.lang['flexform.section.delete.message'] || 'Are you sure you want to delete this section?';
96 var $confirm = Modal.confirm(confirmTitle, confirmMessage);
97 $confirm.on('confirm.button.cancel', function() {
98 Modal.currentModal.trigger('modal-dismiss');
99 });
100 $confirm.on('confirm.button.ok', function(event) {
101 $(evt.target).closest(opts.sectionSelector).hide().addClass(opts.sectionDeletedClass);
102 me.setActionStatus();
103 TYPO3.FormEngine.Validation.validate();
104 Modal.currentModal.trigger('modal-dismiss');
105 });
106 });
107
108 // allow the toggle open/close of the main selection
109 me.$el.on('click', opts.sectionToggleButtonSelector, function(evt) {
110 evt.preventDefault();
111 var $sectionEl = $(this).closest(opts.sectionSelector);
112 me.toggleSection($sectionEl);
113 }).on('click', opts.sectionToggleButtonSelector + ' .form-irre-header-control', function(evt) {
114 evt.stopPropagation();
115 });
116 }
117
118 return me;
119 };
120
121 // initialize ourself
122 me.initialize();
123 };
124
125 // setting some default values
126 TYPO3.FormEngine.FlexFormElement.defaults = {
127 'deleteIconSelector': '.t3js-delete',
128 'sectionSelector': '.t3js-flex-section',
129 'sectionContentSelector': '.t3js-flex-section-content',
130 'sectionHeaderSelector': '.t3js-flex-section-header',
131 'sectionHeaderPreviewSelector': '.t3js-flex-section-header-preview',
132 'sectionActionInputFieldSelector': '.t3js-flex-control-action',
133 'sectionToggleInputFieldSelector': '.t3js-flex-control-toggle',
134 'sectionToggleIconOpenSelector': '.t3js-flex-control-toggle-icon-open',
135 'sectionToggleIconCloseSelector': '.t3js-flex-control-toggle-icon-close',
136 'sectionToggleButtonSelector': '[data-toggle="formengine-flex"]',
137 'flexFormToggleAllSectionsSelector': '.t3js-form-field-toggle-flexsection',
138 'sectionDeletedClass': 't3js-flex-section-deleted',
139 'allowRestructure': 0, // whether the form can be modified
140 'flexformId': false
141 };
142
143
144 /**
145 * Allow flexform sections to be sorted
146 */
147 TYPO3.FormEngine.FlexFormElement.prototype.createSortable = function() {
148 var me = this;
149
150 require(['jquery-ui/sortable'], function () {
151 me.$el.sortable({
152 containment: 'parent',
153 handle: '.t3js-sortable-handle',
154 axis: 'y',
155 tolerance: 'pointer',
156 stop: function () {
157 me.setActionStatus();
158 }
159 });
160 });
161 };
162
163 // Updates the "action"-status for a section. This is used to move and delete elements.
164 TYPO3.FormEngine.FlexFormElement.prototype.setActionStatus = function() {
165 var me = this;
166
167 // Traverse and find how many sections are open or closed, and save the value accordingly
168 me.$el.find(me.options.sectionActionInputFieldSelector).each(function(index) {
169 var actionValue = ($(this).parents(me.options.sectionSelector).hasClass(me.options.sectionDeletedClass) ? 'DELETE' : index);
170 $(this).val(actionValue);
171 });
172 };
173
174 // Toggling flexform elements on/off
175 // hides the flexform section and shows a preview text
176 // or shows the form parts
177 TYPO3.FormEngine.FlexFormElement.prototype.toggleSection = function($sectionEl) {
178
179 var $contentEl = $sectionEl.find(this.options.sectionContentSelector);
180
181 // display/hide the content of this flexform section
182 $contentEl.toggle();
183
184 if ($contentEl.is(':visible')) {
185 // show the open icon, and set the hidden field for toggling to "hidden"
186 $sectionEl.find(this.options.sectionToggleIconOpenSelector).show();
187 $sectionEl.find(this.options.sectionToggleIconCloseSelector).hide();
188 $sectionEl.find(this.options.sectionToggleInputFieldSelector).val(0);
189 } else {
190 // show the close icon, and set the hidden field for toggling to "1"
191 $sectionEl.find(this.options.sectionToggleIconOpenSelector).hide();
192 $sectionEl.find(this.options.sectionToggleIconCloseSelector).show();
193 $sectionEl.find(this.options.sectionToggleInputFieldSelector).val(1);
194 }
195
196 // see if the preview content needs to be generated
197 this.generateSectionPreview($sectionEl);
198 };
199
200 // function to generate the section preview in the header
201 // if the section content is hidden
202 // called on load and when toggling an icon
203 TYPO3.FormEngine.FlexFormElement.prototype.generateSectionPreview = function($sectionEl) {
204 var $contentEl = $sectionEl.find(this.options.sectionContentSelector);
205 var previewContent = '';
206
207 if (!$contentEl.is(':visible')) {
208 $contentEl.find('input[type=text], textarea').each(function() {
209 var content = $($.parseHTML($(this).val())).text();
210 if (content.length > 50) {
211 content = content.substring(0, 50) + '...';
212 }
213 previewContent += (previewContent ? ' / ' : '') + content;
214 });
215 }
216
217 // create a preview container span element
218 if ($sectionEl.find(this.options.sectionHeaderPreviewSelector).length === 0) {
219 $sectionEl.find(this.options.sectionHeaderSelector).find('.t3js-record-title').parent()
220 .append('<span class="' + this.options.sectionHeaderPreviewSelector.replace(/\./, '') + '"></span>');
221 }
222
223 $sectionEl.find(this.options.sectionHeaderPreviewSelector).text(previewContent);
224 };
225
226 // register the flex functions as jQuery Plugin
227 $.fn.t3FormEngineFlexFormElement = function(options) {
228 // apply all util functions to ourself (for use in templates, etc.)
229 return this.each(function() {
230 (new TYPO3.FormEngine.FlexFormElement(this, options));
231 });
232 };
233
234 // Initialization Code
235 $(function() {
236 // run the flexform functions on all containers (which contains one or more sections)
237 $('.t3-flex-container').t3FormEngineFlexFormElement();
238
239 // Add handler to fetch container data on click on "add container" buttons
240 $('.t3js-flex-container-add').on('click', function(e) {
241 var me = $(this);
242 e.preventDefault();
243 $.ajax({
244 url: TYPO3.settings.ajaxUrls['record_flex_container_add'],
245 type: 'POST',
246 cache: false,
247 data: {
248 vanillaUid: me.data('vanillauid'),
249 databaseRowUid: me.data('databaserowuid'),
250 command: me.data('command'),
251 tableName: me.data('tablename'),
252 fieldName: me.data('fieldname'),
253 recordTypeValue: me.data('recordtypevalue'),
254 dataStructureIdentifier: me.data('datastructureidentifier'),
255 flexFormSheetName: me.data('flexformsheetname'),
256 flexFormFieldName: me.data('flexformfieldname'),
257 flexFormContainerName: me.data('flexformcontainername')
258 },
259 success: function(response) {
260 me.closest('.t3-form-field-container').find('.t3-flex-container').append(response.html);
261 $('.t3-flex-container').t3FormEngineFlexFormElement();
262 if (response.scriptCall && response.scriptCall.length > 0) {
263 $.each(response.scriptCall, function (index, value) {
264 eval(value);
265 });
266 }
267 if (response.stylesheetFiles && response.stylesheetFiles.length > 0) {
268 $.each(response.stylesheetFiles, function (index, stylesheetFile) {
269 var element = document.createElement('link');
270 element['rel'] = 'stylesheet';
271 element['type'] = 'text/css';
272 element['href'] = stylesheetFile;
273 document.head.appendChild(element);
274 });
275 }
276 TYPO3.FormEngine.reinitialize();
277 TYPO3.FormEngine.Validation.initializeInputFields();
278 TYPO3.FormEngine.Validation.validate();
279 }
280 });
281 });
282
283 });
284 });