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