%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/bitrix/www/bitrix/js/landing/ui/form/
Upload File :
Create Path :
Current File : /home/bitrix/www/bitrix/js/landing/ui/form/cards_form.js

;(function() {
	"use strict";

	BX.namespace("BX.Landing.UI.Form");

	var throttle = BX.Landing.Utils.throttle;
	var append = BX.Landing.Utils.append;
	var create = BX.Landing.Utils.create;
	var bind = BX.Landing.Utils.bind;
	var unbind = BX.Landing.Utils.unbind;
	var proxy = BX.Landing.Utils.proxy;
	var addClass = BX.Landing.Utils.addClass;
	var removeClass = BX.Landing.Utils.removeClass;
	var hasClass = BX.Landing.Utils.hasClass;
	var style = BX.Landing.Utils.style;
	var slice = BX.Landing.Utils.slice;
	var findParent = BX.Landing.Utils.findParent;
	var offsetTop = BX.Landing.Utils.offsetTop;
	var offsetLeft = BX.Landing.Utils.offsetLeft;
	var rect = BX.Landing.Utils.rect;
	var remove = BX.Landing.Utils.remove;
	var onTransitionEnd = BX.Landing.Utils.onTransitionEnd;
	var random = BX.Landing.Utils.random;
	var clone = BX.Landing.Utils.clone;
	var isString = BX.Landing.Utils.isString;
	var isArray = BX.Landing.Utils.isArray;
	var isEmpty = BX.Landing.Utils.isEmpty;
	var isPlainObject = BX.Landing.Utils.isPlainObject;
	var join = BX.Landing.Utils.join;
	var onCustomEvent = BX.Landing.Utils.onCustomEvent;

	var FormCollection = BX.Landing.UI.Collection.FormCollection;
	var BaseButton = BX.Landing.UI.Button.BaseButton;


	/**
	 * Implements interface for works with cards form
	 *
	 * @extends {BX.Landing.UI.Form.BaseForm}
	 * @param {{
	 * 		[title]: string,
	 * 		[presets]: object,
	 * 		[sync]: string|string[],
	 * 		[forms]: []
	 * 	}} data
	 * @constructor
	 */
	BX.Landing.UI.Form.CardsForm = function(data)
	{
		BX.Landing.UI.Form.BaseForm.apply(this, arguments);
		this.layout.classList.add("landing-ui-form-cards");
		this.type = "cards";
		this.code = data.code;
		this.presets = data.presets;
		this.childForms = new FormCollection();
		this.presetForm = new FormCollection();
		this.sync = data.sync;
		this.forms = data.forms;

		this.onItemClick = throttle(this.onItemClick, 200, this);
		this.onRemoveItemClick = proxy(this.onRemoveItemClick, this);
		this.onRemoveItemMouseenter = proxy(this.onRemoveItemMouseenter, this);
		this.onRemoveItemMouseleave = proxy(this.onRemoveItemMouseleave, this);
		this.onAddCardClick = proxy(this.onAddCardClick, this);

		this.addButton = this.createAddButton();
		this.wheelEventName = this.getWheelEventName();

		setTimeout(function() {
			this.value = this.serialize();
		}.bind(this));

		this.adjustLastFormState();
		append(this.addButton.layout, this.footer);
	};

	/**
	 * Wraps form
	 * @param {BX.Landing.UI.Form.CardForm} form
	 * @param {BX.Landing.UI.Form.CardsForm} parentForm
	 * @return {HTMLElement}
	 */
	function wrapForm(form, parentForm)
	{
		return create("div", {
			props: {className: "landing-ui-form-cards-item"},
			children: [
				create("div", {
					children: [
						create("div", {
							props: {className: "landing-ui-form-card-item-header"},
							events: {click: parentForm.onItemClick},
							children: [
								create("div", {
									props: {className: "landing-ui-form-card-item-header-left"},
									children: [
										create("div", {
											props: {className: "landing-ui-form-card-item-header-left-inner"},
											children: [
												create("span", {props: {className: "landing-ui-form-card-item-header-drag landing-ui-drag"}}),
												create("span", {props: {className: "landing-ui-form-card-item-header-title"}, children: [form.label]})
											]
										}),
										create("span", {
											props: {className: "landing-ui-form-card-item-header-edit"},
											children: [create("span", {props: {className: "fa fa-pencil"}})]
										})
									]
								}),
								create("div", {
									children: [
										create("span", {
											props: {className: "landing-ui-form-card-item-header-remove"},
											children: [create("span", {props: {className: "fa fa-remove"}})],
											events: {
												click: parentForm.onRemoveItemClick,
												mouseenter: parentForm.onRemoveItemMouseenter,
												mouseleave: parentForm.onRemoveItemMouseleave
											}
										})
									]
								})
							]
						}),
						form.layout
					]
				})
			]
		});
	}


	/**
	 * Makes item as draggable
	 * @param {HTMLElement} item
	 * @param {BX.Landing.UI.Form.CardsForm} parentForm
	 */
	function makeDraggable(item, parentForm)
	{
		var dragButton = item.querySelector(".landing-ui-form-card-item-header-drag");
		dragButton.onbxdragstart = onDragStart.bind(this);
		dragButton.onbxdrag = onDrag.bind(this);
		dragButton.onbxdragstop = onDragEnd.bind(this);

		jsDD.registerObject(dragButton);
		bind(dragButton, "mousedown", onDragButtonMousedown);
		bind(dragButton, "mouseup", onDragButtonMouseup);
		bind(dragButton, "click", onDragButtonClick);

		var scrollContainer = findParent(item, {className: "landing-ui-panel-content-body-content"});
		jsDD.setScrollWindow(scrollContainer);

		var itemStartRect;
		var startOffsetY;
		var startScroll;
		var dragIndex;
		var animationOffset;
		var targetItem;
		var currentItem;
		var minOffset;
		var maxOffset;

		function onDragStart()
		{
			scrollContainer = findParent(item, {className: "landing-ui-panel-content-body-content"});
			itemStartRect = rect(item);
			startOffsetY = Math.max(Math.abs(jsDD.start_y - itemStartRect.top), 0);
			startScroll = scrollContainer.scrollTop;
			dragIndex = [].indexOf.call(slice(item.parentElement.children), item);
			currentItem = findParent(jsDD.current_node, {className: "landing-ui-form-cards-item"});

			var itemComputedStyle = getComputedStyle(item);
			var marginTop = parseInt(itemComputedStyle.getPropertyValue("margin-top"));
			marginTop = marginTop === marginTop ? marginTop : 0;
			var marginBottom = parseInt(itemComputedStyle.getPropertyValue("margin-bottom"));
			marginBottom = marginBottom === marginBottom ? marginBottom : 0;
			animationOffset = itemStartRect.height + ((marginTop + marginBottom) / 2);
			minOffset = -offsetTop(currentItem, currentItem.parentElement);
			var parentRect = rect(currentItem.parentElement);
			maxOffset = parentRect.height - offsetTop(currentItem, currentItem.parentElement) - itemStartRect.height;
		}

		function onDrag(x, y)
		{
			var scrollOffset = startScroll - scrollContainer.scrollTop;
			var dragItemOffset = (y - itemStartRect.top - startOffsetY - scrollOffset);

			dragItemOffset = Math.min(Math.max(dragItemOffset, minOffset), maxOffset);

			void style(item, {
				zIndex: 999,
				transform: "translateY("+dragItemOffset+"px)"
			});

			slice(item.parentElement.children).forEach(function(current, index) {
				if (current !== item)
				{
					var currentRect = current.getBoundingClientRect();
					var currentMiddle = currentRect.top + BX.scrollTop(window) + (currentRect.height / 2);

					if (index > dragIndex && y > currentMiddle &&
						current.style.transform !== 'translate3d(0px, '+(-animationOffset)+'px, 0px)' &&
						current.style.transform !== '')
					{
						targetItem = current;

						void style(current, {
							'transform': 'translate3d(0px, '+(-animationOffset)+'px, 0px)',
							'transition': '300ms'
						});
					}

					if (index < dragIndex && y < currentMiddle &&
						current.style.transform !== 'translate3d(0px, '+(animationOffset)+'px, 0px)' &&
						current.style.transform !== '')
					{
						targetItem = current;

						void style(current, {
							'transform': 'translate3d(0px, '+(animationOffset)+'px, 0px)',
							'transition': '300ms'
						});
					}

					if (((index < dragIndex && y > currentMiddle) ||
							(index > dragIndex && y < currentMiddle)) &&
						current.style.transform !== 'translate3d(0px, 0px, 0px)')
					{
						if (currentMiddle > y)
						{
							targetItem = current.previousElementSibling;
						}
						else
						{
							targetItem = current.nextElementSibling;
						}

						void style(current, {
							'transform': 'translate3d(0px, 0px, 0px)',
							'transition': '300ms'
						});
					}
				}
			});
		}

		function onDragEnd()
		{
			slice(item.parentElement.children).forEach(function(currentItem) {
				void style(currentItem, {
					zIndex: null,
					transform: null,
					transition: null
				});
				setTimeout(function() {
					removeClass(currentItem, "landing-ui-form-card-item-draggable");
				}, 50);
			});

			if (currentItem && targetItem && currentItem !== targetItem && currentItem.parentNode === targetItem.parentNode)
			{
				var currentIndex = [].indexOf.call(targetItem.parentElement.children, currentItem);
				var targetIndex = [].indexOf.call(targetItem.parentElement.children, targetItem);

				if (targetItem.parentElement.children.length === targetIndex)
				{
					targetItem.parentElement.appendChild(target);
				}

				if (currentIndex > targetIndex)
				{
					targetItem.parentElement.insertBefore(currentItem, targetItem);
				}

				if (currentIndex < targetIndex && targetItem.parentElement.children.length !== targetIndex)
				{
					targetItem.parentElement.insertBefore(currentItem, targetItem.nextElementSibling);
				}

				if (parentForm.sync)
				{
					var syncSelectors = parentForm.sync;

					if (isString(parentForm.sync))
					{
						syncSelectors = [parentForm.sync];
					}

					if (isArray(syncSelectors))
					{
						syncSelectors.forEach(function(syncSelector) {
							var syncForm = parentForm.forms.find(function(currentForm) {
								return currentForm.code === syncSelector;
							});

							if (syncForm)
							{
								var syncCurrentItem = syncForm.body.children[currentIndex];
								var syncTargetItem = syncForm.body.children[targetIndex];

								if (syncTargetItem.parentElement.children.length === targetIndex)
								{
									syncTargetItem.parentElement.appendChild(target);
								}

								if (currentIndex > targetIndex)
								{
									syncTargetItem.parentElement.insertBefore(syncCurrentItem, syncTargetItem);
								}

								if (currentIndex < targetIndex && syncTargetItem.parentElement.children.length !== targetIndex)
								{
									syncTargetItem.parentElement.insertBefore(syncCurrentItem, syncTargetItem.nextElementSibling);
								}
							}

							syncForm.childForms.forEach(function(currentSyncForms) {
								var index = [].indexOf.call(
									currentSyncForms.layout.parentElement.parentElement.parentElement.children,
									currentSyncForms.layout.parentElement.parentElement
								);

								currentSyncForms.oldIndex = getSelectorIndex(currentSyncForms.selector);
								currentSyncForms.selector = join(currentSyncForms.selector.split("@")[0], "@", index);
							});

							syncForm.childForms.sort(function(a, b) {
								return parseInt(a.selector.split("@")[1]) < parseInt(b.selector.split("@")[1]) ? -1 : 1;
							});
						});
					}
				}
			}

			parentForm.childForms.forEach(function(form) {
				var index = [].indexOf.call(
					form.layout.parentElement.parentElement.parentElement.children,
					form.layout.parentElement.parentElement
				);

				form.oldIndex = getSelectorIndex(form.selector);
				form.selector = join(form.selector.split("@")[0], "@", index);
			});

			parentForm.childForms.sort(function(a, b) {
				return parseInt(a.selector.split("@")[1]) < parseInt(b.selector.split("@")[1]) ? -1 : 1;
			});
		}

		function onDragButtonClick(event)
		{
			event.preventDefault();
			event.stopPropagation();
		}

		function onDragButtonMousedown()
		{
			addClass(item, "landing-ui-form-card-item-draggable");
		}

		function onDragButtonMouseup()
		{
			removeClass(item, "landing-ui-form-card-item-draggable");
		}
	}


	/**
	 * @param {BX.Landing.UI.Form.CardForm} form
	 */
	function initBindings(form)
	{
		var labelSelectors = [];

		if (isString(form.labelBindings))
		{
			labelSelectors.push(form.labelBindings);
		}
		else if (isArray(form.labelBindings))
		{
			labelSelectors = labelSelectors.concat(form.labelBindings);
		}

		form.fields.forEach(function(field) {
			field.layout.hidden = true;
			field.reset();
			field.layout.hidden = false;
		});

		var textItemIndex = -1;

		labelSelectors.forEach(function(selector) {
			var field = form.fields.find(function(currentField) {
				return currentField.selector.split("@")[0] === selector;
			});

			if (field)
			{
				var item = findParent(form.layout, {className: "landing-ui-form-cards-item"});
				var labelContainer;

				if (field instanceof BX.Landing.UI.Field.Link)
				{
					labelContainer = item.querySelector(".landing-card-title-link");
					labelContainer.innerHTML = BX.message("LANDING_CARDS_FORM_ITEM_PLACEHOLDER_TEXT");

					onCustomEvent(field, "BX.Landing.UI.Field:change", function(value) {
						labelContainer.innerHTML = value.text;
					});

					return;
				}

				if (field instanceof BX.Landing.UI.Field.Icon)
				{
					labelContainer = item.querySelector(".landing-card-title-icon").firstElementChild;
					labelContainer.className = "landing-card-title-icon";
					onCustomEvent(field, "BX.Landing.UI.Field:change", function(value) {
						labelContainer.className = "landing-card-title-icon " + value.classList.join(" ");
					});

					return;
				}

				if (field instanceof BX.Landing.UI.Field.Image)
				{
					labelContainer = item.querySelector(".landing-card-title-img");
					labelContainer.style.backgroundColor = "#fafafa";
					labelContainer.innerHTML = "";

					onCustomEvent(field, "BX.Landing.UI.Field:change", function(value) {
						labelContainer.innerHTML = "";
						labelContainer.appendChild(create('img', {props: {src: value.src}}));
					});

					return;
				}

				if (field instanceof BX.Landing.UI.Field.Text)
				{
					textItemIndex += 1;
					var labelContainers = item.querySelectorAll(".landing-card-title-text");
					labelContainer = labelContainers[textItemIndex];

					onCustomEvent(field, "BX.Landing.UI.Field:change", function(value) {
						labelContainer.innerHTML = create("div", {html: value}).innerText;
					});

					if (labelContainer === labelContainers[0])
					{
						labelContainer.innerHTML = BX.message("LANDING_CARDS_FORM_ITEM_PLACEHOLDER_TEXT");
						field.setValue(BX.message("LANDING_CARDS_FORM_ITEM_PLACEHOLDER_TEXT"));
					}
					else
					{
						labelContainer.innerHTML = "";
					}
				}
			}
		});
	}

	/**
	 * Gets code from entity selector
	 * @param selector
	 * @return {string}
	 */
	function getCodeFromSelector(selector)
	{
		return isString(selector) ? selector.split("@")[0] : "";
	}

	/**
	 * Gets selector index
	 * @param selector
	 * @return {*}
	 */
	function getSelectorIndex(selector)
	{
		return isString(selector) ? selector.split("@")[1] : "";
	}

	/**
	 * Makes selector from code with index
	 * @param {string} code
	 * @param {int} index
	 * @return {string}
	 */
	function makeSelector(code, index)
	{
		return join(code.split("@")[0], "@", index);
	}


	BX.Landing.UI.Form.CardsForm.prototype = {
		constructor: BX.Landing.UI.Form.CardsForm,
		__proto__: BX.Landing.UI.Form.BaseForm.prototype,


		/**
		 * Gets wheel event name
		 * @return {string}
		 */
		getWheelEventName: function()
		{
			return !!window.onwheel ? "wheel" : "mousewheel";
		},


		/**
		 * Create "add button"
		 * @return {BX.Landing.UI.Button.BaseButton}
		 */
		createAddButton: function()
		{
			return new BaseButton("add-card-" + random(), {
				className: "landing-ui-card-add-button",
				text: BX.message("LANDING_CARDS_FORM_ADD_BUTTON"),
				onClick: this.onAddCardClick
			});
		},


		/**
		 * Handles item click event
		 * @param event
		 */
		onItemClick: function(event)
		{
			event.preventDefault();

			var target = findParent(event.currentTarget, {className: "landing-ui-form-cards-item"});

			if (!!target && !hasClass(target, "landing-ui-form-card-item-draggable"))
			{
				if (!hasClass(target, "landing-ui-form-cards-item-expand"))
				{
					addClass(target, "landing-ui-form-cards-item-expand");

					onTransitionEnd(target).then(function() {
						void style(target, {
							overflow: "visible"
						});
					});

					void style(target, {
						"height": "auto"
					});
				}
				else
				{
					removeClass(target, "landing-ui-form-cards-item-expand");
					void style(target, null);
				}
			}
		},

		onRemoveItemClick: function(event, preventSync)
		{
			event.stopPropagation();
			if (this.body.children.length > 1)
			{
				var item = findParent(event.currentTarget, {className: "landing-ui-form-cards-item"});
				remove(item);

				var formNode = item.querySelector(".landing-ui-form-card");
				var form = this.childForms.find(function(currentForm) {
					return currentForm.layout === formNode;
				});

				this.childForms.remove(form);

				if (preventSync !== true)
				{
					if (this.sync)
					{
						var syncSelectors = this.sync;

						if (isString(this.sync))
						{
							syncSelectors = [this.sync];
						}

						if (isArray(syncSelectors))
						{
							syncSelectors.forEach(function(syncSelector) {
								var syncedForm = this.forms.find(function(currentForm) {
									return currentForm.code === syncSelector;
								});

								if (syncedForm)
								{
									var childForm = syncedForm.childForms.find(function(currentChildForm) {
										return currentChildForm.selector.split("@")[1] === form.selector.split("@")[1];
									});

									syncedForm.onRemoveItemClick({
										currentTarget: childForm.layout,
										stopPropagation: (function() {})
									}, true);
								}
							}, this);
						}
					}
				}
			}

			this.adjustLastFormState();
		},

		onRemoveItemMouseenter: function(event)
		{
			event.stopPropagation();
			event.preventDefault();

			var header = findParent(event.currentTarget, {className: "landing-ui-form-card-item-header"});
			addClass(header, "landing-ui-form-card-item-header-onremove");
		},

		onRemoveItemMouseleave: function(event)
		{
			var header = findParent(event.currentTarget, {className: "landing-ui-form-card-item-header"});
			removeClass(header, "landing-ui-form-card-item-header-onremove");
		},

		addChildForm: function(form)
		{
			this.childForms.add(form);

			var formWrapper = wrapForm(form, this);
			append(formWrapper, this.body);
			makeDraggable(formWrapper, this);
			this.adjustLastFormState();
		},

		addPresetForm: function(form)
		{
			this.presetForm.add(form);
			var formWrapper = wrapForm(form, this);
			formWrapper.hidden = true;
			append(formWrapper, this.body);
			makeDraggable(formWrapper, this);
			this.adjustLastFormState();
		},


		onAddCardClick: function(preventSync)
		{
			if (isPlainObject(this.presets) && !isEmpty(this.presets))
			{
				this.showPresetsPopup();
			}
			else
			{
				this.addEmptyCard();

				if (preventSync !== true)
				{
					if (this.sync)
					{
						var syncSelectors = this.sync;

						if (isString(this.sync))
						{
							syncSelectors = [this.sync];
						}

						if (isArray(syncSelectors))
						{
							syncSelectors.forEach(function(syncSelector) {
								var form = this.forms.find(function(currentForm) {
									return currentForm.code === syncSelector;
								});

								if (form)
								{
									form.onAddCardClick(true);
								}
							}, this);
						}
					}
				}
			}
		},

		onPresetItemClick: function(presetId)
		{
			var preset = this.presets[presetId];

			var newForm = this.presetForm.find(function(form) {
				return form.preset.id === presetId;
			}).clone();

			newForm.selector = join(newForm.selector.split("@")[0], "@", this.childForms.length);
			newForm.oldIndex = this.childForms.length;
			newForm.preset = clone(preset);
			newForm.preset.id = presetId;
			this.addChildForm(newForm);
			initBindings(newForm);
			this.adjustLastFormState();
			this.popup.close();

			if (isPlainObject(preset.values))
			{
				newForm.fields.forEach(function(field) {
					var code = field.selector.split("@")[0];

					if (code in preset.values)
					{
						field.setValue(preset.values[code]);

						if (field instanceof BX.Landing.UI.Field.Text)
						{
							BX.fireEvent(field.input, "input");
						}
					}

					if (isArray(preset.disallow))
					{
						var isDisallow = !!preset.disallow.find(function(fieldCode) {
							return code === fieldCode;
						});

						if (isDisallow)
						{
							field.layout.hidden = true;
						}
					}
				});
			}
		},

		showPresetsPopup: function()
		{
			if (!this.popup)
			{
				this.popup = new BX.Landing.UI.Tool.Menu({
					id: "catalog_blocks_list",
					bindElement: this.addButton.layout,
					items: Object.keys(this.presets).map(function(preset) {
						return {
							text: this.presets[preset].name,
							className: "landing-ui-form-cards-preset-popup-item menu-popup-no-icon",
							onclick: this.onPresetItemClick.bind(this, preset)
						}
					}, this),
					autoHide: true,
					maxHeight: 176,
					minHeight: 87
				});

				bind(this.popup.popupWindow.popupContainer, "mouseover", this.onMouseOver.bind(this));
				bind(this.popup.popupWindow.popupContainer, "mouseleave", this.onMouseLeave.bind(this));
				bind(top.document, "click", this.onDocumentClick.bind(this));
				append(
					this.popup.popupWindow.popupContainer,
					findParent(this.addButton.layout, {className: "landing-ui-panel-content-body-content"})
				);
			}

			if (this.popup.popupWindow.isShown())
			{
				this.popup.popupWindow.close();
			}
			else
			{
				this.popup.popupWindow.show();
			}

			this.adjustPopupPosition();
		},


		/**
		 * Handles mouse over event
		 */
		onMouseOver: function()
		{
			bind(this.popup.popupWindow.popupContainer, this.wheelEventName, this.onMouseWheel.bind(this));
			bind(this.popup.popupWindow.popupContainer, "touchmove", this.onMouseWheel.bind(this));
		},


		/**
		 * Handles mouse leave event
		 */
		onMouseLeave: function()
		{
			unbind(this.popup.popupWindow.popupContainer, this.wheelEventName, this.onMouseWheel.bind(this));
			unbind(this.popup.popupWindow.popupContainer, "touchmove", this.onMouseWheel.bind(this));
		},


		/**
		 * Handle mouse wheel event
		 * @param event
		 */
		onMouseWheel: function(event)
		{
			event.stopPropagation();
			event.preventDefault();

			var delta = BX.Landing.UI.Panel.Content.getDeltaFromEvent(event);
			var scrollTop = this.popup.popupWindow.contentContainer.scrollTop;

			requestAnimationFrame(function() {
				this.popup.popupWindow.contentContainer.scrollTop = scrollTop - delta.y;
			}.bind(this));
		},


		/**
		 * Handles document click event
		 */
		onDocumentClick: function()
		{
			if (this.popup.popupWindow)
			{
				this.popup.popupWindow.close();
			}
		},


		/**
		 * Adjusts popup position
		 */
		adjustPopupPosition: function()
		{
			if (this.popup.popupWindow)
			{
				requestAnimationFrame(function() {
					var offsetParent = findParent(this.addButton.layout, {className: "landing-ui-panel-content-body-content"});

					var buttonTop = offsetTop(this.addButton.layout, offsetParent);
					var buttonLeft = offsetLeft(this.addButton.layout, offsetParent);
					var buttonRect = this.addButton.layout.getBoundingClientRect();
					var popupRect = this.popup.popupWindow.popupContainer.getBoundingClientRect();

					var yOffset = 14;

					this.popup.popupWindow.popupContainer.style.top = buttonTop + buttonRect.height + yOffset + "px";
					this.popup.popupWindow.popupContainer.style.left = buttonLeft - (popupRect.width / 2) + (buttonRect.width / 2) + "px";
					this.popup.popupWindow.setAngle({
						offset: 83,
						position: "top"
					})
				}.bind(this));
			}
		},


		/**
		 * Adds empty card form
		 */
		addEmptyCard: function()
		{
			var newData = clone(this.childForms[0].data);
			var newSelector = join(newData.selector.split("@")[0], "@", this.childForms.length);
			newData.selector = newSelector;
			var newForm = this.childForms[0].clone(newData);
			newForm.oldIndex = this.childForms.length;
			newForm.selector = newSelector;
			this.addChildForm(newForm);
			initBindings(newForm);
			this.adjustLastFormState();
		},


		/**
		 * Adjusts last form state
		 */
		adjustLastFormState: function()
		{
			if (this.body.children.length === 1)
			{
				addClass(this.body.firstElementChild, "landing-ui-disallow-remove");
				return;
			}

			slice(this.body.children).forEach(function(item) {
				removeClass(item, "landing-ui-disallow-remove");
			});
		},


		/**
		 * Serialize forms
		 * @return {object}
		 */
		serialize: function()
		{
			return this.childForms.map(function(form) {
				return form.serialize();
			});
		},


		/**
		 * Gets indexes map
		 * @return {Object}
		 */
		getIndexesMap: function()
		{
			return this.childForms
				.reduce(function(res, form, index) {
					return res[index] = form.oldIndex, res;
				}, {});
		},


		/**
		 * Gets used presets
		 * @return {object}
		 */
		getUsedPresets: function()
		{
			return this.childForms
				.reduce(function(res, form) {
					if (isPlainObject(form.preset))
					{
						res[getSelectorIndex(form.selector)] = form.preset.id;
					}

					return res;
				}, {});
		},

		isChanged: function()
		{
			return JSON.stringify(this.value) !== JSON.stringify(this.serialize());
		}
	};
})();

Zerion Mini Shell 1.0