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