73f5165bab7521a0d4cec56131b68f542597c6a3
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Resources / Public / JavaScript / Backend / FormEditor / Core.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/Core
16 */
17 define(['jquery'], function($) {
18 'use strict';
19
20 return (function($) {
21
22 /**
23 * @private
24 *
25 * @var object
26 */
27 var _dataBackendEndpoints = {};
28
29 /**
30 * @private
31 *
32 * @var string
33 */
34 var _dataBackendPrototypeName = null;
35
36 /**
37 * @private
38 *
39 * @var string
40 */
41 var _dataBackendPersistenceIdentifier = null;
42
43 /**
44 * @private
45 *
46 * @var object
47 */
48 var _publisherSubscriberTopics = {};
49
50 /**
51 * @private
52 *
53 * @var int
54 */
55 var _publisherSubscriberUid = -1;
56
57 /**
58 * @private
59 *
60 * @var object
61 */
62 var _repositoryFormEditorDefinitions = {};
63
64 /**
65 * @private
66 *
67 * @var object
68 */
69 var _runningAjaxRequests = [];
70
71 /**
72 * @private
73 *
74 * @var object
75 */
76 var _propertyValidationServiceValidators = {};
77
78 /**
79 * @private
80 *
81 * @var int
82 */
83 var _applicationStateStackSize = 10;
84
85 /**
86 * @private
87 *
88 * @var int
89 */
90 var _applicationStateStackPointer = 0;
91
92 /**
93 * @private
94 *
95 * @var object
96 */
97 var _applicationStateStack = [];
98
99 /**
100 * @public
101 *
102 * @return object
103 */
104 function utility() {
105
106 /**
107 * @public
108 *
109 * @param mixed test
110 * @param string message
111 * @param int messageCode
112 * @return void
113 */
114 function assert(test, message, messageCode) {
115 if ('function' === $.type(test)) {
116 test = (test() !== false);
117 }
118 if (!test) {
119 message = message || "Assertion failed";
120 if (messageCode) {
121 message = message + ' (' + messageCode + ')';
122 }
123 if ('undefined' !== typeof Error) {
124 throw new Error(message);
125 }
126 throw message;
127 }
128 };
129
130 /**
131 * @public
132 *
133 * @param mixed value
134 * @return bool
135 */
136 function isUndefinedOrNull(value) {
137 return ('undefined' === $.type(value) || 'null' === $.type(value));
138 };
139
140 /**
141 * @public
142 *
143 * @param mixed value
144 * @return bool
145 */
146 function isNonEmptyString(value) {
147 return ('string' === $.type(value) && value.length > 0);
148 };
149
150 /**
151 * @public
152 *
153 * @param string propertyPath
154 * @param string collectionElementIdentifier
155 * @param string collectionName
156 * @param object formElement
157 * @param boolean allowEmptyReturnValue
158 * @return string
159 * @throws 1475412569
160 * @throws 1475412570
161 * @throws 1475415988
162 * @throws 1475663210
163 */
164 function buildPropertyPath(propertyPath, collectionElementIdentifier, collectionName, formElement, allowEmptyReturnValue) {
165 var newPropertyPath = '';
166
167 allowEmptyReturnValue = !!allowEmptyReturnValue;
168 if (isNonEmptyString(collectionElementIdentifier) || isNonEmptyString(collectionName)) {
169 assert(isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475412569);
170 assert(isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475412570);
171 newPropertyPath = collectionName + '.' + repository().getIndexFromPropertyCollectionElementByIdentifier(collectionElementIdentifier, collectionName, formElement);
172 } else {
173 newPropertyPath = '';
174 }
175
176 if (!isUndefinedOrNull(propertyPath)) {
177 assert(isNonEmptyString(propertyPath), 'Invalid parameter "propertyPath"', 1475415988);
178 if (isNonEmptyString(newPropertyPath)) {
179 newPropertyPath = newPropertyPath + '.' + propertyPath;
180 } else {
181 newPropertyPath = propertyPath;
182 }
183 }
184
185 if (!allowEmptyReturnValue) {
186 assert(isNonEmptyString(newPropertyPath), 'The property path could not be resolved', 1475663210);
187 }
188 return newPropertyPath;
189 };
190
191 /**
192 * @public
193 *
194 * @param object formElement
195 * @return object
196 * @throws 1475377782
197 */
198 function convertToSimpleObject(formElement) {
199 var childFormElements, simpleObject, objectData;
200 assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475377782);
201
202 simpleObject = {};
203 objectData = ('function' === $.type(formElement.getObjectData)) ? formElement.getObjectData() : formElement;
204 childFormElements = objectData['renderables'];
205 delete objectData['renderables'];
206
207 for (var key in objectData) {
208 if (!objectData.hasOwnProperty(key)) {
209 continue;
210 }
211 var value = objectData[key];
212 if (key.match(/^__/)) {
213 continue;
214 }
215
216 if ('object' === $.type(value)) {
217 simpleObject[key] = convertToSimpleObject(value);
218 } else if ('function' !== $.type(value) && 'undefined' !== $.type(value)) {
219 simpleObject[key] = value;
220 }
221 }
222
223 if ('array' === $.type(childFormElements)) {
224 simpleObject['renderables'] = [];
225 for (var i = 0, len = childFormElements.length; i < len; ++i) {
226 simpleObject['renderables'].push(convertToSimpleObject(childFormElements[i]));
227 }
228 }
229
230 return simpleObject;
231 };
232
233 /**
234 * Publish the public methods.
235 */
236 return {
237 assert: assert,
238 convertToSimpleObject: convertToSimpleObject,
239 isNonEmptyString: isNonEmptyString,
240 isUndefinedOrNull: isUndefinedOrNull,
241 buildPropertyPath: buildPropertyPath
242 };
243 };
244
245 /**
246 * @public
247 *
248 * @return object
249 */
250 function propertyValidationService() {
251
252 /**
253 * @public
254 *
255 * @param object formElement
256 * @param object validators
257 * @param string propertyPath
258 * @param string collectionElementIdentifier
259 * @param string collectionName
260 * @param object configuration
261 * @return void
262 * @throws 1475661025
263 * @throws 1475661026
264 * @throws 1479238074
265 */
266 function addValidatorIdentifiersToFormElementProperty(formElement, validators, propertyPath, collectionElementIdentifier, collectionName, configuration) {
267 var formElementIdentifierPath, propertyPath, propertyValidationServiceRegisteredValidators;
268 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475661025);
269 utility().assert('array' === $.type(validators), 'Invalid parameter "validators"', 1475661026);
270 utility().assert('array' === $.type(validators), 'Invalid parameter "validators"', 1479238074);
271
272 formElementIdentifierPath = formElement.get('__identifierPath');
273 propertyPath = utility().buildPropertyPath(propertyPath, collectionElementIdentifier, collectionName, formElement);
274
275 propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators');
276 if (utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[formElementIdentifierPath])) {
277 propertyValidationServiceRegisteredValidators[formElementIdentifierPath] = {};
278 }
279 if (utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath])) {
280 propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath] = {
281 validators: [],
282 configuration: configuration
283 };
284 }
285 for (var i = 0, len = validators.length; i < len; ++i) {
286 if (propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'].indexOf(validators[i]) === -1) {
287 propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'].push(validators[i]);
288 }
289 }
290 getApplicationStateStack().setCurrentState('propertyValidationServiceRegisteredValidators', propertyValidationServiceRegisteredValidators);
291 };
292
293 /**
294 * @public
295 *
296 * @param object formElement
297 * @param string propertyPath
298 * @return void
299 * @throws 1475700618
300 * @throws 1475706896
301 */
302 function removeValidatorIdentifiersFromFormElementProperty(formElement, propertyPath) {
303 var formElementIdentifierPath, propertyValidationServiceRegisteredValidators, registeredValidators;
304 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475700618);
305 utility().assert(utility().isNonEmptyString(propertyPath), 'Invalid parameter "propertyPath"', 1475706896);
306
307 formElementIdentifierPath = formElement.get('__identifierPath');
308
309 registeredValidators = {};
310 propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators');
311 for (var registeredPropertyPath in propertyValidationServiceRegisteredValidators[formElementIdentifierPath]) {
312 if (
313 !propertyValidationServiceRegisteredValidators[formElementIdentifierPath].hasOwnProperty(registeredPropertyPath)
314 || registeredPropertyPath.indexOf(propertyPath) > -1
315 ) {
316 continue;
317 }
318 registeredValidators[registeredPropertyPath] = propertyValidationServiceRegisteredValidators[formElementIdentifierPath][registeredPropertyPath];
319 }
320 propertyValidationServiceRegisteredValidators[formElementIdentifierPath] = registeredValidators;
321 getApplicationStateStack().setCurrentState('propertyValidationServiceRegisteredValidators', propertyValidationServiceRegisteredValidators);
322 };
323
324 /**
325 * @public
326 *
327 * @param string|object formElement
328 * @return void
329 * @throws 1475668189
330 */
331 function removeAllValidatorIdentifiersFromFormElement(formElement) {
332 var propertyValidationServiceRegisteredValidators, registeredValidators;
333 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475668189);
334
335 registeredValidators = {};
336 propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators');
337 for (var formElementIdentifierPath in propertyValidationServiceRegisteredValidators) {
338 if (
339 !propertyValidationServiceRegisteredValidators.hasOwnProperty(formElementIdentifierPath)
340 || formElementIdentifierPath === formElement.get('__identifierPath')
341 || formElementIdentifierPath.indexOf(formElement.get('__identifierPath') + '/') > -1
342 ) {
343 continue;
344 }
345 registeredValidators[formElementIdentifierPath] = propertyValidationServiceRegisteredValidators[formElementIdentifierPath];
346 }
347 getApplicationStateStack().setCurrentState('propertyValidationServiceRegisteredValidators', registeredValidators);
348 };
349
350 /**
351 * @public
352 *
353 * @param string validatorIdentifier
354 * @param function func
355 * @return void
356 * @throws 1475669143
357 * @throws 1475669144
358 * @throws 1475669145
359 */
360 function addValidator(validatorIdentifier, func) {
361 utility().assert(utility().isNonEmptyString(validatorIdentifier), 'Invalid parameter "validatorIdentifier"', 1475669143);
362 utility().assert('function' === $.type(func), 'Invalid parameter "func"', 1475669144);
363 utility().assert('function' !== $.type(_propertyValidationServiceValidators[validatorIdentifier]), 'The validator "' + validatorIdentifier + '" is already registered', 1475669145);
364
365 _propertyValidationServiceValidators[validatorIdentifier] = func;
366 };
367
368 /**
369 * @public
370 *
371 * @param object formElement
372 * @param string propertyPath
373 * @param string errorMessage
374 * @return object
375 * @throws 1475676517
376 * @throws 1475676518
377 */
378 function validateFormElementProperty(formElement, propertyPath) {
379 var configuration, formElementIdentifierPath, propertyValidationServiceRegisteredValidators, validationResults;
380 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475676517);
381 utility().assert(utility().isNonEmptyString(propertyPath), 'Invalid parameter "propertyPath"', 1475676518);
382
383 formElementIdentifierPath = formElement.get('__identifierPath');
384
385 validationResults = [];
386 propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators');
387 configuration = {
388 propertyValidatorsMode: 'AND'
389 };
390
391 if (
392 !utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[formElementIdentifierPath])
393 && 'object' === $.type(propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath])
394 && 'array' === $.type(propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'])
395 ) {
396 configuration = propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['configuration'];
397 for (var i = 0, len = propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'].length; i < len; ++i) {
398 var validatorIdentifier, validationResult;
399
400 validatorIdentifier = propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'][i];
401 if ('function' !== $.type(_propertyValidationServiceValidators[validatorIdentifier])) {
402 continue;
403 }
404 validationResult = _propertyValidationServiceValidators[validatorIdentifier](formElement, propertyPath);
405
406 if (utility().isNonEmptyString(validationResult)) {
407 validationResults.push(validationResult);
408 }
409 }
410 }
411
412 if (
413 validationResults.length > 0
414 && configuration['propertyValidatorsMode'] === 'OR'
415 && validationResults.length !== propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'].length
416 ) {
417 return [];
418 }
419
420 return validationResults;
421 };
422
423 /**
424 * @public
425 *
426 * @param object formElement
427 * @return object
428 * @throws 1475749668
429 */
430 function validateFormElement(formElement) {
431 var formElementIdentifierPath, propertyValidationServiceRegisteredValidators, validationResults;
432 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475749668);
433
434 formElementIdentifierPath = formElement.get('__identifierPath');
435
436 validationResults = [];
437 propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators');
438 if (!utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[formElementIdentifierPath])) {
439 for (var registeredPropertyPath in propertyValidationServiceRegisteredValidators[formElementIdentifierPath]) {
440 var validationResult;
441 if (!propertyValidationServiceRegisteredValidators[formElementIdentifierPath].hasOwnProperty(registeredPropertyPath)) {
442 continue;
443 }
444 validationResult = {
445 propertyPath: registeredPropertyPath,
446 validationResults: validateFormElementProperty(formElement, registeredPropertyPath)
447 };
448 validationResults.push(validationResult);
449 }
450 }
451 return validationResults;
452 };
453
454 /**
455 * @public
456 *
457 * @param array validationResults
458 * @return bool
459 * @throws 1478613477
460 */
461 function validationResultsHasErrors(validationResults) {
462 utility().assert('array' === $.type(validationResults), 'Invalid parameter "validationResults"', 1478613477);
463
464 for (var i = 0, len = validationResults.length; i < len; ++i) {
465 for (var j = 0, len2 = validationResults[i]['validationResults'].length; j < len2; ++j) {
466 if (
467 validationResults[i]['validationResults'][j]['validationResults']
468 && validationResults[i]['validationResults'][j]['validationResults'].length > 0
469 ) {
470 return true;
471 }
472 }
473 }
474 return false;
475 };
476
477 /**
478 * @public
479 *
480 * @param object formElement
481 * @param boolean returnAfterFirstMatch
482 * @param object validationResults
483 * @return object
484 * @throws 1475749668
485 */
486 function validateFormElementRecursive(formElement, returnAfterFirstMatch, validationResults) {
487 var formElements, validationResult, validationResults;
488 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475756764);
489 returnAfterFirstMatch = !!returnAfterFirstMatch;
490
491 validationResults = validationResults || [];
492 validationResult = {
493 formElementIdentifierPath: formElement.get('__identifierPath'),
494 validationResults: validateFormElement(formElement)
495 };
496 validationResults.push(validationResult);
497
498 if (returnAfterFirstMatch && validationResultsHasErrors(validationResults)) {
499 return validationResults;
500 }
501
502 formElements = formElement.get('renderables');
503 if ('array' === $.type(formElements)) {
504 for (var i = 0, len = formElements.length; i < len; ++i) {
505 validateFormElementRecursive(formElements[i], returnAfterFirstMatch, validationResults);
506 if (returnAfterFirstMatch && validationResultsHasErrors(validationResults)) {
507 return validationResults;
508 }
509 }
510 }
511
512 return validationResults;
513 }
514
515 /**
516 * @public
517 *
518 * @param object formElement
519 * @return void
520 * @throws 1475707334
521 */
522 function addValidatorIdentifiersFromFormElementPropertyCollections(formElement) {
523 var formElementTypeDefinition;
524 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475707334);
525
526 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
527
528 if (!utility().isUndefinedOrNull(formElementTypeDefinition['propertyCollections'])) {
529 for (var collectionName in formElementTypeDefinition['propertyCollections']) {
530 if (
531 !formElementTypeDefinition['propertyCollections'].hasOwnProperty(collectionName)
532 || 'array' !== $.type(formElementTypeDefinition['propertyCollections'][collectionName])
533 ) {
534 continue;
535 }
536 for (var i = 0, len1 = formElementTypeDefinition['propertyCollections'][collectionName].length; i < len1; ++i) {
537 if (
538 'array' !== $.type(formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'])
539 || repository().getIndexFromPropertyCollectionElementByIdentifier(formElementTypeDefinition['propertyCollections'][collectionName][i]['identifier'], collectionName, formElement) === -1
540 ) {
541 continue;
542 }
543 for (var j = 0, len2 = formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'].length; j < len2; ++j) {
544 var configuration = {};
545
546 if ('array' !== $.type(formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyValidators'])) {
547 continue;
548 }
549
550 if (
551 !utility().isUndefinedOrNull(formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyValidatorsMode'])
552 && formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyValidatorsMode'] === 'OR'
553 ) {
554 configuration['propertyValidatorsMode'] = 'OR';
555 } else {
556 configuration['propertyValidatorsMode'] = 'AND';
557 }
558 addValidatorIdentifiersToFormElementProperty(
559 formElement,
560 formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyValidators'],
561 formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyPath'],
562 formElementTypeDefinition['propertyCollections'][collectionName][i]['identifier'],
563 collectionName,
564 configuration
565 );
566 }
567 }
568 }
569 }
570 };
571
572 /**
573 * Publish the public methods.
574 */
575 return {
576 addValidatorIdentifiersToFormElementProperty: addValidatorIdentifiersToFormElementProperty,
577 removeValidatorIdentifiersFromFormElementProperty: removeValidatorIdentifiersFromFormElementProperty,
578 removeAllValidatorIdentifiersFromFormElement: removeAllValidatorIdentifiersFromFormElement,
579 validateFormElementProperty: validateFormElementProperty,
580 validateFormElement: validateFormElement,
581 validateFormElementRecursive: validateFormElementRecursive,
582 validationResultsHasErrors: validationResultsHasErrors,
583 addValidator: addValidator,
584 addValidatorIdentifiersFromFormElementPropertyCollections: addValidatorIdentifiersFromFormElementPropertyCollections
585 };
586 };
587
588 /**
589 * @public
590 *
591 * @param string ajaxRequestIdentifier
592 * @return object|null
593 * @throws 1475358064
594 */
595 function getRunningAjaxRequest(ajaxRequestIdentifier) {
596 utility().assert(utility().isNonEmptyString(ajaxRequestIdentifier), 'Invalid parameter "ajaxRequestIdentifier"', 1475358064);
597 return _runningAjaxRequests[ajaxRequestIdentifier] || null;
598 };
599
600 /**
601 * @public
602 *
603 * Implements the "Publish/Subscribe Pattern"
604 *
605 * @return object
606 * @credits Addy Osmani https://addyosmani.com/resources/essentialjsdesignpatterns/book/#highlighter_634280
607 */
608 function publisherSubscriber() {
609
610 /**
611 * @public
612 *
613 * @param string topic
614 * @param mixed args
615 * @return void
616 * @throws 1475358066
617 */
618 function publish(topic, args) {
619 utility().assert(utility().isNonEmptyString(topic), 'Invalid parameter "topic"', 1475358066);
620 if (utility().isUndefinedOrNull(_publisherSubscriberTopics[topic])) {
621 return;
622 }
623
624 for (var i = 0, len = _publisherSubscriberTopics[topic].length; i < len; ++i) {
625 _publisherSubscriberTopics[topic][i].func(topic, args);
626 }
627 };
628
629 /**
630 * @public
631 *
632 * @param string topic
633 * @param function func
634 * @return string
635 * @throws 1475358067
636 */
637 function subscribe(topic, func) {
638 utility().assert(utility().isNonEmptyString(topic), 'Invalid parameter "topic"', 1475358067);
639 utility().assert('function' === $.type(func), 'Invalid parameter "func"', 1475411986);
640
641 if (utility().isUndefinedOrNull(_publisherSubscriberTopics[topic])) {
642 _publisherSubscriberTopics[topic] = [];
643 }
644
645 var token = (++_publisherSubscriberUid).toString();
646 _publisherSubscriberTopics[topic].push({
647 token: token,
648 func: func
649 });
650 return token;
651 };
652
653 /**
654 * @public
655 *
656 * @param string token
657 * @return null|string
658 * @throws 1475358068
659 */
660 function unsubscribe(token) {
661 utility().assert(utility().isNonEmptyString(token), 'Invalid parameter "token"', 1475358068);
662
663 for (var key in _publisherSubscriberTopics) {
664 if (!_publisherSubscriberTopics.hasOwnProperty(key)) {
665 continue;
666 }
667 for (var i = 0, len = _publisherSubscriberTopics[key].length; i < len; ++i) {
668 if (_publisherSubscriberTopics[key][i].token === token) {
669 _publisherSubscriberTopics[key].splice(i, 1);
670 return token;
671 }
672 }
673 }
674 return null;
675 };
676
677 /**
678 * Publish the public methods.
679 */
680 return {
681 publish: publish,
682 subscribe: subscribe,
683 unsubscribe: unsubscribe
684 };
685 };
686
687 /**
688 * @private
689 *
690 * @param object modelToExtend
691 * @param object modelExtension
692 * @param string pathPrefix
693 * @return void
694 * @throws 1474640022
695 * @throws 1475358069
696 * @throws 1475358070
697 * @publish core/formElement/somePropertyChanged
698 */
699 function extendModel(modelToExtend, modelExtension, pathPrefix, disablePublishersOnSet) {
700 utility().assert('object' === $.type(modelToExtend), 'Invalid parameter "modelToExtend"', 1475358069);
701 utility().assert('object' === $.type(modelExtension) || 'array' === $.type(modelExtension), 'Invalid parameter "modelExtension"', 1475358070);
702
703 disablePublishersOnSet = !!disablePublishersOnSet;
704 pathPrefix = pathPrefix || '';
705
706 if ($.isEmptyObject(modelExtension)) {
707 utility().assert('' !== pathPrefix, 'Empty path is not allowed', 1474640022);
708 modelToExtend.on(pathPrefix, 'core/formElement/somePropertyChanged');
709 modelToExtend.set(pathPrefix, modelExtension, disablePublishersOnSet);
710 } else {
711 for (var key in modelExtension) {
712 if (!modelExtension.hasOwnProperty(key)) {
713 continue;
714 }
715 var path = (pathPrefix === '') ? key : pathPrefix + '.' + key;
716
717 modelToExtend.on(path, 'core/formElement/somePropertyChanged');
718
719 if ('object' === $.type(modelExtension[key]) || 'array' === $.type(modelExtension[key])) {
720 extendModel(modelToExtend, modelExtension[key], path, disablePublishersOnSet);
721 } else if (pathPrefix === 'properties.options') {
722 modelToExtend.set(pathPrefix, modelExtension, disablePublishersOnSet);
723 } else {
724 modelToExtend.set(path, modelExtension[key], disablePublishersOnSet);
725 }
726 }
727 }
728 };
729
730 /**
731 * @private
732 *
733 * @param object modelExtension
734 * @return object
735 */
736 function createModel(modelExtension) {
737 var newModel;
738
739 modelExtension = modelExtension || {};
740
741 function M() {
742
743 /**
744 * @private
745 */
746 var _objectData = {};
747
748 /**
749 * @private
750 */
751 var _publisherTopics = {};
752
753 /**
754 * @public
755 *
756 * @param string key
757 * @return mixed|undefined
758 * @throws 1475361755
759 */
760 function get(key) {
761 var firstPartOfPath, obj;
762 utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1475361755);
763
764 obj = _objectData;
765 while (key.indexOf('.') > 0) {
766 firstPartOfPath = key.slice(0, key.indexOf('.'));
767 key = key.slice(firstPartOfPath.length + 1);
768 if (!obj.hasOwnProperty(firstPartOfPath)) {
769 return undefined;
770 }
771 obj = obj[firstPartOfPath];
772 }
773
774 return obj[key];
775 };
776
777 /**
778 * @public
779 *
780 * @param string key
781 * @param mixed value
782 * @param bool disablePublishersOnSet
783 * @return void
784 * @throws 1475361756
785 * @publish mixed
786 */
787 function set(key, value, disablePublishersOnSet) {
788 var obj, oldValue, path;
789 utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1475361756);
790 disablePublishersOnSet = !!disablePublishersOnSet;
791
792 oldValue = get(key);
793 obj = _objectData;
794 path = key;
795
796 while (path.indexOf('.') > 0) {
797 var firstPartOfPath, nextPartOfPath;
798
799 firstPartOfPath = path.slice(0, path.indexOf('.'));
800 path = path.slice(firstPartOfPath.length + 1);
801 if ($.isNumeric(firstPartOfPath)) {
802 firstPartOfPath = parseInt(firstPartOfPath);
803 }
804 if ('undefined' === $.type(obj[firstPartOfPath])) {
805 nextPartOfPath = path.slice(0, path.indexOf('.'));
806 if ($.isNumeric(nextPartOfPath)) {
807 obj[firstPartOfPath] = [];
808 } else {
809 obj[firstPartOfPath] = {};
810 }
811 }
812 obj = obj[firstPartOfPath];
813 }
814 obj[path] = value;
815
816 if (!utility().isUndefinedOrNull(_publisherTopics[key]) && !disablePublishersOnSet) {
817 for (var i = 0, len = _publisherTopics[key].length; i < len; ++i) {
818 publisherSubscriber().publish(_publisherTopics[key][i], [key, value, oldValue, _objectData['__identifierPath']]);
819 }
820 }
821 };
822
823 /**
824 * @public
825 *
826 * @param string key
827 * @param bool disablePublishersOnSet
828 * @return void
829 * @throws 1489321637
830 * @throws 1489319753
831 * @publish mixed
832 */
833 function unset(key, disablePublishersOnSet) {
834 var obj, oldValue, parentPropertyData, parentPropertyPath, propertyToRemove;
835 utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1489321637);
836 disablePublishersOnSet = !!disablePublishersOnSet;
837
838 oldValue = get(key);
839
840 if (key.indexOf('.') > 0) {
841 parentPropertyPath = key.split('.');
842 propertyToRemove = parentPropertyPath.pop();
843 parentPropertyPath = parentPropertyPath.join('.');
844 parentPropertyData = get(parentPropertyPath);
845 delete parentPropertyData[propertyToRemove];
846 } else {
847 assert(false, 'remove toplevel properties is not supported', 1489319753);
848 }
849
850 if (!utility().isUndefinedOrNull(_publisherTopics[key]) && !disablePublishersOnSet) {
851 for (var i = 0, len = _publisherTopics[key].length; i < len; ++i) {
852 publisherSubscriber().publish(_publisherTopics[key][i], [key, undefined, oldValue, _objectData['__identifierPath']]);
853 }
854 }
855 };
856
857 /**
858 * @public
859 *
860 * @param string key
861 * @param string topicName
862 * @return void
863 * @throws 1475361757
864 * @throws 1475361758
865 */
866 function on(key, topicName) {
867 utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1475361757);
868 utility().assert(utility().isNonEmptyString(topicName), 'Invalid parameter "topicName"', 1475361758);
869
870 if ('array' !== $.type(_publisherTopics[key])) {
871 _publisherTopics[key] = [];
872 }
873 if (_publisherTopics[key].indexOf(topicName) === -1) {
874 _publisherTopics[key].push(topicName);
875 }
876 };
877
878 /**
879 * @public
880 *
881 * @param string key
882 * @param string topicName
883 * @return void
884 * @throws 1475361759
885 * @throws 1475361760
886 */
887 function off(key, topicName) {
888 utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1475361759);
889 utility().assert(utility().isNonEmptyString(topicName), 'Invalid parameter "topicName"', 1475361760);
890
891 if ('array' === $.type(_publisherTopics[key])) {
892 _publisherTopics[key] = _publisherTopics[key].filter(function (topicName) {
893 return topicName !== topicName;
894 });
895 }
896 };
897
898 /**
899 * @public
900 *
901 * @return object (dereferenced)
902 */
903 function getObjectData() {
904 return $.extend(true, {}, _objectData);
905 };
906
907 /**
908 * @public
909 *
910 * @return string
911 */
912 function toString() {
913 var childFormElements, objectData;
914
915 objectData = getObjectData();
916 childFormElements = objectData['renderables'] || null;
917 delete objectData['renderables'];
918
919 if (!utility().isUndefinedOrNull(objectData['__parentRenderable'])) {
920 objectData['__parentRenderable'] = objectData['__parentRenderable'].getObjectData()['__identifierPath'] + ' (filtered)';
921 }
922
923 if (null !== childFormElements) {
924 objectData['renderables'] = [];
925 for (var i = 0, len = childFormElements.length; i < len; ++i) {
926 var childFormElement = childFormElements[i];
927 objectData['renderables'].push(JSON.parse(childFormElement.toString()));
928 }
929 }
930
931 return JSON.stringify(objectData, null, 2);
932 };
933
934 /**
935 * @public
936 *
937 * @return object
938 */
939 function clone() {
940 var childFormElements, newModel, newRenderables, objectData;
941
942 objectData = getObjectData();
943 childFormElements = objectData['renderables'] || null;
944 delete objectData['renderables'];
945 delete objectData['__parentRenderable'];
946 objectData['renderables'] = (childFormElements) ? true : null,
947
948 newModel = new M();
949 extendModel(newModel, objectData, '', true);
950
951 if (null !== childFormElements) {
952 newRenderables = [];
953 for (var i = 0, len = childFormElements.length; i < len; ++i) {
954 var childFormElement = childFormElements[i];
955
956 childFormElement = childFormElement.clone();
957 childFormElement.set('__parentRenderable', newModel, true);
958 newRenderables.push(childFormElement);
959 }
960 newModel.set('renderables', newRenderables, true);
961 }
962
963 return newModel;
964 };
965
966 /**
967 * Publish the public methods.
968 */
969 return {
970 get: get,
971 set: set,
972 unset: unset,
973
974 on: on,
975 off: off,
976
977 getObjectData: getObjectData,
978 toString: toString,
979 clone: clone
980 };
981 };
982
983 newModel = new M();
984 extendModel(newModel, modelExtension, '', true);
985
986 return newModel;
987 };
988
989 /**
990 * @public
991 *
992 * @return object
993 */
994 function repository() {
995
996 /**
997 * @public
998 *
999 * @param object typeDefinitions
1000 * @return void
1001 * @throws 1475364394
1002 */
1003 function setFormEditorDefinitions(formEditorDefinitions) {
1004 utility().assert('object' === $.type(formEditorDefinitions), 'Invalid parameter "formEditorDefinitions"', 1475364394);
1005
1006 for (var key1 in formEditorDefinitions) {
1007 if (!formEditorDefinitions.hasOwnProperty(key1) || 'object' !== $.type(formEditorDefinitions[key1])) {
1008 continue;
1009 }
1010 for (var key2 in formEditorDefinitions[key1]) {
1011 if (!formEditorDefinitions[key1].hasOwnProperty(key2)) {
1012 continue;
1013 }
1014 if ('object' !== $.type(formEditorDefinitions[key1][key2])) {
1015 formEditorDefinitions[key1][key2] = {};
1016 }
1017 }
1018 }
1019 _repositoryFormEditorDefinitions = formEditorDefinitions;
1020 };
1021
1022 /**
1023 * @public
1024 *
1025 * @param string typeName
1026 * @param string subject
1027 * @return object (dereferenced)
1028 * @throws 1475364952
1029 * @throws 1475364953
1030 */
1031 function getFormEditorDefinition(definitionName, subject) {
1032 utility().assert(utility().isNonEmptyString(definitionName), 'Invalid parameter "definitionName"', 1475364952);
1033 utility().assert(utility().isNonEmptyString(subject), 'Invalid parameter "subject"', 1475364953);
1034 return $.extend(true, {}, _repositoryFormEditorDefinitions[definitionName][subject]);
1035 };
1036
1037 /**
1038 * @public
1039 *
1040 * @return object
1041 */
1042 function getRootFormElement() {
1043 return getApplicationStateStack().getCurrentState('formDefinition');
1044 };
1045
1046 /**
1047 * @public
1048 *
1049 * @param object formElement
1050 * @param object referenceFormElement
1051 * @param boolean registerPropertyValidators
1052 * @param boolean disablePublishersOnSet
1053 * @return object
1054 * @throws 1475436224
1055 * @throws 1475364956
1056 */
1057 function addFormElement(formElement, referenceFormElement, registerPropertyValidators, disablePublishersOnSet) {
1058 var enclosingCompositeFormElement, identifier, formElementTypeDefinition, parentFormElementsArray, parentFormElementTypeDefinition, referenceFormElementElements, referenceFormElementTypeDefinition;
1059 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475436224);
1060 utility().assert('object' === $.type(referenceFormElement), 'Invalid parameter "referenceFormElement"', 1475364956);
1061
1062 if (utility().isUndefinedOrNull(disablePublishersOnSet)) {
1063 disablePublishersOnSet = true;
1064 }
1065 disablePublishersOnSet = !!disablePublishersOnSet;
1066
1067 registerPropertyValidators = !!registerPropertyValidators;
1068 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1069 referenceFormElementTypeDefinition = repository().getFormEditorDefinition('formElements', referenceFormElement.get('type'));
1070
1071 // formElement != Page / SummaryPage && referenceFormElement == Page / Fieldset / GridContainer / GridRow
1072 if (!formElementTypeDefinition['_isTopLevelFormElement'] && referenceFormElementTypeDefinition['_isCompositeFormElement']) {
1073 if ('array' !== $.type(referenceFormElement.get('renderables'))) {
1074 referenceFormElement.set('renderables', [], disablePublishersOnSet);
1075 }
1076
1077 formElement.set('__parentRenderable', referenceFormElement, disablePublishersOnSet);
1078 formElement.set('__identifierPath', referenceFormElement.get('__identifierPath') + '/' + formElement.get('identifier'), disablePublishersOnSet);
1079 referenceFormElement.get('renderables').push(formElement);
1080 } else {
1081 // referenceFormElement == root form element
1082 if (referenceFormElement.get('__identifierPath') === getApplicationStateStack().getCurrentState('formDefinition').get('__identifierPath')) {
1083 referenceFormElementElements = referenceFormElement.get('renderables');
1084 // referenceFormElement = last page
1085 referenceFormElement = referenceFormElementElements[referenceFormElementElements.length - 1];
1086 // if formElement == Page / SummaryPage && referenceFormElement != Page / SummaryPage
1087 } else if (formElementTypeDefinition['_isTopLevelFormElement'] && !referenceFormElementTypeDefinition['_isTopLevelFormElement']) {
1088 // referenceFormElement = parent Page
1089 referenceFormElement = findEnclosingCompositeFormElementWhichIsOnTopLevel(referenceFormElement);
1090 // formElement == Page / SummaryPage / Fieldset / GridContainer / GridRow
1091 } else if (formElementTypeDefinition['_isCompositeFormElement']) {
1092 enclosingCompositeFormElement = findEnclosingCompositeFormElementWhichIsNotOnTopLevel(referenceFormElement);
1093 if (enclosingCompositeFormElement) {
1094 // referenceFormElement = parent Fieldset / GridContainer / GridRow
1095 referenceFormElement = enclosingCompositeFormElement;
1096 }
1097 }
1098
1099 formElement.set('__parentRenderable', referenceFormElement.get('__parentRenderable'), disablePublishersOnSet);
1100 formElement.set('__identifierPath', referenceFormElement.get('__parentRenderable').get('__identifierPath') + '/' + formElement.get('identifier'), disablePublishersOnSet);
1101 parentFormElementsArray = referenceFormElement.get('__parentRenderable').get('renderables');
1102 parentFormElementsArray.splice(parentFormElementsArray.indexOf(referenceFormElement) + 1, 0, formElement);
1103 }
1104
1105 if (registerPropertyValidators) {
1106 if ('array' === $.type(formElementTypeDefinition['editors'])) {
1107 for (var i = 0, len1 = formElementTypeDefinition['editors'].length; i < len1; ++i) {
1108 var configuration = {};
1109
1110 if ('array' !== $.type(formElementTypeDefinition['editors'][i]['propertyValidators'])) {
1111 continue;
1112 }
1113
1114 if (
1115 !utility().isUndefinedOrNull(formElementTypeDefinition['editors'][i]['propertyValidatorsMode'])
1116 && formElementTypeDefinition['editors'][i]['propertyValidatorsMode'] === 'OR'
1117 ) {
1118 configuration['propertyValidatorsMode'] = 'OR';
1119 } else {
1120 configuration['propertyValidatorsMode'] = 'AND';
1121 }
1122
1123 propertyValidationService().addValidatorIdentifiersToFormElementProperty(
1124 formElement,
1125 formElementTypeDefinition['editors'][i]['propertyValidators'],
1126 formElementTypeDefinition['editors'][i]['propertyPath'],
1127 undefined,
1128 undefined,
1129 configuration
1130 );
1131 }
1132 }
1133 }
1134
1135 return formElement;
1136 };
1137
1138 /**
1139 * @param object formElement
1140 * @param boolean removeRegisteredPropertyValidators
1141 * @param boolean disablePublishersOnSet
1142 * @return void
1143 * @throws 1472553024
1144 * @throws 1475364957
1145 */
1146 function removeFormElement(formElement, removeRegisteredPropertyValidators, disablePublishersOnSet) {
1147 var parentFormElementElements;
1148
1149 if (utility().isUndefinedOrNull(disablePublishersOnSet)) {
1150 disablePublishersOnSet = true;
1151 }
1152 disablePublishersOnSet = !!disablePublishersOnSet;
1153 removeRegisteredPropertyValidators = !!removeRegisteredPropertyValidators;
1154
1155 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364957);
1156 utility().assert('object' === $.type(formElement.get('__parentRenderable')), 'Removing the root element is not allowed', 1472553024);
1157
1158 parentFormElementElements = formElement.get('__parentRenderable').get('renderables');
1159 parentFormElementElements.splice(parentFormElementElements.indexOf(formElement), 1);
1160 formElement.get('__parentRenderable').set('renderables', parentFormElementElements, disablePublishersOnSet);
1161
1162 if (removeRegisteredPropertyValidators) {
1163 propertyValidationService().removeAllValidatorIdentifiersFromFormElement(formElement);
1164 }
1165 };
1166
1167 /**
1168 * @param object formElementToMove
1169 * @param string position
1170 * @param object referenceFormElement
1171 * @param boolean disablePublishersOnSet
1172 * @return object
1173 * @throws 1475364958
1174 * @throws 1475364959
1175 * @throws 1475364960
1176 * @throws 1475364961
1177 * @throws 1475364962
1178 * @throws 1476993731
1179 * @throws 1476993732
1180 */
1181 function moveFormElement(formElementToMove, position, referenceFormElement, disablePublishersOnSet) {
1182 var formElementToMoveTypeDefinition, referenceFormElementParentElements, referenceFormElementElements, referenceFormElementIndex, referenceFormElementTypeDefinition, reSetIdentifierPath;
1183 utility().assert('object' === $.type(formElementToMove), 'Invalid parameter "formElementToMove"', 1475364958);
1184 utility().assert('after' === position || 'before' === position || 'inside' === position, 'Invalid position "' + position + '"', 1475364959);
1185 utility().assert('object' === $.type(referenceFormElement), 'Invalid parameter "referenceFormElement"', 1475364960);
1186
1187 if (utility().isUndefinedOrNull(disablePublishersOnSet)) {
1188 disablePublishersOnSet = true;
1189 }
1190 disablePublishersOnSet = !!disablePublishersOnSet;
1191
1192 formElementToMoveTypeDefinition = repository().getFormEditorDefinition('formElements', formElementToMove.get('type'));
1193 referenceFormElementTypeDefinition = repository().getFormEditorDefinition('formElements', referenceFormElement.get('type'));
1194
1195 removeFormElement(formElementToMove, false);
1196 reSetIdentifierPath = function(formElement, pathPrefix) {
1197 var formElements, newIdentifierPath, oldIdentifierPath, propertyValidationServiceRegisteredValidators;
1198 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364961);
1199 utility().assert(utility().isNonEmptyString(pathPrefix), 'Invalid parameter "pathPrefix"', 1475364962);
1200
1201 oldIdentifierPath = formElement.get('__identifierPath');
1202 newIdentifierPath = pathPrefix + '/' + formElement.get('identifier');
1203
1204 propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators');
1205 if (!utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[oldIdentifierPath])) {
1206 propertyValidationServiceRegisteredValidators[newIdentifierPath] = propertyValidationServiceRegisteredValidators[oldIdentifierPath];
1207 delete propertyValidationServiceRegisteredValidators[oldIdentifierPath];
1208 }
1209 getApplicationStateStack().setCurrentState('propertyValidationServiceRegisteredValidators', propertyValidationServiceRegisteredValidators);
1210
1211 formElement.set('__identifierPath', newIdentifierPath, disablePublishersOnSet);
1212 formElements = formElement.get('renderables');
1213 if ('array' === $.type(formElements)) {
1214 for (var i = 0, len = formElements.length; i < len; ++i) {
1215 reSetIdentifierPath(formElements[i], formElement.get('__identifierPath'));
1216 }
1217 }
1218 };
1219
1220 /**
1221 * This is true on:
1222 * * Drag a Element on a Page Element (tree)
1223 * * Drag a Element on a Section Element (tree)
1224 */
1225 if (position === 'inside') {
1226 // formElementToMove == Page / SummaryPage
1227 utility().assert(!formElementToMoveTypeDefinition['_isTopLevelFormElement'], 'This move is not allowed', 1476993731);
1228 // referenceFormElement != Page / Fieldset / GridContainer / GridRow
1229 utility().assert(referenceFormElementTypeDefinition['_isCompositeFormElement'], 'This move is not allowed', 1476993732);
1230
1231 formElementToMove.set('__parentRenderable', referenceFormElement, disablePublishersOnSet);
1232 reSetIdentifierPath(formElementToMove, referenceFormElement.get('__identifierPath'));
1233
1234 referenceFormElementElements = referenceFormElement.get('renderables');
1235 if (utility().isUndefinedOrNull(referenceFormElementElements)) {
1236 referenceFormElementElements = [];
1237 }
1238 referenceFormElementElements.splice(0, 0, formElementToMove);
1239 referenceFormElement.set('renderables', referenceFormElementElements, disablePublishersOnSet);
1240 } else {
1241 /**
1242 * This is true on:
1243 * * Drag a Page before another Page (tree)
1244 * * Drag a Page after another Page (tree)
1245 */
1246 if (formElementToMoveTypeDefinition['_isTopLevelFormElement'] && referenceFormElementTypeDefinition['_isTopLevelFormElement']) {
1247 referenceFormElementParentElements = referenceFormElement.get('__parentRenderable').get('renderables');
1248 referenceFormElementIndex = referenceFormElementParentElements.indexOf(referenceFormElement);
1249
1250 if (position === 'after') {
1251 referenceFormElementParentElements.splice(referenceFormElementIndex + 1, 0, formElementToMove);
1252 } else {
1253 referenceFormElementParentElements.splice(referenceFormElementIndex, 0, formElementToMove);
1254 }
1255
1256 referenceFormElement.get('__parentRenderable').set('renderables', referenceFormElementParentElements, disablePublishersOnSet);
1257 } else {
1258 /**
1259 * This is true on:
1260 * * Drag a Element before another Element within the same level (tree)
1261 * * Drag a Element after another Element within the same level (tree)
1262 * * Drag a Element before another Element (stage)
1263 * * Drag a Element after another Element (stage)
1264 */
1265 if (formElementToMove.get('__parentRenderable').get('identifier') === referenceFormElement.get('__parentRenderable').get('identifier')) {
1266 referenceFormElementParentElements = referenceFormElement.get('__parentRenderable').get('renderables');
1267 referenceFormElementIndex = referenceFormElementParentElements.indexOf(referenceFormElement);
1268 } else {
1269 /**
1270 * This is true on:
1271 * * Drag a Element before an Element on another page (tree / stage)
1272 * * Drag a Element after an Element on another page (tree / stage)
1273 */
1274 formElementToMove.set('__parentRenderable', referenceFormElement.get('__parentRenderable'), disablePublishersOnSet);
1275 reSetIdentifierPath(formElementToMove, referenceFormElement.get('__parentRenderable').get('__identifierPath'));
1276
1277 referenceFormElementParentElements = referenceFormElement.get('__parentRenderable').get('renderables');
1278 referenceFormElementIndex = referenceFormElementParentElements.indexOf(referenceFormElement);
1279 }
1280
1281 if (position === 'after') {
1282 referenceFormElementParentElements.splice(referenceFormElementIndex + 1, 0, formElementToMove);
1283 } else {
1284 referenceFormElementParentElements.splice(referenceFormElementIndex, 0, formElementToMove);
1285 }
1286
1287 referenceFormElement.get('__parentRenderable').set('renderables', referenceFormElementParentElements, disablePublishersOnSet);
1288 }
1289 }
1290
1291 return formElementToMove;
1292 };
1293
1294 /**
1295 * @param object formElement
1296 * @return int
1297 * @throws 1475364963
1298 */
1299 function getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement(formElement) {
1300 var enclosingCompositeFormElementWhichIsOnTopLevel, formElementTypeDefinition;
1301 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364963);
1302
1303 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1304
1305 if (formElementTypeDefinition['_isTopLevelFormElement'] && formElementTypeDefinition['_isCompositeFormElement']) {
1306 enclosingCompositeFormElementWhichIsOnTopLevel = formElement;
1307 } else if (formElement.get('__identifierPath') === getApplicationStateStack().getCurrentState('formDefinition').get('__identifierPath')) {
1308 enclosingCompositeFormElementWhichIsOnTopLevel = getApplicationStateStack().getCurrentState('formDefinition').get('renderables')[0];
1309 } else {
1310 enclosingCompositeFormElementWhichIsOnTopLevel = findEnclosingCompositeFormElementWhichIsOnTopLevel(formElement);
1311 }
1312 return enclosingCompositeFormElementWhichIsOnTopLevel.get('__parentRenderable').get('renderables').indexOf(enclosingCompositeFormElementWhichIsOnTopLevel);
1313 };
1314
1315 /**
1316 * @param object formElement
1317 * @return object
1318 * @throws 1472556223
1319 * @throws 1475364964
1320 */
1321 function findEnclosingCompositeFormElementWhichIsOnTopLevel(formElement) {
1322 var formElementTypeDefinition;
1323 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364964);
1324 utility().assert('object' === $.type(formElement.get('__parentRenderable')), 'The root element is never encloused by anything', 1472556223);
1325
1326 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1327 while (!formElementTypeDefinition['_isTopLevelFormElement']) {
1328 formElement = formElement.get('__parentRenderable');
1329 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1330 }
1331
1332 return formElement;
1333 };
1334
1335 /**
1336 * @param object formElement
1337 * @return object|null
1338 * @throws 1489447996
1339 */
1340 function findEnclosingGridContainerFormElement(formElement) {
1341 var formElementTypeDefinition;
1342 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1489447996);
1343
1344 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1345 while (!formElementTypeDefinition['_isGridContainerFormElement']) {
1346 if (formElementTypeDefinition['_isTopLevelFormElement']) {
1347 return null;
1348 }
1349 formElement = formElement.get('__parentRenderable');
1350 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1351 }
1352 if (formElementTypeDefinition['_isTopLevelFormElement']) {
1353 return null;
1354 }
1355 return formElement;
1356 };
1357
1358 /**
1359 * @param object formElement
1360 * @return object|null
1361 * @throws 1490520271
1362 */
1363 function findEnclosingGridRowFormElement(formElement) {
1364 var formElementTypeDefinition;
1365 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1490520271);
1366
1367 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1368 while (!formElementTypeDefinition['_isGridRowFormElement']) {
1369 if (formElementTypeDefinition['_isTopLevelFormElement']) {
1370 return null;
1371 }
1372 formElement = formElement.get('__parentRenderable');
1373 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1374 }
1375 if (formElementTypeDefinition['_isTopLevelFormElement']) {
1376 return null;
1377 }
1378 return formElement;
1379 };
1380
1381 /**
1382 * @param object formElement
1383 * @return object|null
1384 * @throws 1475364965
1385 */
1386 function findEnclosingCompositeFormElementWhichIsNotOnTopLevel(formElement) {
1387 var formElementTypeDefinition;
1388 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364965);
1389
1390 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1391 while (!formElementTypeDefinition['_isCompositeFormElement']) {
1392 if (formElementTypeDefinition['_isTopLevelFormElement']) {
1393 return null;
1394 }
1395 formElement = formElement.get('__parentRenderable');
1396 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1397 }
1398 if (formElementTypeDefinition['_isTopLevelFormElement']) {
1399 return null;
1400 }
1401 return formElement;
1402 };
1403
1404 /**
1405 * @return object
1406 */
1407 function getNonCompositeNonToplevelFormElements() {
1408 var collect, nonCompositeNonToplevelFormElements;
1409
1410 collect = function(formElement) {
1411 var formElements, formElementTypeDefinition;
1412 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364961);
1413
1414 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type'));
1415
1416 if (!formElementTypeDefinition['_isTopLevelFormElement'] && !formElementTypeDefinition['_isCompositeFormElement']) {
1417 nonCompositeNonToplevelFormElements.push(formElement);
1418 }
1419
1420 formElements = formElement.get('renderables');
1421 if ('array' === $.type(formElements)) {
1422 for (var i = 0, len = formElements.length; i < len; ++i) {
1423 collect(formElements[i]);
1424 }
1425 }
1426 };
1427
1428 nonCompositeNonToplevelFormElements = [];
1429 collect(getRootFormElement());
1430 return nonCompositeNonToplevelFormElements;
1431 };
1432
1433 /**
1434 * @param string identifier
1435 * @returl bool
1436 * @throws 1475364966
1437 */
1438 function isFormElementIdentifierUsed(identifier) {
1439 var checkIdentifier, identifierFound;
1440 utility().assert(utility().isNonEmptyString(identifier), 'Invalid parameter "identifier"', 1475364966);
1441
1442 checkIdentifier = function(formElement) {
1443 var formElements;
1444
1445 if (formElement.get('identifier') === identifier) {
1446 identifierFound = true;
1447 }
1448
1449 if (!identifierFound) {
1450 formElements = formElement.get('renderables');
1451 if ('array' === $.type(formElements)) {
1452 for (var i = 0, len = formElements.length; i < len; ++i) {
1453 checkIdentifier(formElements[i]);
1454 if (identifierFound) {
1455 break;
1456 }
1457 }
1458 }
1459 }
1460 }
1461
1462 checkIdentifier(getApplicationStateStack().getCurrentState('formDefinition'));
1463 return identifierFound;
1464 };
1465
1466 /**
1467 * @param string formElementType
1468 * @return string
1469 * @throws 1475373676
1470 */
1471 function getNextFreeFormElementIdentifier(formElementType) {
1472 var i, prefix;
1473 utility().assert(utility().isNonEmptyString(formElementType), 'Invalid parameter "formElementType"', 1475373676);
1474
1475 prefix = formElementType.toLowerCase().replace(/[^a-z0-9]/g, '-') + '-';
1476 i = 1;
1477 while (isFormElementIdentifierUsed(prefix + i)) {
1478 i++;
1479 }
1480 return prefix + i;
1481 };
1482
1483 /**
1484 * @param string identifierPath
1485 * @return object
1486 * @throws 1472424333
1487 * @throws 1472424334
1488 * @throws 1472424330
1489 * @throws 1475373677
1490 */
1491 function findFormElementByIdentifierPath(identifierPath) {
1492 var obj, pathParts, pathPartsLength, formElement, formElements;
1493
1494 utility().assert(utility().isNonEmptyString(identifierPath), 'Invalid parameter "identifierPath"', 1475373677);
1495
1496 formElement = getApplicationStateStack().getCurrentState('formDefinition');
1497 pathParts = identifierPath.split('/');
1498 pathPartsLength = pathParts.length;
1499
1500 for (var i = 0; i < pathPartsLength; ++i) {
1501 var key = pathParts[i];
1502 if (i === 0 || i === pathPartsLength) {
1503 utility().assert(key === formElement.get('identifier'), '"' + key + '" does not exist in path "' + identifierPath + '"', 1472424333);
1504 continue;
1505 }
1506
1507 formElements = formElement.get('renderables');
1508 if ('array' === $.type(formElements)) {
1509 obj = null;
1510 for (var j = 0, len = formElements.length; j < len; ++j) {
1511 if (key === formElements[j].get('identifier')) {
1512 obj = formElements[j];
1513 break;
1514 }
1515 }
1516
1517 utility().assert('null' !== $.type(obj), 'Could not find form element "' + key + '" in path "' + identifierPath + '"', 1472424334);
1518 formElement = obj;
1519 } else {
1520 utility().assert(false, 'No form elements found', 1472424330);
1521 }
1522 }
1523 return formElement;
1524 };
1525
1526 /**
1527 * @param string|object formElement
1528 * @return object
1529 */
1530 function findFormElement(formElement) {
1531 if ('object' === $.type(formElement)) {
1532 formElement = formElement.get('__identifierPath');
1533 }
1534 return findFormElementByIdentifierPath(formElement);
1535 };
1536
1537 /**
1538 * @public
1539 *
1540 * @param string collectionElementIdentifier
1541 * @param object collection
1542 * @return undefined|object
1543 * @throws 1475375281
1544 * @throws 1475375282
1545 */
1546 function findCollectionElementByIdentifierPath(collectionElementIdentifier, collection) {
1547 utility().assert(utility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475375281);
1548 utility().assert('array' === $.type(collection), 'Invalid parameter "collection"', 1475375282);
1549
1550 for (var i = 0, len = collection.length; i < len; ++i) {
1551 if (collection[i]['identifier'] === collectionElementIdentifier) {
1552 return collection[i];
1553 }
1554 }
1555
1556 return undefined;
1557 };
1558
1559 /**
1560 * @public
1561 *
1562 * @param string collectionElementIdentifier
1563 * @param string collectionName
1564 * @param object formElement
1565 * @return int
1566 * @throws 1475375283
1567 * @throws 1475375284
1568 * @throws 1475375285
1569 */
1570 function getIndexFromPropertyCollectionElementByIdentifier(collectionElementIdentifier, collectionName, formElement) {
1571 var collection;
1572 utility().assert(utility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475375283);
1573 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475375284);
1574 utility().assert(utility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475375285);
1575
1576 collection = formElement.get(collectionName);
1577 if ('array' === $.type(collection)) {
1578 for (var i = 0, len = collection.length; i < len; ++i) {
1579 if (collection[i]['identifier'] === collectionElementIdentifier) {
1580 return i;
1581 }
1582 }
1583 }
1584 return -1;
1585 };
1586
1587 /**
1588 * @public
1589 *
1590 * @param object collectionElementToAdd
1591 * @param string collectionName
1592 * @param object formElement
1593 * @param string referenceCollectionElementIdentifier
1594 * @param boolean disablePublishersOnSet
1595 * @return object
1596 * @throws 1475375686
1597 * @throws 1475375687
1598 * @throws 1475375688
1599 * @throws 1477413154
1600 */
1601 function addPropertyCollectionElement(collectionElementToAdd, collectionName, formElement, referenceCollectionElementIdentifier, disablePublishersOnSet) {
1602 var collection, formElementTypeDefinition, newCollection, newCollectionElementIndex;
1603 utility().assert('object' === $.type(collectionElementToAdd), 'Invalid parameter "collectionElementToAdd"', 1475375686);
1604 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475375687);
1605 utility().assert(utility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475375688);
1606
1607 if (utility().isUndefinedOrNull(disablePublishersOnSet)) {
1608 disablePublishersOnSet = true;
1609 }
1610 disablePublishersOnSet = !!disablePublishersOnSet;
1611
1612 collection = formElement.get(collectionName);
1613 if ('array' !== $.type(collection)) {
1614 extendModel(formElement, [], collectionName, true);
1615 collection = formElement.get(collectionName);
1616 }
1617
1618 if (utility().isUndefinedOrNull(referenceCollectionElementIdentifier)) {
1619 newCollectionElementIndex = 0;
1620 } else {
1621 newCollectionElementIndex = getIndexFromPropertyCollectionElementByIdentifier(referenceCollectionElementIdentifier, collectionName, formElement) + 1;
1622 utility().assert(-1 < newCollectionElementIndex, 'Could not find collection element ' + referenceCollectionElementIdentifier + ' within collection ' + collectionName, 1477413154);
1623 }
1624
1625 collection.splice(newCollectionElementIndex, 0, collectionElementToAdd);
1626 formElement.set(collectionName, collection, true);
1627
1628 propertyValidationService().removeValidatorIdentifiersFromFormElementProperty(formElement, collectionName);
1629
1630 for (var i = 0, len = collection.length; i < len; ++i) {
1631 extendModel(formElement, collection[i], collectionName + '.' + i, true);
1632 }
1633
1634 formElement.set(collectionName, collection, true);
1635 propertyValidationService().addValidatorIdentifiersFromFormElementPropertyCollections(formElement);
1636 formElement.set(collectionName, collection, disablePublishersOnSet);
1637
1638 return formElement;
1639 };
1640
1641 /**
1642 * @public
1643 *
1644 * @param object formElement
1645 * @param string collectionElementIdentifier
1646 * @param string collectionName
1647 * @param boolean disablePublishersOnSet
1648 * @return void
1649 * @throws 1475375689
1650 * @throws 1475375690
1651 * @throws 1475375691
1652 * @throws 1475375692
1653 */
1654 function removePropertyCollectionElementByIdentifier(formElement, collectionElementIdentifier, collectionName, disablePublishersOnSet) {
1655 var collection, collectionElementIndex;
1656 utility().assert(utility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475375689);
1657 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475375690);
1658 utility().assert(utility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475375691);
1659
1660 collection = formElement.get(collectionName);
1661 utility().assert('array' === $.type(collection), 'The collection "' + collectionName + '" does not exist', 1475375692);
1662
1663 if (utility().isUndefinedOrNull(disablePublishersOnSet)) {
1664 disablePublishersOnSet = true;
1665 }
1666 disablePublishersOnSet = !!disablePublishersOnSet;
1667
1668 propertyValidationService().removeValidatorIdentifiersFromFormElementProperty(formElement, collectionName);
1669 collectionElementIndex = getIndexFromPropertyCollectionElementByIdentifier(collectionElementIdentifier, collectionName, formElement);
1670 collection.splice(collectionElementIndex, 1);
1671 formElement.set(collectionName, collection, disablePublishersOnSet);
1672 propertyValidationService().addValidatorIdentifiersFromFormElementPropertyCollections(formElement);
1673 };
1674
1675 /**
1676 * @param string collectionElementToMoveIdentifier
1677 * @param string position
1678 * @param string referenceCollectionElementIdentifier
1679 * @param string position
1680 * @param object formElement
1681 * @param boolean disablePublishersOnSet
1682 * @return void
1683 * @throws 1477404484
1684 * @throws 1477404485
1685 * @throws 1477404486
1686 * @throws 1477404488
1687 * @throws 1477404489
1688 * @throws 1477404490
1689 */
1690 function movePropertyCollectionElement(collectionElementToMoveIdentifier, position, referenceCollectionElementIdentifier, collectionName, formElement, disablePublishersOnSet) {
1691 var collection, collectionElementToMove, referenceCollectionElement, referenceCollectionElementIndex;
1692
1693 utility().assert('after' === position || 'before' === position, 'Invalid position "' + position + '"', 1477404485);
1694 utility().assert('string' === $.type(referenceCollectionElementIdentifier), 'Invalid parameter "referenceCollectionElementIdentifier"', 1477404486);
1695 utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1477404488);
1696
1697 collection = formElement.get(collectionName);
1698 utility().assert('array' === $.type(collection), 'The collection "' + collectionName + '" does not exist', 1477404490);
1699
1700 collectionElementToMove = findCollectionElementByIdentifierPath(collectionElementToMoveIdentifier, collection);
1701 utility().assert('object' === $.type(collectionElementToMove), 'Invalid parameter "collectionElementToMove"', 1477404484);
1702
1703 removePropertyCollectionElementByIdentifier(formElement, collectionElementToMoveIdentifier, collectionName);
1704
1705 referenceCollectionElementIndex = getIndexFromPropertyCollectionElementByIdentifier(referenceCollectionElementIdentifier, collectionName, formElement);
1706 utility().assert(-1 < referenceCollectionElementIndex, 'Could not find collection element ' + referenceCollectionElementIdentifier + ' within collection ' + collectionName, 1477404489);
1707
1708 if ('before' === position) {
1709 referenceCollectionElement = collection[referenceCollectionElementIndex - 1];
1710 if (utility().isUndefinedOrNull(referenceCollectionElement)) {
1711 referenceCollectionElementIdentifier = undefined;
1712 } else {
1713 referenceCollectionElementIdentifier = referenceCollectionElement['identifier'];
1714 }
1715 }
1716
1717 addPropertyCollectionElement(collectionElementToMove, collectionName, formElement, referenceCollectionElementIdentifier, disablePublishersOnSet)
1718 };
1719
1720 /**
1721 * Publish the public methods.
1722 */
1723 return {
1724 getRootFormElement: getRootFormElement,
1725
1726 getFormEditorDefinition: getFormEditorDefinition,
1727 setFormEditorDefinitions: setFormEditorDefinitions,
1728
1729 findFormElement: findFormElement,
1730 findFormElementByIdentifierPath: findFormElementByIdentifierPath,
1731 findEnclosingCompositeFormElementWhichIsNotOnTopLevel: findEnclosingCompositeFormElementWhichIsNotOnTopLevel,
1732 findEnclosingCompositeFormElementWhichIsOnTopLevel: findEnclosingCompositeFormElementWhichIsOnTopLevel,
1733 findEnclosingGridContainerFormElement: findEnclosingGridContainerFormElement,
1734 findEnclosingGridRowFormElement: findEnclosingGridRowFormElement,
1735 getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement: getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement,
1736 getNonCompositeNonToplevelFormElements: getNonCompositeNonToplevelFormElements,
1737
1738 getNextFreeFormElementIdentifier: getNextFreeFormElementIdentifier,
1739 isFormElementIdentifierUsed: isFormElementIdentifierUsed,
1740
1741 addFormElement: addFormElement,
1742 moveFormElement: moveFormElement,
1743 removeFormElement: removeFormElement,
1744
1745 findCollectionElementByIdentifierPath: findCollectionElementByIdentifierPath,
1746 getIndexFromPropertyCollectionElementByIdentifier: getIndexFromPropertyCollectionElementByIdentifier,
1747 addPropertyCollectionElement: addPropertyCollectionElement,
1748 removePropertyCollectionElementByIdentifier: removePropertyCollectionElementByIdentifier,
1749 movePropertyCollectionElement: movePropertyCollectionElement
1750 };
1751 };
1752
1753 /**
1754 * @public
1755 *
1756 * @return object
1757 */
1758 function factory() {
1759
1760 /**
1761 * @public
1762 *
1763 * @param object configuration
1764 * @param string identifierPathPrefix
1765 * @param object parentFormElement
1766 * @param boolean registerPropertyValidators
1767 * @param boolean disablePublishersOnSet
1768 * @return object
1769 * @throws 1475375693
1770 * @throws 1475436040
1771 * @throws 1475604050
1772 */
1773 function createFormElement(configuration, identifierPathPrefix, parentFormElement, registerPropertyValidators, disablePublishersOnSet) {
1774 var currentChildFormElements, collections, formElementTypeDefinition, identifierPath, rawChildFormElements, formElement, predefinedDefaults;
1775 utility().assert('object' === $.type(configuration), 'Invalid parameter "configuration"', 1475375693);
1776 utility().assert(utility().isNonEmptyString(configuration['identifier']), '"identifier" must not be empty', 1475436040);
1777 utility().assert(utility().isNonEmptyString(configuration['type']), '"type" must not be empty', 1475604050);
1778
1779 registerPropertyValidators = !!registerPropertyValidators;
1780 if (utility().isUndefinedOrNull(disablePublishersOnSet)) {
1781 disablePublishersOnSet = true;
1782 }
1783 disablePublishersOnSet = !!disablePublishersOnSet;
1784
1785 formElementTypeDefinition = repository().getFormEditorDefinition('formElements', configuration['type']);
1786 rawChildFormElements = configuration['renderables'];
1787 delete configuration['renderables'];
1788
1789 collections = {};
1790 predefinedDefaults = formElementTypeDefinition['predefinedDefaults'] || {};
1791 for (var collectionName in configuration) {
1792 if (!configuration.hasOwnProperty(collectionName)) {
1793 continue;
1794 }
1795 if (utility().isUndefinedOrNull(_repositoryFormEditorDefinitions[collectionName])) {
1796 continue;
1797 }
1798
1799 predefinedDefaults[collectionName] = predefinedDefaults[collectionName] || {};
1800 collections[collectionName] = $.extend(
1801 predefinedDefaults[collectionName] || {},
1802 configuration[collectionName]
1803 );
1804
1805 delete predefinedDefaults[collectionName];
1806 delete configuration[collectionName];
1807 }
1808
1809 identifierPathPrefix = identifierPathPrefix || '';
1810 identifierPath = (identifierPathPrefix === '') ? configuration['identifier'] : identifierPathPrefix + '/' + configuration['identifier'];
1811
1812 configuration = $.extend(
1813 predefinedDefaults,
1814 configuration,
1815 {
1816 renderables: (rawChildFormElements) ? true : null,
1817 __parentRenderable: null,
1818 __identifierPath: identifierPath
1819 }
1820 );
1821
1822 formElement = createModel(configuration);
1823 formElement.set('__parentRenderable', parentFormElement || null, disablePublishersOnSet);
1824
1825 for (var collectionName in collections) {
1826 if (!collections.hasOwnProperty(collectionName)) {
1827 continue;
1828 }
1829
1830 for (var i in collections[collectionName]) {
1831 var previousCreatePropertyCollectionElementIdentifier, propertyCollectionElement;
1832 if (!collections[collectionName].hasOwnProperty(i)) {
1833 continue;
1834 }
1835 propertyCollectionElement = createPropertyCollectionElement(
1836 collections[collectionName][i]['identifier'],
1837 collections[collectionName][i],
1838 collectionName
1839 );
1840 if (i > 0) {
1841 previousCreatePropertyCollectionElementIdentifier = collections[collectionName][i - 1]['identifier']
1842 }
1843 repository().addPropertyCollectionElement(propertyCollectionElement, collectionName, formElement, previousCreatePropertyCollectionElementIdentifier, true);
1844 }
1845 }
1846
1847 if (registerPropertyValidators) {
1848 if ('array' === $.type(formElementTypeDefinition['editors'])) {
1849 for (var i = 0, len1 = formElementTypeDefinition['editors'].length; i < len1; ++i) {
1850 var configuration = {};
1851
1852 if ('array' !== $.type(formElementTypeDefinition['editors'][i]['propertyValidators'])) {
1853 continue;
1854 }
1855
1856 if (
1857 !utility().isUndefinedOrNull(formElementTypeDefinition['editors'][i]['propertyValidatorsMode'])
1858 && formElementTypeDefinition['editors'][i]['propertyValidatorsMode'] === 'OR'
1859 ) {
1860 configuration['propertyValidatorsMode'] = 'OR';
1861 } else {
1862 configuration['propertyValidatorsMode'] = 'AND';
1863 }
1864
1865 propertyValidationService().addValidatorIdentifiersToFormElementProperty(
1866 formElement,
1867 formElementTypeDefinition['editors'][i]['propertyValidators'],
1868 formElementTypeDefinition['editors'][i]['propertyPath'],
1869 undefined,
1870 undefined,
1871 configuration
1872 );
1873 }
1874 }
1875 }
1876
1877 if ('array' === $.type(rawChildFormElements)) {
1878 currentChildFormElements = [];
1879 for (var i = 0, len = rawChildFormElements.length; i < len; ++i) {
1880 currentChildFormElements.push(createFormElement(rawChildFormElements[i], identifierPath, formElement, registerPropertyValidators, disablePublishersOnSet));
1881 }
1882 formElement.set('renderables', currentChildFormElements, disablePublishersOnSet);
1883 }
1884 return formElement;
1885 };
1886
1887 /**
1888 * @public
1889 *
1890 * @param string collectionElementIdentifier
1891 * @param object collectionElementConfiguration
1892 * @param string collectionName
1893 * @return object
1894 * @throws 1475377160
1895 * @throws 1475377161
1896 * @throws 1475377162
1897 */
1898 function createPropertyCollectionElement(collectionElementIdentifier, collectionElementConfiguration, collectionName) {
1899 var collectionDefinition, collectionElementPresets;
1900 utility().assert(utility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475377160);
1901 utility().assert('object' === $.type(collectionElementConfiguration), 'Invalid parameter "collectionElementConfiguration"', 1475377161);
1902 utility().assert(utility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475377162);
1903
1904 collectionElementConfiguration['identifier'] = collectionElementIdentifier;
1905 collectionDefinition = repository().getFormEditorDefinition(collectionName, collectionElementIdentifier);
1906 if (collectionDefinition['predefinedDefaults']) {
1907 collectionElementPresets = collectionDefinition['predefinedDefaults'];
1908 } else {
1909 collectionElementPresets = {};
1910 }
1911
1912 return $.extend(collectionElementPresets, collectionElementConfiguration);
1913 };
1914
1915 /**
1916 * Publish the public methods.
1917 */
1918 return {
1919 createFormElement: createFormElement,
1920 createPropertyCollectionElement: createPropertyCollectionElement
1921 };
1922 };
1923
1924 /**
1925 * @public
1926 *
1927 * @return object
1928 */
1929 function dataBackend() {
1930
1931 /**
1932 * @public
1933 *
1934 * @param object endpoints
1935 * @return void
1936 * @throws 1475377488
1937 */
1938 function setEndpoints(endpoints) {
1939 utility().assert('object' === $.type(endpoints), 'Invalid parameter "endpoints"', 1475377488);
1940 _dataBackendEndpoints = endpoints;
1941 };
1942
1943 /**
1944 * @public
1945 *
1946 * @param string prototypeName
1947 * @return void
1948 * @throws 1475377489
1949 */
1950 function setPrototypeName(prototypeName) {
1951 utility().assert(utility().isNonEmptyString(prototypeName), 'Invalid parameter "prototypeName"', 1475928095);
1952 _dataBackendPrototypeName = prototypeName;
1953 };
1954
1955 /**
1956 * @public
1957 *
1958 * @param string persistenceIdentifier
1959 * @return void
1960 * @throws 1475377489
1961 */
1962 function setPersistenceIdentifier(persistenceIdentifier) {
1963 utility().assert(utility().isNonEmptyString(persistenceIdentifier), 'Invalid parameter "persistenceIdentifier"', 1475377489);
1964 _dataBackendPersistenceIdentifier = persistenceIdentifier;
1965 };
1966
1967 /**
1968 * @public
1969 *
1970 * @return void
1971 * @publish core/ajax/saveFormDefinition/success
1972 * @publish core/ajax/error
1973 * @throws 1475520918
1974 */
1975 function saveFormDefinition() {
1976 utility().assert(utility().isNonEmptyString(_dataBackendEndpoints['saveForm']), 'The endpoint "saveForm" is not configured', 1475520918);
1977
1978 if (_runningAjaxRequests['saveForm']) {
1979 _runningAjaxRequests['saveForm'].abort();
1980 }
1981
1982 _runningAjaxRequests['saveForm'] = $.post(_dataBackendEndpoints['saveForm'], {
1983 tx_form_web_formformbuilder: {
1984 formPersistenceIdentifier: _dataBackendPersistenceIdentifier,
1985 formDefinition: utility().convertToSimpleObject(getApplicationStateStack().getCurrentState('formDefinition'))
1986 }
1987 }, function(data, textStatus, jqXHR) {
1988 if (_runningAjaxRequests['saveForm'] !== jqXHR) {
1989 return;
1990 }
1991 _runningAjaxRequests['saveForm'] = null;
1992 if (data['status'] === 'success') {
1993 publisherSubscriber().publish('core/ajax/saveFormDefinition/success', [data]);
1994 } else {
1995 publisherSubscriber().publish('core/ajax/saveFormDefinition/error', [data]);
1996 }
1997 }).fail(function(jqXHR, textStatus, errorThrown) {
1998 publisherSubscriber().publish('core/ajax/error', [jqXHR, textStatus, errorThrown]);
1999 });
2000 };
2001
2002 /**
2003 * @public
2004 *
2005 * @param int pageIndex
2006 * @return void
2007 * @publish core/ajax/renderFormDefinitionPage/success
2008 * @publish core/ajax/error
2009 * @throws 1473447677
2010 * @throws 1475377781
2011 * @throws 1475377782
2012 */
2013 function renderFormDefinitionPage(pageIndex) {
2014 utility().assert($.isNumeric(pageIndex), 'Invalid parameter "pageIndex"', 1475377781);
2015 utility().assert(utility().isNonEmptyString(_dataBackendEndpoints['formPageRenderer']), 'The endpoint "formPageRenderer" is not configured', 1473447677);
2016
2017 if (_runningAjaxRequests['renderFormDefinitionPage']) {
2018 _runningAjaxRequests['renderFormDefinitionPage'].abort();
2019 }
2020
2021 _runningAjaxRequests['renderFormDefinitionPage'] = $.post(_dataBackendEndpoints['formPageRenderer'], {
2022 tx_form_web_formformbuilder: {
2023 formDefinition: utility().convertToSimpleObject(getApplicationStateStack().getCurrentState('formDefinition')),
2024 pageIndex: pageIndex,
2025 prototypeName: _dataBackendPrototypeName
2026 }
2027 }, function(data, textStatus, jqXHR) {
2028 if (_runningAjaxRequests['renderFormDefinitionPage'] !== jqXHR) {
2029 return;
2030 }
2031 _runningAjaxRequests['renderFormDefinitionPage'] = null;
2032 publisherSubscriber().publish('core/ajax/renderFormDefinitionPage/success', [data, pageIndex]);
2033 }).fail(function(jqXHR, textStatus, errorThrown) {
2034 publisherSubscriber().publish('core/ajax/error', [jqXHR, textStatus, errorThrown]);
2035 });
2036 };
2037
2038 /**
2039 * Publish the public methods.
2040 */
2041 return {
2042 renderFormDefinitionPage: renderFormDefinitionPage,
2043 saveFormDefinition: saveFormDefinition,
2044 setEndpoints: setEndpoints,
2045 setPersistenceIdentifier: setPersistenceIdentifier,
2046 setPrototypeName: setPrototypeName
2047 };
2048 };
2049
2050 /**
2051 * @public
2052 *
2053 * @return object
2054 */
2055 function getApplicationStateStack() {
2056
2057 /**
2058 * @public
2059 *
2060 * @param object applicationState
2061 * @param bool disablePublishersOnSet
2062 * @return void
2063 * @publish core/applicationState/add
2064 * @throws 1477847415
2065 */
2066 function add(applicationState, disablePublishersOnSet) {
2067 utility().assert('object' === $.type(applicationState), 'Invalid parameter "applicationState"', 1477847415);
2068 disablePublishersOnSet = !!disablePublishersOnSet;
2069
2070 $.extend(applicationState, {
2071 propertyValidationServiceRegisteredValidators: $.extend(true, {}, getCurrentState('propertyValidationServiceRegisteredValidators'))
2072 });
2073
2074 _applicationStateStack.splice(0, 0, applicationState);
2075 if (_applicationStateStack.length > _applicationStateStackSize) {
2076 _applicationStateStack.splice(_applicationStateStackSize - 1, (_applicationStateStack.length - _applicationStateStackSize));
2077 }
2078
2079 if (!disablePublishersOnSet) {
2080 publisherSubscriber().publish('core/applicationState/add', [applicationState, getCurrentStackPointer(), getCurrentStackSize()]);
2081 }
2082 };
2083
2084 /**
2085 * @public
2086 *
2087 * @param applicationState
2088 * @param bool disablePublishersOnSet
2089 * @return void
2090 * @publish core/applicationState/add
2091 * @throws 1477872641
2092 */
2093 function addAndReset(applicationState, disablePublishersOnSet) {
2094 utility().assert('object' === $.type(applicationState), 'Invalid parameter "applicationState"', 1477872641);
2095
2096 if (_applicationStateStackPointer > 0) {
2097 _applicationStateStack.splice(0, _applicationStateStackPointer);
2098 }
2099
2100 _applicationStateStackPointer = 0;
2101 add(applicationState, true);
2102
2103 if (!disablePublishersOnSet) {
2104 publisherSubscriber().publish('core/applicationState/add', [getCurrentState(), getCurrentStackPointer(), getCurrentStackSize()]);
2105 }
2106 };
2107
2108 /**
2109 * @public
2110 *
2111 * @param string
2112 * @return object
2113 * @throws 1477932754
2114 */
2115 function getCurrentState(type) {
2116 if (!utility().isUndefinedOrNull(type)) {
2117 utility().assert(
2118 'formDefinition' === type
2119 || 'currentlySelectedPageIndex' === type
2120 || 'currentlySelectedFormElementIdentifierPath' === type
2121 || 'propertyValidationServiceRegisteredValidators' === type,
2122
2123 'Invalid parameter "type"', 1477932754
2124 );
2125
2126 if ('undefined' === $.type(_applicationStateStack[_applicationStateStackPointer])) {
2127 return undefined;
2128 }
2129 return _applicationStateStack[_applicationStateStackPointer][type];
2130 }
2131 return _applicationStateStack[_applicationStateStackPointer];
2132 };
2133
2134 /**
2135 * @public
2136 *
2137 * @param string
2138 * @param mixed
2139 * @return void
2140 * @throws 1477934111
2141 */
2142 function setCurrentState(type, value) {
2143 utility().assert(
2144 'formDefinition' === type
2145 || 'currentlySelectedPageIndex' === type
2146 || 'currentlySelectedFormElementIdentifierPath' === type
2147 || 'propertyValidationServiceRegisteredValidators' === type,
2148
2149 'Invalid parameter "type"', 1477934111
2150 );
2151 _applicationStateStack[_applicationStateStackPointer][type] = value;
2152 };
2153
2154 /**
2155 * @public
2156 *
2157 * @param int
2158 * @return void
2159 * @throws 1477846933
2160 */
2161 function setMaximalStackSize(stackSize) {
2162 utility().assert('number' === $.type(stackSize), 'Invalid parameter "size"', 1477846933);
2163 _applicationStateStackSize = stackSize;
2164 };
2165
2166 /**
2167 * @public
2168 *
2169 * @return int
2170 */
2171 function getMaximalStackSize() {
2172 return _applicationStateStackSize;
2173 };
2174
2175 /**
2176 * @public
2177 *
2178 * @return int
2179 */
2180 function getCurrentStackSize() {
2181 return _applicationStateStack.length;
2182 };
2183
2184 /**
2185 * @public
2186 *
2187 * @return object
2188 */
2189 function getCurrentStackPointer() {
2190 return _applicationStateStackPointer;
2191 };
2192
2193 /**
2194 * @public
2195 *
2196 * @param int
2197 * @return void
2198 * @throws 1477852138
2199 */
2200 function setCurrentStackPointer(stackPointer) {
2201 utility().assert('number' === $.type(stackPointer), 'Invalid parameter "size"', 1477852138);
2202 if (stackPointer < 0) {
2203 _applicationStateStackPointer = 0;
2204 } else if (stackPointer > _applicationStateStack.length - 1) {
2205 _applicationStateStackPointer = _applicationStateStack.length - 1;
2206 } else {
2207 _applicationStateStackPointer = stackPointer;
2208 }
2209 };
2210
2211
2212 /**
2213 * @public
2214 *
2215 * @return void
2216 */
2217 function decrementCurrentStackPointer() {
2218 setCurrentStackPointer(--_applicationStateStackPointer);
2219 };
2220
2221 /**
2222 * @public
2223 *
2224 * @return void
2225 */
2226 function incrementCurrentStackPointer() {
2227 setCurrentStackPointer(++_applicationStateStackPointer);
2228 };
2229
2230 /**
2231 * Publish the public methods.
2232 */
2233 return {
2234 add: add,
2235 addAndReset: addAndReset,
2236 getCurrentState: getCurrentState,
2237 setCurrentState: setCurrentState,
2238 getCurrentStackPointer: getCurrentStackPointer,
2239 setCurrentStackPointer: setCurrentStackPointer,
2240 decrementCurrentStackPointer: decrementCurrentStackPointer,
2241 incrementCurrentStackPointer: incrementCurrentStackPointer,
2242 setMaximalStackSize: setMaximalStackSize,
2243 getMaximalStackSize: getMaximalStackSize,
2244 getCurrentStackSize: getCurrentStackSize
2245 };
2246 };
2247
2248 /**
2249 * Publish the public methods.
2250 * Implements the "Revealing Module Pattern".
2251 */
2252 return {
2253 getDataBackend: dataBackend,
2254 getFactory: factory,
2255 getPublisherSubscriber: publisherSubscriber,
2256 getRepository: repository,
2257 getUtility: utility,
2258 getPropertyValidationService: propertyValidationService,
2259 getRunningAjaxRequest: getRunningAjaxRequest,
2260 getApplicationStateStack: getApplicationStateStack
2261 };
2262 })($);
2263 });