Current Path : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/js/landing/node/text/src/ |
Current File : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/js/landing/node/text/src/text.js |
import { Event } from 'main.core'; import { Base } from 'landing.node.base'; import { TableEditor } from 'landing.node.tableeditor'; const escapeText = BX.Landing.Utils.escapeText; const matchers = BX.Landing.Utils.Matchers; const changeTagName = BX.Landing.Utils.changeTagName; const textToPlaceholders = BX.Landing.Utils.textToPlaceholders; export class Text extends Base { constructor(options) { super(options); this.currentNode = null; this.type = 'text'; this.tableBaseFontSize = '22'; this.onClick = this.onClick.bind(this); this.onPaste = this.onPaste.bind(this); this.onDrop = this.onDrop.bind(this); this.onInput = this.onInput.bind(this); this.onKeyDown = this.onKeyDown.bind(this); this.onMousedown = this.onMousedown.bind(this); this.onMouseup = this.onMouseup.bind(this); // Bind on node events this.bindEvents(this.node); Event.bind(document, 'mouseup', this.onMouseup); } bindEvents(node) { Event.bind(node, 'mousedown', this.onMousedown); Event.bind(node, 'click', this.onClick); Event.bind(node, 'paste', this.onPaste); Event.bind(node, 'drop', this.onDrop); Event.bind(node, 'input', this.onInput); Event.bind(node, 'keydown', this.onKeyDown); } /** * Handles allow inline edit event */ onAllowInlineEdit() { // Show title "Click to edit" for node this.node.setAttribute('title', escapeText(BX.Landing.Loc.getMessage('LANDING_TITLE_OF_TEXT_NODE'))); } /** * Handles change event * @param {boolean} [preventAdjustPosition] * @param {?boolean} [preventHistory = false] */ onChange(preventAdjustPosition, preventHistory) { super.onChange.call(this, preventHistory); if (!preventAdjustPosition) { BX.Landing.UI.Panel.EditorPanel.getInstance().adjustPosition(this.node); } if (!preventHistory) { BX.Landing.History.getInstance().push(); } } onKeyDown(event) { if (event.code === 'Backspace') { this.onBackspaceDown(event); } this.onInput(event); } onInput(event) { clearTimeout(this.inputTimeout); const key = event.keyCode || event.which; if (!(key === 90 && (/win/i.test(top.window.navigator.userAgent) ? event.ctrlKey : event.metaKey))) { this.inputTimeout = setTimeout(() => { if (this.lastValue !== this.getValue()) { this.onChange(true); this.lastValue = this.getValue(); } }, 400); } if (this.isTable(event)) { const tableFontSize = parseInt(window.getComputedStyle(event.srcElement).getPropertyValue('font-size'), 10); if ( event.srcElement.textContent === '' && BX.Dom.hasClass(event.srcElement, 'landing-table-td') && tableFontSize < this.tableBaseFontSize ) { BX.Dom.addClass(event.srcElement, 'landing-table-td-height'); } else { BX.Dom.removeClass(event.srcElement, 'landing-table-td-height'); } } } /** * Handles escape press event */ onEscapePress() { // Hide editor by press on Escape button if (this.isEditable()) { if (this === this.currentNode) { BX.Landing.UI.Panel.EditorPanel.getInstance().hide(); } this.disableEdit(); } } /** * Handles drop event on this node * * @param {DragEvent} event */ onDrop(event) { // Prevents drag and drop any content into editor area event.preventDefault(); } /** * Handles paste event on this node * * @param {ClipboardEvent} event * @param {function} event.preventDefault * @param {object} event.clipboardData */ onPaste(event) { event.preventDefault(); if (event.clipboardData && event.clipboardData.getData) { const sourceText = event.clipboardData.getData('text/plain'); let encodedText = BX.Text.encode(sourceText); if (this.isLinkPasted(sourceText)) { encodedText = this.prepareToLink(encodedText); } const formattedHtml = encodedText.replaceAll('\n', '<br>'); document.execCommand('insertHTML', false, formattedHtml); } else { // ie11 const text = window.clipboardData.getData('text'); document.execCommand('paste', true, BX.Text.encode(text)); } this.onChange(); } /** * Handles click on document */ onDocumentClick(event) { if (this.isEditable() && !this.fromNode) { BX.Landing.UI.Panel.EditorPanel.getInstance().hide(); this.disableEdit(); } this.fromNode = false; } onMousedown(event) { BX.Event.EventEmitter.emit('BX.Landing.Node.Text:onMousedown'); if (!this.manifest.group) { this.fromNode = true; if (this.manifest.allowInlineEdit !== false && BX.Landing.Main.getInstance().isControlsEnabled()) { event.stopPropagation(); this.enableEdit(); if (this.isTable(event)) { this.disableEdit(); this.currentNode.node.querySelectorAll('.landing-table-container') .forEach((table) => { if (!table.hasAttribute('table-prepare')) { this.prepareNewTable(table); } }); const tableFontSize = parseInt(window.getComputedStyle(event.srcElement).getPropertyValue('font-size'), 10); if ( event.srcElement.textContent === '' && BX.Dom.hasClass(event.srcElement, 'landing-table-td') && tableFontSize < this.tableBaseFontSize ) { BX.Dom.addClass(event.srcElement, 'landing-table-td-height'); } else { BX.Dom.removeClass(event.srcElement, 'landing-table-td-height'); } } else { if (!this.manifest.textOnly && !BX.Landing.UI.Panel.StylePanel.getInstance().isShown()) { BX.Landing.UI.Panel.EditorPanel.getInstance().show(this.node, null, this.buttons); } if (this.nodeTableContainerList) { this.nodeTableContainerList.forEach((tableContainer) => { tableContainer.tableEditor.unselect(tableContainer.tableEditor); }); } } BX.Landing.UI.Tool.ColorPicker.hideAll(); } requestAnimationFrame(() => { if (event.target.nodeName === 'A' || event.target.parentElement.nodeName === 'A') { const range = document.createRange(); range.selectNode(event.target); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); } }); } } onMouseup() { setTimeout(() => { this.fromNode = false; }, 10); } /** * Click on field - switch edit mode. */ onClick(event) { if (this.isTable(event)) { this.addTableButtons(event); } event.stopPropagation(); event.preventDefault(); this.fromNode = false; if (event.target.nodeName === 'A' || event.target.parentElement.nodeName === 'A') { const range = document.createRange(); range.selectNode(event.target); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); } } /** * Checks that is editable * @return {boolean} */ isEditable(): boolean { return this.node.isContentEditable; } /** * Enables edit mode */ enableEdit(): void { const currentNode = this.currentNode; if (currentNode) { const node = this.currentNode.node; const nodeTableContainerList = node.querySelectorAll('.landing-table-container'); if (nodeTableContainerList.length > 0) { nodeTableContainerList.forEach((nodeTableContainer) => { if (!nodeTableContainer.tableEditor) { nodeTableContainer.tableEditor = new TableEditor(nodeTableContainer, this.currentNode); } }); this.nodeTableContainerList = nodeTableContainerList; } } if (!this.isEditable() && !BX.Landing.UI.Panel.StylePanel.getInstance().isShown()) { if (this !== this.currentNode && this.currentNode !== null) { this.disableEdit(); } this.currentNode = this; BX.Landing.Node.Text.currentNode = this.currentNode; this.buttons = []; this.buttons.push(this.getDesignButton()); if (this.isHeader()) { this.buttons.push(this.getChangeTagButton()); this.getChangeTagButton().onChangeHandler = this.onChangeTag.bind(this); } this.lastValue = this.getValue(); this.node.contentEditable = true; this.node.setAttribute('title', ''); } } /** * Gets design button for editor * @return {BX.Landing.UI.Button.Design} */ getDesignButton(): BX.Landing.UI.Button.Design { if (!this.designButton) { this.designButton = new BX.Landing.UI.Button.Design('design', { html: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_DESIGN'), attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_DESIGN') }, onClick: function() { BX.Landing.UI.Panel.EditorPanel.getInstance().hide(); this.disableEdit(); this.onDesignShow(this.manifest.code); }.bind(this), }); } this.designButton.insertBefore = 'ai_copilot'; return this.designButton; } /** * Disables edit mode */ disableEdit() { if (this.isEditable()) { this.node.contentEditable = false; if (this.lastValue !== this.getValue()) { this.onChange(); this.lastValue = this.getValue(); } if (this.isAllowInlineEdit()) { this.node.setAttribute('title', escapeText(BX.Landing.Loc.getMessage('LANDING_TITLE_OF_TEXT_NODE'))); } } } /** * Gets form field * @return {BX.Landing.UI.Field.Text} */ getField(): BX.Landing.UI.Field.Text { if (this.field) { this.field.setValue(this.node.innerHTML); this.field.content = this.node.innerHTML; } else { this.field = new BX.Landing.UI.Field.Text({ selector: this.selector, title: this.manifest.name, content: this.node.innerHTML, textOnly: this.manifest.textOnly, bind: this.node, }); if (this.isHeader()) { this.field.changeTagButton = this.getChangeTagButton(); } } return this.field; } /** * Sets node value * @param value * @param {?boolean} [preventSave = false] * @param {?boolean} [preventHistory = false] */ setValue(value, preventSave, preventHistory) { this.preventSave(preventSave); this.lastValue = this.isSavePrevented() ? this.getValue() : this.lastValue; this.node.innerHTML = value; this.onChange(false, preventHistory); } /** * Gets node value * @return {string} */ getValue(): string { if (this.node.querySelector('.landing-table-container') !== null) { const node = this.node.cloneNode(true); this.prepareTable(node); return textToPlaceholders(node.innerHTML); } return textToPlaceholders(this.node.innerHTML); } /** * Checks that this node is header * @return {boolean} */ isHeader(): boolean { return matchers.headerTag.test(this.node.nodeName); } /** * Checks that this node is table * @return {boolean} */ isTable(event): boolean { let nodeIsTable = false; if (this.currentNode && event) { this.currentNode.node.querySelectorAll('.landing-table-container') .forEach((table) => { if (table.contains(event.srcElement)) { nodeIsTable = true; } }); } return nodeIsTable; } /** * Delete br tags in new table and add flag after this */ prepareNewTable(table) { table.querySelectorAll('br').forEach((tdTag) => { tdTag.remove(); }); table.setAttribute('table-prepare', 'true'); this.currentNode.onChange(true); } addTableButtons(event) { const buttons = []; let neededButtons = []; let setTd = []; const tableButtons = this.getTableButtons(); const tableAlignButtons = [tableButtons[0], tableButtons[1], tableButtons[2], tableButtons[3]]; const node = this.currentNode.node; let table = null; let isCell = false; let isButtonAddRow = false; let isButtonAddCol = false; let isNeedTablePanel = true; if ( BX.Dom.hasClass(event.srcElement, 'landing-table') || BX.Dom.hasClass(event.srcElement, 'landing-table-col-dnd') ) { isNeedTablePanel = false; } if (BX.Dom.hasClass(event.srcElement, 'landing-table-row-add')) { isButtonAddRow = true; } if (BX.Dom.hasClass(event.srcElement, 'landing-table-col-add')) { isButtonAddCol = true; } let hideButtons = []; const nodeTableList = node.querySelectorAll('.landing-table'); if (nodeTableList.length > 0) { nodeTableList.forEach((nodeTable) => { if (nodeTable.contains(event.srcElement)) { table = nodeTable; return true; } return false; }); } let isSelectedAll; tableButtons.forEach((tableButton) => { tableButton.options.srcElement = event.srcElement; tableButton.options.node = node; tableButton.options.table = table; }); if (BX.Dom.hasClass(event.srcElement, 'landing-table-row-dnd')) { setTd = event.srcElement.parentNode.children; setTd = [...setTd]; neededButtons = this.getAmountTableRows(table) > 1 ? [0, 1, 2, 3, 4, 5, 6] : [0, 1, 2, 3, 4, 5]; neededButtons.forEach((neededButton) => { tableButtons[neededButton].options.target = 'row'; tableButtons[neededButton].options.setTd = setTd; buttons.push(tableButtons[neededButton]); }); } if (BX.Dom.hasClass(event.srcElement.parentNode, 'landing-table-col-dnd')) { const childNodes = event.srcElement.parentElement.parentElement.childNodes; const childNodesArray = [...childNodes]; const childNodesArrayPrepare = []; childNodesArray.forEach((childNode) => { if (childNode.nodeType === 1) { childNodesArrayPrepare.push(childNode); } }); const neededPosition = childNodesArrayPrepare.indexOf(event.srcElement.parentElement); const rows = event.srcElement.parentElement.parentElement.parentElement.childNodes; rows.forEach((row) => { if (row.nodeType === 1) { const rowChildPrepare = []; row.childNodes.forEach((rowChildNode) => { if (rowChildNode.nodeType === 1) { rowChildPrepare.push(rowChildNode); } }); if (rowChildPrepare[neededPosition]) { setTd.push(rowChildPrepare[neededPosition]); } } }); neededButtons = this.getAmountTableCols(table) > 1 ? [0, 1, 2, 3, 4, 5, 7] : [0, 1, 2, 3, 4, 5]; neededButtons.forEach((neededButton) => { tableButtons[neededButton].options.target = 'col'; tableButtons[neededButton].options.setTd = setTd; buttons.push(tableButtons[neededButton]); }); } if (BX.Dom.hasClass(event.srcElement, 'landing-table-th-select-all')) { if (BX.Dom.hasClass(event.srcElement, 'landing-table-th-select-all-selected')) { isSelectedAll = true; const rows = event.srcElement.parentElement.parentElement.childNodes; rows.forEach((row) => { row.childNodes.forEach((th) => { setTd.push(th); }); }); neededButtons = [0, 1, 2, 3, 4, 5, 8, 9, 10]; neededButtons.forEach((neededButton) => { tableButtons[neededButton].options.target = 'table'; tableButtons[neededButton].options.setTd = setTd; buttons.push(tableButtons[neededButton]); }); } else { isSelectedAll = false; BX.Landing.UI.Panel.EditorPanel.getInstance().hide(); } } if ( BX.Dom.hasClass(event.srcElement, 'landing-table-td') || event.srcElement.closest('.landing-table-td') !== null ) { setTd.push(event.srcElement); neededButtons = [3, 2, 1, 0]; neededButtons.forEach((neededButton) => { tableButtons[neededButton].options.target = 'cell'; tableButtons[neededButton].options.setTd = setTd; tableButtons[neededButton].insertAfter = 'strikeThrough'; buttons.push(tableButtons[neededButton]); }); isCell = true; hideButtons = ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull', 'createTable', 'pasteTable']; } let activeAlignButtonId = null; const setActiveAlignButtonId = []; setTd.forEach((th) => { if (th.nodeType === 1) { activeAlignButtonId = undefined; if (BX.Dom.hasClass(th, 'text-left')) { activeAlignButtonId = 'alignLeft'; } if (BX.Dom.hasClass(th, 'text-center')) { activeAlignButtonId = 'alignCenter'; } if (BX.Dom.hasClass(th, 'text-right')) { activeAlignButtonId = 'alignRight'; } if (BX.Dom.hasClass(th, 'text-justify')) { activeAlignButtonId = 'alignJustify'; } setActiveAlignButtonId.push(activeAlignButtonId); } }); let count = 0; let isIdentical = true; while (count < setActiveAlignButtonId.length && isIdentical) { if (count > 0 && setActiveAlignButtonId[count] !== setActiveAlignButtonId[count - 1]) { isIdentical = false; } count++; } activeAlignButtonId = isIdentical ? setActiveAlignButtonId[0] : undefined; if (activeAlignButtonId) { tableAlignButtons.forEach((tableAlignButton) => { if (tableAlignButton.id === activeAlignButtonId) { BX.Dom.addClass(tableAlignButton.layout, 'landing-ui-active'); } }); } if (buttons[0] && buttons[1] && buttons[2] && buttons[3]) { buttons[0].options.alignButtons = tableAlignButtons; buttons[1].options.alignButtons = tableAlignButtons; buttons[2].options.alignButtons = tableAlignButtons; buttons[3].options.alignButtons = tableAlignButtons; } if (!this.manifest.textOnly) { if (isNeedTablePanel) { if (!isButtonAddRow && !isButtonAddCol && table) { if (isCell) { BX.Landing.UI.Panel.EditorPanel.getInstance().show(table.parentNode, null, buttons, true, hideButtons); } else if (isSelectedAll === false) { BX.Landing.UI.Panel.EditorPanel.getInstance().hide(); } else { BX.Landing.UI.Panel.EditorPanel.getInstance().show(table.parentNode, null, buttons, true); } } } else { BX.Landing.UI.Panel.EditorPanel.getInstance().hide(); } } } /** * Gets change tag button * @return {BX.Landing.UI.Button.ChangeTag} */ getChangeTagButton(): BX.Landing.UI.Button.ChangeTag { if (!this.changeTagButton) { this.changeTagButton = new BX.Landing.UI.Button.ChangeTag('changeTag', { html: `<span class="landing-ui-icon-editor-${this.node.nodeName.toLowerCase()}"></span>`, attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_CHANGE_TAG') }, onChange: this.onChangeTag.bind(this), }); } this.changeTagButton.insertAfter = 'unlink'; this.changeTagButton.activateItem(this.node.nodeName); return this.changeTagButton; } getTableButtons(): [] { this.buttons = []; this.buttons.push( new BX.Landing.UI.Button.AlignTable( 'alignLeft', { html: '<span class="landing-ui-icon-editor-left"></span>', attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_ALIGN_LEFT') }, }, this.currentNode, ), new BX.Landing.UI.Button.AlignTable( 'alignCenter', { html: '<span class="landing-ui-icon-editor-center"></span>', attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_ALIGN_CENTER') }, }, this.currentNode, ), new BX.Landing.UI.Button.AlignTable( 'alignRight', { html: '<span class="landing-ui-icon-editor-right"></span>', attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_ALIGN_RIGHT') }, }, this.currentNode, ), new BX.Landing.UI.Button.AlignTable( 'alignJustify', { html: '<span class="landing-ui-icon-editor-justify"></span>', attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_ALIGN_JUSTIFY') }, }, this.currentNode, ), new BX.Landing.UI.Button.TableColorAction( 'tableTextColor', { text: BX.Landing.Loc.getMessage('EDITOR_ACTION_SET_FORE_COLOR'), attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_COLOR') }, }, this.currentNode, ), new BX.Landing.UI.Button.TableColorAction( 'tableBgColor', { html: '<i class="landing-ui-icon-editor-fill-color"></i>', attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_TABLE_CELL_BG') }, }, this.currentNode, ), new BX.Landing.UI.Button.DeleteElementTable( 'deleteRow', { html: '<span class="landing-ui-icon-editor-delete"></span>', attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_DELETE_ROW_TABLE') }, }, this.currentNode, ), new BX.Landing.UI.Button.DeleteElementTable( 'deleteCol', { html: '<span class="landing-ui-icon-editor-delete"></span>', attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_DELETE_COL_TABLE') }, }, this.currentNode, ), new BX.Landing.UI.Button.StyleTable( 'styleTable', { html: ` ${BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_TABLE_STYLE')} <i class="fas fa-chevron-down g-ml-8"></i> `, attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_TABLE_STYLE') }, }, this.currentNode, ), new BX.Landing.UI.Button.CopyTable( 'copyTable', { text: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_TABLE_COPY'), attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_TABLE_COPY') }, }, this.currentNode, ), new BX.Landing.UI.Button.DeleteTable( 'deleteTable', { html: '<span class="landing-ui-icon-editor-delete"></span>', attrs: { title: BX.Landing.Loc.getMessage('LANDING_TITLE_OF_EDITOR_ACTION_TABLE_DELETE') }, }, this.currentNode, ), ); return this.buttons; } /** * Handles change tag event * @param value * @param {?boolean} [preventHistory = false] */ onChangeTag(value, preventHistory) { this.node = changeTagName(this.node, value); this.bindEvents(this.node); if (!this.getField().isEditable() && !preventHistory) { this.disableEdit(); this.enableEdit(); } const data = {}; data[this.selector] = value; if (!preventHistory) { this.changeOptionsHandler(data) .then(() => { BX.Landing.History.getInstance().push(); }) .catch(() => {}); } } getAmountTableCols(table): number { return table.querySelectorAll('.landing-table-col-dnd').length; } getAmountTableRows(table): number { return table.querySelectorAll('.landing-table-row-dnd').length; } prepareTable(node): HTMLElement { const setClassesForRemove = [ 'table-selected-all', 'landing-table-th-select-all-selected', 'landing-table-cell-selected', 'landing-table-row-selected', 'landing-table-th-selected', 'landing-table-th-selected-cell', 'landing-table-th-selected-top', 'landing-table-th-selected-x', 'landing-table-tr-selected-left', 'landing-table-tr-selected-y', 'landing-table-col-selected', 'landing-table-tr-selected', 'table-selected-all-right', 'table-selected-all-bottom', ]; setClassesForRemove.forEach((className) => { node.querySelectorAll(`.${className}`).forEach((element) => { BX.Dom.removeClass(element, className); }); }); return node; } onBackspaceDown(event) { const selection = window.getSelection(); const position = selection.getRangeAt(0).startOffset; if (position === 0) { let focusNode = selection.focusNode; if (!BX.Type.isNil(focusNode) && focusNode.nodeType !== 3) { if (focusNode.firstChild.nodeType === 3 && focusNode.firstChild.firstChild.nodeType === 3) { focusNode = focusNode.firstChild.firstChild; } else if (focusNode.firstChild.nodeType === 3) { focusNode = null; } else { focusNode = focusNode.firstChild; } } if (focusNode) { const focusNodeParent = focusNode.parentNode; const allowedNodeName = new Set(['BLOCKQUOTE', 'UL']); if (focusNodeParent && allowedNodeName.has(focusNodeParent.nodeName)) { const focusNodeContainer = document.createElement('div'); focusNodeContainer.append(focusNode); focusNodeParent.append(focusNodeContainer); } let contentNode = focusNode.parentNode.parentNode; while (contentNode && !allowedNodeName.has(contentNode.nodeName)) { contentNode = contentNode.parentNode; } if (contentNode && contentNode.childNodes.length === 1) { contentNode.after(focusNode.parentNode); contentNode.remove(); event.preventDefault(); } } } } isLinkPasted(text): boolean { const reg = /^https?:\/\/(?:www\.)?[\w#%+.:=@~-]{1,256}\.[\d()A-Za-z]{1,6}\b[\w#%&()+./:=?@~-]*$/; return Boolean(reg.test(text)); } prepareToLink(text): HTMLElement { return `<a class='g-bg-transparent' href='${text}' target='_blank'> ${text} </a>`; } }