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