Your IP : 3.129.245.116
;(function() {
"use strict";
BX.namespace("BX.Landing");
// Utils
var deepFreeze = BX.Landing.Utils.deepFreeze;
var style = BX.Landing.Utils.style;
var insertAfter = BX.Landing.Utils.insertAfter;
var insertBefore = BX.Landing.Utils.insertBefore;
var append = BX.Landing.Utils.append;
var isPlainObject = BX.Landing.Utils.isPlainObject;
var isBoolean = BX.Landing.Utils.isBoolean;
var isNumber = BX.Landing.Utils.isNumber;
var isString = BX.Landing.Utils.isString;
var isArray = BX.Landing.Utils.isArray;
var isEmpty = BX.Landing.Utils.isEmpty;
var addClass = BX.Landing.Utils.addClass;
var removeClass = BX.Landing.Utils.removeClass;
var hasClass = BX.Landing.Utils.hasClass;
var toggleClass = BX.Landing.Utils.toggleClass;
var create = BX.Landing.Utils.create;
var debounce = BX.Landing.Utils.debounce;
var throttle = BX.Landing.Utils.throttle;
var fireCustomEvent = BX.Landing.Utils.fireCustomEvent;
var onCustomEvent = BX.Landing.Utils.onCustomEvent;
var bind = BX.Landing.Utils.bind;
var unbind = BX.Landing.Utils.unbind;
var getClass = BX.Landing.Utils.getClass;
var rect = BX.Landing.Utils.rect;
var setTextContent = BX.Landing.Utils.setTextContent;
var translateY = BX.Landing.Utils.translateY;
var nextSibling = BX.Landing.Utils.nextSibling;
var prevSibling = BX.Landing.Utils.prevSibling;
var join = BX.Landing.Utils.join;
var slice = BX.Landing.Utils.slice;
var decodeDataValue = BX.Landing.Utils.decodeDataValue;
var encodeDataValue = BX.Landing.Utils.encodeDataValue;
var data = BX.Landing.Utils.data;
var attr = BX.Landing.Utils.attr;
var removePanels = BX.Landing.Utils.removePanels;
var getCSSSelector = BX.Landing.Utils.getCSSSelector;
var remove = BX.Landing.Utils.remove;
var clone = BX.Landing.Utils.clone;
var trim = BX.Landing.Utils.trim;
var prepend = BX.Landing.Utils.prepend;
var random = BX.Landing.Utils.random;
var htmlToElement = BX.Landing.Utils.htmlToElement;
var proxy = BX.Landing.Utils.proxy;
var escapeText = BX.Landing.Utils.escapeText;
var isValidElementId = BX.Landing.Utils.isValidElementId;
// Collections
var BaseCollection = BX.Landing.Collection.BaseCollection;
var NodeCollection = BX.Landing.Collection.NodeCollection;
var FormCollection = BX.Landing.UI.Collection.FormCollection;
var CardCollection = BX.Landing.Collection.CardCollection;
var PanelCollection = BX.Landing.UI.Collection.PanelCollection;
// Panels
var BaseButtonPanel = BX.Landing.UI.Panel.BaseButtonPanel;
var CardActionPanel = BX.Landing.UI.Panel.CardAction;
var ContentEditPanel = BX.Landing.UI.Panel.ContentEdit;
// Buttons
var BaseButton = BX.Landing.UI.Button.BaseButton;
var ActionButton = BX.Landing.UI.Button.ActionButton;
var PlusButton = BX.Landing.UI.Button.Plus;
var CardActionButton = BX.Landing.UI.Button.CardAction;
// Factories
var StyleFactory = BX.Landing.UI.Factory.StyleFactory;
// Forms
var BaseForm = BX.Landing.UI.Form.BaseForm;
var StyleForm = BX.Landing.UI.Form.StyleForm;
var CardForm = BX.Landing.UI.Form.CardForm;
var CardsForm = BX.Landing.UI.Form.CardsForm;
// Other
var Group = BX.Landing.Group;
var BlockEvent = BX.Landing.Event.Block;
var TabCard = BX.Landing.UI.Card.TabCard;
var DynamicFieldsGroup = BX.Landing.UI.Card.DynamicFieldsGroup;
// noinspection JSUnusedLocalSymbols
/**
* Access denied
* @type {string}
*/
var ACCESS_D = "D";
/**
* Design only
* @type {string}
*/
var ACCESS_V = "V";
/**
* Edit without delete
* @type {string}
*/
var ACCESS_W = "W";
/**
* All access
* @type {string}
*/
var ACCESS_X = "X";
function getTypeSettings(prop)
{
let lp = BX.Landing.Main.getInstance();
let namespaces = Object.keys(lp.options.style);
for (let i = 0; i < namespaces.length; i++)
{
let namespace = namespaces[i];
let type = lp.options.style[namespace]["style"][prop];
if (!type)
{
continue;
}
type.attrKey = prop;
if (prop === 'background')
{
const backgroundOverlayItems = lp.options.style[namespace]['style']['background-overlay']
.items.filter((obj) => obj.name !== 'g-bg--after');
type.items = [...type.items, ...backgroundOverlayItems];
}
return type;
}
return null;
}
function getAttrsTypeSettings(prop)
{
let lp = BX.Landing.Main.getInstance();
let namespaces = Object.keys(lp.options.attrs);
for (let i = 0; i < namespaces.length; i++)
{
let namespace = namespaces[i];
let attr = lp.options.attrs[namespace]["attrs"][prop];
if (!attr)
{
continue;
}
attr.attrKey = prop;
return attr;
}
return {};
}
function isGroup(prop)
{
let lp = BX.Landing.Main.getInstance();
let namespaces = Object.keys(lp.options.style);
for (let i = 0; i < namespaces.length; i++)
{
let namespace = namespaces[i];
if (!lp.options.style[namespace]["group"])
{
continue;
}
if (prop in lp.options.style[namespace]["group"])
{
return true;
}
}
return false;
}
function getGroupTypes(group)
{
let lp = BX.Landing.Main.getInstance();
let namespaces = Object.keys(lp.options.style);
for (let i = 0; i < namespaces.length; i++)
{
let namespace = namespaces[i];
if (!lp.options.style[namespace]["group"])
{
continue;
}
if (lp.options.style[namespace]["group"][group])
{
return lp.options.style[namespace]["group"][group];
}
}
return [];
}
/**
* Shows loader for button
* @param {BX.Landing.UI.Button.BaseButton} button
*/
function showButtonLoader(button)
{
if (!!button)
{
if (!button.loader)
{
button.loader = new BX.Loader({
target: button.layout,
size: 16
});
void style(button.loader.layout.querySelector(".main-ui-loader-svg-circle"), {
"stroke-width": "4px"
});
}
button.loader.show();
addClass(button.text, "landing-ui-hide-icon");
}
}
/**
* Hides button loader
* @param {BX.Landing.UI.Button.BaseButton} button
*/
function hideButtonLoader(button)
{
if (!!button)
{
if (button.loader)
{
button.loader.hide();
removeClass(button.text, "landing-ui-hide-icon");
}
}
}
/**
* @param {string} selector
* @return {boolean|*}
*/
function isNodeSelector(selector)
{
return !!selector && selector.includes("@");
}
var onBlockInitDebounced = BX.debounce(function() {
BX.Landing.PageObject.getBlocks().forEach(function(block) {
block.adjustSortButtonsState();
});
}, 400);
onCustomEvent("BX.Landing.Block:init", onBlockInitDebounced);
/**
* Implements interface for works with landing block
*
* @param {HTMLElement} element
* @param {blockOptions} options
*
* @property {BX.Landing.UI.Collection.PanelCollection.<BX.Landing.UI.Panel.BasePanel>} panels - Panels collection
* @property {BX.Landing.Collection.CardCollection.<BX.Landing.Block.Card>} cards - Cards collection
* @property {BX.Landing.Collection.NodeCollection.<BX.Landing.Node>} nodes - Nodes collection
* @property {blockManifest} manifest
*
* @constructor
*/
BX.Landing.Block = function(element, options)
{
this.node = element;
this.parent = element.parentElement;
this.content = element.firstElementChild;
this.siteId = data(element.parentElement, "data-site");
this.lid = data(element.parentElement, "data-landing");
this.id = isNumber(parseInt(options.id)) ? parseInt(options.id) : 0;
this.selector = join("#block", (isNumber(options.id) ? options.id : 0), " > :first-child");
this.repoId = isNumber(options.repoId) ? options.repoId : null;
this.active = isBoolean(options.active) ? options.active : true;
this.allowedByTariff = isBoolean(options.allowedByTariff) ? options.allowedByTariff : true;
this.manifest = isPlainObject(options.manifest) ? options.manifest : {};
this.manifest.nodes = isPlainObject(options.manifest.nodes) ? options.manifest.nodes : {};
this.manifest.cards = isPlainObject(options.manifest.cards) ? options.manifest.cards : {};
this.manifest.attrs = isPlainObject(options.manifest.attrs) ? options.manifest.attrs : {};
this.manifest.style = isPlainObject(options.manifest.style) ? options.manifest.style : {};
if (isPlainObject(options.manifest.style))
{
this.styleNodes = isPlainObject(options.manifest.style.nodes) ? options.manifest.style.nodes : {};
}
this.onStyleInputWithDebounce = debounce(this.onStyleInput, 300, this);
this.changeTimeout = null;
this.php = options.php;
this.designed = options.designed;
this.access = options.access;
this.anchor = options.anchor;
this.savedAnchor = options.anchor;
this.requiredUserActionOptions = options.requiredUserAction;
this.dynamicParams = options.dynamicParams || {};
this.sections = options.sections ? options.sections.split(',') : [];
// Make entities collections
this.nodes = new NodeCollection();
this.cards = new CardCollection();
this.panels = new PanelCollection();
this.groups = new BaseCollection();
this.changedNodes = new BaseCollection();
this.styles = new BaseCollection();
this.forms = new FormCollection();
this.menu = [];
if (isPlainObject(this.requiredUserActionOptions) && !isEmpty(this.requiredUserActionOptions))
{
this.showRequiredUserAction(this.requiredUserActionOptions);
this.requiredUserActionIsShown = true;
}
this.onEditorEnabled = this.onEditorEnabled.bind(this);
this.onEditorDisabled = this.onEditorDisabled.bind(this);
this.adjustPanelsPosition = this.adjustPanelsPosition.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onStorage = this.onStorage.bind(this);
this.onBlockRemove = this.onBlockRemove.bind(this);
// Make manifest read only
deepFreeze(this.manifest);
// Apply block state
this.node.classList[this.active ? "remove" : "add"]("landing-block-disabled");
// Sets state
this.state = "ready";
// Init panels
this.initPanels();
this.initStyles();
this.initMenu();
this.adjustContextSensitivityStyles();
var envOptions = BX.Landing.Env.getInstance().getOptions();
if (this.isDefaultCrmFormBlock() || this.isCrmFormBlock())
{
const uri = new BX.Uri(window.top.location.toString());
if (BX.Text.toBoolean(uri.getQueryParam('replacedLanding')))
{
uri.removeQueryParam('replacedLanding');
top.window.history.replaceState({}, document.title, uri.toString());
this.onStyleShow();
setTimeout(() => {
const y = this.node.offsetTop;
BX.Landing.PageObject.getEditorWindow().scrollTo(0, y);
}, 300);
}
else
{
const showOptions = {
formId: envOptions.formEditorData.formOptions.id,
formOptions: this.getCrmFormOptions(),
block: this,
showWithOptions: true,
};
if (BX.Text.toBoolean(uri.getQueryParam('formCreated')))
{
showOptions.state = 'presets';
}
void BX.Landing.UI.Panel.FormSettingsPanel
.getInstance()
.show(showOptions)
;
}
}
BX.Landing.PageObject.getBlocks().push(this);
// Fire block init event
var eventData = {};
if (this.requiredUserActionIsShown)
{
eventData.requiredUserActionIsShown = true;
eventData.layout = this.node.firstElementChild;
eventData.button = this.node.firstElementChild.querySelector(".ui-btn");
}
fireCustomEvent(window, "BX.Landing.Block:init", [this.createEvent({data: eventData})]);
onCustomEvent("BX.Landing.Editor:enable", this.onEditorEnabled);
onCustomEvent("BX.Landing.Editor:disable", this.onEditorDisabled);
onCustomEvent("BX.Landing.Block:afterRemove", this.onBlockRemove);
bind(this.node, "mousemove", this.onMouseMove);
bind(this.node, "keydown", this.adjustPanelsPosition);
bind(top, "storage", this.onStorage);
};
BX.Landing.Block.storage = new BX.Landing.Collection.BlockCollection();
BX.Landing.Block.prototype = {
/**
* Handles mouse move event on block node
* Implements lazy initialization entities of block
* @private
*/
onMouseMove: function()
{
if (this.state === "ready")
{
unbind(this.node, "mousemove", this.onMouseMove);
this.initEntities();
this.lazyInitPanels();
this.state = "complete";
}
},
getBlockNode: function()
{
return this.node;
},
isAllowedByTariff: function()
{
return this.allowedByTariff;
},
showRequiredUserAction: function(data)
{
let container = this.node;
if (data.targetNodeSelector)
{
container = this.node.querySelector(data.targetNodeSelector);
}
container.innerHTML = (
"<div class=\"landing-block-user-action\">" +
"<div class=\"landing-block-user-action-inner\">" +
(data.header ? (
"<h3>"+"<i class=\"fa fa-exclamation-triangle g-mr-15\"></i>"+data.header+"</h3><hr>"
) : "") +
(data.description ? (
"<p>"+data.description+"</p>"
) : "") +
((data.href || data.onClick || data.className) && data.text ? (
"<div>" +
"<a href=\""+data.href+"\" class=\"landing-trusted-link ui-btn "+data.className+"\" target=\""+(data.target ? data.target : '')+"\">"+data.text+"</a>" +
"</div>"
) : "") +
"</div>" +
"</div>"
);
if (data.onClick)
{
var button = container.querySelector('.landing-block-user-action .ui-btn');
bind(button, 'click', function(event) {
event.preventDefault();
try
{
BX.evalGlobal(data.onClick);
}
catch (err)
{
console.error(err);
}
});
}
},
/**
* Disables content links and buttons
*/
disableLinks: function()
{
var selector = "a:not([class*='landing-ui']):not(.landing-trusted-link), .btn:not([class*='landing-ui']):not(.landing-trusted-link), button:not([class*='landing-ui']):not(.landing-trusted-link), input:not([class*='landing-ui'])";
var items = slice(this.content.querySelectorAll(selector));
items.forEach(function(item) {
var isChildOfNode = this.nodes.some(function(node) {
return node.node.contains(item);
});
var isMenuItem = this.menu.some(function(menu) {
return menu.root.contains(item);
});
if (!this.nodes.getByNode(item) && !isChildOfNode && !isMenuItem)
{
item.style.pointerEvents = "none";
}
}, this);
},
/**
* Adjusts context sensitivity styles
*/
adjustContextSensitivityStyles: function()
{
if (hasClass(this.parent, "landing-sidebar"))
{
if (!hasClass(this.content, "landing-adjusted"))
{
var selectors = Object.keys(this.manifest.style.nodes);
var needAdjust = selectors.filter(function(selector) {
return (
!!this.manifest.style.nodes[selector].type &&
this.manifest.style.nodes[selector].type.indexOf("columns") !== -1
);
}, this);
if (isEmpty(needAdjust))
{
return;
}
var columnsSettings = getTypeSettings("columns");
if (columnsSettings === null)
{
return;
}
needAdjust.forEach(function(selector) {
var styleNode = this.styles.get(selector);
if (styleNode)
{
styleNode.setIsSelectGroup(true);
styleNode.setValue("col-lg-12", columnsSettings.items);
styleNode.unsetIsSelectGroupFlag();
}
}, this);
var blockStyleNode = this.styles.get(this.selector);
if (blockStyleNode)
{
blockStyleNode.setValue("landing-adjusted", ["landing-adjusted"]);
}
this.saveStyles();
}
}
},
/**
* Forces block initialization
*/
forceInit: function()
{
this.onMouseMove();
},
/**
* Creates block event object
* @param {object} [options]
* @returns {BX.Landing.Event.Block}
*/
createEvent: function(options)
{
return new BlockEvent({
block: this.node,
node: !!options && !!options.node ? options.node : null,
card: !!options && !!options.card ? options.card : null,
data: (!!options && options.data) || {},
onForceInit: this.forceInit.bind(this)
});
},
/**
* Initializes block entities
* @private
*/
initEntities: function()
{
this.initCards();
this.initNodes();
this.initGroups();
this.disableLinks();
},
initMenu: function()
{
if (BX.type.isPlainObject(this.manifest.menu))
{
this.menu = Object.entries(this.manifest.menu).map(function(entry) {
var code = entry[0];
var value = entry[1];
return new BX.Landing.Menu.Menu({
code: code,
root: this.node.querySelector(code),
manifest: value,
block: this.id,
});
}, this);
}
},
initCardsLabels: function()
{
this.cards.forEach(function(card) {
card.label = this.createCardLabel(card.node, card.manifest);
}, this);
},
/**
* Initializes groups
*/
initGroups: function()
{
var groupsIds = [];
var groups = isPlainObject(this.manifest.groups) ? this.manifest.groups : {};
this.nodes.forEach(function(node) {
if (isString(node.manifest.group) && !groupsIds.includes(node.manifest.group))
{
groupsIds.push(node.manifest.group);
}
});
groupsIds.forEach(function(groupId) {
var nodes = this.nodes
.filter(function(node) {
return node.manifest.group === groupId;
})
.reduce(function(accumulator, node) {
var nodeIndex = parseInt(node.selector.split("@")[1]);
if (!accumulator[nodeIndex])
{
accumulator[nodeIndex] = new NodeCollection();
}
accumulator[nodeIndex].push(node);
return accumulator;
}, {});
Object.keys(nodes).forEach(function(key) {
this.groups.add(
new Group({
id: groupId,
name: groups[groupId],
nodes: nodes[key],
onClick: this.onGroupClick.bind(this)
})
);
}, this);
}, this);
},
/**
* Handles event on group click
* @param {BX.Landing.Group} group
*/
onGroupClick: function(group)
{
if (!BX.Landing.UI.Panel.StylePanel.getInstance().isShown())
{
this.showContentPanel({
name: group.name,
nodes: group.nodes,
compact: true,
nodesOnly: true,
showAll: true,
hideCheckbox: true
});
}
},
/**
* Initializes block panels
*/
initPanels: function()
{
// Make "add block after this block" button
if (!this.panels.get("create_action"))
{
var createPanel = new BaseButtonPanel(
"create_action",
"landing-ui-panel-create-action"
);
createPanel.addButton(
new PlusButton("insert_after", {
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_CREATE"),
onClick: throttle(this.addBlockAfterThis, 600, this)
})
);
createPanel.show();
this.addPanel(createPanel);
if (this.isCrmFormPage())
{
var createBeforePanel = new BaseButtonPanel(
"create_before_action",
"landing-ui-panel-create-before-action"
);
createBeforePanel.addButton(
new PlusButton("insert_before", {
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_CREATE"),
onClick: throttle(this.addBlockBeforeThis, 600, this)
})
);
createBeforePanel.show();
this.addPanel(createBeforePanel);
}
createPanel.buttons[0].on("mouseover", this.onCreateButtonMouseover.bind(this));
createPanel.buttons[0].on("mouseout", this.onCreateButtonMouseout.bind(this));
}
},
isLastChildInArea: function()
{
return this.parent.querySelector("[class*='block-wrapper']:last-of-type") === this.node;
},
onCreateButtonMouseover: function()
{
if (this.isLastChildInArea() ||
hasClass(this.parent, "landing-header") ||
hasClass(this.parent, "landing-footer"))
{
var areas = BX.Landing.Main.getInstance().getLayoutAreas();
if (areas.length > 1)
{
var addBlockPanel = this.panels.get("create_action");
var addButton = addBlockPanel.buttons.get("insert_after");
switch (true)
{
case hasClass(this.parent, "landing-main"):
addButton.setText([
BX.Landing.Loc.getMessage("ACTION_BUTTON_CREATE"),
BX.Landing.Loc.getMessage("LANDING_ADD_BLOCK_TO_MAIN")
].join(" "));
break;
case hasClass(this.parent, "landing-header"):
addButton.setText([
BX.Landing.Loc.getMessage("ACTION_BUTTON_CREATE"),
BX.Landing.Loc.getMessage("LANDING_ADD_BLOCK_TO_HEADER")
].join(" "));
break;
case hasClass(this.parent, "landing-sidebar"):
addButton.setText([
BX.Landing.Loc.getMessage("ACTION_BUTTON_CREATE"),
BX.Landing.Loc.getMessage("LANDING_ADD_BLOCK_TO_SIDEBAR")
].join(" "));
break;
case hasClass(this.parent, "landing-footer"):
addButton.setText([
BX.Landing.Loc.getMessage("ACTION_BUTTON_CREATE"),
BX.Landing.Loc.getMessage("LANDING_ADD_BLOCK_TO_FOOTER")
].join(" "));
break;
}
clearTimeout(this.fadeTimeout);
this.fadeTimeout = setTimeout(function() {
addClass(this.parent, "landing-area-highlight");
areas.forEach(function(area) {
if (area !== this.parent)
{
addClass(area, "landing-area-fade");
}
}, this);
}.bind(this), 400);
}
}
},
onCreateButtonMouseout: function()
{
clearTimeout(this.fadeTimeout);
if (this.isLastChildInArea() ||
hasClass(this.parent, "landing-header") ||
hasClass(this.parent, "landing-footer"))
{
var areas = BX.Landing.Main.getInstance().getLayoutAreas();
if (areas.length > 1)
{
var addButton = this.panels.get("create_action").buttons[0];
addButton.setText(BX.Landing.Loc.getMessage("ACTION_BUTTON_CREATE"));
removeClass(this.parent, "landing-area-highlight");
areas.forEach(function(area) {
removeClass(area, "landing-area-fade");
}, this);
}
}
},
isInSidebar: function()
{
return !!this.node.closest(".landing-sidebar");
},
initSidebarActionPanel: function()
{
if (this.isInSidebar() && !this.panels.contains("sidebar_actions"))
{
var sidebarActionsPanel = new BaseButtonPanel(
"sidebar_actions",
"landing-ui-panel-sidebar-actions"
);
sidebarActionsPanel.addButton(
new ActionButton("showSidebarActions", {
onClick: this.onShowSidebarActionsClick.bind(this),
})
);
this.addPanel(sidebarActionsPanel);
sidebarActionsPanel.show();
}
},
showFeedbackForm: function() {
BX.Landing.Main.getInstance().showSliderFeedbackForm({
blockName: this.manifest.block.name,
blockCode: this.manifest.code,
blockSection: this.manifest.block.section,
landingId: BX.Landing.Main.getInstance().id,
target: "blockActions"
});
if (this.blockActionsMenu)
{
this.blockActionsMenu.close();
}
if (this.sidebarActionsMenu)
{
this.sidebarActionsMenu.close();
}
},
onShowSidebarActionsClick: function(event)
{
var bindElement = (
this.panels.get("sidebar_actions").buttons.get('showSidebarActions')
);
if (!this.sidebarActionsMenu)
{
this.sidebarActionsMenu = BX.Main.MenuManager.create({
id: this.id + '_sidebar_actions',
bindElement: bindElement.layout,
className: "landing-ui-block-actions-popup",
angle: {position: "top", offset: 95},
offsetTop: -6,
offsetLeft: -26,
events: {
onPopupClose: function() {
this.panels.get("sidebar_actions").buttons.get("showSidebarActions").deactivate();
removeClass(this.node, "landing-ui-hover");
}.bind(this)
},
items: [
(function() {
if (
(isPlainObject(this.manifest.nodes) || isPlainObject(this.manifest.attrs))
&& this.isAllowedByTariff()
)
{
return new BX.Main.MenuItem({
id: "content",
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_CONTENT"),
onclick: function() {
this.onShowContentPanel();
this.sidebarActionsMenu.close();
}.bind(this)
});
}
}.bind(this))(),
(function() {
if (isPlainObject(this.manifest.style) && this.isAllowedByTariff())
{
return new BX.Main.MenuItem({
id: "style",
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_STYLE"),
onclick: function() {
this.onStyleShow();
this.sidebarActionsMenu.close();
}.bind(this),
className: this.access < ACCESS_V ? "landing-ui-disabled" : ""
});
}
}.bind(this))(),
(function() {
if (isPlainObject(this.manifest.style) && this.isAllowedByTariff())
{
return new BX.Main.MenuItem({
id: "designblock",
text: BX.Landing.Loc.getMessage("LANDING_BLOCKS_ACTIONS_DESIGN_BLOCK"),
className: !this.isDesignBlockAllowed() ? "landing-ui-disabled" : "",
onclick: function() {
this.onDesignerBlockClick();
this.sidebarActionsMenu.close();
}.bind(this)
});
}
}.bind(this))(),
(function() {
if (this.isAllowedByTariff())
{
return new BX.Main.MenuItem({
delimiter: true,
});
}
}.bind(this))(),
(function() {
var allPlacements = BX.Landing.Main.getInstance().options.placements.blocks;
if (isPlainObject(allPlacements) && (this.manifest.code in allPlacements || allPlacements["*"]))
{
var placementsList = [];
if (this.manifest.code in allPlacements)
{
Object.keys(allPlacements[this.manifest.code]).forEach(function(key) {
placementsList.push(allPlacements[this.manifest.code][key]);
}, this);
}
if (allPlacements["*"])
{
Object.keys(allPlacements["*"]).forEach(function(key) {
placementsList.push(allPlacements["*"][key]);
}, this);
}
if (placementsList.length)
{
if (typeof BX.Landing.PageObject.getRootWindow().BX.rest !== "undefined" &&
typeof BX.Landing.PageObject.getRootWindow().BX.rest.AppLayout !== "undefined")
{
var codes = ["*", this.manifest.code];
for (var i = 0, c = codes.length; i < c; i++)
{
var MessageInterface = BX.Landing.PageObject.getRootWindow().BX.rest.AppLayout.initializePlacement(
"LANDING_BLOCK_" + codes[i]
);
if (MessageInterface)
{
MessageInterface.prototype.refreshBlock = function(params, cb) {
var block = BX.Landing.PageObject.getBlocks().get(params.id);
if (block)
{
block
.reload()
.then(cb);
}
};
}
}
}
return new BX.Main.MenuItem({
id: "actions",
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_CONTENT_MORE"),
items: placementsList.map(function(placement) {
return new BX.Main.MenuItem({
id: "placement_" + placement.id + "_" + random(),
text: encodeDataValue(placement.title),
onclick: this.onPlacementClick.bind(this, placement)
})
}, this),
className: this.access < ACCESS_V ? "landing-ui-disabled" : ""
});
}
}
}.bind(this))(),
new BX.Main.MenuItem({
id: "down",
text: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_SORT_DOWN"),
onclick: function() {
this.moveDown();
this.sidebarActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
id: "up",
text: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_SORT_UP"),
onclick: function() {
this.moveUp();
this.sidebarActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
delimiter: true,
}),
new BX.Main.MenuItem({
id: "show_hide",
text: BX.Landing.Loc.getMessage(this.isEnabled() ? "ACTION_BUTTON_HIDE" : "ACTION_BUTTON_SHOW"),
className: !this.isChangeStateBlockAllowed() ? "landing-ui-disabled" : "",
onclick: function() {
this.onStateChange();
this.sidebarActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
delimiter: true,
}),
new BX.Main.MenuItem({
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_ACTIONS_CUT"),
className: !this.isRemoveBlockAllowed() ? "landing-ui-disabled" : "",
onclick: function() {
BX.Landing.Main.getInstance().onCutBlock.bind(BX.Landing.Main.getInstance(), this)();
this.sidebarActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_ACTIONS_COPY"),
onclick: function() {
BX.Landing.Main.getInstance().onCopyBlock.bind(BX.Landing.Main.getInstance(), this)();
this.sidebarActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
id: "block_paste",
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_ACTIONS_PASTE"),
title: window.localStorage.landingBlockName,
className: this.isPasteBlockAllowed() ? "": "landing-ui-disabled",
onclick: function() {
BX.Landing.Main.getInstance().onPasteBlock.bind(BX.Landing.Main.getInstance(), this)();
this.sidebarActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
delimiter: true,
}),
new BX.Main.MenuItem({
text: BX.Landing.Loc.getMessage("LANDING_BLOCKS_ACTIONS_FEEDBACK_BUTTON"),
onclick: this.showFeedbackForm.bind(this)
}),
new BX.Main.MenuItem({
text: BX.Landing.Loc.getMessage("LANDING_BLOCKS_ACTIONS_SAVE_BLOCK_BUTTON_MSGVER_1"),
className: !this.isSaveBlockInLibraryAllowed() ? "landing-ui-disabled" : "",
onclick: function() {
this.saveBlock();
this.sidebarActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
delimiter: true,
}),
new BX.Main.MenuItem({
id: "remove",
text: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_REMOVE"),
onclick: function() {
this.deleteBlock();
this.sidebarActionsMenu.close();
}.bind(this),
className: !this.isRemoveBlockAllowed() ? "landing-ui-disabled" : ""
})
]
});
}
this.sidebarActionsMenu.show();
addClass(this.node, "landing-ui-hover");
},
/**
* Initializes action panels of block
* @private
*/
lazyInitPanels: function()
{
if (this.isInSidebar())
{
this.initSidebarActionPanel();
}
var allPlacements = BX.Landing.Main.getInstance().options.placements.blocks;
// Make content actions panel
if (
!this.panels.contains("content_actions")
&& (
(isPlainObject(this.manifest.nodes) && !isEmpty(this.manifest.nodes))
|| (isPlainObject(this.manifest.style) && !isEmpty(this.manifest.style))
|| (isPlainObject(allPlacements) && !isEmpty(allPlacements))
)
)
{
var contentPanel = new BaseButtonPanel(
"content_actions",
"landing-ui-panel-content-action"
);
contentPanel.addButton(
new ActionButton("collapse", {
html: "<span class='fas fa-caret-right'></span>",
onClick: this.onCollapseActionPanel.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_COLLAPSE")},
separate: true,
})
);
if (this.isAllowedByTariff())
{
if (isPlainObject(this.manifest.style))
{
contentPanel.addButton(
new ActionButton("designblock", {
text: BX.Landing.Loc.getMessage("LANDING_BLOCKS_ACTIONS_DESIGN_BLOCK"),
onClick: this.onDesignerBlockClick.bind(this),
disabled: !this.isDesignBlockAllowed(),
attrs: { title: BX.Landing.Loc.getMessage("LANDING_BLOCKS_ACTIONS_DESIGN_BLOCK") },
separate: true,
})
);
contentPanel.addButton(
new ActionButton("style", {
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_STYLE"),
onClick: this.onStyleShow.bind(this),
disabled: !this.isStyleModifyAllowed(),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_DESIGN")},
separate: true,
})
);
}
if (isPlainObject(this.manifest.nodes) || isPlainObject(this.manifest.attrs) )
{
contentPanel.addButton(
new ActionButton("content", {
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_CONTENT"),
onClick: this.onShowContentPanel.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_EDIT")},
separate: true,
})
);
}
}
else {
contentPanel.addButton(
new ActionButton("expired", {
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_EXPIRED"),
separate: true,
})
);
}
if (isPlainObject(allPlacements) && (this.manifest.code in allPlacements || allPlacements["*"]))
{
var placementsList = [];
if (this.manifest.code in allPlacements)
{
Object.keys(allPlacements[this.manifest.code]).forEach(function(key) {
placementsList.push(allPlacements[this.manifest.code][key]);
}, this);
}
if (allPlacements["*"])
{
Object.keys(allPlacements["*"]).forEach(function(key) {
placementsList.push(allPlacements["*"][key]);
}, this);
}
if (placementsList.length)
{
contentPanel.addButton(
new ActionButton("actions", {
html: BX.Landing.Loc.getMessage("ACTION_BUTTON_CONTENT_MORE"),
onClick: this.onPlacementButtonClick.bind(this, placementsList),
separate: true,
})
);
if (typeof BX.Landing.PageObject.getRootWindow().BX.rest !== "undefined" &&
typeof BX.Landing.PageObject.getRootWindow().BX.rest.AppLayout !== "undefined")
{
var codes = ["*", this.manifest.code];
for (var i = 0, c = codes.length; i < c; i++)
{
var MessageInterface = BX.Landing.PageObject.getRootWindow().BX.rest.AppLayout.initializePlacement(
"LANDING_BLOCK_" + codes[i]
);
if (MessageInterface)
{
MessageInterface.prototype.refreshBlock = function(params, cb) {
var block = BX.Landing.PageObject.getBlocks().get(params.id);
if (block)
{
block
.reload()
.then(cb);
}
};
}
}
}
}
}
if (isPlainObject(this.manifest.style))
{
var blockDisplay = new ActionButton("block_display_info", {
html: " ",
separate: true,
onClick: this.onStyleShow.bind(this),
});
bind(blockDisplay.layout, "mouseenter", this.onBlockDisplayMouseenter.bind(this));
bind(blockDisplay.layout, "mouseleave", this.onBlockDisplayMouseleave.bind(this));
contentPanel.addButton(
blockDisplay
);
}
contentPanel.show();
this.addPanel(contentPanel);
}
// Make block actions panel
if (!this.panels.get("block_action"))
{
var blockPanel = new BaseButtonPanel(
"block_action",
"landing-ui-panel-block-action"
);
var block = this.getBlockFromRepository(this.manifest.code);
if (block && block.restricted)
{
var restrictedButton = new ActionButton("restricted", {
html: "!",
className: "landing-ui-block-restricted-button",
onClick: this.onRestrictedButtonClick.bind(this),
separate: true
});
bind(restrictedButton.layout, "mouseenter", this.onRestrictedButtonMouseenter.bind(this));
bind(restrictedButton.layout, "mouseleave", this.onRestrictedButtonMouseleave.bind(this));
blockPanel.addButton(restrictedButton);
}
blockPanel.addButton(
new ActionButton("down", {
html: BX.Landing.Loc.getMessage("ACTION_BUTTON_DOWN"),
onClick: this.moveDown.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_SORT_DOWN")}
})
);
blockPanel.addButton(
new ActionButton("up", {
html: BX.Landing.Loc.getMessage("ACTION_BUTTON_UP"),
onClick: this.moveUp.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_SORT_UP")}
})
);
blockPanel.addButton(
new ActionButton("actions", {
html: BX.Landing.Loc.getMessage("ACTION_BUTTON_ACTIONS"),
onClick: this.showBlockActionsMenu.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_ADDITIONAL_ACTIONS")}
})
);
blockPanel.addButton(
new ActionButton("remove", {
html: BX.Landing.Loc.getMessage("ACTION_BUTTON_REMOVE"),
disabled: !this.isRemoveBlockAllowed(),
onClick: this.deleteBlock.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_REMOVE")}
})
);
blockPanel.addButton(
new ActionButton("collapse", {
html: "<span class='fas fa-caret-right'></span>",
onClick: this.onCollapseActionPanel.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_BLOCK_ACTION_COLLAPSE")},
separate: true
})
);
blockPanel.show();
this.addPanel(blockPanel);
}
this.adjustPanelsPosition();
this.adjustSortButtonsState();
},
onCollapseActionPanel: function()
{
toggleClass(this.parent, "landing-ui-collapse");
},
getBlockFromRepository: function(code)
{
var blocks = BX.Landing.Main.getInstance().options.blocks;
var categories = Object.keys(blocks);
var category = categories.find(function(categoryId) {
return code in blocks[categoryId].items;
});
if (category)
{
return blocks[category].items[code];
}
},
onRestrictedButtonClick: function(event)
{
event.preventDefault();
},
onPlacementClick: function(placement)
{
BX.rest.AppLayout.openApplication(
placement.app_id,
{
ID: this.id,
CODE: this.manifest.code,
LID: BX.Landing.Main.getInstance().id
},
{
PLACEMENT: 'LANDING_BLOCK_' + placement.placement,
PLACEMENT_ID: placement.id
}
);
if (this.blockPlacementsActionsMenu)
{
this.blockPlacementsActionsMenu.close();
}
},
onPlacementButtonClick: function(placements)
{
this.panels.get("content_actions").buttons.get("actions").activate();
if (!this.blockPlacementsActionsMenu)
{
var blockActionButton = this.panels.get("content_actions").buttons.get("actions");
var blockActionMenuId = join("block_", this.id, "content_placement_actions_", random());
var menuItems = placements.map(function(placement) {
return new BX.Main.MenuItem({
id: "placement_" + (placement.id || random()) + "_" + random(),
text: encodeDataValue(placement.title),
disabled: placement.disabled === true,
onclick: (typeof placement.onClick === 'function')
? placement.onClick
: this.onPlacementClick.bind(this, placement)
})
}, this);
this.blockPlacementsActionsMenu = new BX.PopupMenuWindow({
id: blockActionMenuId,
bindElement: blockActionButton.layout,
items: menuItems,
angle: {position: "top", offset: 80},
offsetTop: -6,
events: {
onPopupClose: function() {
this.panels.get("content_actions").buttons.get("actions").deactivate();
removeClass(this.node, "landing-ui-hover");
}.bind(this)
}
});
}
addClass(this.node, "landing-ui-hover");
this.blockPlacementsActionsMenu.show();
},
onDesignerBlockClick: function()
{
// get actual block content before designer edit
var oldContent = null;
BX.Landing.Backend.getInstance()
.action("Block::getContent", {
block: this.id,
lid: this.lid,
siteId: this.siteId,
editMode: 1
})
.then(function(response) {
oldContent = response.content;
});
// open slider with designer
var envOptions = BX.Landing.Env.getInstance().getOptions();
var sliderUrl = envOptions.params.sef_url["design_block"]
.replace("__block_id__", this.id)
.replace("__site_show__", this.siteId)
.replace("__landing_edit__", this.lid)
+ "&code=" + this.manifest.code
+ "&designed=" + (this.designed ? "Y" : "N")
+ "&deviceCode=" + BX.Landing.Main.getInstance().getDeviceCode();
BX.SidePanel.Instance.open(
sliderUrl,
{
cacheable: false,
allowChangeHistory: false,
requestMethod: "post",
customLeftBoundary: 40,
events: {
onClose: function(event)
{
// get actual block content after designer edit
BX.Landing.Backend.getInstance()
.action("Block::getContent", {
block: this.id,
lid: this.lid,
siteId: this.siteId,
editMode: 1
})
.then(function(response) {
var newContent = response.content;
if (oldContent !== newContent)
{
BX.Landing.History.getInstance().push();
this.reload().then(function()
{
fireCustomEvent("BX.Landing.Block:onDesignerBlockSave", [this.id]);
}.bind(this));
// analytic label on close
var metrika = new BX.Landing.Metrika(true);
metrika.sendLabel(
null,
"designerBlock",
"close" +
"&designed=" + (this.designed ? "Y" : "N") +
"&code=" + this.manifest.code
);
}
}.bind(this));
}.bind(this)
}
}
);
if (this.blockPlacementsActionsMenu)
{
this.blockPlacementsActionsMenu.close();
}
},
isDesignBlockAllowed: function()
{
return !(this.access < ACCESS_W || this.php || (this.isCrmFormPage() && this.isCrmFormBlock()));
},
isStyleModifyAllowed:function()
{
return !(this.access < ACCESS_V || isEmpty(this.manifest.style));
},
isEditBlockAllowed: function()
{
return this.access >= ACCESS_W;
},
isRemoveBlockAllowed: function()
{
return !(this.access < ACCESS_X || (this.isCrmFormBlock() && this.isDefaultCrmFormBlock()));
},
isPasteBlockAllowed: function()
{
return window.localStorage.landingBlockId && !this.isDefaultCrmFormBlock();
},
isSaveBlockInLibraryAllowed: function()
{
return !this.isDefaultCrmFormBlock();
},
isChangeStateBlockAllowed: function()
{
return !((this.access < ACCESS_W) || this.isDefaultCrmFormBlock());
},
saveBlock: function()
{
BX.Landing.Main.getInstance().showSaveBlock(this);
},
onRestrictedButtonMouseenter: function(event)
{
clearTimeout(this.displayBlockTimer);
this.displayBlockTimer = setTimeout(function(target) {
BX.Landing.UI.Tool.Suggest.getInstance().show(target, {
description: BX.Landing.Loc.getMessage("LANDING_BLOCK_RESTRICTED_TEXT")
});
}.bind(this), 200, event.currentTarget);
},
onRestrictedButtonMouseleave: function()
{
clearTimeout(this.displayBlockTimer);
BX.Landing.UI.Tool.Suggest.getInstance().hide();
},
onBlockDisplayMouseenter: function(event)
{
clearTimeout(this.displayBlockTimer);
this.displayBlockTimer = setTimeout(function(target) {
BX.Landing.UI.Tool.Suggest.getInstance().show(
target,
{
name: create("div", {
props: {className: "landing-ui-block-display-message-header"},
html: BX.Landing.Loc.getMessage("LANDING_BLOCK_DISABLED_ON_DESKTOP_NAME_2")
}).outerHTML,
description: this.getBlockDisplayItems()
}
);
}.bind(this), 300, event.currentTarget);
},
onBlockDisplayMouseleave: function()
{
clearTimeout(this.displayBlockTimer);
BX.Landing.UI.Tool.Suggest.getInstance().hide();
},
getBlockDisplayItems: function()
{
function createItem(mod)
{
return create("div", {
props: {className: "landing-ui-block-display-message"},
attrs: {"data-mod": mod},
children: [
create("div", {
props: {className: "landing-ui-block-display-message-left"},
html: " "
}),
create("div", {
props: {className: "landing-ui-block-display-message-right"},
children: [
create("p", {html: BX.Landing.Loc.getMessage("LANDING_BLOCK_HIDDEN_ON_"+(mod ? mod.toUpperCase() : ""))})
]
})
]
});
}
var result = create("div");
if (hasClass(this.content, "l-d-lg-none"))
{
result.appendChild(createItem("desktop"));
}
if (hasClass(this.content, "l-d-md-none"))
{
result.appendChild(createItem("tablet"));
}
if (hasClass(this.content, "l-d-xs-none"))
{
result.appendChild(createItem("mobile"));
}
return result.outerHTML;
},
/**
* Adjusts block panels position
*/
adjustPanelsPosition: function()
{
var blockRect = rect(this.node);
var contentActionsPanel = this.panels.get("content_actions");
var blockActionsPanel = this.panels.get("block_action");
var action = blockRect.height < 80 ? addClass : removeClass;
if (contentActionsPanel)
{
action(contentActionsPanel.layout, "landing-ui-panel-actions-compact");
}
if (blockActionsPanel)
{
action(blockActionsPanel.layout, "landing-ui-panel-actions-compact");
}
},
/**
* Handles editor enable event
* Event fires when the editor was show
* @param {HTMLElement} element
*/
onEditorEnabled: function(element)
{
if (this.node.contains(element))
{
addClass(this.node, "landing-ui-hover");
}
},
/**
* Handles editor disable event
* Event fires when the editor was hidden
*/
onEditorDisabled: function()
{
removeClass(this.node, "landing-ui-hover");
},
onStorage: function()
{
var menu = (this.blockActionsMenu || this.sidebarActionsMenu);
if (menu)
{
var item = menu.getMenuItem("block_paste");
if (item)
{
if (window.localStorage.landingBlockId)
{
item.layout.item.setAttribute("title", window.localStorage.landingBlockName);
removeClass(item.layout.item, "landing-ui-disabled");
addClass(item.layout.item, "menu-popup-no-icon");
}
else
{
item.layout.item.setAttribute("title", "");
addClass(item.layout.item, "landing-ui-disabled");
}
}
}
},
/**
* Shows block popup menu with additional action menu
*/
showBlockActionsMenu: function()
{
this.panels.get("block_action").buttons.get("actions").activate();
if (!this.blockActionsMenu)
{
var useSmallOffset = hasClass(this.node.parentElement, "landing-sidebar");
var blockActionButton = this.panels.get("block_action").buttons.get("actions");
var blockActionMenuId = join("block_", this.id, "_actions_", random());
var landing = BX.Landing.Main.getInstance();
this.blockActionsMenu = new BX.PopupMenuWindow({
id: blockActionMenuId,
bindElement: blockActionButton.layout,
className: "landing-ui-block-actions-popup",
angle: {position: "top", offset: useSmallOffset ? 70 : 146},
offsetTop: -6,
offsetLeft: -26,
events: {
onPopupClose: function() {
this.panels.get("block_action").buttons.get("actions").deactivate();
removeClass(this.node, "landing-ui-hover");
}.bind(this),
onPopupShow: function() {
BX.Event.EventEmitter.emit('BX.Landing.PopupMenuWindow:onShow');
}.bind(this)
},
items: [
new BX.Main.MenuItem({
id: "show_hide",
text: BX.Landing.Loc.getMessage(this.isEnabled() ? "ACTION_BUTTON_HIDE" : "ACTION_BUTTON_SHOW"),
className: !this.isChangeStateBlockAllowed() ? "landing-ui-disabled" : "",
onclick: function() {
this.onStateChange();
this.blockActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_ACTIONS_CUT"),
className: !this.isRemoveBlockAllowed() ? "landing-ui-disabled" : "",
onclick: function() {
landing.onCutBlock.bind(landing, this)();
this.blockActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_ACTIONS_COPY"),
className: this.isDefaultCrmFormBlock() ? "landing-ui-disabled" : "",
onclick: function() {
landing.onCopyBlock.bind(landing, this)();
this.blockActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
id: "block_paste",
text: BX.Landing.Loc.getMessage("ACTION_BUTTON_ACTIONS_PASTE"),
title: window.localStorage.landingBlockName,
className: this.isPasteBlockAllowed() ? "": "landing-ui-disabled",
onclick: function() {
landing.onPasteBlock.bind(landing, this)();
this.blockActionsMenu.close();
}.bind(this)
}),
new BX.Main.MenuItem({
text: BX.Landing.Loc.getMessage("LANDING_BLOCKS_ACTIONS_FEEDBACK_BUTTON"),
onclick: this.showFeedbackForm.bind(this)
}),
new BX.Main.MenuItem({
delimiter: true,
}),
new BX.Main.MenuItem({
text: BX.Landing.Loc.getMessage("LANDING_BLOCKS_ACTIONS_SAVE_BLOCK_BUTTON_MSGVER_1"),
className: !this.isSaveBlockInLibraryAllowed() ? "landing-ui-disabled" : "",
onclick: function() {
this.saveBlock();
this.blockActionsMenu.close();
}.bind(this)
}),
]
});
}
addClass(this.node, "landing-ui-hover");
this.blockActionsMenu.show();
},
/**
* Moves the block up one position
* @param {boolean} [preventHistory = false] - Add this action to history or not. By default - add
*/
moveUp: function(preventHistory)
{
var prev = prevSibling(this.node, "block-wrapper");
var current = this.node;
if (prev)
{
var result = Promise.all([
translateY(current, -rect(prev).height),
translateY(prev, rect(current).height)
]);
result.then(function() {
void style(current, {"transform": null, "transition": null});
void style(prev, {"transform": null, "transition": null});
insertBefore(current, prev);
if (!preventHistory || typeof preventHistory === "object")
{
BX.Landing.Backend.getInstance()
.action(
"Landing::upBlock",
{block: this.id, lid: this.lid, siteId: this.siteId},
{code: this.manifest.code}
)
.then(() => {
BX.Landing.History.getInstance().push();
});
}
}.bind(this));
}
},
/**
* Moves the block down one position
* @param {boolean} [preventHistory = false] - Add this action to history or not. By default - add
*/
moveDown: function(preventHistory)
{
var next = nextSibling(this.node, "block-wrapper");
var current = this.node;
if (!!next)
{
var result = Promise.all([
translateY(current, rect(next).height),
translateY(next, -rect(current).height)
]);
result.then(function() {
void style(current, {"transform": null, "transition": null});
void style(next, {"transform": null, "transition": null});
insertAfter(current, next);
if (!preventHistory || typeof preventHistory === "object")
{
BX.Landing.Backend.getInstance()
.action(
"Landing::downBlock",
{block: this.id, lid: this.lid, siteId: this.siteId},
{code: this.manifest.code}
)
.then(() => {
BX.Landing.History.getInstance().push();
});
}
}.bind(this));
}
},
/**
* Adds panel into this block
* @param {BX.Landing.UI.Panel.BasePanel} panel
* @param {*} [target = this.node]
*/
addPanel: function(panel, target)
{
if (!this.panels.contains(panel))
{
this.panels.add(panel);
if (!target)
{
if (panel.id === 'content_edit' && window.parent)
{
let topWindow = BX.Landing.PageObject.getRootWindow();
append(panel.layout, topWindow.document.body);
}
else
{
append(panel.layout, this.node);
}
}
else
{
insertBefore(panel.layout, target);
}
}
},
/**
*
* @return {{code: string, id: string, type: string}|null}
*/
getBlockFormId: function()
{
var formScriptNode = this.node.querySelector('script[data-b24-form]');
if (BX.Type.isDomNode(formScriptNode))
{
var formId = BX.Dom.attr(formScriptNode, 'data-b24-form');
if (BX.Type.isStringFilled(formId))
{
var parsedFormId = formId.split('/');
if (BX.Type.isArray(parsedFormId) && parsedFormId.length === 3)
{
var instanceId = '';
var formUid = BX.Dom.attr(
formScriptNode.previousSibling.firstChild,
'id'
);
if (formUid)
{
instanceId = formUid.replace('b24-', '');
}
return {
id: parsedFormId[1],
type: parsedFormId[0],
code: parsedFormId[2],
instanceId: instanceId
};
}
}
}
formScriptNode = this.node.querySelector('[data-b24form]');
if (BX.Type.isDomNode(formScriptNode))
{
formId = BX.Dom.attr(formScriptNode, 'data-b24form');
if (BX.Type.isStringFilled(formId))
{
parsedFormId = formId.split('|');
if (BX.Type.isArray(parsedFormId) && parsedFormId.length === 3)
{
instanceId = '';
formUid = BX.Dom.attr(
formScriptNode.querySelector('.b24-form > div[id]'),
'id'
);
if (formUid)
{
instanceId = formUid.replace('b24-', '');
}
return {
id: parsedFormId[0],
type: parsedFormId[2] || 'inline',
code: parsedFormId[1],
instanceId: instanceId
};
}
}
}
return null;
},
getCrmFormOptions: function()
{
var formNode = this.node.querySelector('[data-b24form-use-style]');
var useAllowed = BX.Dom.attr(formNode, 'data-b24form-use-style');
var primaryMatcher = /--primary([\da-fA-F]{2})/;
if (BX.Type.isDomNode(formNode) && BX.Text.toBoolean(useAllowed))
{
var designOptions = BX.Dom.attr(formNode, 'data-b24form-design');
if (BX.Type.isPlainObject(designOptions))
{
var primaryColor = BX.Dom.style(document.documentElement, '--primary').trim();
Object.entries(designOptions.color).forEach(function(entry) {
if (
entry[1] === '--primary'
|| entry[1].match(primaryMatcher) !== null
)
{
designOptions.color[entry[0]] = entry[1].replace('--primary', primaryColor);
}
});
return {
data: {
design: designOptions,
}
};
}
}
return {};
},
isCrmFormPage: function()
{
return BX.Landing.Env.getInstance().getOptions().specialType === 'crm_forms';
},
isCrmFormBlock: function()
{
return this.isCrmFormPage() && BX.Dom.attr(this.node, 'data-subtype') === 'form';
},
isDefaultCrmFormBlock: function()
{
return BX.Dom.hasClass(this.node, 'block-66-90-form-new-default');
},
/**
* Handles show panel event
* @private
*/
onShowContentPanel: function()
{
var formId = this.getBlockFormId();
var type = BX.Text.capitalize(
BX.Landing.Env.getInstance().getOptions().params.type
);
if (
BX.Type.isPlainObject(formId)
&& type !== 'SMN'
)
{
var rootWindow = BX.Landing.PageObject.getRootWindow();
void (function() {
if (BX.Landing.UI.Panel.FormSettingsPanel)
{
return Promise.resolve([
rootWindow.BX.Landing.UI.Panel,
BX.Landing.UI.Panel
]);
}
return Promise
.all([
rootWindow.BX.Runtime
.loadExtension('landing.ui.panel.formsettingspanel'),
BX.Runtime
.loadExtension('landing.ui.panel.formsettingspanel')
]);
})()
.then(function(result) {
var FormSettingsPanel = result[1].FormSettingsPanel;
if (FormSettingsPanel)
{
return FormSettingsPanel
.getInstance()
.show({
formId: formId.id,
instanceId: formId.instanceId,
formOptions: this.getCrmFormOptions(),
block: this,
});
}
}.bind(this));
}
else
{
this.showContentPanel();
}
BX.Landing.UI.Panel.EditorPanel.getInstance().hide();
},
/**
* Handles state change event
* @private
*/
onStateChange: function()
{
if (this.isEnabled())
{
this.disable();
}
else
{
this.enable();
}
},
/**
* Checks that block is enabled
* @return {boolean}
*/
isEnabled: function()
{
return this.active;
},
/**
* Enables block
*/
enable: function()
{
this.active = true;
removeClass(this.node, "landing-block-disabled");
let menu = (this.blockActionsMenu || this.sidebarActionsMenu);
if (menu)
{
setTextContent(menu.getMenuItem("show_hide").getLayout().text, BX.Landing.Loc.getMessage("ACTION_BUTTON_HIDE"));
}
fireCustomEvent("BX.Landing.Block:changeState", [this.id, true]);
BX.Landing.Backend.getInstance().action(
"Landing::showBlock",
{block: this.id, lid: this.lid, siteId: this.siteId},
{code: this.manifest.code}
);
},
/**
* Disables block
*/
disable: function()
{
this.active = false;
addClass(this.node, "landing-block-disabled");
let menu = (this.blockActionsMenu || this.sidebarActionsMenu);
if (menu)
{
setTextContent(menu.getMenuItem("show_hide").getLayout().text, BX.Landing.Loc.getMessage("ACTION_BUTTON_SHOW"));
}
fireCustomEvent("BX.Landing.Block:changeState", [this.id, false]);
BX.Landing.Backend.getInstance().action(
"Landing::hideBlock",
{block: this.id, lid: this.lid, siteId: this.siteId},
{code: this.manifest.code}
);
},
/**
* Creates card label
* @param {HTMLElement} node
* @param {cardManifest} manifest
* @return {div}
*/
createCardLabel: function(node, manifest)
{
var labelSelectors = [];
if (isString(manifest.label))
{
labelSelectors.push(manifest.label);
}
else if (isArray(manifest.label))
{
labelSelectors = labelSelectors.concat(manifest.label);
}
var cardNodes = this.nodes.filter(function(currentNode) {
return node.contains(currentNode.node);
});
var children = [];
labelSelectors.forEach(function(selector) {
var labelNode = cardNodes.find(function(currentNode) {
return currentNode.manifest.code === selector;
});
if (labelNode)
{
var currentLabel;
if (labelNode instanceof BX.Landing.Node.Text)
{
currentLabel = create("span", {
props: {className: "landing-card-title-text"},
html: escapeText(create("div", {html: labelNode.getValue()}).innerText)
});
children.push(currentLabel);
onCustomEvent(labelNode.getField(), "change", function(value) {
currentLabel.innerHTML = escapeText(create("div", {html: value}).innerText);
});
return;
}
if (labelNode instanceof BX.Landing.Node.Link)
{
currentLabel = create("span", {
props: {className: "landing-card-title-link"},
html: escapeText(labelNode.getValue().text)
});
children.push(currentLabel);
onCustomEvent(labelNode.getField(), "change", function(value) {
currentLabel.innerHTML = escapeText(value.text);
});
return;
}
if (labelNode instanceof BX.Landing.Node.Icon)
{
currentLabel = create("span", {
props: {className: "landing-card-title-icon"},
children: [create("span", {props: {className: labelNode.getValue().classList.join(" ")}})]
});
children.push(currentLabel);
onCustomEvent(labelNode.getField(), "change", function(value) {
currentLabel.firstChild.className = "landing-card-title-icon " + value.classList.join(" ");
});
return;
}
if (labelNode instanceof BX.Landing.Node.Img)
{
currentLabel = create("span", {
props: {className: "landing-card-title-img"},
attrs: {
style: "background-color: #fafafa"
},
children: [create('img', {props: {src: labelNode.getValue().src}})]
});
children.push(currentLabel);
onCustomEvent(labelNode.getField(), "change", function(value) {
currentLabel.innerHTML = "";
currentLabel.appendChild(create('img', {props: {src: value.src}}));
});
}
}
}, this);
return create("div", {
props: {className: "landing-card-title"},
children: !isEmpty(children) ? children : manifest.name
});
},
/**
* Init Cards of Block.
*/
initCards: function()
{
if (this.access < ACCESS_W)
{
return;
}
this.cards.clear();
this.forEachCard(function(node, selector, index) {
var manifest = BX.clone(this.manifest.cards[selector]);
var cardSelector = join(selector, "@", index);
if (this.isDynamicCards(selector))
{
manifest.allowInlineEdit = false;
}
removePanels(node);
var instance = new BX.Landing.Block.Card(node, manifest, cardSelector);
this.cards.add(instance);
if (manifest.allowInlineEdit !== false)
{
var cardAction = new CardActionPanel(
"cardAction",
"landing-ui-panel-block-card-action"
);
cardAction.show();
instance.addPanel(cardAction);
cardAction.addButton(new CardActionButton("clone", {
html: " ",
onClick: function(event) {
event.stopPropagation();
if (instance.manifest.sync)
{
var syncedSelectors = instance.manifest.sync;
if (isString(instance.manifest.sync))
{
syncedSelectors = [instance.manifest.sync];
}
if (isArray(syncedSelectors))
{
syncedSelectors.forEach(function(currentSyncSelector) {
this.cloneCard(join(currentSyncSelector, "@", index));
}, this);
}
}
this.cloneCard(cardSelector);
}.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_CARD_ACTION_CLONE")}
}));
cardAction.addButton(new CardActionButton("remove", {
html: " ",
onClick: function(event) {
event.stopPropagation();
if (instance.manifest.sync)
{
var syncedSelectors = instance.manifest.sync;
if (isString(instance.manifest.sync))
{
syncedSelectors = [instance.manifest.sync];
}
if (isArray(syncedSelectors))
{
syncedSelectors.forEach(function(currentSyncSelector) {
this.removeCard(join(currentSyncSelector, "@", index));
}, this);
}
}
this.removeCard(cardSelector);
}.bind(this),
attrs: {title: BX.Landing.Loc.getMessage("LANDING_TITLE_OF_CARD_ACTION_REMOVE")}
}));
}
instance.selector = cardSelector;
instance.sortIndex = index;
this.adjustCardRemoveButton(cardSelector);
});
this.cards.sort(function(a, b) {
return a.getIndex() > b.getIndex();
});
},
/**
* Clones Card.
* @param {string} selector - Selector of Card, which want clone.
* @param {?boolean} [preventHistory = false]
* @return {Promise}
*/
cloneCard: function(selector, preventHistory)
{
var card = this.cards.getBySelector(selector);
var cloneButton = card.panels.get("cardAction").buttons.get("clone");
var requestData = {block: this.id, selector: selector, lid: this.lid, siteId: this.siteId};
var queryParams = {code: this.manifest.code};
var self = this;
showButtonLoader(cloneButton);
let clonePromise = Promise.resolve();
if ((isBoolean(preventHistory) && !preventHistory) || !isBoolean(preventHistory))
{
requestData.preventHistory = 0;
clonePromise = BX.Landing.Backend.getInstance()
.action("Landing\\Block::cloneCard", requestData, queryParams)
.then(result => {
BX.Landing.History.getInstance().push();
return result;
});
}
return clonePromise
.then(function() {
fireCustomEvent("BX.Landing.Block:Card:beforeAdd", [
self.createEvent({card: card.node})
]);
})
// Clone
.then(function() {
var clonedCard = BX.clone(card.node);
removePanels(clonedCard);
insertAfter(clonedCard, card.node);
return clonedCard;
})
// After clone
.then(function(clonedCard) {
hideButtonLoader(cloneButton);
fireCustomEvent("BX.Landing.Block:Card:add", [
self.createEvent({card: clonedCard})
]);
self.initEntities();
self.initStyles();
})
// Handle errors
.catch(function() {
hideButtonLoader(cloneButton);
return Promise.reject();
});
},
/**
* Removes Card.
* @param {String} selector - Selector of Card, which want remove.
* @param {?boolean} [preventHistory = false]
* @return {Promise}
*/
removeCard: function(selector, preventHistory)
{
var card = this.cards.getBySelector(selector);
var removeButton = card.panels.get("cardAction").buttons.get("remove");
var requestData = {block: this.id, selector: selector, lid: this.lid, siteId: this.siteId};
var queryParams = {code: this.manifest.code};
var self = this;
showButtonLoader(removeButton);
let removePromise = Promise.resolve();
if ((isBoolean(preventHistory) && !preventHistory) || !isBoolean(preventHistory))
{
requestData.preventHistory = 0;
removePromise = BX.Landing.Backend.getInstance()
.action("Landing\\Block::removeCard", requestData, queryParams)
.then(result => {
BX.Landing.History.getInstance().push();
return result;
});
}
return removePromise
// Before remove
.then(function() {
fireCustomEvent("BX.Landing.Block:Card:beforeRemove", [
self.createEvent({card: card.node})
]);
removePanels(card.node);
})
// Remove
.then(function() {
self.cards.remove(card);
card.node.remove();
self.initEntities();
self.adjustCardRemoveButton(selector);
})
// After remove
.then(function() {
var afterEvent = self.createEvent({data: {selector: selector}});
fireCustomEvent("BX.Landing.Block:Card:remove", [afterEvent]);
hideButtonLoader(removeButton);
})
.catch(function() {
hideButtonLoader(removeButton);
return Promise.reject();
});
},
adjustCardRemoveButton: function(selector)
{
var card = this.cards.getBySelector(selector);
if (card)
{
var cardAction = card.panels.get("cardAction");
if (cardAction)
{
var cardIntoSlider = BX.hasClass(card.node, 'landing-block-card-carousel-element');
var cardsParent = card.node.closest(".landing-block-node-carousel-container");
if (!cardIntoSlider || !cardsParent)
{
var lastCardInCollection = card.node.parentElement.children.length === 1;
if (lastCardInCollection)
{
cardAction.buttons.get("remove").disable();
}
else
{
cardAction.buttons.get("remove").enable();
}
}
else
{
var cardsAmount = cardsParent.querySelectorAll('.landing-block-card-carousel-element').length;
if (cardsAmount > 1)
{
cardAction.buttons.get("remove").enable();
}
else
{
cardAction.buttons.get("remove").disable();
}
}
}
}
},
/**
* Adds card
* @param {{[index]: !int, container: !HTMLElement, content: string, selector: string}} settings
* @param {boolean} [preventHistory]
* @return {Promise}
*/
addCard: function(settings, preventHistory)
{
var selector = settings.selector.split("@")[0] + (settings.index > 0 ? "@"+(settings.index-1) : "");
var requestData = {
block: this.id,
content: settings.content,
selector: selector,
lid: this.lid,
siteId: this.siteId,
};
var queryParams = {code: this.manifest.code};
var container = settings.container;
var card = create("div", {html: settings.content}).firstElementChild;
var self = this;
let addPromise = Promise.resolve();
if ((isBoolean(preventHistory) && !preventHistory) || !isBoolean(preventHistory))
{
requestData.preventHistory = 0;
addPromise = BX.Landing.Backend.getInstance()
.action("Landing\\Block::addCard", requestData, queryParams)
.then(result => {
BX.Landing.History.getInstance().push();
return result;
});
}
return addPromise
.then(function() {
fireCustomEvent("BX.Landing.Block:Card:beforeAdd", [
self.createEvent({card: card})
]);
})
.then(function() {
var targetCard;
if (settings.index <= 0)
{
targetCard = self.cards.find(function(card) {
return card.selector.includes(selector.split("@")[0])
});
if (targetCard)
{
prepend(card, targetCard.node.parentNode);
}
}
else
{
targetCard = self.cards.getBySelector(selector.split("@")[0] + "@" + (settings.index-1));
if (targetCard)
{
insertAfter(card, targetCard.node);
}
}
removePanels(container);
self.initEntities();
fireCustomEvent("BX.Landing.Block:Card:add", [
self.createEvent({card: card})
]);
})
},
/**
* @callback cardCallback
* @param {HTMLElement} node
* @param {string} selector
* @param {int} index
*/
/**
* Applies callback function for each card node
* @param {cardCallback} callback
*/
forEachCard: function(callback)
{
var cardSelectors = Object.keys(this.manifest.cards);
cardSelectors.map(function(cardSelector) {
var cards = slice(this.node.querySelectorAll(cardSelector));
cards.forEach(function(node, index) {
callback.apply(this, [node, cardSelector, index]);
}, this);
}, this);
},
/**
* Init Nodes of Block.
*/
initNodes: function()
{
if (this.access < ACCESS_W)
{
return;
}
var nodes = [];
this.forEachNodeElements(function(element, selector, index) {
var instance = this.nodes.getByNode(element);
var nodeSelector = join(selector, "@", index);
if (!instance)
{
var handler = getClass(this.manifest.nodes[selector].handler);
var presetNode = element.closest('[data-card-preset]');
var manifest = clone(this.manifest.nodes[selector]);
var disallowField = false;
manifest.sections = this.sections;
if (presetNode)
{
var presetId = presetNode.dataset.cardPreset;
Object.keys(this.manifest.cards).forEach(function(cardSelector) {
if (presetNode.matches(cardSelector))
{
if (isPlainObject(this.manifest.cards[cardSelector].presets) &&
isPlainObject(this.manifest.cards[cardSelector].presets[presetId]) &&
isArray(this.manifest.cards[cardSelector].presets[presetId].disallow))
{
var isDisallow = this.manifest.cards[cardSelector].presets[presetId].disallow.find(function(disallowSelector) {
return selector === disallowSelector;
});
if (isDisallow)
{
manifest.allowInlineEdit = false;
disallowField = true;
}
}
}
}, this);
}
var isDynamic = this.cards.some(function(currentCard) {
var cardCode = currentCard.selector.split("@")[0];
return (
this.isDynamicCards(cardCode)
&& currentCard.node.contains(element)
);
}, this);
if (isDynamic)
{
manifest.allowInlineEdit = false;
}
else
{
var isCardNode = this.cards.some(function(currentCard) {
return currentCard.node.contains(element);
});
if (!isCardNode)
{
if (this.isDynamic())
{
manifest.allowInlineEdit = false;
}
}
}
instance = new handler({
node: element,
manifest: manifest,
selector: nodeSelector,
onChange: this.onNodeChange.bind(this),
onChangeOptions: this.onNodeOptionsChange.bind(this),
onAttributeChange: this.onAttributeChange.bind(this),
onDesignShow: this.onStyleShow.bind(this),
uploadParams: {
action: "Block::uploadFile",
block: this.id
}
});
if (disallowField)
{
instance.getField().layout.hidden = true;
}
this.nodes.add(instance);
}
instance.selector = nodeSelector;
nodes.push(instance);
});
this.nodes.clear();
nodes.forEach(function(node) {
this.nodes.add(node);
}, this);
this.nodes.sort(function(a, b) {
return a.getIndex() > b.getIndex();
});
},
/**
* Handles node options change event
* @param {*} data
* @return {Promise<Object, Object>}
*/
onNodeOptionsChange: function(data)
{
if (!isEmpty(data))
{
this.initStyles();
var queryParams = {code: this.manifest.code};
var requestBody = {};
requestBody.data = data;
requestBody.block = this.id;
requestBody.siteId = this.siteId;
return BX.Landing.Backend.getInstance()
.action("Block::changeNodeName", requestBody, queryParams);
}
},
/**
* @callback forEachNodeElementsCallback
* @param {HTMLElement} [element]
* @param {string} [selector]
* @param {int} [index]
*/
/**
* Applies callback for each element of node
* @param {forEachNodeElementsCallback} callback
*/
forEachNodeElements: function(callback)
{
Object.keys(this.manifest.nodes).forEach(function(selector) {
try {
slice(this.node.querySelectorAll(selector)).forEach(function(element, index) {
if (!element.matches("[data-id=\"content_edit\"] *"))
{
callback.apply(this, [element, selector, index]);
}
}, this);
} catch(err) {}
}, this);
},
/**
* Shows content edit panel
* @param {{name: string, nodes: BX.Landing.Collection.NodeCollection, [compact]: boolean, [nodesOnly]}} [options]
*/
showContentPanel: function(options)
{
var nodes = !!options && options.nodes ? options.nodes : null;
var formName = !!options && options.name ? options.name : null;
var nodesOnly = !!options && options.nodesOnly ? options.nodesOnly : false;
var showAll = !!options && options.showAll ? options.showAll : false;
var compactMode = !!options && options.compact;
var hideCheckbox = !!options && options.hideCheckbox;
var contentPanel = this.panels.get("content_edit");
if (!contentPanel)
{
contentPanel = new ContentEditPanel("content_edit", {
title: BX.Landing.Loc.getMessage("LANDING_CONTENT_PANEL_TITLE"),
subTitle: this.manifest.block.name,
onSaveHandler: this.onContentSave.bind(this),
onCancelHandler: this.onContentCancel.bind(this),
});
var formId = this.getBlockFormId();
var type = BX.Text.capitalize(
BX.Landing.Env.getInstance().getOptions().params.type
);
if (
BX.Type.isPlainObject(formId)
&& type !== 'SMN'
)
{
var button = new BX.UI.Button({
text: BX.Landing.Loc.getMessage('LANDING_SHOW_FORM_EDITOR'),
color: BX.UI.Button.Color.LIGHT_BORDER,
round: true,
className: 'landing-ui-panel-top-button',
onclick: function() {
contentPanel.hide()
.then(function() {
this.onShowContentPanel();
}.bind(this));
}.bind(this)
});
BX.Dom.style(button.render(), {
position: 'absolute',
right: '50px',
});
BX.Dom.append(button.render(), contentPanel.header);
}
this.addPanel(contentPanel);
}
contentPanel.compact(compactMode);
contentPanel.clear();
var block = this.getBlockFromRepository(this.manifest.code);
if (block && block.restricted)
{
append(this.getRestrictedMessage(), contentPanel.content);
}
this.tmpContent = create("div", {
props: {hidden: true}
});
this.content.appendChild(this.tmpContent);
var html = "";
Object.keys(this.manifest.cards).forEach(function(cardSelector) {
var card = this.manifest.cards[cardSelector];
if (isPlainObject(card.presets))
{
Object.keys(card.presets).forEach(function(presetId) {
var preset = card.presets[presetId];
html += preset.html;
}, this);
}
}, this);
this.tmpContent.innerHTML = html;
this.initEntities();
this.initCardsLabels();
var forms = this.getEditForms({
nodes: nodes,
formName: formName,
nodesOnly: nodesOnly,
showAll: showAll,
hideCheckbox: hideCheckbox
});
forms.forEach(function(form) {
contentPanel.appendForm(form);
});
this.tmpContent.innerHTML = "";
contentPanel.show();
BX.Event.bind(contentPanel.layout, 'click', this.onContentPanelClick.bind(this));
setTimeout(function() {
this.lastBlockState = this.fetchRequestData(contentPanel, true);
}.bind(this), 300);
},
onContentPanelClick: function()
{
BX.Event.EventEmitter.emit('BX.Landing.UI.Panel.ContentEdit:onClick');
},
createHistoryEntry: function(newState)
{
Promise
.all([
this.lastBlockState,
newState
])
.then(function(states) {
var undoState = states[0];
var redoState = states[1];
BX.Landing.History.getInstance().push(
new BX.Landing.History.Entry({
block: this.id,
selector: "#block" + this.id,
command: "updateBlockState",
undo: undoState,
redo: redoState
})
);
}.bind(this));
return Promise.resolve(clone(newState));
},
/**
* Updates block's content.
* @param {string} content
* @param {boolean} [preventHistory = false] - Add this action to history or not. By default - add
*/
updateContent: function(content, preventHistory)
{
let updatePromise = Promise.resolve();
if ((isBoolean(preventHistory) && !preventHistory) || !isBoolean(preventHistory))
{
updatePromise = BX.Landing.Backend.getInstance().action(
"Block::updateContent",
{
lid: this.lid,
block: this.id,
content: content.replaceAll(' style="', ' bxstyle="'),
preventHistory: 0,
},
{code: this.manifest.code}
);
}
const reloadPromise = this.reload();
return Promise.all([updatePromise, reloadPromise]);
},
updateBlockState: function(state, preventHistory)
{
if (
BX.type.isPlainObject(state)
&& BX.type.isPlainObject(state.dynamicParams)
)
{
this.dynamicParams = clone(state.dynamicParams);
}
else
{
this.dynamicParams = {};
}
Promise.resolve(state)
.then(function(currentState) {
return preventHistory ? currentState : this.createHistoryEntry(currentState);
}.bind(this))
.then(this.applyMenuChanges.bind(this))
.then(this.applyContentChanges.bind(this))
.then(this.applyCardsChanges.bind(this))
.then(this.applyAttributeChanges.bind(this))
.then(this.applySettingsChanges.bind(this))
.then(data => {
return this.saveChanges.bind(this)(data, preventHistory);
})
// Reload only blocks with component
.then(this.reload.bind(this))
.catch(console.warn);
var contentPanel = this.panels.get('content_edit');
if (contentPanel)
{
var contentForms = new FormCollection();
contentPanel.forms.forEach(function(form) {
if (form.type !== "attrs")
{
contentForms.add(form);
if (form.childForms && form.childForms.length > 0)
{
form.childForms.forEach(function(childForm) {
contentForms.add(childForm);
});
}
}
});
contentForms.fetchFields().forEach(function(field) {
if (field.tag)
{
var node = this.nodes.getBySelector(field.selector);
if (node)
{
node.onChangeTag(field.tag);
}
}
}, this);
}
},
getRestrictedMessage: function()
{
return create("div", {
props: {className: "ui-alert ui-alert-warning"},
html: BX.Landing.Loc.getMessage("LANDING_BLOCK_RESTRICTED_TEXT"),
attrs: {style: "margin-bottom: 20px"}
})
},
/**
* Handles event on style panel show
*/
onStyleShow: function(selector = null)
{
BX.Landing.UI.Panel.EditorPanel.getInstance().hide();
BX.Landing.PageObject.getInstance().design()
.then((stylePanel) => {
if (isPlainObject(this.styleNodes))
{
if (stylePanel.isShown() && this.id === stylePanel.blockId)
{
stylePanel.forms.forEach((form) => {
if (form.selector === this.selector)
{
stylePanel.scrollElement = form;
form.collapsed = false;
BX.Dom.removeClass(form.layout, 'landing-ui-form-style--collapsed');
setTimeout(() => {
stylePanel.scrollElement.layout.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest',
});
}, 100);
}
else
{
form.collapsed = true;
BX.Dom.addClass(form.layout, 'landing-ui-form-style--collapsed');
}
});
}
else
{
stylePanel.clear();
const sortedStyleNodes = this.getSortedStyleNodes(this.styleNodes);
stylePanel.prepareFooter(this.isExistMultiSelectionNode(sortedStyleNodes));
if (selector === null || typeof selector !== 'string')
{
this.showStylePanel(this.selector, stylePanel.blockId);
sortedStyleNodes.forEach((key) => {
let currentTarget = null;
this.styles.forEach((styles) => {
if (styles.selector === key)
{
currentTarget = styles.currentTarget;
}
});
this.showStylePanel(key, stylePanel.blockId, currentTarget, true);
});
}
else
{
this.showStylePanel(this.selector, stylePanel.blockId, null, true);
sortedStyleNodes.forEach((key) => {
let currentTarget = null;
this.styles.forEach((styles) => {
if (styles.selector === key)
{
currentTarget = styles.currentTarget;
}
});
this.showStylePanel(key, stylePanel.blockId, currentTarget, true);
});
setTimeout(() => {
stylePanel.forms.forEach((form) => {
if (form.selector === selector)
{
stylePanel.scrollElement = form;
form.collapsed = false;
BX.Dom.removeClass(form.layout, 'landing-ui-form-style--collapsed');
setTimeout(() => {
stylePanel.scrollElement.layout.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest',
});
}, 500);
}
else
{
form.collapsed = true;
BX.Dom.addClass(form.layout, 'landing-ui-form-style--collapsed');
}
});
}, 1000);
}
}
}
else
{
this.showStylePanel(this.selector, stylePanel.blockId);
}
});
},
/**
* Gets className postfix --lg --md --sm
*/
getPostfix: function()
{
return "";
},
/**
* Expands type groups
* @param {string|string[]} types
* @returns {string[]}
*/
expandTypeGroups: function(types)
{
var result = [];
if (!BX.type.isArray(types))
{
types = [types];
}
types.forEach(function(type) {
if (isGroup(type))
{
getGroupTypes(type).forEach(function(groupType) {
result.push(groupType);
});
}
else
{
result.push(type);
}
});
return result;
},
/**
* Makes style editor form for style node
* @param {string} selector
* @param {{
* type: string|string[],
* name: string,
* [props],
* [title]
* }} settings
* @param {boolean} [isBlock = false]
* @param {HTMLElement} currentTarget
* @param {boolean} collapsed
* @returns {?BX.Landing.UI.Form.StyleForm}
*/
createStyleForm: function(selector, settings, isBlock, currentTarget, collapsed = false)
{
var form = this.forms.get(selector);
if (form)
{
this.forms.remove(form);
}
var type = !!settings.props ? settings.props : !!settings.type ? settings.type : null;
var name = !!settings.title ? settings.title : !!settings.name ? settings.name : "";
if (!!type && !!name)
{
var styleFactory = new StyleFactory({frame: window, postfix: this.getPostfix()});
form = new StyleForm({
id: selector,
title: name,
selector,
iframe: window,
collapsed: collapsed,
currentTarget: currentTarget,
});
type = this.expandTypeGroups(type).reduce(function(acc, item) {
if (!acc.includes(item))
{
acc.push(item);
}
return acc;
}, []);
type.forEach(function(type) {
var typeSettings = getTypeSettings(type);
if (typeSettings === null)
{
return;
}
var styleNode = this.styles.get(selector);
var field = styleFactory.createField({
block: this,
styleNode: styleNode,
selector: !isBlock ? this.makeRelativeSelector(selector) : selector,
property: typeSettings.property,
multiple: typeSettings.multiple === true,
style: type,
pseudoElement: typeSettings["pseudo-element"],
pseudoClass: typeSettings["pseudo-class"],
attrKey: typeSettings.attrKey,
type: typeSettings.type,
subtype: typeSettings.subtype,
title: typeSettings.name,
items: typeSettings.items,
help: typeSettings.help,
onChange: onChange.bind(this),
onReset: onReset.bind(this)
});
// when field changed
function onChange(value, items, postfix, affect) {
// false handler by some fields events
if (value instanceof BX.Event.BaseEvent)
{
return;
}
var exclude = !!typeSettings.exclude ? getTypeSettings(typeSettings.exclude) : null;
if (exclude)
{
form.fields.forEach(function(field) {
if (field.style === typeSettings.exclude)
{
field.reset();
}
});
}
const event = this.createEvent({
data: {
selector: selector,
value: value,
items: items,
postfix: postfix,
affect: affect,
exclude: exclude
}
});
fireCustomEvent(window, "BX.Landing.Block:beforeApplyStyleChanges", [event]);
styleNode.setValue(value, items, postfix, affect, exclude);
const data = {node: styleNode.getNode(), data: styleNode.getValue()};
fireCustomEvent("BX.Landing.Block:updateStyleWithoutDebounce", [
this.createEvent(data)
]);
this.onStyleInputWithDebounce(data, false);
}
// when field reset
function onReset(items, postfix, affect) {
// todo: add cache for backend
BX.Landing.Backend.getInstance()
.action("Landing\\Block::getContentFromRepository", {
code: this.manifest.code
})
.then(function(response) {
var repo = document.createElement('div');
repo.id = 'fake';
repo.innerHTML = response;
repo.style.display = 'none';
window.document.body.append(repo);
var targetNode = null;
var targetSelector = null;
if (isBlock)
{
targetSelector = '#fake > :first-child';
targetNode = repo.firstElementChild;
}
else
{
targetSelector = '#fake ' + selector;
var index = styleNode.getElementIndex(styleNode.getTargetElement());
targetNode = repo.querySelectorAll(targetSelector)[index];
}
var fakeStyleNode = new BX.Landing.UI.Style({
iframe: window,
selector: targetSelector,
relativeSelector: targetSelector,
node: targetNode
});
initFieldByStyleNode(fakeStyleNode);
// match new class list
var resetStyleValue = fakeStyleNode.getValue();
var resetClasses = [];
var currStyleValue = styleNode.getValue();
items.forEach(function(item) {
if(resetStyleValue.classList.indexOf(item.value) !== -1)
{
resetClasses.push(item.value);
}
var currIndex = currStyleValue.classList.indexOf(item.value);
if(currIndex !== -1)
{
delete currStyleValue.classList[currIndex];
}
});
resetStyleValue.classList = currStyleValue.classList.concat(resetClasses);
resetStyleValue.className = resetStyleValue.classList;
onChange.bind(this)(resetStyleValue, items, postfix, affect);
repo.remove();
}.bind(this))
.catch(function(error){
// todo: show err panel
console.error("Error on reset", error);
});
}
// when field init
function initFieldByStyleNode(styleNode)
{
styleNode.setInlineProperty(field.getInlineProperties());
styleNode.setComputedProperty(field.getComputedProperties());
styleNode.setPseudoElement(field.getPseudoElement());
var preventEvent = true;
var styleValue = styleNode.getValue(true);
if (field.getInlineProperties().length > 0 || field.getComputedProperties().length > 0)
{
field.setValue(styleValue.style, preventEvent);
}
else
{
styleValue.classList.forEach(className => {
if (typeSettings.items.some(item => item.value === className))
{
// buttons group set value via onFrameLoad
if (!!field.buttons && field.multiple === true)
{
return;
}
field.setValue(className, preventEvent);
}
});
}
}
initFieldByStyleNode(styleNode);
form.addField(field);
}, this);
this.forms.add(form);
}
form.fields.forEach(function(field) {
if (field.popup)
{
field.popup.close();
}
});
return form;
},
initStyles: function()
{
if (this.access < ACCESS_V)
{
return;
}
this.styles.clear();
var node = new BX.Landing.UI.Style({
id: this.selector,
iframe: window,
selector: this.selector,
relativeSelector: this.selector,
onClick: this.onStyleClick.bind(this, this.selector)
});
this.styles.add(node);
if (isPlainObject(this.manifest.style) &&
isPlainObject(this.manifest.style.nodes))
{
Object.keys(this.manifest.style.nodes).forEach(function(selector) {
var node = new BX.Landing.UI.Style({
id: selector,
iframe: window,
selector: selector,
relativeSelector: this.makeRelativeSelector(selector),
onClick: this.onStyleClick.bind(this, selector)
});
this.styles.add(node);
}, this);
}
},
onStyleClick: function(selector)
{
BX.Landing.PageObject.getInstance().design()
.then((stylePanel) => {
const options = this.getStyleOptions(selector);
this.styles.forEach((styles) => {
if (styles.selector.split('@')[0] === selector)
{
stylePanel.forms.forEach((form) => {
if (
form.selector === selector
&& form.currentTarget !== styles.currentTarget
)
{
const isBlock = this.isBlockSelector(selector);
const newForm = this.createStyleForm(selector, options, isBlock, styles.currentTarget, true);
this.replaceStyleForm(newForm, stylePanel);
}
});
}
});
if (this.id === stylePanel.blockId)
{
let currentForm = null;
stylePanel.forms.forEach((form) => {
if (
form.selector === selector
|| (options.type === 'crm-form' && form.specialType === 'crm_forms')
)
{
currentForm = form;
}
});
if (currentForm !== null)
{
stylePanel.scrollElement = currentForm;
currentForm.collapsed = false;
BX.Dom.removeClass(currentForm.layout, 'landing-ui-form-style--collapsed');
setTimeout(() => {
stylePanel.scrollElement.layout.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest',
});
}, 100);
}
}
else
{
stylePanel.clear();
if (selector === this.selector)
{
this.showStylePanel(this.selector, stylePanel.blockId);
}
else
{
this.showStylePanel(this.selector, stylePanel.blockId, null, true);
}
const sortedStyleNodes = this.getSortedStyleNodes(this.styleNodes);
stylePanel.prepareFooter(this.isExistMultiSelectionNode(sortedStyleNodes));
sortedStyleNodes.forEach((key) => {
let currentTarget = null;
this.styles.forEach((styles) => {
if (styles.selector === key)
{
currentTarget = styles.currentTarget;
}
});
const collapsed = key !== selector;
this.showStylePanel(key, stylePanel.blockId, currentTarget, collapsed);
});
if (this.id !== stylePanel.blockId)
{
const intervalId = setInterval(() => {
if (!this.stylePanel.content.hidden && this.scrollElement)
{
this.scrollElement.layout.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest',
});
clearInterval(intervalId);
}
}, 100);
}
}
});
},
replaceStyleForm: function(newForm, stylePanel)
{
let oldForm = null;
stylePanel.forms.forEach((form) => {
if (newForm.selector === form.id)
{
oldForm = form;
}
});
if (oldForm)
{
stylePanel.replaceForm(newForm, oldForm);
}
},
/**
* Sorting nodes as they are found in the DOM
* @param nodes
* @return {object}
*/
getSortedStyleNodes: function(nodes) {
const contentHtml = this.content.innerHTML;
const indexMap = {};
const negativeIndexMap = {};
Object.keys(nodes).forEach(key => {
const index = contentHtml.indexOf(key.substring(1));
if (index !== -1)
{
indexMap[key] = index;
}
else
{
negativeIndexMap[key] = index;
}
});
let sortedNodes = Object.keys(indexMap).sort((a, b) => indexMap[a] - indexMap[b]);
const negativeNodes = Object.values(Object.keys(negativeIndexMap));
for (let i = 0; i < negativeNodes.length; i++) {
sortedNodes.push(negativeNodes[i]);
}
return sortedNodes;
},
/**
* Check if exist multi selection node
* @param nodes
* @return {boolean}
*/
isExistMultiSelectionNode: function(nodes) {
return nodes.some(node => this.content.querySelectorAll(node).length > 1);
},
/**
* Makes selector relative this block
* @param {string} selector
* @return {string}
*/
makeRelativeSelector: function(selector)
{
return join(this.selector, " ", selector);
},
/**
* Makes absolute selector
* @param {string} selector
* @return {string}
*/
makeAbsoluteSelector: function(selector)
{
selector = selector || this.selector;
selector = trim(selector);
var find = selector === this.selector ? " > :first-child" : this.selector;
return trim(selector.replace(find, "").replace("!", ""));
},
/**
* Saves block style changes
* @param {boolean} [preventHistory = false] - Add this action to history or not. By default - add
*/
saveStyles: function(preventHistory)
{
const styles = this.styles.fetchChanges();
if (styles.length)
{
styles.forEach(function(style) {
if (style.selector === this.selector)
{
style.selector = style.selector.replace(" > :first-child", "");
}
if (!style.isSelectGroup() && style.selector !== this.makeAbsoluteSelector(this.selector))
{
style.selector = join(style.selector.split("@")[0], "@", style.getElementIndex(style.getNode()[0]));
}
if (style.isSelectGroup())
{
style.selector = style.selector.split("@")[0];
}
}, this);
if ((isBoolean(preventHistory) && !preventHistory) || !isBoolean(preventHistory))
{
const post = styles.fetchValues();
BX.Landing.Backend.getInstance()
.action(
"Landing\\Block::updateStyles",
{block: this.id, data: post, lid: this.lid, siteId: this.siteId, preventHistory: 0},
{code: this.manifest.code},
)
.then(() => {
BX.Landing.History.getInstance().push();
});
}
}
},
/**
* Shows style editor panel
*/
showStylePanel: function(selector, blockId, currentTarget = null, collapsed = false)
{
var FormSettingsPanel = BX.Reflection.getClass('BX.Landing.UI.Panel.FormSettingsPanel');
var formMode = (
FormSettingsPanel
&& FormSettingsPanel.getInstance().isShown()
|| BX.Landing.Main.getInstance().isControlsExternal()
);
var isBlock = this.isBlockSelector(selector);
var options = this.getStyleOptions(selector);
BX.Landing.PageObject.getInstance().design()
.then(function(stylePanel) {
stylePanel.clearContent();
stylePanel.blockId = this.id;
if (options.type === 'crm-form')
{
var rootWindow = BX.Landing.PageObject.getRootWindow();
return Promise
.all([
rootWindow.BX.Runtime.loadExtension('landing.formstyleadapter'),
BX.Runtime.loadExtension('landing.formstyleadapter')
])
.then(function(result) {
var FormStyleAdapter = result[1].FormStyleAdapter;
var formStyleAdapter = new FormStyleAdapter({
formId: this.getBlockFormId().id,
instanceId: this.getBlockFormId().instanceId,
currentBlock: this,
});
return Promise.all([
stylePanel.show(formMode),
formStyleAdapter.load()
]);
}.bind(this))
.catch(function(error) {
console.error(error);
});
}
return stylePanel
.show(formMode)
.then(function(result) {
return [result];
});
}.bind(this))
.then(function(result) {
if (!result)
{
return;
}
var stylePanel = result[0];
this.stylePanel = stylePanel;
var formStyleAdapter = result[1];
if (formStyleAdapter)
{
const form = formStyleAdapter.getStyleForm(collapsed);
stylePanel.appendForm(form);
if (collapsed === false)
{
this.scrollElement = form;
}
return;
}
if (isArray(options.type) || isString(options.type))
{
if (options.type.length)
{
const form = this.createStyleForm(selector, options, isBlock, currentTarget, collapsed);
stylePanel.appendForm(form);
if (collapsed === false)
{
this.scrollElement = form;
}
}
}
if (isPlainObject(options.additional))
{
selector = options.selector ? options.selector : selector;
stylePanel.appendForm(
this.createAdditionalForm({
form: StyleForm,
selector: selector,
group: options.additional,
attrsType: options.additional.attrsType,
onChange: this.onAttributeChange.bind(this),
name: options.additional.name,
})
);
return;
}
if (isArray(options.additional))
{
options.additional.forEach(function(group) {
stylePanel.appendForm(
this.createAdditionalForm({
form: StyleForm,
selector: selector,
group: group,
onChange: this.onAttributeChange.bind(this),
name: options.additional[0].name,
})
);
}, this);
}
}.bind(this))
.catch(function(error) {
if (BX.Type.isArrayFilled(error))
{
var accessDeniedCode = 510;
var isAccessDenied = error.some(function(error) {
return String(error.code) === String(accessDeniedCode);
});
if (isAccessDenied)
{
BX.Dom.append(
this.getAccessMessage(),
BX.Landing.UI.Panel.StylePanel.getInstance().content
);
}
}
}.bind(this));
},
getAccessMessage: function()
{
if (!this.accessMessage)
{
this.accessMessage = BX.create({
tag: 'div',
props: {className: 'landing-ui-access-error-message'},
children: [
BX.create({
tag: 'div',
props: {className: 'landing-ui-access-error-message-text'},
text: BX.Landing.Loc.getMessage('LANDING_CRM_ACCESS_ERROR_MESSAGE')
})
]
})
}
return this.accessMessage;
},
/**
* Gets style options for selector
* @param {!string} selector
* @return {object}
*/
getStyleOptions: function(selector)
{
if (this.isBlockSelector(selector))
{
return this.prepareBlockOptions(this.manifest.style.block);
}
return this.manifest.style.nodes[selector];
},
/**
* Creates additional form
* @param {object} options
* @return {BX.Landing.UI.Form.StyleForm}
*/
createAdditionalForm: function(options)
{
var form = new options.form({ title: options.name, type: 'attrs', collapsed: true });
var attrsSet = [];
if (!BX.Type.isUndefined(options.group.attrs))
{
attrsSet = options.group.attrs;
}
else
{
options.attrsType.forEach((atr) => {
let attrSetItem = getAttrsTypeSettings(atr);
if (attrSetItem)
{
attrsSet.push(attrSetItem);
}
})
}
attrsSet.forEach(function(attrOptions) {
var currentSelector = attrOptions.selector || options.selector;
var field;
if (isArray(attrOptions.tabs))
{
var card = new TabCard({
tabs: attrOptions.tabs.map(function(tabOptions) {
return {
id: random(),
name: tabOptions.name,
active: tabOptions.active,
fields: tabOptions.attrs.map(function(attrOptions) {
return this.createAttributeField(
attrOptions,
attrOptions.selector || options.selector,
options.onChange
);
}, this)
};
}, this)
});
form.addCard(card);
return;
}
field = this.createAttributeField(attrOptions, currentSelector, options.onChange);
form.addField(field);
}, this);
BX.Event.EventEmitter.subscribe('BX.Landing.UI.Form.StyleForm:attributeChange', (event) => {
var eventData = event.data;
this.prepareAttributeValue(eventData, form);
});
return form;
},
prepareAttributeValue: function(eventData, form) {
var dependencySet = eventData.data.dependency;
if (dependencySet)
{
dependencySet.forEach((dependency) => {
var value = eventData.getValue();
var index = dependency['conditions'].indexOf(value);
if (index >= 0)
{
form.fields.forEach((field) => {
if (field.attribute === dependency['attribute'])
{
//action 'changeValue'
if (dependency['action'] === 'changeValue')
{
var value = field.getValue();
var index = dependency['attributeCurrentValues'].indexOf(value);
if (index >= 0)
{
field.setValue(dependency['attributeNewValue'], true);
this.onAttributeChange(field);
}
}
//action 'hideSetting' need to do
}
})
}
})
}
},
prepareBlockOptions: function(options)
{
options = isPlainObject(options) ? options : {};
options = clone(options);
options.name = BX.Landing.Loc.getMessage("BLOCK_STYLE_OPTIONS");
if (!isPlainObject(options.type) && !isString(options.type) && !isArray(options.type))
{
options.type = [
"display",
"background",
"padding-top",
"padding-bottom",
"padding-left",
"padding-right",
"margin-top"
];
}
return options;
},
/**
* Creates attribute field
* @param {object} options
* @param {?string} selector
* @param {function} [onAttributeChange]
* @return {BX.Landing.UI.Field.BaseField}
*/
createAttributeField: function(options, selector, onAttributeChange)
{
var fieldFactory = this.createFieldFactory(selector, onAttributeChange);
var element = this.getElementBySelector(selector);
if (!element && selector.includes("@"))
{
var selectorFragments = selector.split("@");
var elements = this.getElementsBySelector(selectorFragments[0]);
if (elements.length && elements[parseInt(selectorFragments[1])])
{
element = elements[parseInt(selectorFragments[1])];
}
}
var fieldOptions = clone(options);
if (fieldOptions.value === null || fieldOptions.value === undefined)
{
fieldOptions.value = "";
}
if (element)
{
var value = data(element, fieldOptions.attribute);
if (BX.Type.isNil(value))
{
value = attr(element, fieldOptions.attribute);
}
if (value !== null)
{
fieldOptions.value = value;
}
}
return fieldFactory.create(fieldOptions);
},
onAttributeChange: function(field)
{
BX.Event.EventEmitter.emit('BX.Landing.UI.Form.StyleForm:attributeChange', field);
clearTimeout(this.attributeChangeTimeout);
if (!this.requestData)
{
this.requestData = {};
}
this.appendAttrFieldValue(this.requestData, field);
Promise.resolve(this.requestData)
.then(this.applyAttributeChanges.bind(this))
.then(this.saveChanges.bind(this))
// Reload only blocks with component
.then(this.reload.bind(this))
.then(function() {
this.requestData = null;
}.bind(this));
},
appendSettingsFieldValue: function(requestData, field)
{
requestData["settings"] = requestData["settings"] || {};
requestData["settings"][field.attribute] = field.getValue();
return requestData;
},
/**
* @param {object} requestData
* @param {BX.Landing.UI.Field.BaseField} field
* @return {object}
*/
appendAttrFieldValue: function(requestData, field)
{
var selector = this.makeAbsoluteSelector(field.selector);
var value = field.getValue();
requestData[selector] = requestData[selector] || {};
requestData[selector]["attrs"] = requestData[selector]["attrs"] || {};
if(BX.Type.isArray(field.attribute))
{
field.attribute.forEach(function(attr){
var attrData = attr.replace('data-', '');
var itemValue = value[attrData];
if(itemValue !== undefined)
{
try {
itemValue = encodeDataValue(itemValue);
} catch(e) {
itemValue = field.getValue()[attrData];
}
requestData[selector]["attrs"][attr] = itemValue;
}
});
}
else
{
try {
value = encodeDataValue(value);
} catch(e) {
value = field.getValue();
}
requestData[selector]["attrs"][field.attribute] = value;
}
return requestData;
},
appendMenuValue: function(requestData, menu)
{
requestData[menu.code] = menu.serialize();
return requestData;
},
/**
* Gets element by selector
* @param selector
* @return {*}
*/
getElementBySelector: function(selector)
{
if (this.isBlockSelector(selector))
{
return this.content;
}
var element;
try
{
element = this.node.querySelector(selector);
}
catch (err)
{
element = null;
}
return element;
},
getElementsBySelector: function(selector)
{
if (this.isBlockSelector(selector))
{
return [this.content];
}
var elements;
try
{
elements = slice(this.node.querySelectorAll(selector));
}
catch (err)
{
elements = [];
}
return elements;
},
/**
* Checks that this selector is block selector
* @param {string} selector
* @return {boolean}
*/
isBlockSelector: function(selector)
{
return !selector || selector === this.selector || ("#block"+this.id) === selector;
},
/**
* Creates FieldFactory instance
* @param {?string} selector
* @param {function} [onChange]
* @return {BX.Landing.UI.Factory.FieldFactory}
*/
createFieldFactory: function(selector, onChange)
{
return new BX.Landing.UI.Factory.FieldFactory({
selector: !this.isBlockSelector(selector) ? this.makeRelativeSelector(selector) : selector,
uploadParams: {
action: "Block::uploadFile",
block: this.id,
lid: BX.Landing.Main.getInstance().id,
id: BX.Landing.Main.getInstance().options.site_id
},
linkOptions: {
siteId: BX.Landing.Main.getInstance().options.site_id,
landingId: BX.Landing.Main.getInstance().id,
filter: {
"=TYPE": BX.Landing.Main.getInstance().options.params.type
}
},
onValueChange: onChange || (function() {})
});
},
/**
* Delete current block.
* @param {?boolean} [preventHistory = false]
* @return {void}
*/
deleteBlock: function(preventHistory)
{
var button = this.panels.get("block_action").buttons.get("remove");
button.loader = button.loader || new BX.Loader({target: button.layout, size: 28});
button.loader.show();
addClass(button.text, "landing-ui-hide-icon");
void style(button.loader.layout.querySelector(".main-ui-loader-svg-circle"), {
"stroke-width": "4px"
});
void style(button.loader.layout.querySelector(".main-ui-loader-svg"), {
"margin-top": "-10px"
});
BX.Landing.UI.Panel.EditorPanel.getInstance().hide();
if (this.blockActionsMenu)
{
BX.Main.MenuManager.destroy(this.blockActionsMenu.id);
}
if (this.sidebarActionsMenu)
{
BX.Main.MenuManager.destroy(this.sidebarActionsMenu.id);
}
if (String(window.localStorage.getItem("landingBlockId")) === String(this.id))
{
window.localStorage.removeItem("landingBlockId");
}
let removePromise = Promise.resolve();
if ((isBoolean(preventHistory) && !preventHistory) || !isBoolean(preventHistory))
{
removePromise = BX.Landing.Backend.getInstance()
.action(
"Landing::markDeletedBlock",
{block: this.id, lid: this.lid, siteId: this.siteId, preventHistory: 0},
{code: this.manifest.code}
)
.then(result => {
// Change history steps
BX.Landing.History.getInstance().push();
return result;
});
}
removePromise
.then(() => {
button.loader.hide();
removeClass(button.text, "landing-ui-hide-icon");
var event = this.createEvent();
fireCustomEvent("BX.Landing.Block:remove", [event]);
slice(this.node.querySelectorAll(".landing-ui-panel")).forEach(remove);
BX.Landing.PageObject.getBlocks().remove(this);
remove(this.node);
fireCustomEvent("Landing.Block:onAfterDelete", [this]);
fireCustomEvent("BX.Landing.Block:afterRemove", [event]);
}, () => {
button.loader.hide();
removeClass(button.text, "landing-ui-hide-icon");
});
},
getFormEditorAddBlockTour: function()
{
var rootWindow = BX.Landing.PageObject.getRootWindow();
return new rootWindow.BX.UI.Tour.Guide({
steps: [
{
target: '[data-id="save_settings"]',
title: BX.Landing.Loc.getMessage('LANDING_FORM_EDITOR_ADD_BLOCK_TOUR_STEP_1_TITLE'),
text: BX.Landing.Loc.getMessage('LANDING_FORM_EDITOR_ADD_BLOCK_TOUR_STEP_1_TEXT'),
},
],
});
},
/**
* Shows blocks list panel
*/
addBlockAfterThis: function()
{
var formSettingsPanel =
(BX.Landing.UI && BX.Landing.UI.Panel && BX.Landing.UI.Panel.FormSettingsPanel)
? BX.Landing.UI.Panel.FormSettingsPanel.getInstance()
: null;
if (
this.isCrmFormPage()
&& formSettingsPanel
&& formSettingsPanel.isShown()
)
{
if (!formSettingsPanel.isChanged())
{
formSettingsPanel
.hide()
.then(function() {
BX.Landing.Main.getInstance().showBlocksPanel(this, null, null, true);
}.bind(this));
}
else
{
this.getFormEditorAddBlockTour().start();
}
}
else
{
BX.Landing.Main.getInstance().showBlocksPanel(this);
}
},
addBlockBeforeThis: function()
{
var formSettingsPanel = BX.Landing.UI.Panel.FormSettingsPanel.getInstance();
if (
this.isCrmFormPage()
&& formSettingsPanel.isShown()
)
{
if (!formSettingsPanel.isChanged())
{
formSettingsPanel
.hide()
.then(function() {
BX.Landing.Main.getInstance().showBlocksPanel(this, null, null, true);
}.bind(this));
}
else
{
this.getFormEditorAddBlockTour().start();
}
}
else
{
BX.Landing.Main.getInstance().showBlocksPanel(this, null, null, true);
}
},
getFormEditorDesignTour: function()
{
var rootWindow = BX.Landing.PageObject.getRootWindow();
return new rootWindow.BX.UI.Tour.Guide({
steps: [
{
target: '[data-id="save_settings"]',
title: BX.Landing.Loc.getMessage('LANDING_FORM_EDITOR_FORM_DESIGN_TOUR_STEP_1_TITLE'),
text: BX.Landing.Loc.getMessage('LANDING_FORM_EDITOR_FORM_DESIGN_TOUR_STEP_1_TEXT'),
},
],
});
},
onFormDesignClick: function()
{
var formSelector = Object.entries(this.manifest.style.nodes).reduce(function(acc, item) {
if (item[1].type === 'crm-form')
{
return item[0];
}
return acc;
}, null);
BX.Landing.PageObject.getInstance().design()
.then((stylePanel) => {
if (formSelector)
{
this.showStylePanel(formSelector, stylePanel.blockId);
}
else
{
this.showStylePanel(this.selector, stylePanel.blockId);
}
});
},
/**
* Handles node content change event
* @param {BX.Landing.Node} node
* @param {?boolean} [preventHistory = false]
*/
onNodeChange: function(node, preventHistory)
{
const event = this.createEvent({node: node.node});
fireCustomEvent("BX.Landing.Block:Node:update", [event]);
if (!node.isSavePrevented())
{
clearTimeout(this.changeTimeout);
this.changedNodes.add(node);
this.changeTimeout = setTimeout(() => {
if ((isBoolean(preventHistory) && !preventHistory) || !isBoolean(preventHistory))
{
BX.Landing.Backend.getInstance()
.action(
"Landing\\Block::updateNodes",
{
block: this.id,
data: this.changedNodes.fetchValues(),
additional: this.changedNodes.fetchAdditionalValues(),
lid: this.lid,
siteId: this.siteId,
preventHistory: 0,
},
{code: this.manifest.code}
);
}
this.changedNodes.clear();
}, 300);
}
},
/**
* Checks that data contains pseudo selector
* @param {object} data
* @return {boolean}
*/
containsPseudoSelector: function(data)
{
return Object.keys(data).some(function(selector) {
var result;
if (selector === "cards")
{
return false;
}
if (selector === "dynamicState")
{
return false;
}
if (
BX.type.isPlainObject(this.manifest.menu)
&& selector in this.manifest.menu
)
{
return false;
}
try
{
if (selector !== "#block" + this.id && selector !== "")
{
result = !this.node.querySelector(selector);
}
else
{
result = false;
}
}
catch(err)
{
result = !isNodeSelector(selector);
}
return result;
}, this);
},
/**
* Check if data contains attributes, that require block reload
* @param {object} data
* @return {boolean}
*/
containsReloadRequireAttributes: function(data)
{
if (
isPlainObject(data)
&& isPlainObject(this.manifest)
&& isPlainObject(this.manifest.attrs)
)
{
return Object.keys(this.manifest.attrs).some(function (selector)
{
return this.manifest.attrs[selector].some(function (attr)
{
if (
attr.requireReload
&& isPlainObject(data[selector])
&& isPlainObject(data[selector].attrs)
&& data[selector].attrs[attr.attribute]
)
{
return true;
}
return false;
}, this);
}, this);
}
return false;
},
/**
* Applies content changes
* @param {object} data
* @return {Promise<Object>}
*/
applyContentChanges: function(data)
{
if (!isPlainObject(data))
{
return Promise.reject(
new TypeError("BX.Landing.Block.applyContentChanges: data isn't object")
);
}
var eventData = clone(data);
Object.keys(eventData).forEach(function(selector) {
if (!isNodeSelector(selector))
{
delete eventData[selector];
}
});
if (!isEmpty(eventData))
{
var event = this.createEvent({data: eventData});
fireCustomEvent(window, "BX.Landing.Block:beforeApplyContentChanges", [event]);
}
var valuePromises = [];
Object.keys(data).forEach(function(selector) {
if (isNodeSelector(selector))
{
var node = this.nodes.getBySelector(selector);
if (node)
{
var valuePromise = node.setValue(data[selector], true, true);
node.preventSave(false);
if (valuePromise)
{
valuePromises.push(valuePromise);
valuePromise.then(function() {
data[selector] = node.getValue();
});
}
else
{
data[selector] = node.getValue();
}
}
}
}, this);
return Promise
.all(valuePromises)
.then(function() {
return data;
});
},
applyMenuChanges: function(data)
{
if (!isPlainObject(data))
{
return Promise.reject(
new TypeError("BX.Landing.Block.applyContentChanges: data isn't object")
);
}
var menuKeys = Object.keys(this.manifest.menu || {});
if (menuKeys.length > 0)
{
menuKeys.forEach(function(code) {
if (code in data)
{
var menu = this.menu.find(function(menuItem) {
return menuItem.code === code;
});
menu.rebuild(data[code]);
}
}.bind(this));
data.forceReload = true;
}
this.initMenu();
return Promise.resolve(data);
},
/**
* @private
* @param data
* @return {Promise}
*/
applyCardsChanges: function(data)
{
if (!isPlainObject(data))
{
return Promise.reject(
new TypeError("BX.Landing.Block.applyCardsChanges: data isn't object")
);
}
var nodePromises = [];
if ('cards' in data && isPlainObject(data.cards))
{
fireCustomEvent("BX.Landing.Block:Cards:beforeUpdate", [
this.createEvent()
]);
var map = {};
Object.keys(data.cards)
.forEach(function(code) {
var container = this.node.querySelector(code).parentElement;
var oldCards = this.node.querySelectorAll(code);
// Card data
var values = data.cards[code].values;
var presets = data.cards[code].presets;
var indexes = data.cards[code].indexes;
var source = data.cards[code].source;
// Remove old cards
container.innerHTML = "";
// Make source
Object.keys(values)
.forEach(function(index) {
source[index] = {value: 0, type: "card"};
if (!isEmpty(presets) && !isEmpty(presets[index]))
{
if (
!oldCards[indexes[index]]
|| !BX.type.isString(indexes[index])
)
{
source[index].type = "preset";
source[index].value = presets[index];
return;
}
}
if (oldCards[indexes[index]])
{
source[index].type = "card";
source[index].value = indexes[index];
}
}, this);
// Make new cards
Object.keys(values)
.forEach(function(index) {
if (source[index].type === "preset")
{
var preset = this.manifest.cards[code]["presets"][source[index].value]["html"];
append(htmlToElement(preset), container);
return;
}
append(clone(oldCards[source[index].value]), container);
}, this);
// Reinitialize entities
this.initNodes();
this.initCards();
this.initGroups();
// Apply nodes values
Object.keys(values)
.forEach(function(index) {
var card = values[index];
Object.keys(card)
.forEach(function(key) {
map[key] = key in map ? map[key] + 1 : 0;
var node = this.nodes.getBySelector(join(key, "@", map[key]));
if (node)
{
var newValue = card[key];
var oldValue = node.getValue();
if (isPlainObject(newValue) && isString(newValue.url))
{
newValue.url = decodeDataValue(newValue.url);
}
if (isPlainObject(oldValue) && isString(oldValue.url))
{
oldValue.url = decodeDataValue(oldValue.url);
}
try
{
newValue = JSON.stringify(newValue);
} catch (e)
{
newValue = card[key];
}
try
{
oldValue = JSON.stringify(oldValue);
} catch (e)
{
oldValue = node.getValue();
}
var nodePromise = node.setValue(card[key], true, true) || Promise.resolve();
node.preventSave(false);
nodePromise.then(function(selectorKey, mapKey, cardKey) {
card[join(selectorKey, "@", mapKey)] = node.getValue();
if (node.manifest.type === "img" || node.manifest.type === "icon")
{
card[join(selectorKey, "@", mapKey)]["url"] = encodeDataValue(cardKey["url"]);
}
delete card[key];
}.bind(this, key, map[key], card[key]));
nodePromises.push(nodePromise);
}
}, this);
}, this);
Promise
.all(nodePromises)
.then(function() {
// Reinitialize additional entities
this.initCardsLabels();
this.initStyles();
// Remove unnecessary
delete data.cards[code].presets;
delete data.cards[code].indexes;
}.bind(this));
}, this);
Promise
.all(nodePromises)
.then(function() {
fireCustomEvent("BX.Landing.Block:Cards:update", [
this.createEvent()
]);
}.bind(this));
}
return Promise
.all(nodePromises)
.then(function() {
return Promise.resolve(data);
});
},
applySettingsChanges: function(requestData)
{
if (!isPlainObject(requestData))
{
return Promise.reject(
new TypeError("BX.Landing.Block.applyAttributeChanges: requestData isn't object")
);
}
if (isPlainObject(requestData.settings) &&
!isEmpty(requestData.settings))
{
if (requestData.settings.id)
{
this.content.id = requestData.settings.id;
}
}
return Promise.resolve(requestData);
},
/**
* Applies attributes changes
* @param {Object} requestData
*/
applyAttributeChanges: function(requestData)
{
if (!isPlainObject(requestData))
{
return Promise.reject(
new TypeError("BX.Landing.Block.applyAttributeChanges: requestData isn't object")
);
}
var eventData = clone(requestData);
Object.keys(requestData).forEach(function(selector) {
if (!(isPlainObject(requestData[selector]) && "attrs" in requestData[selector]))
{
delete eventData[selector];
}
});
if (!isEmpty(eventData))
{
var event = this.createEvent({data: eventData});
fireCustomEvent(window, "BX.Landing.Block:beforeApplyAttributesChanges", [event]);
}
var self = this;
Object.keys(requestData).forEach(function(selector) {
if (isPlainObject(requestData[selector]) && "attrs" in requestData[selector])
{
var elements = self.getElementsBySelector(selector);
if (!elements.length && selector.includes("@"))
{
var selectorFragments = selector.split("@");
elements = self.getElementsBySelector(selectorFragments[0]);
if (elements[parseInt(selectorFragments[1])])
{
elements = [elements[parseInt(selectorFragments[1])]];
}
}
Object.keys(requestData[selector].attrs).forEach(function(attribute) {
elements.forEach(function(element) {
var decodedValue = decodeDataValue(requestData[selector]["attrs"][attribute]);
if (!attribute.includes("data-"))
{
attr(element, attribute, decodedValue);
}
else
{
data(element, attribute, decodedValue);
}
fireCustomEvent("BX.Landing.Block:Node:updateAttr", [
self.createEvent({
node: element,
data: requestData[selector]["attrs"]
})
]);
});
});
}
});
return Promise.resolve(requestData);
},
/**
* Saves changes on backend
* @param {Object} data
* @return {Promise<Object>}
*/
saveChanges: function(data, preventHistory)
{
if (!isPlainObject(data))
{
return Promise.reject(
new TypeError("BX.Landing.Block.saveChanges: data isn't object")
);
}
if (Object.keys(data).length)
{
var updateNodeParams = {code: this.manifest.code};
var updateNodesData = {block: this.id, data: data, lid: this.lid, siteId: this.siteId};
var batch = {};
if (isPlainObject(data.settings) &&
!isEmpty(data.settings))
{
if (data.settings.id)
{
batch.changeAnchor = {
action: "Block::changeAnchor",
data: {
block: this.id,
lid: this.lid,
data: data.settings.id
}
};
}
delete data.settings;
}
if (!isEmpty(data))
{
var nodes = new NodeCollection();
Object.keys(updateNodesData).forEach(function(selector) {
nodes.add(this.nodes.getBySelector(selector));
}, this);
batch.updateNodes = {
action: "Block::updateNodes",
data: updateNodesData,
additional: nodes.fetchAdditionalValues()
};
}
if (!isEmpty(data.cards))
{
var cardsData = clone(data.cards);
delete data.cards;
var cardsSelectors = BX.Landing.Utils.arrayUnique(Object.keys(cardsData));
cardsSelectors = cardsSelectors.length === 1 ? cardsSelectors + " *" : cardsSelectors.join(" *, ");
var cardsNodesAdditionalValues = this.nodes.matches(cardsSelectors).fetchAdditionalValues();
batch.updateCards = {
action: "Block::updateCards",
data: {
block: this.id,
lid: this.lid,
siteId: this.siteId,
data: cardsData,
additional: cardsNodesAdditionalValues
}
};
}
if (data.cardsFirst)
{
var oldBatch = batch;
batch = {};
if (oldBatch.changeAnchor)
{
batch.changeAnchor = oldBatch.changeAnchor;
}
if (oldBatch.updateCards)
{
batch.updateCards = oldBatch.updateCards;
}
if (oldBatch.updateNodes)
{
batch.updateNodes = oldBatch.updateNodes;
}
delete data.cardsFirst;
}
if ((isBoolean(preventHistory) && !preventHistory) || !isBoolean(preventHistory))
{
return BX.Landing.Backend.getInstance()
.batch("Landing\\Block::updateNodes", batch, updateNodeParams)
.then(function() {
return Promise.resolve(data);
});
}
}
return Promise.resolve(data);
},
/**
* Fetches request data from content panel
* @param {BX.Landing.UI.Panel.BasePanel} panel
* @param {boolean} all
* @return {Promise<Object>}
*/
fetchRequestData: function(panel, all)
{
var requestData = {};
var forms = {};
var fetchFields = function(collection, all) {
return all ? collection : collection.fetchChanges();
};
forms.attrs = new FormCollection();
forms.cards = new FormCollection();
forms.dynamicCards = new FormCollection();
forms.dynamicBlock = new FormCollection();
forms.content = new FormCollection();
forms.settings = new FormCollection();
forms.menu = new FormCollection();
panel.forms
.forEach(function(form) {
forms[form.type].push(form);
});
fetchFields(forms.content.fetchFields(), all)
.reduce(proxy(this.appendContentFieldValue, this), requestData);
var attrFields = new BaseCollection();
forms.cards.forEach(function(form) {
form.childForms.forEach(function(childForm) {
childForm.fields.forEach(function(field) {
if (field.type === "attr")
{
attrFields.add(field);
}
});
});
});
fetchFields(attrFields, true)
.reduce(proxy(this.appendAttrFieldValue, this), requestData);
forms.cards
.reduce(proxy(this.appendCardsFormValue, this), requestData);
forms.dynamicCards
.reduce(proxy(this.appendDynamicCardsFormValue, this), requestData);
forms.dynamicBlock
.reduce(proxy(this.appendDynamicBlockFormValue, this), requestData);
fetchFields(forms.attrs.fetchFields(), all)
.reduce(proxy(this.appendAttrFieldValue, this), requestData);
fetchFields(forms.settings.fetchFields(), all)
.reduce(proxy(this.appendSettingsFieldValue, this), requestData);
forms.menu
.reduce(proxy(this.appendMenuValue, this), requestData);
requestData.dynamicState = Object.keys(this.manifest.cards)
.reduce(function(acc, cardsCode) {
acc[cardsCode] = (
BX.type.isPlainObject(requestData.dynamicParams)
&& cardsCode in requestData.dynamicParams
);
return acc;
}, {});
requestData.dynamicState.wrapper = (
!!requestData.dynamicParams
&& "wrapper" in requestData.dynamicParams
);
return Promise.resolve(requestData);
},
/**
* Appends content field value to request data
* @param {object} requestData
* @param {BX.Landing.UI.Field.BaseField} field
* @return {object}
*/
appendContentFieldValue: function(requestData, field)
{
return requestData[field.selector] = field.getValue(), requestData;
},
/**
* Appends cards value to request data
* @param {object} requestData
* @param {BX.Landing.UI.Form.CardsForm} form
* @return {object}
*/
appendCardsFormValue: function(requestData, form)
{
requestData.cards = requestData.cards || {};
requestData.cards[form.code] = {};
requestData.cards[form.code]['values'] = form.serialize();
requestData.cards[form.code]['presets'] = form.getUsedPresets();
requestData.cards[form.code]['indexes'] = form.getIndexesMap();
requestData.cards[form.code]['source'] = {};
return requestData;
},
appendDynamicCardsFormValue: function(requestData, form)
{
requestData.dynamicParams = requestData.dynamicParams || {};
requestData.dynamicParams[form.code] = {};
requestData.dynamicParams[form.code] = form.serialize();
return requestData;
},
appendDynamicBlockFormValue: function(requestData, form)
{
requestData.dynamicParams = requestData.dynamicParams || {};
requestData.dynamicParams.wrapper = form.serialize();
return requestData;
},
/**
* Reloads block
* @todo Refactoring
* @param {object} data
* @return {Promise}
*/
reload: function(data)
{
if (isPlainObject(data))
{
var isNeedReload = this.containsPseudoSelector(data) || this.containsReloadRequireAttributes(data);
if (!isNeedReload)
{
return Promise.resolve(data);
}
}
var loader = new BX.Loader({target: this.parent.parentElement, color: "rgba(255, 255, 255, .8)"});
loader.layout.style.position = "fixed";
loader.layout.style.zIndex = "999";
loader.show();
BX.Landing.Main.getInstance().showOverlay();
var self = this;
return BX.Landing.Backend.getInstance()
.action("Block::getContent", {
block: this.id,
lid: this.lid,
siteId: this.siteId,
editMode: 1
})
.then(function(response) {
var event = this.createEvent();
fireCustomEvent("BX.Landing.Block:remove", [event]);
BX.Landing.Main.getInstance().currentBlock = self;
BX.Landing.Main.getInstance().currentArea = self.parent;
return BX.Landing.Main.getInstance().addBlock(response, true);
}.bind(this))
.then(function(block) {
self.node = block;
return Promise.resolve(data);
})
.then(function(data) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(data);
loader.hide();
BX.Landing.Main.getInstance().hideOverlay();
}, 800);
});
});
},
/**
* Handles content save event
*/
onContentSave: function()
{
var contentPanel = this.panels.get("content_edit");
if (contentPanel)
{
contentPanel.hide();
this.fetchRequestData(contentPanel)
.then(function(requestData) {
fireCustomEvent("BX.Landing.Block:onContentSave", [this.id]);
return Object.assign(
{},
requestData,
{
cardsFirst: true
}
);
}.bind(this))
.then(this.updateBlockState.bind(this));
}
},
/**
* Handles content cancel edit event
*/
onContentCancel: function()
{
this.panels.get("content_edit").hide();
this.tmpContent.innerHTML = "";
this.anchor = this.savedAnchor;
},
/**
* Gets cards CSS selector
* @return {string}
*/
getCardsSelector: function()
{
var cardsSelectors = Object.keys(this.manifest.cards);
var allCards = join(cardsSelectors.join(","), ", ");
var allCardsChild = join(cardsSelectors.join(" *,"), " *");
return join(allCards, allCardsChild);
},
/**
* Style change handler
* @param event
* @param {boolean} [preventHistory = false] - Add this action to history or not. By default - add
*/
onStyleInput: function(event, preventHistory)
{
this.saveStyles(preventHistory);
const styleEvent = this.createEvent(event);
fireCustomEvent("BX.Landing.Block:updateStyle", [styleEvent]);
},
/**
* @private
* @param {object} options
* @return {BX.Landing.UI.Form.BaseForm}
*/
getBlockEditForm: function(options)
{
var preparedOptions = {};
if (BX.type.isPlainObject(options))
{
preparedOptions = Object.assign({}, options);
}
var blockNodes = preparedOptions.nodes || this.nodes;
// exclude nodes from cards
if (this.cards.length > 0 && !options.hideCheckbox)
{
blockNodes = this.nodes.notMatches(
this.getCardsSelector()
);
}
var selectors = Object.keys(this.manifest.nodes);
blockNodes = selectors
.reduce(function(acc, selector) {
if (!selector.includes(':'))
{
blockNodes
.matches(selector)
.getVisible()
.filter(function(node) {
return node.manifest.allowFormEdit !== false;
})
.forEach(function(node) {
acc.push(node);
});
}
return acc;
}, new NodeCollection());
var onBlockFormTypeChangeHandler = this.onBlockFormTypeChange.bind(this);
var dynamicState = !!(
!options.skipBlockState
&& BX.type.isPlainObject(this.dynamicParams)
&& this.dynamicParams.wrapper
);
var help = "";
var helps = BX.Landing.Main.getInstance().options.helps;
if (BX.type.isPlainObject(helps))
{
help = helps.DYNAMIC_BLOCKS;
}
var headerCheckbox = {
text: BX.Landing.Loc.getMessage("LANDING_BLOCK__MAKE_A_DYNAMIC"),
onChange: onBlockFormTypeChangeHandler,
state: dynamicState,
help: help
};
var blockForm = new BaseForm({
title: options.formName || BX.Landing.Loc.getMessage("BLOCK_ELEMENTS"),
description: this.manifest.block.formDescription,
type: "content",
code: this.id,
headerCheckbox: (function() {
if (!options.hideCheckbox && this.manifest.block.dynamic !== false)
{
return headerCheckbox;
}
return undefined;
}.bind(this))()
});
if (dynamicState)
{
setTimeout(function() {
onBlockFormTypeChangeHandler({
form: blockForm,
state: true
});
});
}
blockNodes.forEach(function(node) {
blockForm.addField(node.getField());
});
return blockForm;
},
getMenuEditForms: function()
{
return this.menu.map(function(menu) {
return menu.getForm();
}, this);
},
/**
* @private
* @return {BX.Landing.UI.Form.BaseForm}
*/
getAttrsEditForm: function()
{
var keys = Object.keys(this.manifest.attrs);
var fields = [];
keys.forEach(function(selector) {
var attr = this.manifest.attrs[selector];
if (!attr.hidden)
{
attr = !isArray(attr) ? [attr] : attr;
attr.forEach(function(options) {
if (!options.hidden && isString(options.type))
{
fields.push(
this.createAttributeField(options, options.selector || selector)
);
}
}, this);
}
}, this);
var attrsForm = new BaseForm({
id: "attr",
type: "attrs",
title: BX.Landing.Loc.getMessage("BLOCK_SETTINGS"),
description: this.manifest.block.attrsFormDescription
});
fields.forEach(function(field) {
attrsForm.addField(field);
});
return attrsForm;
},
/**
* @private
* @return {Array}
*/
getAttrsAdditionalEditForms: function()
{
var keys = Object.keys(this.manifest.attrs);
var forms = [];
keys.forEach(function(selector) {
var attr = this.manifest.attrs[selector];
if (!attr.hidden)
{
attr = !isArray(attr) ? [attr] : attr;
attr.forEach(function(options) {
if (!options.hidden && isString(options.type))
{
return;
}
if (isString(options.name) && options.attrs)
{
forms.push(
this.createAdditionalForm({
form: BaseForm,
selector: selector,
group: options,
onChange: (function() {}),
})
);
}
}, this);
}
}, this);
return forms;
},
/**
* @private
* @param skipState
* @return {Array}
*/
getCardsEditForms: function(skipState)
{
var cardsSelectors = Object.keys(this.manifest.cards);
var nodesSelectors = Object.keys(this.manifest.nodes);
var forms = [];
var groupedCards = cardsSelectors.reduce(function(acc, selector) {
var cards = this.cards.filter(function(card) {
return card.selector.split("@")[0] === selector;
});
if (cards.length > 0)
{
cards.sort(function(a, b) {
return a.sortIndex - b.sortIndex;
});
acc.set(selector, cards);
}
return acc;
}.bind(this), new Map());
groupedCards.forEach(function(cards, selector) {
var checkboxState = (
BX.type.isPlainObject(this.dynamicParams)
&& selector in this.dynamicParams
&& !skipState
);
var onCardsFormTypeHandler = this.onCardsFormTypeChange.bind(this);
var groupLabel = this.manifest.cards[selector]['group_label'];
var help = "";
var helps = BX.Landing.Main.getInstance().options.helps;
if (BX.type.isPlainObject(helps))
{
help = helps.DYNAMIC_BLOCKS;
}
var headerCheckbox = {
text: BX.Landing.Loc.getMessage("LANDING_CARDS__MAKE_A_DYNAMIC"),
onChange: onCardsFormTypeHandler,
state: checkboxState,
help: help
};
var cardsForm = new CardsForm({
title: groupLabel || BX.Landing.Loc.getMessage("LANDING_CARDS_FROM_TITLE"),
code: selector.split("@")[0],
presets: cards[0].manifest.presets,
sync: cards[0].manifest.sync,
description: cards[0].manifest.formDescription,
forms: forms,
headerCheckbox: (function() {
if (this.manifest.block.dynamic !== false)
{
return headerCheckbox;
}
return undefined;
}.bind(this))()
});
forms.push(cardsForm);
if (checkboxState)
{
setTimeout(function() {
onCardsFormTypeHandler({
form: cardsForm,
state: true
});
});
}
cards.forEach(function(card) {
var cardForm = new CardForm({
label: card.getLabel() || card.getName(),
labelBindings: card.manifest.label,
selector: card.selector,
preset: card.preset
});
var sortedCardNodes = new NodeCollection();
var cardNodes = this.nodes.filter(function(node) {
return card.node.contains(node.node)
});
if (cardNodes.length)
{
nodesSelectors.forEach(function(nodeSelector) {
var matches = cardNodes.matches(nodeSelector);
matches.forEach(sortedCardNodes.add, sortedCardNodes);
}, this);
sortedCardNodes.forEach(function(node) {
if (node.manifest.allowFormEdit !== false)
{
cardForm.addField(node.getField());
}
});
var additional = this.manifest.cards[selector].additional;
if (isPlainObject(additional))
{
if (isArray(additional.attrs))
{
additional.attrs.forEach(function(attr) {
var attrField = this.createAttributeField(attr, card.selector, (function() {}));
attrField.type = "attr";
cardForm.addField(attrField);
}, this);
}
}
if (this.tmpContent.contains(card.node))
{
cardsForm.addPresetForm(cardForm);
}
else
{
cardsForm.addChildForm(cardForm);
}
}
}, this);
}, this);
return forms;
},
/**
* @private
* @return {BX.Landing.UI.Form.BaseForm}
*/
getBlockSettingsForm: function()
{
var blockSettingsForm = new BaseForm({
title: BX.Landing.Loc.getMessage("BLOCK_SETTINGS"),
type: "settings"
});
var fieldFactory = this.createFieldFactory("!" + this.selector);
var errorMessage = null;
var baseUrl = BX.Landing.Main.getInstance().options.url;
if (baseUrl[0] === "/")
{
baseUrl = top.location.origin + baseUrl;
}
this.savedAnchor = (this.anchor || this.node.id);
var previewText = join(baseUrl, "#", (this.anchor || this.node.id));
var anchorField = fieldFactory.create({
type: "text",
name: BX.Landing.Loc.getMessage("BLOCK_SETTINGS_ANCHOR_FIELD"),
description: "<span class='landing-ui-anchor-preview'>"+BX.Text.encode(previewText)+"</span>",
attribute: "id",
value: this.anchor || this.node.id,
onInput: function() {
var preview = anchorField.layout.querySelector(".landing-ui-anchor-preview");
if (preview)
{
preview.innerHTML = BX.Text.encode(join(baseUrl, "#", BX.Text.decode(anchorField.getValue())));
}
this.anchor = anchorField.getValue();
if (errorMessage)
{
remove(errorMessage);
}
if (this.node.id !== anchorField.getValue() &&
document.getElementById(anchorField.getValue()))
{
errorMessage = BX.Landing.UI.Field.BaseField.createDescription(
BX.Landing.Loc.getMessage("BLOCK_SETTINGS_ANCHOR_FIELD_VALIDATE_ERROR")
);
addClass(errorMessage, "landing-ui-error");
append(errorMessage, anchorField.layout);
}
if (!isValidElementId(anchorField.getValue()))
{
errorMessage = BX.Landing.UI.Field.BaseField.createDescription(
BX.Landing.Loc.getMessage("BLOCK_SETTINGS_ANCHOR_FIELD_VALIDATE_INVALID_ID")
);
addClass(errorMessage, "landing-ui-error");
append(errorMessage, anchorField.layout);
}
}.bind(this)
});
blockSettingsForm.addField(anchorField);
return blockSettingsForm;
},
/**
* Gets content edit forms
* @param {object} options
* @return {BX.Landing.UI.Collection.FormCollection}
*/
getEditForms: function(options)
{
var preparedOptions = {};
if (BX.type.isPlainObject(options))
{
preparedOptions = Object.assign({}, options);
}
if (arguments.length > 1)
{
preparedOptions.nodes = arguments[0];
preparedOptions.formName = arguments[1];
preparedOptions.nodesOnly = arguments[2];
preparedOptions.showAll = arguments[3];
preparedOptions.skipCardsState = arguments[4];
preparedOptions.skipBlockState = arguments[5];
}
var forms = new FormCollection();
if (this.access >= ACCESS_W)
{
var isEditable = !(
isEmpty(this.manifest.nodes)
&& isEmpty(this.manifest.attrs)
&& isEmpty(this.manifest.menu)
);
if (isEditable)
{
// Block form
var blockEditForm = this.getBlockEditForm(preparedOptions);
if (blockEditForm.fields.length > 0)
{
forms.add(blockEditForm);
}
var menuEditForms = this.getMenuEditForms(preparedOptions);
if (menuEditForms.length > 0)
{
menuEditForms.forEach(function(menuForm) {
forms.add(menuForm);
});
}
if (!preparedOptions.nodesOnly)
{
// Attrs forms
var attrsEditForm = this.getAttrsEditForm();
if (attrsEditForm.fields.length > 0)
{
forms.add(attrsEditForm);
}
// Attrs additional forms
var attrsAdditionalEditForms = this.getAttrsAdditionalEditForms();
if (attrsAdditionalEditForms.length > 0)
{
attrsAdditionalEditForms.forEach(function(form) {
forms.add(form);
});
}
// Cards forms
var cardsEditForms = this.getCardsEditForms(preparedOptions.skipCardsState);
if (cardsEditForms.length > 0)
{
cardsEditForms.forEach(function(form) {
forms.add(form);
});
}
}
}
// Block settings
var blockSettingsForm = this.getBlockSettingsForm();
if (blockSettingsForm.fields.length > 0)
{
forms.push(blockSettingsForm);
}
}
return forms;
},
/**
* @return {boolean}
*/
isLastBlockInArea: function()
{
return this.parent.querySelectorAll(".block-wrapper").length < 2;
},
/**
* Handles block remove event
*/
onBlockRemove: function()
{
this.adjustSortButtonsState();
},
/**
* Adjusts sort buttons state (disable/enable)
*/
adjustSortButtonsState: function()
{
var actionPanel = this.panels.get("block_action");
if (actionPanel)
{
if (this.isLastBlockInArea())
{
actionPanel.buttons.get("up").disable();
actionPanel.buttons.get("down").disable();
}
else
{
actionPanel.buttons.get("up").enable();
actionPanel.buttons.get("down").enable();
}
}
},
getFieldType: function(field)
{
var node = this.nodes.getBySelector(field.selector);
if (node)
{
return node.type;
}
return null;
},
getTypeReferences: function(references, type)
{
return references.filter(function(reference) {
return reference.type === type;
});
},
convertReferencesToDropdownItems: function(references)
{
var items = references.map(function(reference) {
return {name: reference.name, value: reference.id};
});
items.push({
name: BX.Landing.Loc.getMessage('LANDING_BLOCK__DYNAMIC_REFERENCE_HIDE'),
html: (
"<span class=\"landing-ui-field-dropdown-sep\"></span>" + BX.Landing.Loc.getMessage('LANDING_BLOCK__DYNAMIC_REFERENCE_HIDE')
),
value: '@hide'
});
return items;
},
getDefaultDropdownItems: function()
{
return [
{name: BX.Landing.Loc.getMessage("LANDING_CARDS__DYNAMIC_FIELD_NOT_SET"), value: ""}
];
},
getDynamicFiledValue: function(cardsCode, fieldSelector)
{
var state = this.dynamicParams || {};
if (
BX.type.isPlainObject(state[cardsCode])
&& BX.type.isPlainObject(state[cardsCode].references)
)
{
return state[cardsCode].references[fieldSelector];
}
},
convertToDynamicFields: function(fields, cardCode, references)
{
return fields.map(function(field) {
var type = this.getFieldType(field);
if (
type !== "text"
&& type !== "img"
&& type !== "link"
&& type !== "link_ref"
)
{
return field;
}
var typeReferences = this.getTypeReferences(references, type);
var dropDownItems = this.convertReferencesToDropdownItems(typeReferences);
var value = this.getDynamicFiledValue(cardCode, field.selector);
if (type === "link")
{
if (
BX.type.isPlainObject(typeReferences[0])
&& BX.type.isArray(typeReferences[0].actions)
)
{
return new BX.Landing.UI.Field.ClickAction({
title: field.title,
selector: field.selector,
reference: typeReferences[0],
linkField: field,
value: value
});
}
return field;
}
if (dropDownItems.length === 0)
{
dropDownItems = this.getDefaultDropdownItems();
}
if (type === "img")
{
return new BX.Landing.UI.Field.DynamicImage({
title: field.title,
selector: field.selector,
dropdownItems: dropDownItems,
value: BX.type.isString(value) ? {id: value} : value,
hideCheckbox: cardCode === "wrapper"
});
}
return new BX.Landing.UI.Field.DynamicDropdown({
title: field.title,
selector: field.selector,
dropdownItems: dropDownItems,
value: BX.type.isString(value) ? {id: value} : value,
hideCheckbox: cardCode === "wrapper" || type === "link_ref"
});
}, this);
},
createDynamicCardsForm: function(options)
{
var help = "";
var helps = BX.Landing.Main.getInstance().options.helps;
if (BX.type.isPlainObject(helps))
{
help = helps.DYNAMIC_BLOCKS;
}
var dynamicForm = new BX.Landing.UI.Form.DynamicCardsForm({
title: options.title,
code: options.code,
type: "dynamicCards",
dynamicParams: options.dynamicParams,
headerCheckbox: {
text: BX.Landing.Loc.getMessage("LANDING_CARDS__MAKE_A_DYNAMIC"),
onChange: this.onCardsFormTypeChange.bind(this),
state: true,
help: help
},
onSourceChange: function(source)
{
var dynamicFields = this.convertToDynamicFields(
options.form.childForms[0].fields,
options.code,
source.references
);
var dynamicGroup = new DynamicFieldsGroup({
id: "references",
items: dynamicFields
});
var detailPageField = dynamicForm.detailPageGroup.fields[0];
if (!BX.Type.isStringFilled(detailPageField.getValue().href))
{
var content = {text: '', href: ''};
if (source && source.default && source.default.detail)
{
content.href = source.default.detail;
}
detailPageField.setValue(content);
detailPageField.hrefInput.makeDisplayedHrefValue();
}
var oldCard = dynamicForm.cards.get('references');
dynamicForm.replaceCard(oldCard, dynamicGroup);
}.bind(this)
});
return dynamicForm;
},
onCardsFormTypeChange: function(event)
{
var contentPanel = this.panels.get("content_edit");
var isDynamicEnabled = !!event.state;
if (isDynamicEnabled)
{
var dynamicCardParams = {};
if (
BX.type.isPlainObject(this.dynamicParams)
&& this.dynamicParams[event.form.code]
)
{
dynamicCardParams = this.dynamicParams[event.form.code];
}
var value = Object.assign(
{},
dynamicCardParams
);
if (BX.type.isPlainObject(value.settings))
{
if (!('pagesCount' in value.settings))
{
value.settings.pagesCount = event.form.childForms.length;
}
}
else
{
value.settings = {
pagesCount: event.form.childForms.length
};
}
var dynamicForm = this.createDynamicCardsForm({
title: event.form.title,
code: event.form.code,
form: event.form,
dynamicParams: value
});
contentPanel.replaceForm(event.form, dynamicForm);
return;
}
delete this.dynamicParams[event.form.code];
var staticForm = this.getCardsEditForms(true)
.find(function(item) {
return item.code === event.form.code;
});
contentPanel.replaceForm(event.form, staticForm);
},
isDynamicCards: function(cardsCode)
{
return cardsCode in this.dynamicParams;
},
onBlockFormTypeChange: function(event)
{
var contentPanel = this.panels.get("content_edit");
var isDynamicEnabled = !!event.state;
let restrictMessage = this.content.parentElement.querySelector('.landing-html-lock');
if (restrictMessage)
{
if (!isDynamicEnabled)
{
this.content.style.display = 'flex';
restrictMessage.style.display = 'none';
}
else
{
this.content.style.display = 'none';
restrictMessage.style.display = 'flex';
}
}
if (isDynamicEnabled)
{
var dynamicForm = this.createDynamicBlockForm({
title: event.form.title,
code: event.form.code,
form: event.form,
dynamicParams: this.dynamicParams
});
contentPanel.replaceForm(event.form, dynamicForm);
return;
}
delete this.dynamicParams.wrapper;
var staticForm = this.getBlockEditForm({
skipBlockState: true
});
contentPanel.replaceForm(event.form, staticForm);
},
createDynamicBlockForm: function(options)
{
var help = "";
var helps = BX.Landing.Main.getInstance().options.helps;
if (BX.type.isPlainObject(helps))
{
help = helps.DYNAMIC_BLOCKS;
}
var dynamicForm = new BX.Landing.UI.Form.DynamicBlockForm({
title: options.title,
code: this.id,
type: "dynamicBlock",
dynamicParams: options.dynamicParams,
headerCheckbox: {
text: BX.Landing.Loc.getMessage("LANDING_BLOCK__MAKE_A_DYNAMIC"),
onChange: this.onBlockFormTypeChange.bind(this),
state: true,
help: help
},
onSourceChange: function(source)
{
var oldCard = dynamicForm.cards.get('references');
if (BX.type.isPlainObject(source))
{
var dynamicFields = this.convertToDynamicFields(
options.form.fields,
"wrapper",
source.references
);
var dynamicGroup = new DynamicFieldsGroup({
id: "references",
items: dynamicFields
});
dynamicForm.replaceCard(oldCard, dynamicGroup);
return;
}
dynamicForm.removeCard(oldCard);
}.bind(this)
});
return dynamicForm;
},
isDynamic: function(code)
{
code = code || this.id;
var panel = this.panels.get('content_edit');
if (panel)
{
var form = panel.forms.toArray().find(function(form) {
return form.code === code;
});
if (form)
{
return form.isCheckboxChecked();
}
}
code = code === this.id ? "wrapper" : code;
return (
!!this.dynamicParams
&& code in this.dynamicParams
);
}
};
})();