ff2a1174374adfcff3a74ebb41eee5635b4d0e02
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Resources / Public / JavaScript / Backend / FormEditor / StageComponent.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/Form/Backend/FormEditor/StageComponent
16 */
17 define(['jquery',
18 'TYPO3/CMS/Form/Backend/FormEditor/Helper',
19 'TYPO3/CMS/Backend/Icons',
20 'TYPO3/CMS/Form/Backend/Vendor/jquery.mjs.nestedSortable'
21 ], function($, Helper, Icons) {
22 'use strict';
23
24 return (function($, Helper, Icons) {
25
26 /**
27 * @private
28 *
29 * @var object
30 */
31 var _configuration = null;
32
33 /**
34 * @private
35 *
36 * @var object
37 */
38 var _defaultConfiguration = {
39 domElementClassNames: {
40 formElementIsComposit: 't3-form-element-composit',
41 formElementIsTopLevel: 't3-form-element-toplevel',
42 noNesting: 'mjs-nestedSortable-no-nesting',
43 selected: 'selected',
44 sortable: 'sortable',
45 previewViewPreviewElement: 't3-form-element-preview'
46 },
47 domElementDataAttributeNames: {
48 abstractType: 'data-element-abstract-type',
49 noSorting: 'data-no-sorting'
50 },
51 domElementDataAttributeValues: {
52 abstractViewToolbar: 'elementToolbar',
53 abstractViewToolbarNewElement: 'stageElementToolbarNewElement',
54 abstractViewToolbarNewElementSplitButton: 'stageElementToolbarNewElementSplitButton',
55 abstractViewToolbarNewElementSplitButtonAfter: 'stageElementToolbarNewElementSplitButtonAfter',
56 abstractViewToolbarNewElementSplitButtonInside: 'stageElementToolbarNewElementSplitButtonInside',
57 abstractViewToolbarRemoveElement: 'stageElementToolbarRemoveElement',
58 buttonHeaderRedo: 'redoButton',
59 buttonHeaderUndo: 'undoButton',
60 buttonPaginationPrevious: 'buttonPaginationPrevious',
61 buttonPaginationNext: 'buttonPaginationNext',
62 'FormElement-_ElementToolbar': 'FormElement-_ElementToolbar',
63 'FormElement-_UnknownElement': 'FormElement-_UnknownElement',
64 'FormElement-AdvancedPassword': 'FormElement-AdvancedPassword',
65 'FormElement-Checkbox': 'FormElement-Checkbox',
66 'FormElement-ContentElement': 'FormElement-ContentElement',
67 'FormElement-DatePicker': 'FormElement-DatePicker',
68 'FormElement-Fieldset': 'FormElement-Fieldset',
69 'FormElement-GridContainer': 'FormElement-GridContainer',
70 'FormElement-GridRow': 'FormElement-GridRow',
71 'FormElement-FileUpload': 'FormElement-FileUpload',
72 'FormElement-Hidden': 'FormElement-Hidden',
73 'FormElement-ImageUpload': 'FormElement-ImageUpload',
74 'FormElement-MultiCheckbox': 'FormElement-MultiCheckbox',
75 'FormElement-MultiSelect': 'FormElement-MultiSelect',
76 'FormElement-Page': 'FormElement-Page',
77 'FormElement-Password': 'FormElement-Password',
78 'FormElement-RadioButton': 'FormElement-RadioButton',
79 'FormElement-SingleSelect': 'FormElement-SingleSelect',
80 'FormElement-StaticText': 'FormElement-StaticText',
81 'FormElement-SummaryPage': 'FormElement-SummaryPage',
82 'FormElement-Text': 'FormElement-Text',
83 'FormElement-Textarea': 'FormElement-Textarea',
84 formElementIcon: 'elementIcon',
85 iconValidator: 't3-form-icon-validator',
86 multiValueContainer: 'multiValueContainer',
87 paginationTitle: 'paginationTitle',
88 stageHeadline: 'formDefinitionLabel',
89 stagePanel: 'stagePanel',
90 validatorsContainer: 'validatorsContainer',
91 validatorIcon: 'validatorIcon'
92 },
93 isSortable: true
94 };
95
96 /**
97 * @private
98 *
99 * @var object
100 */
101 var _formEditorApp = null;
102
103 /**
104 * @private
105 *
106 * @var object
107 */
108 var _stageDomElement = null;
109
110 /* *************************************************************
111 * Private Methodes
112 * ************************************************************/
113
114 /**
115 * @private
116 *
117 * @return void
118 * @throws 1478268638
119 */
120 function _helperSetup() {
121 assert('function' === $.type(Helper.bootstrap),
122 'The view model helper does not implement the method "bootstrap"',
123 1478268638
124 );
125 Helper.bootstrap(getFormEditorApp());
126 };
127
128 /**
129 * @private
130 *
131 * @return object
132 */
133 function getFormEditorApp() {
134 return _formEditorApp;
135 };
136
137 /**
138 * @public
139 *
140 * @param object
141 * @return object
142 */
143 function getHelper(configuration) {
144 if (getUtility().isUndefinedOrNull(configuration)) {
145 return Helper.setConfiguration(_configuration);
146 }
147 return Helper.setConfiguration(configuration);
148 };
149
150 /**
151 * @private
152 *
153 * @return object
154 */
155 function getUtility() {
156 return getFormEditorApp().getUtility();
157 };
158
159 /**
160 * @private
161 *
162 * @return object
163 */
164 function getViewModel() {
165 return getFormEditorApp().getViewModel();
166 };
167
168 /**
169 * @private
170 *
171 * @param mixed test
172 * @param string message
173 * @param int messageCode
174 * @return void
175 */
176 function assert(test, message, messageCode) {
177 return getFormEditorApp().assert(test, message, messageCode);
178 };
179
180 /**
181 * @private
182 *
183 * @return object
184 */
185 function getRootFormElement() {
186 return getFormEditorApp().getRootFormElement();
187 };
188
189 /**
190 * @private
191 *
192 * @return object
193 */
194 function getCurrentlySelectedFormElement() {
195 return getFormEditorApp().getCurrentlySelectedFormElement();
196 };
197
198 /**
199 * @private
200 *
201 * @return object
202 */
203 function getPublisherSubscriber() {
204 return getFormEditorApp().getPublisherSubscriber();
205 };
206
207 /**
208 * @private
209 *
210 * @param object
211 * @param string
212 * @return mixed
213 */
214 function getFormElementDefinition(formElement, formElementDefinitionKey) {
215 return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey);
216 };
217
218 /**
219 * @private
220 *
221 * @return object
222 * @return string
223 * @return void
224 */
225 function _setTemplateTextContent(domElement, content) {
226 if (getUtility().isNonEmptyString(content)) {
227 $(domElement).text(content);
228 }
229 }
230
231 /**
232 * @private
233 *
234 * @param object
235 * @param object
236 * @return void
237 * @publish view/stage/abstract/render/template/perform
238 */
239 function _renderTemplateDispatcher(formElement, template) {
240 switch (formElement.get('type')) {
241 case 'Checkbox':
242 renderCheckboxTemplate(formElement, template);
243 break;
244 case 'FileUpload':
245 case 'ImageUpload':
246 renderFileUploadTemplates(formElement, template);
247 break;
248 case 'SingleSelect':
249 case 'RadioButton':
250 case 'MultiSelect':
251 case 'MultiCheckbox':
252 renderSelectTemplates(formElement, template);
253 break;
254 case 'Textarea':
255 case 'AdvancedPassword':
256 case 'Password':
257 case 'Text':
258 case 'DatePicker':
259 renderSimpleTemplateWithValidators(formElement, template);
260 break;
261 case 'Fieldset':
262 case 'GridContainer':
263 case 'GridRow':
264 case 'SummaryPage':
265 case 'Page':
266 case 'StaticText':
267 case 'Hidden':
268 case 'ContentElement':
269 renderSimpleTemplate(formElement, template);
270 break;
271 }
272 getPublisherSubscriber().publish('view/stage/abstract/render/template/perform', [formElement, template]);
273 };
274
275 /**
276 * @private
277 *
278 * @param object
279 * @return object
280 * @throws 1478987818
281 */
282 function _renderNestedSortableListItem(formElement) {
283 var childFormElements, childList, listItem, template;
284
285 listItem = $('<li></li>');
286 if (!getFormElementDefinition(formElement, '_isCompositeFormElement')) {
287 listItem.addClass(getHelper().getDomElementClassName('noNesting'));
288 }
289
290 if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) {
291 listItem.addClass(getHelper().getDomElementClassName('formElementIsTopLevel'));
292 }
293 if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
294 listItem.addClass(getHelper().getDomElementClassName('formElementIsComposit'));
295 }
296
297 try {
298 template = getHelper().getTemplate('FormElement-' + formElement.get('type')).clone();
299 } catch(error) {
300 template = getHelper().getTemplate('FormElement-_UnknownElement').clone();
301 assert(
302 template.length,
303 'No template found for element "' + formElement.get('__identifierPath') + '"',
304 1478987818
305 );
306 }
307
308 template = $('<div></div>')
309 .attr(getHelper().getDomElementDataAttribute('elementIdentifier'), formElement.get('__identifierPath'))
310 .append($(template.html()));
311
312 if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
313 template.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isCompositeFormElement');
314 }
315 if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) {
316 template.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isTopLevelFormElement');
317 }
318 listItem.append(template);
319
320 _renderTemplateDispatcher(formElement, template);
321
322 childFormElements = formElement.get('renderables');
323 childList = null;
324 if ('array' === $.type(childFormElements)) {
325 childList = $('<ol></ol>');
326 if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) {
327 childList.addClass(getHelper().getDomElementClassName('sortable'));
328 }
329 for (var i = 0, len = childFormElements.length; i < len; ++i) {
330 childList.append(_renderNestedSortableListItem(childFormElements[i]));
331 }
332 }
333
334 if (childList) {
335 listItem.append(childList);
336 }
337 return listItem;
338 };
339
340 /**
341 * @private
342 *
343 * @return void
344 * @publish view/stage/abstract/dnd/start
345 * @publish view/stage/abstract/dnd/stop
346 * @publish view/stage/abstract/dnd/change
347 * @publish view/stage/abstract/dnd/update
348 */
349 function _addSortableEvents() {
350 $('ol.' + getHelper().getDomElementClassName('sortable'), _stageDomElement).nestedSortable({
351 forcePlaceholderSize: true,
352 handle: 'div' + getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'),
353 helper: 'clone',
354 items: 'li:not(' + getHelper().getDomElementDataAttribute('noSorting', 'bracesWithKey') + ')',
355 opacity: .6,
356 revert: 250,
357 delay: 200,
358 tolerance: 'pointer',
359 toleranceElement: '> div',
360
361 isAllowed: function (placeholder, placeholderParent, currentItem) {
362 var formElementIdentifierPath, formElementTypeDefinition, targetFormElementIdentifierPath, targetFormElementTypeDefinition;
363
364 formElementIdentifierPath = getAbstractViewFormElementIdentifierPathWithinDomElement($(currentItem));
365 targetFormElementIdentifierPath = getAbstractViewFormElementIdentifierPathWithinDomElement($(placeholderParent));
366 if (!targetFormElementIdentifierPath) {
367 targetFormElementIdentifierPath = getFormEditorApp().getCurrentlySelectedPage();
368 }
369
370 formElementTypeDefinition = getFormElementDefinition(formElementIdentifierPath);
371 targetFormElementTypeDefinition = getFormElementDefinition(targetFormElementIdentifierPath);
372
373 if (
374 formElementTypeDefinition['_isGridContainerFormElement']
375 && getFormEditorApp().findEnclosingGridContainerFormElement(targetFormElementIdentifierPath)
376 ) {
377 return false;
378 }
379
380 if (
381 formElementTypeDefinition['_isGridRowFormElement']
382 && !targetFormElementTypeDefinition['_isGridContainerFormElement']
383 ) {
384 return false;
385 }
386
387 if (
388 !formElementTypeDefinition['_isGridContainerFormElement']
389 && !formElementTypeDefinition['_isGridRowFormElement']
390 && targetFormElementTypeDefinition['_isGridContainerFormElement']
391 ) {
392 return false;
393 }
394
395 return true;
396 },
397 start: function(e, o) {
398 getPublisherSubscriber().publish('view/stage/abstract/dnd/start', [$(o.item), $(o.placeholder)]);
399 },
400 stop: function(e, o) {
401 getPublisherSubscriber().publish('view/stage/abstract/dnd/stop', [
402 getAbstractViewFormElementIdentifierPathWithinDomElement($(o.item))
403 ]);
404 },
405 change: function(e, o) {
406 var enclosingCompositeFormElement, parentFormElementIdentifierPath;
407
408 parentFormElementIdentifierPath = getAbstractViewParentFormElementIdentifierPathWithinDomElement($(o.placeholder));
409 if (parentFormElementIdentifierPath) {
410 enclosingCompositeFormElement = getFormEditorApp()
411 .findEnclosingCompositeFormElementWhichIsNotOnTopLevel(parentFormElementIdentifierPath);
412 }
413 getPublisherSubscriber().publish('view/stage/abstract/dnd/change', [
414 $(o.placeholder),
415 parentFormElementIdentifierPath, enclosingCompositeFormElement
416 ]);
417 },
418 update: function(e, o) {
419 var nextFormElementIdentifierPath, movedFormElement, movedFormElementIdentifierPath, parentFormElementIdentifierPath, previousFormElementIdentifierPath;
420
421 movedFormElementIdentifierPath = getAbstractViewFormElementIdentifierPathWithinDomElement($(o.item));
422 previousFormElementIdentifierPath = getAbstractViewSiblingFormElementIdentifierPathWithinDomElement($(o.item), 'prev');
423 nextFormElementIdentifierPath = getAbstractViewSiblingFormElementIdentifierPathWithinDomElement($(o.item), 'next');
424
425 getPublisherSubscriber().publish('view/stage/abstract/dnd/update', [
426 $(o.item),
427 movedFormElementIdentifierPath,
428 previousFormElementIdentifierPath,
429 nextFormElementIdentifierPath
430 ]);
431 }
432 });
433 };
434
435 /* *************************************************************
436 * Public Methodes
437 * ************************************************************/
438
439 /**
440 * @public
441 *
442 * @return object
443 */
444 function getStageDomElement() {
445 return _stageDomElement;
446 };
447
448 /**
449 * @public
450 *
451 * @param object
452 * @return object
453 * @throws 1479037151
454 */
455 function buildTitleByFormElement(formElement) {
456 if (getUtility().isUndefinedOrNull(formElement)) {
457 formElement = getRootFormElement();
458 }
459 assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479037151);
460
461 return $('<span></span>')
462 .text((formElement.get('label') ? formElement.get('label') : formElement.get('identifier')));
463 };
464
465 /**
466 * @public
467 *
468 * @param string title
469 * @return void
470 */
471 function setStageHeadline(title) {
472 if (getUtility().isUndefinedOrNull(title)) {
473 title = buildTitleByFormElement();
474 }
475
476 $(getHelper().getDomElementDataIdentifierSelector('stageHeadline')).html(title);
477 };
478
479 /**
480 * @public
481 *
482 * @return object
483 */
484 function getStagePanelDomElement() {
485 return $(getHelper().getDomElementDataIdentifierSelector('stagePanel'));
486 };
487
488 /**
489 * @public
490 *
491 * @return void
492 */
493 function renderPagination() {
494 var pageCount;
495
496 pageCount = getRootFormElement().get('renderables').length;
497
498 getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonPaginationPrevious')));
499 getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonPaginationNext')));
500
501 if (getFormEditorApp().getCurrentlySelectedPageIndex() === 0) {
502 getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonPaginationPrevious')));
503 }
504
505 if (pageCount === 1 || getFormEditorApp().getCurrentlySelectedPageIndex() === (pageCount - 1)) {
506 getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonPaginationNext')));
507 }
508
509 $(getHelper().getDomElementDataIdentifierSelector('paginationTitle')).text(
510 getFormElementDefinition(getRootFormElement(), 'paginationTitle')
511 .replace('{0}', getFormEditorApp().getCurrentlySelectedPageIndex() + 1)
512 .replace('{1}', pageCount)
513 );
514 };
515
516 /**
517 * @public
518 *
519 * @return void
520 */
521 function renderUndoRedo() {
522 getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo')));
523 getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo')));
524
525 if (getFormEditorApp().getCurrentApplicationStatePosition() + 1 >= getFormEditorApp().getCurrentApplicationStates()) {
526 getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo')));
527 }
528 if (getFormEditorApp().getCurrentApplicationStatePosition() === 0) {
529 getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo')));
530 }
531 };
532
533 /**
534 * @public
535 *
536 * @param object
537 * @return string
538 */
539 function getAllFormElementDomElements() {
540 return $( getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'),
541 _stageDomElement
542 );
543 };
544
545 /* *************************************************************
546 * Abstract stage
547 * ************************************************************/
548
549 /**
550 * @public
551 *
552 * @param int
553 * @return object
554 * @throws 1478721208
555 */
556 function renderFormDefinitionPageAsSortableList(pageIndex) {
557 assert(
558 'number' === $.type(pageIndex),
559 'Invalid parameter "pageIndex"',
560 1478721208
561 );
562
563 return $('<ol></ol>')
564 .append(_renderNestedSortableListItem(getRootFormElement().get('renderables')[pageIndex]));
565 };
566
567 /**
568 * @public
569 *
570 * @param object
571 * @return string
572 */
573 function getAbstractViewParentFormElementWithinDomElement(element) {
574 return $(element)
575 .parent()
576 .closest('li')
577 .find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'))
578 .first();
579 };
580
581 /**
582 * @public
583 *
584 * @param object
585 * @return string
586 */
587 function getAbstractViewParentFormElementIdentifierPathWithinDomElement(element) {
588 return getAbstractViewParentFormElementWithinDomElement(element)
589 .attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
590 };
591
592 /**
593 * @public
594 *
595 * @param object
596 * @return string
597 */
598 function getAbstractViewFormElementWithinDomElement(element) {
599 return $(element)
600 .find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'))
601 .first();
602 };
603
604 /**
605 * @public
606 *
607 * @param object
608 * @return string
609 */
610 function getAbstractViewFormElementIdentifierPathWithinDomElement(element) {
611 return getAbstractViewFormElementWithinDomElement($(element))
612 .attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
613 };
614
615 /**
616 * @private
617 *
618 * @param object
619 * @param string
620 * @return string
621 */
622 function getAbstractViewSiblingFormElementIdentifierPathWithinDomElement(element, position) {
623 var formElementIdentifierPath;
624
625 if (getUtility().isUndefinedOrNull(position)) {
626 position = 'prev';
627 }
628 formElementIdentifierPath = getAbstractViewFormElementIdentifierPathWithinDomElement(element);
629 element = (position === 'prev') ? $(element).prev('li') : $(element).next('li');
630 return element.find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'))
631 .not(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath]))
632 .first()
633 .attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
634 };
635
636 /**
637 * @public
638 *
639 * @param string|object
640 * @return object
641 */
642 function getAbstractViewFormElementDomElement(formElement) {
643 var formElementIdentifierPath;
644
645 if ('string' === $.type(formElement)) {
646 formElementIdentifierPath = formElement;
647 } else {
648 if (getUtility().isUndefinedOrNull(formElement)) {
649 formElementIdentifierPath = getCurrentlySelectedFormElement().get('__identifierPath');
650 } else {
651 formElementIdentifierPath = formElement.get('__identifierPath');
652 }
653 }
654 return $(getHelper()
655 .getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath]), _stageDomElement);
656 };
657
658 /**
659 * @public
660 *
661 * @return void
662 */
663 function removeAllStageToolbars() {
664 $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbar'), _stageDomElement).off().empty().remove();
665 };
666
667 /**
668 * @public
669 *
670 * @param object
671 * @return object
672 * @publish view/insertElements/perform/after
673 * @publish view/insertElements/perform/inside
674 * @throws 1479035778
675 */
676 function createAbstractViewFormElementToolbar(formElement) {
677 var formElementTypeDefinition, template;
678 assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479035778);
679
680 formElementTypeDefinition = getFormElementDefinition(formElement);
681 if (formElementTypeDefinition['_isTopLevelFormElement']) {
682 return $();
683 }
684
685 template = getHelper().getTemplate('FormElement-_ElementToolbar').clone();
686 if (!template.length) {
687 return $();
688 }
689
690 template = $($(template.html()));
691
692 getHelper().getTemplatePropertyDomElement('_type', template).text(formElement.get('type'));
693 getHelper().getTemplatePropertyDomElement('_identifier', template).text(formElement.get('identifier'));
694
695 if (formElementTypeDefinition['_isCompositeFormElement']) {
696 getViewModel().hideComponent($(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElement'), template));
697
698 $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElementSplitButtonAfter'), template).on('click', function(e) {
699 var disableElementTypes, onlyEnableElementTypes;
700
701 disableElementTypes = [];
702 onlyEnableElementTypes = [];
703 if (formElementTypeDefinition['_isGridRowFormElement']) {
704 onlyEnableElementTypes = ['GridRow'];
705 disableElementTypes = [];
706 } else {
707 disableElementTypes = ['GridRow'];
708 }
709
710 getPublisherSubscriber().publish('view/stage/abstract/elementToolbar/button/newElement/clicked', [
711 'view/insertElements/perform/after',
712 {
713 disableElementTypes: disableElementTypes,
714 onlyEnableElementTypes: onlyEnableElementTypes
715 }
716 ]
717 );
718 });
719 $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElementSplitButtonInside'), template).on('click', function(e) {
720 var disableElementTypes, onlyEnableElementTypes;
721
722 disableElementTypes = ['GridRow'];
723 onlyEnableElementTypes = [];
724 if (formElementTypeDefinition['_isGridContainerFormElement']) {
725 onlyEnableElementTypes = ['GridRow'];
726 disableElementTypes = [];
727 } else if (formElementTypeDefinition['_isGridRowFormElement']) {
728 onlyEnableElementTypes = [];
729 disableElementTypes = ['GridContainer', 'GridRow'];
730 }
731
732 getPublisherSubscriber().publish('view/stage/abstract/elementToolbar/button/newElement/clicked', [
733 'view/insertElements/perform/inside',
734 {
735 disableElementTypes: disableElementTypes,
736 onlyEnableElementTypes: onlyEnableElementTypes
737 }
738 ]
739 );
740 });
741 } else {
742 getViewModel().hideComponent($(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElementSplitButton'), template));
743
744 $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElement'), template).on('click', function(e) {
745 var disableElementTypes, onlyEnableElementTypes;
746
747 disableElementTypes = [];
748 onlyEnableElementTypes = [];
749 if (getFormEditorApp().findEnclosingGridContainerFormElement(formElement)) {
750 disableElementTypes = ['GridContainer', 'GridRow'];
751 } else {
752 disableElementTypes = ['GridRow'];
753 }
754
755 getPublisherSubscriber().publish(
756 'view/stage/abstract/elementToolbar/button/newElement/clicked', [
757 'view/insertElements/perform/after',
758 {
759 disableElementTypes: disableElementTypes
760 }
761 ]
762 );
763 });
764 }
765
766 $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarRemoveElement'), template).on('click', function(e) {
767 getViewModel().showRemoveFormElementModal();
768 });
769
770 return template;
771 };
772
773 /**
774 * @public
775 *
776 * @param object
777 * @param object
778 * @param bool
779 * @return void
780 */
781 function createAndAddAbstractViewFormElementToolbar(selectedFormElementDomElement, formElement, useFadeEffect) {
782 var toolbar;
783 if (getUtility().isUndefinedOrNull(formElement)) {
784 formElement = getCurrentlySelectedFormElement();
785 }
786
787 if (useFadeEffect) {
788 createAbstractViewFormElementToolbar(formElement).fadeOut(0, function() {
789 selectedFormElementDomElement.prepend($(this));
790 $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbar'), selectedFormElementDomElement).fadeIn('fast');
791 });
792 } else {
793 selectedFormElementDomElement.prepend(createAbstractViewFormElementToolbar(formElement));
794 }
795
796 };
797
798 /**
799 * @public
800 *
801 * @param int
802 * @param function
803 * @return void
804 * @publish view/stage/dnd/stop
805 * @publish view/stage/element/clicked
806 * @throws 1478169511
807 */
808 function renderAbstractStageArea(pageIndex, callback) {
809 if (getUtility().isUndefinedOrNull(pageIndex)) {
810 pageIndex = getFormEditorApp().getCurrentlySelectedPageIndex();
811 }
812 _stageDomElement.off().empty().append(renderFormDefinitionPageAsSortableList(pageIndex));
813
814 _stageDomElement.on("click", function(e) {
815 var formElementIdentifierPath;
816
817 formElementIdentifierPath = $(e.target)
818 .closest(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'))
819 .attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
820 if (
821 getUtility().isUndefinedOrNull(formElementIdentifierPath)
822 || !getUtility().isNonEmptyString(formElementIdentifierPath)
823 ) {
824 return;
825 }
826
827 getPublisherSubscriber().publish('view/stage/element/clicked', [formElementIdentifierPath]);
828 });
829
830 if (_configuration['isSortable']) {
831 _addSortableEvents();
832 }
833
834 if ('function' === $.type(callback)) {
835 callback();
836 }
837 };
838
839
840 /* *************************************************************
841 * Preview stage
842 * ************************************************************/
843
844 /**
845 * @public
846 *
847 * @param string html
848 * @return void
849 * @throws 1475424409
850 */
851 function renderPreviewStageArea(html) {
852 assert(getUtility().isNonEmptyString(html), 'Invalid parameter "html"', 1475424409);
853
854 _stageDomElement.off().empty().html(html);
855
856 $(':input', _stageDomElement).prop('disabled', 'disabled').on('click dblclick select focus keydown keypress keyup mousedown mouseup', function(e) {
857 return e.preventDefault();
858 });
859
860 $('form', _stageDomElement).submit(function(e) {
861 return e.preventDefault();
862 });
863
864 getAllFormElementDomElements().each(function(i, element) {
865 var formElement, metaLabel;
866
867 formElement = getFormEditorApp()
868 .getFormElementByIdentifierPath($(this).data('elementIdentifierPath'));
869
870 if (
871 !getFormElementDefinition(formElement, '_isTopLevelFormElement')
872 && getFormElementDefinition(formElement, '_isCompositeFormElement')
873 && !getFormElementDefinition(formElement, '_isGridContainerFormElement')
874 ) {
875 $(this).tooltip({
876 title: 'identifier: ' + formElement.get('identifier') + ' (type: ' + formElement.get('type') + ')',
877 placement : 'rigth'
878 });
879 } else if (
880 !getFormElementDefinition(formElement, '_isTopLevelFormElement')
881 && !getFormElementDefinition(formElement, '_isCompositeFormElement')
882 ) {
883 $(this).tooltip({
884 title: 'identifier: ' + formElement.get('identifier') + ' (type: ' + formElement.get('type') + ')',
885 placement : 'left'
886 });
887 }
888
889 if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) {
890 $(this).addClass(getHelper().getDomElementClassName('formElementIsTopLevel'));
891 }
892 if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
893 $(this).addClass(getHelper().getDomElementClassName('formElementIsComposit'));
894 }
895 });
896
897 };
898
899 /* *************************************************************
900 * Template rendering
901 * ************************************************************/
902
903 /**
904 * @public
905 *
906 * @param object
907 * @param template
908 * @param function
909 * @return void
910 */
911 function eachTemplateProperty(formElement, template, callback) {
912 $(getHelper().getDomElementDataAttribute('templateProperty', 'bracesWithKey'), template).each(function(i, element) {
913 var propertyPath, propertyValue;
914
915 propertyPath = $(element).attr(getHelper().getDomElementDataAttribute('templateProperty'));
916 propertyValue = formElement.get(propertyPath);
917
918 if ('function' === $.type(callback)) {
919 callback(propertyPath, propertyValue, element);
920 }
921 });
922 };
923
924 /**
925 * @private
926 *
927 * @return object
928 * @return object
929 * @return void
930 */
931 function renderCheckboxTemplate(formElement, template) {
932 renderSimpleTemplateWithValidators(formElement, template);
933
934 eachTemplateProperty(formElement, template, function(propertyPath, propertyValue, domElement) {
935 if (
936 ('boolean' === $.type(propertyValue) && propertyValue)
937 || propertyValue === 'true'
938 || propertyValue === 1
939 || propertyValue === "1"
940 ) {
941 $(domElement).addClass(getHelper().getDomElementClassName('noNesting'));
942 }
943 });
944 };
945
946 /**
947 * @public
948 *
949 * @return object
950 * @return object
951 * @return void
952 * @throws 1479035696
953 */
954 function renderSimpleTemplate(formElement, template) {
955 assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479035696);
956
957 eachTemplateProperty(formElement, template, function(propertyPath, propertyValue, domElement) {
958 _setTemplateTextContent(domElement, propertyValue);
959 });
960
961 Icons.getIcon(
962 getFormElementDefinition(formElement, 'iconIdentifier'),
963 Icons.sizes.small,
964 null,
965 Icons.states.default,
966 Icons.markupIdentifiers.inline
967 ).done(function(icon) {
968 $(getHelper().getDomElementDataIdentifierSelector('formElementIcon'), template)
969 .append($(icon).addClass(getHelper().getDomElementClassName('icon')));
970 });
971
972 getHelper()
973 .getTemplatePropertyDomElement('_type', template)
974 .append(formElement.get('type'));
975 getHelper()
976 .getTemplatePropertyDomElement('_identifier', template)
977 .append(formElement.get('identifier'));
978 };
979
980 /**
981 * @public
982 *
983 * @return object
984 * @return object
985 * @return void
986 * @throws 1479035674
987 */
988 function renderSimpleTemplateWithValidators(formElement, template) {
989 var validators, validatorsCountWithoutRequired, validatorsTemplateContent;
990 assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479035674);
991
992 renderSimpleTemplate(formElement, template);
993
994 validatorsTemplateContent = $(
995 getHelper().getDomElementDataIdentifierSelector('validatorsContainer'),
996 $(template)
997 ).clone();
998
999 $(getHelper().getDomElementDataIdentifierSelector('validatorsContainer'), $(template)).empty();
1000 validators = formElement.get('validators');
1001
1002 if ('array' === $.type(validators)) {
1003 validatorsCountWithoutRequired = 0;
1004 if (validators.length > 0) {
1005 for (var i = 0, len = validators.length; i < len; ++i) {
1006 var collectionElementConfiguration, rowTemplate;
1007
1008 if ('NotEmpty' === validators[i]['identifier']) {
1009 getHelper()
1010 .getTemplatePropertyDomElement('_required', template)
1011 .text('*');
1012 continue;
1013 }
1014 validatorsCountWithoutRequired++;
1015
1016 collectionElementConfiguration = getFormEditorApp()
1017 .getFormEditorDefinition('validators', validators[i]['identifier']);
1018 rowTemplate = $($(validatorsTemplateContent).clone());
1019
1020 getHelper()
1021 .getTemplatePropertyDomElement('_label', rowTemplate)
1022 .append(collectionElementConfiguration['label']);
1023 $(getHelper().getDomElementDataIdentifierSelector('validatorsContainer'), $(template))
1024 .append(rowTemplate.html());
1025 }
1026
1027 if (validatorsCountWithoutRequired > 0) {
1028 Icons.getIcon(
1029 getHelper().getDomElementDataAttributeValue('iconValidator'),
1030 Icons.sizes.small,
1031 null,
1032 Icons.states.default,
1033 Icons.markupIdentifiers.inline
1034 ).done(function(icon) {
1035 $(getHelper().getDomElementDataIdentifierSelector('validatorIcon'), $(template))
1036 .append($(icon).addClass(getHelper().getDomElementClassName('icon')));
1037 });
1038 }
1039 }
1040 }
1041 };
1042
1043 /**
1044 * @public
1045 *
1046 * @return object
1047 * @return object
1048 * @return void
1049 */
1050 function renderSelectTemplates(formElement, template) {
1051 var appendMultiValue, defaultValue, multiValueTemplateContent, propertyPath, propertyValue;
1052
1053 multiValueTemplateContent = $(
1054 getHelper().getDomElementDataIdentifierSelector('multiValueContainer'),
1055 $(template)
1056 ).clone();
1057 $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template)).empty();
1058
1059 renderSimpleTemplateWithValidators(formElement, template);
1060
1061 propertyPath = $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template))
1062 .attr(getHelper().getDomElementDataAttribute('templateProperty'));
1063
1064 propertyValue = formElement.get(propertyPath);
1065
1066 appendMultiValue = function(label, value, defaultValue) {
1067 var isPreselected, rowTemplate;
1068
1069 isPreselected = false;
1070 rowTemplate = $($(multiValueTemplateContent).clone());
1071
1072 for (var defaultValueKey in defaultValue) {
1073 if (!defaultValue.hasOwnProperty(defaultValueKey)) {
1074 continue;
1075 }
1076 if (defaultValue[defaultValueKey] === value) {
1077 isPreselected = true;
1078 break;
1079 }
1080 }
1081
1082 getHelper().getTemplatePropertyDomElement('_label', rowTemplate).append(label);
1083
1084 if (isPreselected) {
1085 getHelper().getTemplatePropertyDomElement('_label', rowTemplate).addClass(
1086 getHelper().getDomElementClassName('selected')
1087 );
1088 }
1089
1090 $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template))
1091 .append(rowTemplate.html());
1092 };
1093
1094 defaultValue = formElement.get('defaultValue');
1095
1096 if (getFormEditorApp().getUtility().isUndefinedOrNull(defaultValue)) {
1097 defaultValue = {};
1098 } else if ('string' === $.type(defaultValue)) {
1099 defaultValue = {0: defaultValue};
1100 }
1101
1102 if ('object' === $.type(propertyValue)) {
1103 for (var propertyValueKey in propertyValue) {
1104 if (!propertyValue.hasOwnProperty(propertyValueKey)) {
1105 continue;
1106 }
1107 appendMultiValue(propertyValue[propertyValueKey], propertyValueKey, defaultValue);
1108 }
1109 } else if ('array' === $.type(propertyValue)) {
1110 for (var propertyValueKey in propertyValue) {
1111 if (!propertyValue.hasOwnProperty(propertyValueKey)) {
1112 continue;
1113 }
1114 if (getUtility().isUndefinedOrNull(propertyValue[propertyValueKey]['_label'])) {
1115 appendMultiValue(propertyValue[propertyValueKey], propertyValueKey, defaultValue);
1116 } else {
1117 appendMultiValue(propertyValue[propertyValueKey]['_label'], propertyValue[propertyValueKey]['_value'], defaultValue);
1118 }
1119 }
1120 }
1121 };
1122
1123 /**
1124 * @public
1125 *
1126 * @return object
1127 * @return object
1128 * @return void
1129 */
1130 function renderFileUploadTemplates(formElement, template) {
1131 var appendMultiValue, multiValueTemplateContent, propertyPath, propertyValue;
1132
1133 multiValueTemplateContent = $(
1134 getHelper().getDomElementDataIdentifierSelector('multiValueContainer'),
1135 $(template)
1136 ).clone();
1137 $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template)).empty();
1138
1139 renderSimpleTemplateWithValidators(formElement, template);
1140
1141 propertyPath = $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template))
1142 .attr(getHelper().getDomElementDataAttribute('templateProperty'));
1143 propertyValue = formElement.get(propertyPath);
1144
1145 appendMultiValue = function(value) {
1146 var rowTemplate;
1147
1148 rowTemplate = $($(multiValueTemplateContent).clone());
1149
1150 getHelper().getTemplatePropertyDomElement('_value', rowTemplate).append(value);
1151 $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template))
1152 .append(rowTemplate.html());
1153 };
1154
1155 if ('object' === $.type(propertyValue)) {
1156 for (var propertyValueKey in propertyValue) {
1157 if (!propertyValue.hasOwnProperty(propertyValueKey)) {
1158 continue;
1159 }
1160 appendMultiValue(propertyValue[propertyValueKey]);
1161 }
1162 } else if ('array' === $.type(propertyValue)) {
1163 for (var i = 0, len = propertyValue.length; i < len; ++i) {
1164 appendMultiValue(propertyValue[i]);
1165 }
1166 }
1167 };
1168
1169 /**
1170 * @public
1171 *
1172 * @param object
1173 * @param object
1174 * @param object
1175 * @return this
1176 * @throws 1478992119
1177 */
1178 function bootstrap(formEditorApp, appendToDomElement, configuration) {
1179 _formEditorApp = formEditorApp;
1180 assert('object' === $.type(appendToDomElement), 'Invalid parameter "appendToDomElement"', 1478992119);
1181
1182 _stageDomElement = $(appendToDomElement);
1183 _configuration = $.extend(true, _defaultConfiguration, configuration || {});
1184 _helperSetup();
1185 return this;
1186 };
1187
1188 /**
1189 * Publish the public methods.
1190 * Implements the "Revealing Module Pattern".
1191 */
1192 return {
1193 bootstrap: bootstrap,
1194 buildTitleByFormElement: buildTitleByFormElement,
1195 createAndAddAbstractViewFormElementToolbar: createAndAddAbstractViewFormElementToolbar,
1196 createAbstractViewFormElementToolbar: createAbstractViewFormElementToolbar,
1197 eachTemplateProperty: eachTemplateProperty,
1198 getAbstractViewFormElementDomElement: getAbstractViewFormElementDomElement,
1199 getAbstractViewFormElementWithinDomElement: getAbstractViewFormElementWithinDomElement,
1200 getAbstractViewFormElementIdentifierPathWithinDomElement: getAbstractViewFormElementIdentifierPathWithinDomElement,
1201 getAbstractViewParentFormElementWithinDomElement: getAbstractViewParentFormElementWithinDomElement,
1202 getAbstractViewParentFormElementIdentifierPathWithinDomElement: getAbstractViewParentFormElementIdentifierPathWithinDomElement,
1203 getAbstractViewSiblingFormElementIdentifierPathWithinDomElement: getAbstractViewSiblingFormElementIdentifierPathWithinDomElement,
1204 getAllFormElementDomElements: getAllFormElementDomElements,
1205 getStageDomElement: getStageDomElement,
1206 getStagePanelDomElement: getStagePanelDomElement,
1207 removeAllStageToolbars: removeAllStageToolbars,
1208 renderAbstractStageArea: renderAbstractStageArea,
1209 renderCheckboxTemplate: renderCheckboxTemplate,
1210 renderFileUploadTemplates: renderFileUploadTemplates,
1211 renderFormDefinitionPageAsSortableList: renderFormDefinitionPageAsSortableList,
1212 renderPagination: renderPagination,
1213 renderPreviewStageArea: renderPreviewStageArea,
1214 renderSelectTemplates: renderSelectTemplates,
1215 renderSimpleTemplate: renderSimpleTemplate,
1216 renderSimpleTemplateWithValidators: renderSimpleTemplateWithValidators,
1217 renderUndoRedo: renderUndoRedo,
1218 setStageHeadline: setStageHeadline
1219 };
1220 })($, Helper, Icons);
1221 });