7d81ced8bf54fdd01f96af3333cfa7ec0e508159
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / jsfunc.inline.js
1 /*<![CDATA[*/
2
3 /*
4 * This file is part of the TYPO3 CMS project.
5 *
6 * It is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License, either version 2
8 * of the License, or any later version.
9 *
10 * For the full copyright and license information, please read the
11 * LICENSE.txt file that was distributed with this source code.
12 *
13 * The TYPO3 project - inspiring people to share!
14 */
15
16 /**
17 * Inline-Relational-Record Editing
18 */
19
20 var inline = {
21 classVisible: 'panel-visible',
22 classCollapsed: 'panel-collapsed',
23 structureSeparator: '-',
24 flexFormSeparator: '---',
25 flexFormSubstitute: ':',
26 noTitleString: (TYPO3.lang ? TYPO3.lang['FormEngine.noRecordTitle'] : '[No title]'),
27 lockedAjaxMethod: {},
28 sourcesLoaded: {},
29 data: {},
30 isLoading: false,
31
32 addToDataArray: function (object) {
33 TYPO3.jQuery.each(object, function (key, value) {
34 if (!inline.data[key]) {
35 inline.data[key] = {};
36 }
37 TYPO3.jQuery.extend(inline.data[key], value);
38 });
39 },
40 setNoTitleString: function (value) {
41 this.noTitleString = value;
42 },
43 toggleEvent: function (event) {
44 var $triggerElement = TYPO3.jQuery(event.target);
45 if ($triggerElement.parents('.t3js-formengine-irre-control').length == 1) {
46 return;
47 }
48
49 var $recordHeader = TYPO3.jQuery(this);
50 inline.expandCollapseRecord(
51 $recordHeader.attr('id').replace(/_header$/, ''),
52 $recordHeader.attr('data-expandSingle'),
53 $recordHeader.attr('data-returnURL')
54 );
55 },
56 expandCollapseRecord: function (objectId, expandSingle, returnURL) {
57 var currentUid = this.parseObjectId('none', objectId, 1);
58 var objectPrefix = this.parseObjectId('full', objectId, 0, 1);
59 var escapedObjectId = this.escapeObjectId(objectId);
60
61 var $currentObject = TYPO3.jQuery('#' + escapedObjectId + '_div');
62 // if content is not loaded yet, get it now from server
63 if (inline.isLoading) {
64 return false;
65 } else if (TYPO3.jQuery('#' + escapedObjectId + '_fields').length > 0 && TYPO3.jQuery('#' + escapedObjectId + '_fields').html().substr(0, 16) === '<!--notloaded-->') {
66 inline.isLoading = true;
67 var headerIdentifier = '#' + escapedObjectId + '_header';
68 // add loading-indicator
69 require(['nprogress'], function (NProgress) {
70 inline.progress = NProgress;
71 inline.progress.configure({parent: headerIdentifier, showSpinner: false});
72 inline.progress.start();
73 });
74 return this.getRecordDetails(objectId, returnURL);
75 }
76
77 var isCollapsed = $currentObject.hasClass(this.classCollapsed);
78 var collapse = [];
79 var expand = [];
80
81 // if only a single record should be visibly for that set of records
82 // and the record clicked itself is no visible, collapse all others
83 if (expandSingle && $currentObject.hasClass(this.classCollapsed)) {
84 collapse = this.collapseAllRecords(objectId, objectPrefix, currentUid);
85 }
86
87 inline.toggleElement(objectId);
88
89 if (this.isNewRecord(objectId)) {
90 this.updateExpandedCollapsedStateLocally(objectId, isCollapsed);
91 } else if (isCollapsed) {
92 expand.push(currentUid);
93 } else if (!isCollapsed) {
94 collapse.push(currentUid);
95 }
96
97 this.setExpandedCollapsedState(objectId, expand.join(','), collapse.join(','));
98
99 return false;
100 },
101
102 toggleElement: function (objectId) {
103 var escapedObjectId = this.escapeObjectId(objectId);
104 var $jQueryObject = TYPO3.jQuery('#' + escapedObjectId + '_div');
105
106 if ($jQueryObject.hasClass(this.classCollapsed)) {
107 $jQueryObject.removeClass(this.classCollapsed).addClass(this.classVisible);
108 $jQueryObject.find('#' + escapedObjectId + '_header .t3-icon-irre-collapsed').removeClass('t3-icon-irre-collapsed').addClass('t3-icon-irre-expanded');
109 } else {
110 $jQueryObject.removeClass(this.classVisible).addClass(this.classCollapsed);
111 $jQueryObject.find('#' + escapedObjectId + '_header .t3-icon-irre-expanded').addClass('t3-icon-irre-collapsed').removeClass('t3-icon-irre-expanded');
112 }
113 },
114 collapseAllRecords: function (objectId, objectPrefix, callingUid) {
115 // get the form field, where all records are stored
116 var objectName = 'data' + this.parseObjectId('parts', objectId, 3, 2, true);
117 var formObj = document.getElementsByName(objectName);
118 var collapse = [];
119
120 if (formObj.length) {
121 // the uid of the calling object (last part in objectId)
122 var recObjectId = '', escapedRecordObjectId;
123
124 var records = this.trimExplode(',', formObj[0].value);
125 for (var i = 0; i < records.length; i++) {
126 recObjectId = objectPrefix + this.structureSeparator + records[i];
127 escapedRecordObjectId = this.escapeObjectId(recObjectId);
128
129 var $recordEntry = TYPO3.jQuery('#' + escapedRecordObjectId + '_div');
130 if (records[i] != callingUid && $recordEntry.hasClass(this.classVisible)) {
131 $recordEntry.removeClass(this.classVisible).addClass(this.classCollapsed);
132 if (this.isNewRecord(recObjectId)) {
133 this.updateExpandedCollapsedStateLocally(recObjectId, 0);
134 } else {
135 collapse.push(records[i]);
136 }
137 }
138 }
139 }
140
141 return collapse;
142 },
143
144 updateExpandedCollapsedStateLocally: function (objectId, value) {
145 var ucName = 'uc[inlineView]' + this.parseObjectId('parts', objectId, 3, 2, true);
146 var ucFormObj = document.getElementsByName(ucName);
147 if (ucFormObj.length) {
148 ucFormObj[0].value = value;
149 }
150 },
151
152 getRecordDetails: function (objectId, returnURL) {
153 var context = this.getContext(this.parseObjectId('full', objectId, 0, 1));
154 inline.makeAjaxCall('details', [objectId, returnURL], true, context);
155 return false;
156 },
157
158 createNewRecord: function (objectId, recordUid) {
159 if (this.isBelowMax(objectId)) {
160 var context = this.getContext(objectId);
161 if (recordUid) {
162 objectId += this.structureSeparator + recordUid;
163 }
164 this.makeAjaxCall('create', [objectId], true, context);
165 } else {
166 var message = TYPO3.lang['FormEngine.maxItemsAllowed'].replace('{0}', this.data.config[objectId].max);
167 var matches = objectId.match(/^(data-\d+-.*?-\d+-.*?)-(.*?)$/);
168 var title = '';
169 if (matches) {
170 title = TYPO3.jQuery('#' + matches[1] + '_records').data('title');
171 }
172 top.TYPO3.Notification.error(title, message, 5);
173 }
174 return false;
175 },
176
177 synchronizeLocalizeRecords: function (objectId, type) {
178 var context = this.getContext(objectId);
179 this.makeAjaxCall('synchronizelocalize', [objectId, type], true, context);
180 return false;
181 },
182
183 setExpandedCollapsedState: function (objectId, expand, collapse) {
184 var context = this.getContext(objectId);
185 this.makeAjaxCall('expandcollapse', [objectId, expand, collapse], false, context);
186 },
187
188 makeAjaxCall: function (method, params, lock, context) {
189 var url = '', urlParams = '', options = {};
190 if (method && params && params.length && this.lockAjaxMethod(method, lock)) {
191 url = TYPO3.settings.ajaxUrls['record_inline_' + method];
192 urlParams = '';
193 for (var i = 0; i < params.length; i++) {
194 urlParams += '&ajax[' + i + ']=' + encodeURIComponent(params[i]);
195 }
196 if (context) {
197 urlParams += '&ajax[context]=' + encodeURIComponent(JSON.stringify(context));
198 }
199 options = {
200 type: 'POST',
201 data: urlParams,
202 success: function (data, message, jqXHR) {
203 inline.isLoading = false;
204 inline.processAjaxResponse(method, jqXHR);
205 if (inline.progress) {
206 inline.progress.done();
207 }
208 },
209 error: function (jqXHR, statusText, errorThrown) {
210 inline.isLoading = false;
211 inline.showAjaxFailure(method, jqXHR);
212 if (inline.progress) {
213 inline.progress.done();
214 }
215 }
216 };
217
218 TYPO3.jQuery.ajax(url, options);
219 }
220 },
221
222 lockAjaxMethod: function (method, lock) {
223 if (!lock || !inline.lockedAjaxMethod[method]) {
224 inline.lockedAjaxMethod[method] = true;
225 return true;
226 } else {
227 return false;
228 }
229 },
230
231 unlockAjaxMethod: function (method) {
232 inline.lockedAjaxMethod[method] = false;
233 },
234
235 processAjaxResponse: function (method, xhr, json) {
236 var addTag = null, processedCount = 0, element = null, errorCatch = [], sourcesWaiting = [];
237 if (!json && xhr) {
238 json = xhr.responseJSON;
239 }
240 // If there are elements the should be added to the <HEAD> tag (e.g. for RTEhtmlarea):
241 if (json.stylesheetFiles) {
242 var head = inline.getDomHeadTag();
243 var headTags = inline.getDomHeadChildren(head);
244 TYPO3.jQuery.each(json.stylesheetFiles, function (index, stylesheetFile) {
245 if (!stylesheetFile) {
246 return;
247 }
248 var element = document.createElement('link');
249 element['rel'] = 'stylesheet';
250 element['type'] = 'text/css';
251 element['href'] = stylesheetFile;
252 head.appendChild(element);
253 processedCount++;
254 delete(json.stylesheetFiles[index]);
255 });
256 }
257 if (processedCount) {
258 window.setTimeout(function () {
259 inline.reprocessAjaxResponse(method, json, sourcesWaiting);
260 }, 40);
261 } else {
262 if (method) {
263 inline.unlockAjaxMethod(method);
264 }
265 if (json.scriptCall && json.scriptCall.length > 0) {
266 TYPO3.jQuery.each(json.scriptCall, function (index, value) {
267 eval(value);
268 });
269 }
270 TYPO3.FormEngine.reinitialize();
271 TYPO3.FormEngine.Validation.initializeInputFields();
272 TYPO3.FormEngine.Validation.validate();
273 }
274 },
275
276 // Check if dynamically added scripts are loaded and restart inline.processAjaxResponse():
277 reprocessAjaxResponse: function (method, json, sourcesWaiting) {
278 var sourcesLoaded = true;
279 if (sourcesWaiting && sourcesWaiting.length) {
280 TYPO3.jQuery.each(sourcesWaiting, function (index, source) {
281 if (!inline.sourcesLoaded[source]) {
282 sourcesLoaded = false;
283 return false;
284 }
285 });
286 }
287 if (sourcesLoaded) {
288 TYPO3.jQuery.each(sourcesWaiting, function (index, source) {
289 delete(inline.sourcesLoaded[source]);
290 });
291 window.setTimeout(function () {
292 inline.processAjaxResponse(method, null, json);
293 }, 80);
294 } else {
295 window.setTimeout(function () {
296 inline.reprocessAjaxResponse(method, json, sourcesWaiting);
297 }, 40);
298 }
299 },
300
301 sourceLoadedHandler: function (element) {
302 if (element && element.src) {
303 inline.sourcesLoaded[element.src] = true;
304 }
305 },
306
307 showAjaxFailure: function (method, xhr) {
308 inline.unlockAjaxMethod(method);
309 top.TYPO3.Notification.error('Error ' + xhr.status, xhr.statusText, 0);
310 },
311
312 // foreign_selector: used by selector box (type='select')
313 importNewRecord: function (objectId) {
314 var $selector = TYPO3.jQuery('#' + this.escapeObjectId(objectId) + '_selector');
315 var selectedIndex = $selector.prop('selectedIndex');
316 if (selectedIndex != -1) {
317 var context = this.getContext(objectId);
318 var selectedValue = $selector.val();
319 if (!this.data.unique || !this.data.unique[objectId]) {
320 $selector.find('option').eq(selectedIndex).prop('selected', false);
321 }
322 this.makeAjaxCall('create', [objectId, selectedValue], true, context);
323 }
324 return false;
325 },
326
327 // foreign_selector: used by element browser (type='group/db')
328 importElement: function (objectId, table, uid, type) {
329 var context = this.getContext(objectId);
330 inline.makeAjaxCall('create', [objectId, uid], true, context);
331 },
332
333 importElementMultiple: function (objectId, table, uidArray, type) {
334 TYPO3.jQuery.each(uidArray, function (index, uid) {
335 inline.delayedImportElement(objectId, table, uid, type);
336 });
337 },
338 delayedImportElement: function (objectId, table, uid, type) {
339 if (inline.lockedAjaxMethod['create'] == true) {
340 window.setTimeout("inline.delayedImportElement('" + objectId + "','" + table + "'," + uid + ", null );",
341 300);
342 } else {
343 inline.importElement(objectId, table, uid, type);
344 }
345 },
346 // Check uniqueness for element browser:
347 checkUniqueElement: function (objectId, table, uid, type) {
348 if (this.checkUniqueUsed(objectId, uid, table)) {
349 return {passed: false, message: 'There is already a relation to the selected element!'};
350 } else {
351 return {passed: true};
352 }
353 },
354
355 // Checks if a record was used and should be unique:
356 checkUniqueUsed: function (objectId, uid, table) {
357 if (!this.data.unique || !this.data.unique[objectId]) {
358 return false;
359 }
360
361 var unique = this.data.unique[objectId];
362 var values = this.getValuesFromHashMap(unique.used);
363
364 // for select: only the uid is stored
365 if (unique['type'] == 'select') {
366 if (values.indexOf(uid) != -1) {
367 return true;
368 }
369
370 // for group/db: table and uid is stored in an assoc array
371 } else if (unique.type == 'groupdb') {
372 for (var i = values.length - 1; i >= 0; i--) {
373 // if the pair table:uid is already used:
374 if (values[i].table == table && values[i].uid == uid) {
375 return true;
376 }
377 }
378 }
379
380 return false;
381 },
382
383 setUniqueElement: function (objectId, table, uid, type, elName) {
384 var recordUid = this.parseFormElementName('none', elName, 1, 1);
385 // alert(objectId+'/'+table+'/'+uid+'/'+recordUid);
386 this.setUnique(objectId, recordUid, uid);
387 },
388
389 getKeysFromHashMap: function (unique) {
390 return TYPO3.jQuery.map(unique, function (value, key) {
391 return key;
392 });
393 },
394
395 getValuesFromHashMap: function (hashMap) {
396 return TYPO3.jQuery.map(hashMap, function (value, key) {
397 return value;
398 });
399 },
400
401 // Remove all select items already used
402 // from a newly retrieved/expanded record
403 removeUsed: function (objectId, recordUid) {
404 if (!this.data.unique || !this.data.unique[objectId]) {
405 return;
406 }
407
408 var unique = this.data.unique[objectId];
409 if (unique.type != 'select') {
410 return;
411 }
412
413 var formName = 'data' + this.parseObjectId('parts', objectId, 3, 1, true);
414 var formObj = document.getElementsByName(formName);
415 var recordObj = document.getElementsByName('data[' + unique.table + '][' + recordUid + '][' + unique.field + ']');
416 var values = this.getValuesFromHashMap(unique.used);
417 if (recordObj.length) {
418 if (recordObj[0].hasOwnProperty('options')) {
419 var selectedValue = recordObj[0].options[recordObj[0].selectedIndex].value;
420 for (var i = 0; i < values.length; i++) {
421 if (values[i] != selectedValue) {
422 var $recordObject = TYPO3.jQuery(recordObj[0]);
423 this.removeSelectOption($recordObject, values[i]);
424 }
425 }
426 }
427 }
428 },
429 // this function is applied to a newly inserted record by AJAX
430 // it removes the used select items, that should be unique
431 setUnique: function (objectId, recordUid, selectedValue) {
432 if (!this.data.unique || !this.data.unique[objectId]) {
433 return;
434 }
435 var $selector = TYPO3.jQuery('#' + this.escapeObjectId(objectId) + '_selector');
436
437 var unique = this.data.unique[objectId];
438 if (unique.type == 'select') {
439 if (!(unique.selector && unique.max == -1)) {
440 var formName = 'data' + this.parseObjectId('parts', objectId, 3, 1, true);
441 var formObj = document.getElementsByName(formName);
442 var recordObj = document.getElementsByName('data[' + unique.table + '][' + recordUid + '][' + unique.field + ']');
443 var values = this.getValuesFromHashMap(unique.used);
444 if ($selector.length) {
445 // remove all items from the new select-item which are already used in other children
446 if (recordObj.length) {
447 var $recordObject = TYPO3.jQuery(recordObj[0]);
448 for (var i = 0; i < values.length; i++) {
449 this.removeSelectOption($recordObject, values[i]);
450 }
451 // set the selected item automatically to the first of the remaining items if no selector is used
452 if (!unique.selector) {
453 selectedValue = recordObj[0].options[0].value;
454 recordObj[0].options[0].selected = true;
455 this.updateUnique(recordObj[0], objectId, formName, recordUid);
456 this.handleChangedField(recordObj[0], objectId + '[' + recordUid + ']');
457 }
458 }
459 for (var i = 0; i < values.length; i++) {
460 this.removeSelectOption($selector, values[i]);
461 }
462 if (typeof this.data.unique[objectId].used.length != 'undefined') {
463 this.data.unique[objectId].used = {};
464 }
465 this.data.unique[objectId].used[recordUid] = selectedValue;
466 }
467 // remove the newly used item from each select-field of the child records
468 if (formObj.length && selectedValue) {
469 var records = this.trimExplode(',', formObj[0].value);
470 for (var i = 0; i < records.length; i++) {
471 recordObj = document.getElementsByName('data[' + unique.table + '][' + records[i] + '][' + unique.field + ']');
472 if (recordObj.length && records[i] != recordUid) {
473 var $recordObject = TYPO3.jQuery(recordObj[0]);
474 this.removeSelectOption($recordObject, selectedValue);
475 }
476 }
477 }
478 }
479 } else if (unique.type == 'groupdb') {
480 // add the new record to the used items:
481 this.data.unique[objectId].used[recordUid] = {'table': unique.elTable, 'uid': selectedValue};
482 }
483
484 // remove used items from a selector-box
485 if (unique.selector == 'select' && selectedValue) {
486 this.removeSelectOption($selector, selectedValue);
487 this.data.unique[objectId]['used'][recordUid] = selectedValue;
488 }
489 },
490
491 domAddNewRecord: function (method, insertObjectId, objectPrefix, htmlData) {
492 var $insertObject = TYPO3.jQuery('#' + this.escapeObjectId(insertObjectId));
493 if (this.isBelowMax(objectPrefix)) {
494 if (method == 'bottom') {
495 $insertObject.append(htmlData);
496 } else if (method == 'after') {
497 $insertObject.after(htmlData);
498 }
499 } else {
500 var message = TYPO3.lang['FormEngine.maxItemsAllowed'].replace('{0}', this.data.config[objectPrefix].max);
501 var title = $insertObject.data('title');
502 top.TYPO3.Notification.error(title, message);
503 }
504 },
505
506 domAddRecordDetails: function (objectId, objectPrefix, expandSingle, htmlData) {
507 var hiddenValue, formObj, valueObj;
508 var escapeObjectId = this.escapeObjectId(objectId);
509 var $objectDiv = TYPO3.jQuery('#' + escapeObjectId + '_fields');
510 if ($objectDiv.length == 0 || $objectDiv.html().substr(0, 16) !== '<!--notloaded-->') {
511 return;
512 }
513
514 var elName = this.parseObjectId('full', objectId, 2, 0, true);
515
516 var $formObj = TYPO3.jQuery('[data-formengine-input-name="' + elName + '[hidden]"]');
517 var $valueObj = TYPO3.jQuery('[name="' + elName + '[hidden]"]');
518
519 // It might be the case that a child record
520 // cannot be hidden at all (no hidden field)
521 if ($formObj.length && $valueObj.length) {
522 hiddenValue = $formObj[0].checked;
523 $formObj.first().remove();
524 $valueObj.first().remove();
525 }
526
527 // Update DOM
528 $objectDiv.html(htmlData);
529
530 formObj = document.querySelector('[data-formengine-input-name="' + elName + '[hidden]"]');
531 valueObj = document.getElementsByName(elName + '[hidden]');
532
533 // Set the hidden value again
534 if (typeof formObj !== 'undefined' && formObj !== null && valueObj.length) {
535 valueObj[0].value = hiddenValue ? 1 : 0;
536 formObj.checked = hiddenValue;
537 }
538
539 // now that the content is loaded, set the expandState
540 this.expandCollapseRecord(objectId, expandSingle);
541 },
542
543 // Get script and link elements from head tag:
544 getDomHeadChildren: function (head) {
545 var headTags = [];
546 TYPO3.jQuery('head script, head link').each(function () {
547 headTags.push(this);
548 });
549 return headTags;
550 },
551
552 getDomHeadTag: function () {
553 if (document && document.head) {
554 return document.head;
555 } else {
556 var $head = TYPO3.jQuery('head');
557 if ($head.length) {
558 return $head.get(0);
559 }
560 }
561 return false;
562 },
563
564 // Search whether elements exist in a given haystack:
565 searchInDomTags: function (haystack, needle) {
566 var result = false;
567 TYPO3.jQuery.each(haystack, function (index, element) {
568 if (element.nodeName.toUpperCase() == needle.name) {
569 var attributesCount = Object.keys(needle.attributes).length;
570 var attributesFound = 0;
571 if (element.getAttribute) {
572 for (var attribute in needle.attributes) {
573 if (needle.attributes.hasOwnProperty(attribute) && element.getAttribute(attribute.key) === attribute.value) {
574 attributesFound++;
575 }
576 }
577 }
578 if (attributesFound === attributesCount) {
579 result = true;
580 return true;
581 }
582 }
583 });
584 return result;
585 },
586
587 changeSorting: function (objectId, direction) {
588 var objectName = 'data' + this.parseObjectId('parts', objectId, 3, 2, true);
589 var objectPrefix = this.parseObjectId('full', objectId, 0, 1);
590 var formObj = document.getElementsByName(objectName);
591
592 if (!formObj.length) {
593 return false;
594 }
595
596 // the uid of the calling object (last part in objectId)
597 var callingUid = this.parseObjectId('none', objectId, 1);
598 var records = this.trimExplode(',', formObj[0].value);
599 var current = records.indexOf(callingUid);
600 var changed = false;
601
602 // move up
603 if (direction > 0 && current > 0) {
604 records[current] = records[current - 1];
605 records[current - 1] = callingUid;
606 changed = true;
607
608 // move down
609 } else if (direction < 0 && current < records.length - 1) {
610 records[current] = records[current + 1];
611 records[current + 1] = callingUid;
612 changed = true;
613 }
614
615 if (changed) {
616 formObj[0].value = records.join(',');
617 var cAdj = direction > 0 ? 1 : 0; // adjustment
618 var objectIdPrefix = '#' + this.escapeObjectId(objectPrefix) + this.structureSeparator;
619 TYPO3.jQuery(objectIdPrefix + records[current - cAdj] + '_div').insertBefore(
620 TYPO3.jQuery(objectIdPrefix + records[current + 1 - cAdj] + '_div')
621 );
622 this.redrawSortingButtons(objectPrefix, records);
623 }
624
625 return false;
626 },
627
628 dragAndDropSorting: function (element) {
629 var objectId = element.getAttribute('id').replace(/_records$/, '');
630 var objectName = 'data' + inline.parseObjectId('parts', objectId, 3, 0, true);
631 var formObj = document.getElementsByName(objectName);
632 var $element = TYPO3.jQuery(element);
633
634 if (!formObj.length) {
635 return;
636 }
637
638 var checked = [];
639 var order = [];
640 $element.find('.sortableHandle').each(function (i, e) {
641 order.push(TYPO3.jQuery(e).data('id').toString());
642 });
643 var records = this.trimExplode(',', formObj[0].value);
644
645 // check if ordered uid is really part of the records
646 // virtually deleted items might still be there but ordering shouldn't saved at all on them
647 for (var i = 0; i < order.length; i++) {
648 if (records.indexOf(order[i]) != -1) {
649 checked.push(order[i]);
650 }
651 }
652
653 formObj[0].value = checked.join(',');
654
655 if (inline.data.config && inline.data.config[objectId]) {
656 var table = inline.data.config[objectId].table;
657 inline.redrawSortingButtons(objectId + inline.structureSeparator + table, checked);
658 }
659 },
660
661 createDragAndDropSorting: function (objectId) {
662 require(['jquery', 'jquery-ui/sortable'], function ($) {
663 var $sortingContainer = $('#' + inline.escapeObjectId(objectId));
664
665 if ($sortingContainer.hasClass('ui-sortable')) {
666 $sortingContainer.sortable('enable');
667 return;
668 }
669
670 $sortingContainer.sortable(
671 {
672 containment: 'parent',
673 handle: '.sortableHandle',
674 zIndex: '4000',
675 axis: 'y',
676 tolerance: 'pointer',
677 stop: function () {
678 inline.dragAndDropSorting($sortingContainer[0]);
679 }
680 }
681 );
682 });
683 },
684
685 destroyDragAndDropSorting: function (objectId) {
686 require(['jquery', 'jquery-ui/sortable'], function ($) {
687 var $sortingContainer = $('#' + inline.escapeObjectId(objectId));
688 if (!$sortingContainer.hasClass('ui-sortable')) {
689 return;
690 }
691 $sortingContainer.sortable('disable');
692 });
693 },
694
695 redrawSortingButtons: function (objectPrefix, records) {
696 var i, $headerObj, sortUp, sortDown;
697
698 // if no records were passed, fetch them from form field
699 if (typeof records == 'undefined') {
700 records = [];
701 var objectName = 'data' + this.parseObjectId('parts', objectPrefix, 3, 1, true);
702 var formObj = document.getElementsByName(objectName);
703 if (formObj.length) {
704 records = this.trimExplode(',', formObj[0].value);
705 }
706 }
707
708 for (i = 0; i < records.length; i++) {
709 if (!records[i].length) {
710 continue;
711 }
712
713 $headerObj = TYPO3.jQuery('#' + this.escapeObjectId(objectPrefix) + this.structureSeparator + records[i] + '_header');
714 sortUp = $headerObj.find('.sortingUp');
715 sortDown = $headerObj.find('.sortingDown');
716
717 if (sortUp) {
718 sortUp.css('visibility', (i == 0 ? 'hidden' : 'visible'));
719 }
720 if (sortDown) {
721 sortDown.css('visibility', (i == records.length - 1 ? 'hidden' : 'visible'));
722 }
723 }
724 },
725
726 memorizeAddRecord: function (objectPrefix, newUid, afterUid, selectedValue) {
727 if (this.isBelowMax(objectPrefix)) {
728 var objectName = 'data' + this.parseObjectId('parts', objectPrefix, 3, 1, true);
729 var formObj = document.getElementsByName(objectName);
730
731 if (formObj.length) {
732 var records = [];
733 if (formObj[0].value.length) {
734 records = this.trimExplode(',', formObj[0].value);
735 }
736
737 if (afterUid) {
738 var newRecords = [];
739 for (var i = 0; i < records.length; i++) {
740 if (records[i].length) {
741 newRecords.push(records[i]);
742 }
743 if (afterUid == records[i]) {
744 newRecords.push(newUid);
745 }
746 }
747 records = newRecords;
748 } else {
749 records.push(newUid);
750 }
751 formObj[0].value = records.join(',');
752 }
753
754 this.redrawSortingButtons(objectPrefix, records);
755
756 if (this.data.unique && this.data.unique[objectPrefix]) {
757 var unique = this.data.unique[objectPrefix];
758 this.setUnique(objectPrefix, newUid, selectedValue);
759 }
760 }
761
762 // if we reached the maximum of possible records after this action, hide the new buttons
763 if (!this.isBelowMax(objectPrefix)) {
764 var objectParent = this.parseObjectId('full', objectPrefix, 0, 1);
765 var md5 = this.getObjectMD5(objectParent);
766 this.hideElementsWithClassName('.inlineNewButton' + (md5 ? '.' + md5 : ''), objectParent);
767 this.hideElementsWithClassName('.inlineNewRelationButton' + (md5 ? '.' + md5 : ''), objectParent);
768 this.hideElementsWithClassName('.inlineNewFileUploadButton' + (md5 ? '.' + md5 : ''), objectParent);
769 this.hideElementsWithClassName('.t3js-online-media-add-btn' + (md5 ? '.' + md5 : ''), objectParent);
770 this.hideElementsWithClassName('.inlineForeignSelector' + (md5 ? '.' + md5 : ''), 't3-form-field-item');
771 }
772
773 if (TBE_EDITOR) {
774 TBE_EDITOR.fieldChanged_fName(objectName, formObj);
775 }
776 },
777
778 memorizeRemoveRecord: function (objectName, removeUid) {
779 var formObj = document.getElementsByName(objectName);
780 if (formObj.length) {
781 var parts = [],
782 indexOfRemoveUid = -1;
783 if (formObj[0].value.length) {
784 parts = this.trimExplode(',', formObj[0].value);
785 indexOfRemoveUid = parts.indexOf(removeUid);
786 if (indexOfRemoveUid !== -1) {
787 delete parts[indexOfRemoveUid];
788 }
789 formObj[0].value = parts.join(',');
790 if (TBE_EDITOR) {
791 TBE_EDITOR.fieldChanged_fName(objectName, formObj);
792 }
793 return parts.length;
794 }
795 }
796 return false;
797 },
798
799 updateUnique: function (srcElement, objectPrefix, formName, recordUid) {
800 if (!this.data.unique || !this.data.unique[objectPrefix]) {
801 return;
802 }
803
804 var unique = this.data.unique[objectPrefix];
805 var oldValue = unique.used[recordUid];
806
807 if (unique.selector == 'select') {
808 var selector = $(objectPrefix + '_selector');
809 this.removeSelectOption(selector, srcElement.value);
810 if (typeof oldValue != 'undefined') {
811 this.readdSelectOption(selector, oldValue, unique);
812 }
813 }
814
815 if (unique.selector && unique.max == -1) {
816 return;
817 }
818
819 var formObj = document.getElementsByName(formName);
820 if (!unique || !formObj.length) {
821 return;
822 }
823
824 var records = this.trimExplode(',', formObj[0].value);
825 var recordObj;
826 for (var i = 0; i < records.length; i++) {
827 recordObj = document.getElementsByName('data[' + unique.table + '][' + records[i] + '][' + unique.field + ']');
828 if (recordObj.length && recordObj[0] != srcElement) {
829 var $recordObject = TYPO3.jQuery(recordObj[0]);
830 this.removeSelectOption($recordObject, srcElement.value);
831 if (typeof oldValue != 'undefined') {
832 this.readdSelectOption($recordObject, oldValue, unique);
833 }
834 }
835 }
836 this.data.unique[objectPrefix].used[recordUid] = srcElement.value;
837 },
838
839 revertUnique: function (objectPrefix, elName, recordUid) {
840 if (!this.data.unique || !this.data.unique[objectPrefix]) {
841 return;
842 }
843
844 var unique = this.data.unique[objectPrefix];
845 var fieldObj = elName ? document.getElementsByName(elName + '[' + unique.field + ']') : null;
846
847 if (unique.type == 'select') {
848 if (!fieldObj || !fieldObj.length) {
849 return;
850 }
851
852 delete(this.data.unique[objectPrefix].used[recordUid]);
853
854 if (unique.selector == 'select') {
855 if (!isNaN(fieldObj[0].value)) {
856 var $selector = TYPO3.jQuery('#' + this.escapeObjectId(objectPrefix) + '_selector');
857 this.readdSelectOption($selector, fieldObj[0].value, unique);
858 }
859 }
860
861 if (unique.selector && unique.max == -1) {
862 return;
863 }
864
865 var formName = 'data' + this.parseObjectId('parts', objectPrefix, 3, 1, true);
866 var formObj = document.getElementsByName(formName);
867 if (!formObj.length) {
868 return;
869 }
870
871 var records = this.trimExplode(',', formObj[0].value);
872 var recordObj;
873 // walk through all inline records on that level and get the select field
874 for (var i = 0; i < records.length; i++) {
875 recordObj = document.getElementsByName('data[' + unique.table + '][' + records[i] + '][' + unique.field + ']');
876 if (recordObj.length) {
877 var $recordObject = TYPO3.jQuery(recordObj[0]);
878 this.readdSelectOption($recordObject, fieldObj[0].value, unique);
879 }
880 }
881 } else if (unique.type == 'groupdb') {
882 // alert(objectPrefix+'/'+recordUid);
883 delete(this.data.unique[objectPrefix].used[recordUid])
884 }
885 },
886
887 enableDisableRecord: function (objectIdentifier, fieldName) {
888 var elName = this.parseObjectId('full', objectIdentifier, 2, 0, true) + '[' + fieldName + ']';
889 var formObj = document.querySelector('[data-formengine-input-name="' + elName + '"]');
890 var valueObj = document.getElementsByName(elName);
891 var escapedObjectIdentifier = this.escapeObjectId(objectIdentifier);
892 var $container = TYPO3.jQuery('#' + escapedObjectIdentifier + '_div');
893 var $icon = $container.find('.t3js-' + escapedObjectIdentifier + '_disabled .t3js-icon');
894
895 // It might be the case that there's no hidden field
896 if (typeof formObj !== 'undefined' && formObj !== null && valueObj.length) {
897 formObj.click();
898 valueObj[0].value = formObj.checked ? 1 : 0;
899 TBE_EDITOR.fieldChanged_fName(elName, elName);
900 }
901
902 if ($icon.length) {
903 require(['TYPO3/CMS/Backend/Icons'], function(Icons) {
904 var hiddenClass = 't3-form-field-container-inline-hidden',
905 isHidden = $container.hasClass(hiddenClass),
906 toggleIcon;
907
908 if (isHidden) {
909 toggleIcon = 'actions-edit-hide';
910 $container.removeClass(hiddenClass);
911 } else {
912 toggleIcon = 'actions-edit-unhide';
913 $container.addClass(hiddenClass);
914 }
915
916 Icons.getIcon(toggleIcon, Icons.sizes.small).done(function(markup) {
917 $icon.replaceWith(markup);
918 });
919 });
920 }
921
922 return false;
923 },
924
925 deleteRecord: function (objectId, options) {
926 var i, j, inlineRecords, records, childObjectId, childTable;
927 var objectPrefix = this.parseObjectId('full', objectId, 0, 1);
928 var elName = this.parseObjectId('full', objectId, 2, 0, true);
929 var shortName = this.parseObjectId('parts', objectId, 2, 0, true);
930 var recordUid = this.parseObjectId('none', objectId, 1);
931 var beforeDeleteIsBelowMax = this.isBelowMax(objectPrefix);
932
933 // revert the unique settings if available
934 this.revertUnique(objectPrefix, elName, recordUid);
935
936 // Remove from TBE_EDITOR (required fields, required range, etc.):
937 if (TBE_EDITOR && TBE_EDITOR.removeElement) {
938 var removeStack = [];
939 // Iterate over all child records:
940 inlineRecords = TYPO3.jQuery('.inlineRecord', '#' + objectId + '_div');
941 // Remove nested child records from TBE_EDITOR required/range checks:
942 for (i = inlineRecords.length - 1; i >= 0; i--) {
943 if (inlineRecords.get(i).value.length) {
944 records = this.trimExplode(',', inlineRecords.get(i).value);
945 childObjectId = this.data.map[inlineRecords.get(i).name];
946 childTable = this.data.config[childObjectId].table;
947 for (j = records.length - 1; j >= 0; j--) {
948 removeStack.push('data[' + childTable + '][' + records[j] + ']');
949 }
950 }
951 }
952 removeStack.push('data' + shortName);
953 TBE_EDITOR.removeElementArray(removeStack);
954 }
955
956 // Mark this container as deleted
957 TYPO3.jQuery('#' + this.escapeObjectId(objectId) + '_div')
958 .addClass('inlineIsDeletedRecord')
959 .addClass('t3js-inline-record-deleted');
960
961 // If the record is new and was never saved before, just remove it from DOM:
962 if (this.isNewRecord(objectId) || options && options.forceDirectRemoval) {
963 this.fadeAndRemove(objectId + '_div');
964 // If the record already exists in storage, mark it to be deleted on clicking the save button:
965 } else {
966 document.getElementsByName('cmd' + shortName + '[delete]')[0].disabled = false;
967 TYPO3.jQuery('#' + this.escapeObjectId(objectId) + '_div').fadeOut(200);
968 }
969
970 var recordCount = this.memorizeRemoveRecord(
971 'data' + this.parseObjectId('parts', objectId, 3, 2, true),
972 recordUid
973 );
974
975 if (recordCount <= 1) {
976 this.destroyDragAndDropSorting(this.parseObjectId('full', objectId, 0, 2) + '_records');
977 }
978 this.redrawSortingButtons(objectPrefix);
979
980 // if the NEW-button was hidden and now we can add again new children, show the button
981 if (!beforeDeleteIsBelowMax && this.isBelowMax(objectPrefix)) {
982 var objectParent = this.parseObjectId('full', objectPrefix, 0, 1);
983 var md5 = this.getObjectMD5(objectParent);
984 this.showElementsWithClassName('.inlineNewButton' + (md5 ? '.' + md5 : ''), objectParent);
985 this.showElementsWithClassName('.inlineNewRelationButton' + (md5 ? '.' + md5 : ''), objectParent);
986 this.showElementsWithClassName('.inlineNewFileUploadButton' + (md5 ? '.' + md5 : ''), objectParent);
987 this.showElementsWithClassName('.t3js-online-media-add-btn' + (md5 ? '.' + md5 : ''), objectParent);
988 this.showElementsWithClassName('.inlineForeignSelector' + (md5 ? '.' + md5 : ''), 't3-form-field-item');
989 }
990 TYPO3.FormEngine.Validation.validate();
991 return false;
992 },
993
994 parsePath: function (path) {
995 var backSlash = path.lastIndexOf('\\');
996 var normalSlash = path.lastIndexOf('/');
997
998 if (backSlash > 0) {
999 path = path.substring(0, backSlash + 1);
1000 } else if (normalSlash > 0) {
1001 path = path.substring(0, normalSlash + 1);
1002 } else {
1003 path = '';
1004 }
1005
1006 return path;
1007 },
1008
1009 parseFormElementName: function (wrap, formElementName, rightCount, skipRight) {
1010 var idParts = this.splitFormElementName(formElementName);
1011
1012 if (!wrap) {
1013 wrap = 'full';
1014 }
1015 if (!skipRight) {
1016 skipRight = 0;
1017 }
1018
1019 var elParts = [];
1020 for (var i = 0; i < skipRight; i++) {
1021 idParts.pop();
1022 }
1023
1024 if (rightCount > 0) {
1025 for (var i = 0; i < rightCount; i++) {
1026 elParts.unshift(idParts.pop());
1027 }
1028 } else {
1029 for (var i = 0; i < -rightCount; i++) {
1030 idParts.shift();
1031 }
1032 elParts = idParts;
1033 }
1034
1035 return this.constructFormElementName(wrap, elParts);
1036 },
1037
1038 splitFormElementName: function (formElementName) {
1039 // remove left and right side "data[...|...]" -> '...|...'
1040 formElementName = formElementName.substr(0, formElementName.lastIndexOf(']')).substr(formElementName.indexOf('[') + 1);
1041 return formElementName.split('][');
1042 },
1043
1044 splitObjectId: function (objectId) {
1045 objectId = objectId.substr(objectId.indexOf(this.structureSeparator) + 1);
1046 objectId = objectId.split(this.flexFormSeparator).join(this.flexFormSubstitute);
1047 return objectId.split(this.structureSeparator);
1048 },
1049
1050 constructFormElementName: function (wrap, parts) {
1051 var elReturn;
1052
1053 if (wrap == 'full') {
1054 elReturn = 'data[' + parts.join('][') + ']';
1055 elReturn = elReturn.split(this.flexFormSubstitute).join('][');
1056 } else if (wrap == 'parts') {
1057 elReturn = '[' + parts.join('][') + ']';
1058 elReturn = elReturn.split(this.flexFormSubstitute).join('][');
1059 } else if (wrap == 'none') {
1060 elReturn = parts.length > 1 ? parts : parts.join('');
1061 }
1062
1063 return elReturn;
1064 },
1065
1066 constructObjectId: function (wrap, parts) {
1067 var elReturn;
1068
1069 if (wrap == 'full') {
1070 elReturn = 'data' + this.structureSeparator + parts.join(this.structureSeparator);
1071 elReturn = elReturn.split(this.flexFormSubstitute).join(this.flexFormSeparator);
1072 } else if (wrap == 'parts') {
1073 elReturn = this.structureSeparator + parts.join(this.structureSeparator);
1074 elReturn = elReturn.split(this.flexFormSubstitute).join(this.flexFormSeparator);
1075 } else if (wrap == 'none') {
1076 elReturn = parts.length > 1 ? parts : parts.join('');
1077 }
1078
1079 return elReturn;
1080 },
1081
1082 parseObjectId: function (wrap, objectId, rightCount, skipRight, returnAsFormElementName) {
1083 var idParts = this.splitObjectId(objectId);
1084
1085 if (!wrap) {
1086 wrap = 'full';
1087 }
1088 if (!skipRight) {
1089 skipRight = 0;
1090 }
1091
1092 var elParts = [];
1093 for (var i = 0; i < skipRight; i++) {
1094 idParts.pop();
1095 }
1096
1097 if (rightCount > 0) {
1098 for (var i = 0; i < rightCount; i++) {
1099 elParts.unshift(idParts.pop());
1100 }
1101 } else {
1102 for (var i = 0; i < -rightCount; i++) {
1103 idParts.shift();
1104 }
1105 elParts = idParts;
1106 }
1107
1108 var elReturn;
1109 if (returnAsFormElementName) {
1110 elReturn = this.constructFormElementName(wrap, elParts);
1111 } else {
1112 elReturn = this.constructObjectId(wrap, elParts);
1113 }
1114
1115 return elReturn;
1116 },
1117
1118 handleChangedField: function (formField, objectId) {
1119 var formObj;
1120 if (typeof formField == 'object') {
1121 formObj = formField;
1122 } else {
1123 formObj = document.getElementsByName(formField);
1124 if (formObj.length) {
1125 formObj = formObj[0];
1126 }
1127 }
1128
1129 if (formObj != undefined) {
1130 var value;
1131 if (formObj.nodeName == 'SELECT') {
1132 value = formObj.options[formObj.selectedIndex].text;
1133 } else {
1134 value = formObj.value;
1135 }
1136 TYPO3.jQuery('#' + this.escapeObjectId(objectId) + '_label').text(value.length ? value : this.noTitleString);
1137 }
1138 return true;
1139 },
1140
1141 arrayAssocCount: function (object) {
1142 var count = 0;
1143 if (typeof object.length != 'undefined') {
1144 count = object.length;
1145 } else {
1146 for (var i in object) {
1147 count++;
1148 }
1149 }
1150 return count;
1151 },
1152
1153 isBelowMax: function (objectPrefix) {
1154 var isBelowMax = true;
1155 var objectName = 'data' + this.parseObjectId('parts', objectPrefix, 3, 1, true);
1156 var formObj = document.getElementsByName(objectName);
1157
1158 if (this.data.config && this.data.config[objectPrefix] && formObj.length) {
1159 var recordCount = formObj[0].value ? this.trimExplode(',', formObj[0].value).length : 0;
1160 if (recordCount >= this.data.config[objectPrefix].max) {
1161 isBelowMax = false;
1162 }
1163 }
1164 if (isBelowMax && this.data.unique && this.data.unique[objectPrefix]) {
1165 var unique = this.data.unique[objectPrefix];
1166 if (this.arrayAssocCount(unique.used) >= unique.max && unique.max >= 0) {
1167 isBelowMax = false;
1168 }
1169 }
1170 return isBelowMax;
1171 },
1172
1173 getOptionsHash: function ($selectObj) {
1174 var optionsHash = {};
1175 $selectObj.find('option').each(function (i, option) {
1176 optionsHash[option.value] = i;
1177 });
1178 return optionsHash;
1179 },
1180
1181 removeSelectOption: function ($selectObj, value) {
1182 var optionsHash = this.getOptionsHash($selectObj);
1183 if (optionsHash[value] != undefined) {
1184 $selectObj.find('option').eq(optionsHash[value]).remove();
1185 }
1186 },
1187
1188 readdSelectOption: function ($selectObj, value, unique) {
1189 if (!$selectObj.length) {
1190 return;
1191 }
1192
1193 var index = null;
1194 var optionsHash = this.getOptionsHash($selectObj);
1195 var possibleValues = this.getKeysFromHashMap(unique.possible);
1196
1197 for (var possibleValue in unique.possible) {
1198 if (possibleValue == value) {
1199 break;
1200 }
1201 if (optionsHash[possibleValue] != undefined) {
1202 index = optionsHash[possibleValue];
1203 }
1204 }
1205
1206 if (index == null) {
1207 index = 0;
1208 } else if (index < $selectObj.find('option').length) {
1209 index++;
1210 }
1211 // recreate the <option> tag
1212 var readdOption = document.createElement('option');
1213 readdOption.text = unique.possible[value];
1214 readdOption.value = value;
1215 // add the <option> at the right position
1216 // I didn't find a possibility to add an option to a predefined position
1217 // with help of an index in jQuery. So we realized it the "old" style
1218 var selectObj = $selectObj.get(0);
1219 selectObj.add(readdOption, document.all ? index : selectObj.options[index]);
1220 },
1221
1222 hideElementsWithClassName: function (selector, parentElement) {
1223 TYPO3.jQuery('#' + parentElement).find(selector).fadeOut(200);
1224 },
1225
1226 showElementsWithClassName: function (selector, parentElement) {
1227 TYPO3.jQuery('#' + parentElement).find(selector).fadeIn(200);
1228 },
1229
1230 // sets the opacity to 0.2 and then fades in to opacity 1
1231 fadeOutFadeIn: function (objectId) {
1232 TYPO3.jQuery('#' + this.escapeObjectId(objectId)).css({opacity: 0.2}).fadeTo(200, 1, 'linear');
1233 },
1234
1235 isNewRecord: function (objectId) {
1236 var $selector = TYPO3.jQuery('#' + this.escapeObjectId(objectId) + '_div');
1237 return $selector.length && $selector.hasClass('inlineIsNewRecord')
1238 ? true
1239 : false;
1240 },
1241
1242 // Find and fix nested of inline and tab levels if a new element was created dynamically (it doesn't know about its nesting):
1243 findContinuedNestedLevel: function (nested, objectId) {
1244 if (this.data.nested && this.data.nested[objectId]) {
1245 // Remove the first element from the new nested stack, it's just a hint:
1246 nested.shift();
1247 nested = this.data.nested[objectId].concat(nested);
1248 return nested;
1249 } else {
1250 return nested;
1251 }
1252 },
1253
1254 getObjectMD5: function (objectPrefix) {
1255 var md5 = false;
1256 if (this.data.config && this.data.config[objectPrefix] && this.data.config[objectPrefix].md5) {
1257 md5 = this.data.config[objectPrefix].md5;
1258 }
1259 return md5
1260 },
1261
1262 fadeAndRemove: function (element) {
1263 TYPO3.jQuery('#' + this.escapeObjectId(element)).fadeOut(200, function () {
1264 TYPO3.jQuery(this).remove();
1265 TYPO3.FormEngine.Validation.validate();
1266 });
1267 },
1268
1269 getContext: function (objectId) {
1270 var result = null;
1271
1272 if (objectId !== '' && typeof this.data.config[objectId] !== 'undefined' && typeof this.data.config[objectId].context !== 'undefined') {
1273 result = this.data.config[objectId].context;
1274 }
1275
1276 return result;
1277 },
1278
1279 /**
1280 * Escapes object identifiers to be used in jQuery.
1281 *
1282 * @param string objectId
1283 * @return string
1284 */
1285 escapeObjectId: function (objectId) {
1286 var escapedObjectId;
1287 escapedObjectId = objectId.replace(/:/g, '\\:');
1288 escapedObjectId = escapedObjectId.replace(/\./g, '\\.');
1289 return escapedObjectId;
1290 },
1291
1292 /**
1293 * Escapes object identifiers to be used as jQuery selector.
1294 *
1295 * @param string objectId
1296 * @return string
1297 * @deprecated since TYPO3 CMS v8, this method will be removed in TYPO3 CMS v9. Use $.escapeSelector() instead, which was added with jQuery 3.0.
1298 */
1299 escapeSelectorObjectId: function (objectId) {
1300 return $.escapeSelector(objectId);
1301 },
1302
1303 /**
1304 * Helper function to get clean trimmed array from comma list
1305 *
1306 * @param {String} delimiter
1307 * @param {String} string
1308 * @returns {Array}
1309 */
1310 trimExplode: function(delimiter, string) {
1311 var result = [];
1312 var items = string.split(delimiter);
1313 for (var i=0; i<items.length; i++) {
1314 var item = items[i].trim();
1315 if (item.length > 0) {
1316 result.push(item);
1317 }
1318 }
1319 return result;
1320 }
1321 };
1322
1323 /*]]>*/
1324 (function ($) {
1325 $(function () {
1326 $(document).on('click', '[data-toggle="formengine-inline"]', function() {
1327 inline.toggleEvent();
1328 });
1329 });
1330 })(TYPO3.jQuery);