/** * Internal floating toolbar manager. * * Renders schema-declared toolbar chrome against one active editor host * contract so text and non-text hosts share the same floating UI behavior. * * Exposes: SFE.ToolbarManager */ (function() { 'use strict'; window.MWP = window.MWP || {}; window.MWP.SFE = window.MWP.SFE || {}; const SFE = window.MWP.SFE; SFE.ManagerData = SFE.ManagerData || {}; const TOOLBAR_FORMAT_ICONS = { undo: '', redo: '', bold: '', italic: '', strikethrough: '', link: '', alignNone: '', alignWide: '', alignFull: '', alignLeft: '', alignCenter: '', alignRight: '', textAlignLeft: '', textAlignCenter: '', textAlignRight: '', orderedList: '', unorderedList: '', indent: '', outdent: '', textAlignmentDropdown: '', replaceMedia: 'Replace', }; /** * Create one runtime toolbar button definition. * * @param {Object} config Button configuration. * @returns {Object} Runtime toolbar button definition. */ function createToolbarButton(config = {}) { return { icon: config.icon || '', title: config.title || '', action: typeof config.action === 'function' ? config.action : () => {}, className: config.className || '', formatKey: config.formatKey || '', formatType: config.formatType || '', value: Object.prototype.hasOwnProperty.call(config, 'value') ? config.value : undefined, tag: config.tag || '', activeTags: Array.isArray(config.activeTags) ? config.activeTags : [], }; } /** * Create one runtime toolbar dropdown definition. * * @param {Object} config Dropdown configuration. * @returns {Object} Runtime toolbar dropdown definition. */ function createToolbarDropdown(config = {}) { return { type: 'dropdown', title: config.title || '', defaultIcon: config.defaultIcon || '', options: Array.isArray(config.options) ? config.options.filter(Boolean) : [], formatKey: config.formatKey || '', }; } /** * Execute one schema block-attribute operation from a toolbar action. * * @param {Object|null} editor Editor host. * @param {string} operationId Schema operation id. * @param {*} value Requested operation value. * @returns {void} */ function executeSchemaBlockAttributeOperation(editor, operationId, value) { const operationExecutor = SFE.SchemaOperationExecutor || null; if (!operationExecutor || typeof operationExecutor.executeBlockAttributeOperation !== 'function') { return; } operationExecutor.executeBlockAttributeOperation({ editorHost: editor, operationId, value, saveHistory: true, }); } /** * Execute one schema list operation from a toolbar action. * * @param {Object|null} editor Editor host. * @param {string} operationId Schema or primitive operation id. * @returns {void} */ function executeSchemaListOperation(editor, operationId) { if (typeof editor?.executeListStructureOperation !== 'function') { return; } editor.executeListStructureOperation({ kind: operationId, }); } /** * Execute one list type switch from a toolbar action. * * @param {Object|null} editor Editor host. * @param {string} listType List type token. * @returns {void} */ function executeListTypeOperation(editor, listType) { const createTag = listType === 'ordered' ? 'ol' : 'ul'; const oppositeTag = listType === 'ordered' ? 'UL' : 'OL'; const operationExecutor = SFE.SchemaOperationExecutor || null; if (editor?._linkUIActive && typeof editor.closeLinkUI === 'function') { editor.closeLinkUI(); } const selection = window.getSelection(); if (selection?.rangeCount > 0) { const range = selection.getRangeAt(0); let startLi = range.startContainer; let endLi = range.endContainer; while (startLi && startLi !== editor?.element && startLi.tagName !== 'LI') { startLi = startLi.parentNode; } while (endLi && endLi !== editor?.element && endLi.tagName !== 'LI') { endLi = endLi.parentNode; } if (startLi && endLi && startLi !== endLi) { return; } } if ( (editor?.element?.tagName === 'UL' || editor?.element?.tagName === 'OL') && operationExecutor && typeof operationExecutor.executeCurrentListTypeChange === 'function' ) { const result = operationExecutor.executeCurrentListTypeChange({ editorHost: editor, value: listType, }); if (result) { return; } } const listItem = typeof editor?.getCurrentListItem === 'function' ? editor.getCurrentListItem() : null; if (!listItem) { if (typeof editor?.insertListManual === 'function') { editor.insertListManual(createTag); setTimeout(() => { const list = typeof editor?.getParentList === 'function' ? editor.getParentList() : null; if (list) { list.classList.add('wp-block-list'); } }, 10); } return; } const currentList = listItem.parentNode; if ( currentList && currentList.tagName === oppositeTag && typeof editor?.changeListType === 'function' ) { editor.changeListType(currentList, createTag); } } /** * Build one toolbar button for a schema token that toggles inline formatting. * * @param {string} token Token name. * @param {string} title Button title. * @param {string} icon Icon markup. * @param {string} tagName Inline tag name. * @param {string[]} activeTags Active-tag list for toolbar state. * @returns {Object} Runtime toolbar button definition. */ function createInlineFormatButton(token, title, icon, tagName, activeTags) { return createToolbarButton({ formatKey: token, title, icon, activeTags, action: (editor) => { if (typeof editor?.toggleInlineFormat !== 'function') { return; } editor.executeAction(() => { editor.toggleInlineFormat(tagName); }, { saveHistory: false }); }, }); } /** * Build one toolbar button for a schema token that opens link editing. * * @param {string} token Token name. * @param {string} title Button title. * @returns {Object} Runtime toolbar button definition. */ function createLinkButton(token, title) { return createToolbarButton({ formatKey: token, title, icon: TOOLBAR_FORMAT_ICONS.link, action: (editor) => { if (typeof editor?.showLinkUI !== 'function') { return; } const usesElementScopedLinkEditing = typeof editor.supportsElementLinkEditing === 'function' ? editor.supportsElementLinkEditing() : false; const existingLink = typeof editor.getParentElement === 'function' ? editor.getParentElement('a') : null; editor.showLinkUI(usesElementScopedLinkEditing ? null : existingLink); }, }); } /** * Determine whether schema declares element-scoped link editing for the * active text component. * * @param {HTMLElement|null} element Active editable element. * @param {Object} editorOptions Normalized component editor options. * @returns {boolean} True when the component edits its root anchor directly. */ function supportsElementScopedLinkToken(element, editorOptions = {}) { if (!element || element.tagName !== 'A') { return false; } const inlineFormatCapabilities = ( editorOptions?.inlineFormatCapabilities && typeof editorOptions.inlineFormatCapabilities === 'object' ) ? editorOptions.inlineFormatCapabilities : null; const attributeCapabilities = ( editorOptions?.attributeCapabilities && typeof editorOptions.attributeCapabilities === 'object' ) ? editorOptions.attributeCapabilities : null; const buttonLinkCapability = inlineFormatCapabilities?.buttonLink; const buttonLinkTag = typeof buttonLinkCapability?.tag === 'string' ? buttonLinkCapability.tag.trim().toLowerCase() : ''; if (buttonLinkTag !== 'a') { return false; } const attributes = Array.isArray(attributeCapabilities?.buttonLink?.attributes) ? attributeCapabilities.buttonLink.attributes .map(value => (typeof value === 'string' ? value.trim() : '')) .filter(Boolean) : []; return attributes.includes('url'); } /** * Build one toolbar button for a schema list indentation action. * * @param {string} token Token name. * @param {string} title Button title. * @param {string} icon Icon markup. * @param {string} operationId Schema list operation id. * @returns {Object} Runtime toolbar button definition. */ function createListIndentButton(token, title, icon, operationId) { return createToolbarButton({ formatKey: token, title, icon, action: (editor) => { executeSchemaListOperation(editor, operationId); }, }); } /** * Build one toolbar option for schema-backed block alignment. * * @param {string} optionKey Icon/format lookup key. * @param {string} title Option title. * @param {string} value Alignment value. * @returns {Object} Runtime toolbar option definition. */ function createBlockAlignOption(optionKey, title, value) { return createToolbarButton({ formatKey: optionKey, formatType: 'blockAlign', title, icon: TOOLBAR_FORMAT_ICONS[optionKey], value, action: (editor) => { executeSchemaBlockAttributeOperation(editor, 'set_align', value); }, }); } /** * Build one toolbar option for schema-backed text alignment. * * @param {string} optionKey Icon/format lookup key. * @param {string} title Option title. * @param {string} value Alignment value. * @returns {Object} Runtime toolbar option definition. */ function createTextAlignmentOption(optionKey, title, value) { return createToolbarButton({ formatKey: optionKey, formatType: 'textAlignment', title, icon: TOOLBAR_FORMAT_ICONS[optionKey], value, action: (editor) => { executeSchemaBlockAttributeOperation(editor, 'set_text_align', value); }, }); } /** * Build one toolbar option for schema-backed heading level changes. * * @param {string|number} level Heading level or element-tag value. * @returns {Object} Runtime toolbar option definition. */ function createHeadingLevelOption(level) { const value = typeof level === 'string' ? level.trim().toLowerCase() : level; const tag = typeof value === 'number' ? `h${value}` : value; const levelNumber = Number.parseInt(String(tag).replace(/^h/i, ''), 10); const title = tag === 'p' ? 'Paragraph' : tag === 'div' ? 'Div' : `Heading ${levelNumber}`; return createToolbarButton({ formatKey: String(tag), title, icon: title, tag, value, action: (editor) => { executeSchemaBlockAttributeOperation(editor, 'set_heading_level', value); }, }); } /** * Return the canonical built-in block-align toolbar option map. * * @returns {Map} Built-in block-align options keyed by value. */ function getBlockAlignOptionMap() { return new Map([ ['none', createBlockAlignOption('alignNone', 'None', 'none')], ['wide', createBlockAlignOption('alignWide', 'Wide Width', 'wide')], ['full', createBlockAlignOption('alignFull', 'Full Width', 'full')], ['left', createBlockAlignOption('alignLeft', 'Align Left', 'left')], ['center', createBlockAlignOption('alignCenter', 'Align Center', 'center')], ['right', createBlockAlignOption('alignRight', 'Align Right', 'right')], ]); } /** * Create the schema-backed text-alignment dropdown. * * @returns {Object} Runtime toolbar dropdown definition. */ function createTextAlignmentDropdown() { return createToolbarDropdown({ formatKey: 'textAlignment', title: 'Text Alignment', defaultIcon: TOOLBAR_FORMAT_ICONS.textAlignmentDropdown, options: [ createTextAlignmentOption('textAlignLeft', 'Align Text Left', 'left'), createTextAlignmentOption('textAlignCenter', 'Align Text Center', 'center'), createTextAlignmentOption('textAlignRight', 'Align Text Right', 'right'), ], }); } function getSchemaBlockAlignValues(editorOptions = {}) { const operations = Array.isArray(editorOptions?.operations) ? editorOptions.operations : []; const operation = operations.find(candidate => ( String(candidate?.id || '').trim() === 'set_align' && String(candidate?.kind || '').trim() === 'block_attribute_change' )) || null; const operationValues = Array.isArray(operation?.values) ? operation.values : null; const capabilityValues = Array.isArray(editorOptions?.attributeCapabilities?.align?.values) ? editorOptions.attributeCapabilities.align.values : null; const values = operationValues && operationValues.length ? operationValues : capabilityValues; return Array.isArray(values) ? values.map(value => String(value || '').trim().toLowerCase()).filter(Boolean) : []; } function createBlockAlignDropdown(editorOptions = {}) { const allowedValues = getSchemaBlockAlignValues(editorOptions); const optionMap = getBlockAlignOptionMap(); const options = allowedValues.map(value => optionMap.get(value)).filter(Boolean); if (!options.length) { return null; } return createToolbarDropdown({ formatKey: 'align', title: 'Align', defaultIcon: options[0].icon, options }); } /** * Create the schema-backed heading-level dropdown. * * @returns {Object} Runtime toolbar dropdown definition. */ function createHeadingDropdown(editorOptions = {}) { const operation = (editorOptions.operations || []).find(candidate => ( String(candidate?.id || '').trim() === 'set_heading_level' )) || null; const values = Array.isArray(operation?.values) ? operation.values : (editorOptions.attributeCapabilities?.headingLevels?.values || []); return createToolbarDropdown({ formatKey: 'headingLevels', title: 'Heading Level', defaultIcon: 'H', options: values.map(createHeadingLevelOption), }); } /** * Create the shared media-replace toolbar button. * * @returns {Object} Runtime toolbar button definition. */ function createReplaceMediaButton() { return createToolbarButton({ formatKey: 'replaceMedia', icon: TOOLBAR_FORMAT_ICONS.replaceMedia, title: 'Replace Media', className: 'mwp-sfe-editor-btn-text', action: (editor) => { if (typeof editor?.showMediaReplaceUI === 'function') { editor.showMediaReplaceUI(); } } }); } /** * Resolve one built-in schema toolbar token to a runtime format definition. * * @param {string} token Built-in schema toolbar token. * @param {HTMLElement|null} element Active editable element. * @param {Object} editorOptions Normalized editor options. * @returns {Object|null} Runtime toolbar definition or null. */ function resolveSchemaFormatToken(token, element, editorOptions = {}) { const normalizedToken = typeof token === 'string' ? token.trim() : ''; if (!normalizedToken) return null; switch (normalizedToken) { case 'undo': return createToolbarButton({ formatKey: 'undo', title: 'Undo', icon: TOOLBAR_FORMAT_ICONS.undo, action: (editor) => editor?.undo?.(), }); case 'redo': return createToolbarButton({ formatKey: 'redo', title: 'Redo', icon: TOOLBAR_FORMAT_ICONS.redo, action: (editor) => editor?.redo?.(), }); case 'bold': return createInlineFormatButton('bold', 'Bold', TOOLBAR_FORMAT_ICONS.bold, 'strong', ['strong', 'b']); case 'italic': return createInlineFormatButton('italic', 'Italic', TOOLBAR_FORMAT_ICONS.italic, 'em', ['em', 'i']); case 'strikethrough': return createInlineFormatButton('strikethrough', 'Strikethrough', TOOLBAR_FORMAT_ICONS.strikethrough, 's', ['s', 'strike']); case 'link': return createLinkButton('link', 'Link'); case 'buttonLink': return !supportsElementScopedLinkToken(element, editorOptions) ? null : createLinkButton('buttonLink', 'Button Link'); case 'textAlignment': return createTextAlignmentDropdown(); case 'align': return createBlockAlignDropdown(editorOptions); case 'headingLevels': return createHeadingDropdown(editorOptions); case 'orderedList': return createToolbarButton({ formatKey: 'orderedList', title: 'Ordered List', icon: TOOLBAR_FORMAT_ICONS.orderedList, action: (editor) => executeListTypeOperation(editor, 'ordered'), }); case 'unorderedList': return createToolbarButton({ formatKey: 'unorderedList', title: 'Unordered List', icon: TOOLBAR_FORMAT_ICONS.unorderedList, action: (editor) => executeListTypeOperation(editor, 'unordered'), }); case 'indent': return createListIndentButton('indent', 'Indent', TOOLBAR_FORMAT_ICONS.indent, 'indent_list_item'); case 'outdent': return createListIndentButton('outdent', 'Outdent', TOOLBAR_FORMAT_ICONS.outdent, 'outdent_list_item'); case 'replaceMedia': return createReplaceMediaButton(); default: return null; } } /** * Convert one nested schema format token spec into concrete toolbar configs. * * @param {Array} formatsSpec Nested schema token spec. * @param {HTMLElement|null} element Active editable element. * @param {Object} editorOptions Normalized editor options. * @param {number} depth Current recursion depth. * @returns {Array|null} Concrete toolbar config tree. */ function buildFormatsFromSchemaSpec(formatsSpec, element, editorOptions = {}, depth = 0) { if (depth > 3 || !Array.isArray(formatsSpec)) return null; const resolved = []; formatsSpec.forEach(item => { if (typeof item === 'string') { const format = resolveSchemaFormatToken(item, element, editorOptions); if (format) { resolved.push(format); } return; } if (Array.isArray(item)) { const group = buildFormatsFromSchemaSpec(item, element, editorOptions, depth + 1); if (Array.isArray(group) && group.length) { resolved.push(group); } } }); return resolved.length ? resolved : null; } /** * Flatten one nested toolbar definition tree into a single format list. * * @param {Array} items Nested toolbar definition tree. * @returns {Object[]} Flat format list. */ function flattenFormats(items = []) { const flattened = []; (items || []).forEach((item) => { if (Array.isArray(item)) { flattened.push(...flattenFormats(item)); return; } if (!item || typeof item !== 'object') { return; } flattened.push(item); if (item.type === 'dropdown' && Array.isArray(item.options)) { flattened.push(...flattenFormats(item.options)); } }); return flattened; } class ToolbarManager { constructor(host, options = {}) { this.host = host || null; this.options = options || {}; this.toolbar = null; this._toolbarPointerdownHandler = null; this._activeToolbarDropdownWrapper = null; if (this.host && typeof this.host.attachToolbarManager === 'function') { this.host.attachToolbarManager(this); } } getFormats() { return Array.isArray(this.host?.formats) ? this.host.formats : []; } /** * Return one flat runtime toolbar format list. * * @returns {Object[]} Flat toolbar format list. */ getFlatFormats() { return flattenFormats(this.getFormats()); } createToolbar() { if (this.toolbar && this.toolbar.parentNode) this.toolbar.remove(); this.toolbar = document.createElement('div'); this.toolbar.className = 'mwp-sfe-editor-toolbar'; const selectorSvg = ''; const createButton = (format) => { const btn = document.createElement('button'); btn.type = 'button'; btn.className = ['mwp-sfe-editor-btn', format.className || ''].filter(Boolean).join(' '); btn.innerHTML = format.icon; btn.title = format.title; btn.dataset.format = format.title; if (format.title === 'Undo') btn.dataset.action = 'undo'; if (format.title === 'Redo') btn.dataset.action = 'redo'; btn.addEventListener('mousedown', (event) => event.preventDefault()); btn.addEventListener('click', (event) => { event.preventDefault(); format.action(this.host); }); return btn; }; const buildItems = (items, container) => { items.forEach(item => { if (Array.isArray(item)) { const group = document.createElement('div'); group.className = 'mwp-sfe-btn-group'; buildItems(item, group); container.appendChild(group); } else if (item.type === 'dropdown') { const wrapper = document.createElement('div'); wrapper.className = 'mwp-sfe-dropdown'; const toggle = document.createElement('button'); toggle.className = 'mwp-sfe-editor-btn mwp-sfe-dropdown-toggle'; toggle.title = item.title; if (item.formatKey === 'headingLevels') { wrapper.classList.add('mwp-sfe-dropdown-heading-level'); toggle.innerHTML = `${item.defaultIcon} ${selectorSvg}`; } else { toggle.innerHTML = `${item.defaultIcon}`; } const content = document.createElement('div'); content.className = 'mwp-sfe-dropdown-content'; item.options.forEach(opt => { const optBtn = createButton(opt); optBtn.addEventListener('click', () => { if (item.formatKey === 'headingLevels') { toggle.innerHTML = `${opt.icon} ${selectorSvg}`; } else { toggle.innerHTML = `${opt.icon}`; } content.classList.remove('mwp-sfe-show'); }); content.appendChild(optBtn); }); toggle.addEventListener('mousedown', (event) => event.preventDefault()); toggle.addEventListener('click', (event) => { event.preventDefault(); this.toolbar.querySelectorAll('.mwp-sfe-dropdown-content.mwp-sfe-show').forEach(el => { if (el !== content) el.classList.remove('mwp-sfe-show'); }); content.classList.toggle('mwp-sfe-show'); this._activeToolbarDropdownWrapper = content.classList.contains('mwp-sfe-show') ? wrapper : null; }); wrapper.appendChild(toggle); wrapper.appendChild(content); if (container.className !== 'mwp-sfe-btn-group') { const group = document.createElement('div'); group.className = 'mwp-sfe-btn-group'; group.appendChild(wrapper); container.appendChild(group); } else { container.appendChild(wrapper); } } else { const btn = createButton(item); if (container.className !== 'mwp-sfe-btn-group') { const group = document.createElement('div'); group.className = 'mwp-sfe-btn-group'; group.appendChild(btn); container.appendChild(group); } else { container.appendChild(btn); } } }); }; buildItems(this.getFormats(), this.toolbar); this.attachToolbarDropdownCloseHandler(); const toolbarContainer = this.host?.options?.toolbarContainer || null; if (toolbarContainer) { toolbarContainer.innerHTML = ''; toolbarContainer.appendChild(this.toolbar); } else if (this.host?.element?.parentNode) { this.host.element.parentNode.insertBefore(this.toolbar, this.host.element); } this.updateUndoRedoButtons(); } closeToolbarDropdowns() { if (!this.toolbar) { return; } this.toolbar.querySelectorAll('.mwp-sfe-dropdown-content.mwp-sfe-show').forEach((element) => { element.classList.remove('mwp-sfe-show'); }); this._activeToolbarDropdownWrapper = null; } attachToolbarDropdownCloseHandler() { if (!this.toolbar) { return; } if (this._toolbarPointerdownHandler) { document.removeEventListener('pointerdown', this._toolbarPointerdownHandler, true); } this._toolbarPointerdownHandler = (event) => { if ( !this.toolbar || ( this._activeToolbarDropdownWrapper && this._activeToolbarDropdownWrapper.contains(event.target) ) ) { return; } this.closeToolbarDropdowns(); }; document.addEventListener('pointerdown', this._toolbarPointerdownHandler, true); } updateUndoRedoButtons() { if (!this.toolbar) return; const undoBtn = this.toolbar.querySelector('[data-action="undo"]'); const redoBtn = this.toolbar.querySelector('[data-action="redo"]'); const canUndo = typeof this.host?.canUndo === 'function' ? this.host.canUndo() : ( typeof this.host?.historyIndex === 'number' && this.host.historyIndex > 0 ); const canRedo = typeof this.host?.canRedo === 'function' ? this.host.canRedo() : ( typeof this.host?.historyIndex === 'number' && Array.isArray(this.host?.history) && this.host.historyIndex < this.host.history.length - 1 ); if (undoBtn) { undoBtn.disabled = !canUndo; } if (redoBtn) { redoBtn.disabled = !canRedo; } } setToolbarButtonDisabled(title, isDisabled) { if (!this.toolbar || !title) { return; } const button = this.toolbar.querySelector(`button[title="${title}"]`); if (button) { button.disabled = !!isDisabled; } } updateToolbarState() { if (!this.toolbar || !this.host) return; const selection = window.getSelection(); const hasSelectionInEditor = ( selection && selection.rangeCount && typeof this.host.isSelectionInEditor === 'function' && this.host.isSelectionInEditor() ); const parents = []; if (this.host.element?.tagName) { parents.push(this.host.element.tagName.toLowerCase()); } if (hasSelectionInEditor) { const range = selection.getRangeAt(0); let node = range.commonAncestorContainer; while (node && node !== this.host.element) { if (node.nodeType === 1) parents.push(node.tagName.toLowerCase()); node = node.parentNode; } } this.toolbar.querySelectorAll('.mwp-sfe-editor-btn').forEach(btn => { btn.classList.remove('mwp-sfe-editor-btn-active'); }); const checkActive = (format, tags) => { if (!format || !Array.isArray(tags) || !tags.some(tag => parents.includes(tag))) { return false; } const btn = this.toolbar.querySelector(`button[title="${format.title}"]`); if (btn) btn.classList.add('mwp-sfe-editor-btn-active'); return true; }; if (hasSelectionInEditor) { this.getFlatFormats().forEach((format) => { if (!Array.isArray(format?.activeTags) || !format.activeTags.length) { return; } checkActive(format, format.activeTags); }); } const currentAlign = typeof this.host.getBlockAlignState === 'function' ? this.host.getBlockAlignState() : 'none'; const currentTextAlignment = typeof this.host.getTextAlignmentState === 'function' ? this.host.getTextAlignmentState() : 'left'; const currentHeadingLevel = typeof this.host.getHeadingLevelState === 'function' ? this.host.getHeadingLevelState() : null; const getDropdownFormats = (items) => { const dropdowns = []; (items || []).forEach(item => { if (Array.isArray(item)) { dropdowns.push(...getDropdownFormats(item)); return; } if (item && item.type === 'dropdown') { dropdowns.push(item); } }); return dropdowns; }; getDropdownFormats(this.getFormats()).forEach(item => { const toggle = this.toolbar.querySelector(`button[title="${item.title}"]`); if (!toggle) return; const activeOption = item.options.find(opt => { const headingLevel = typeof this.host.getHeadingLevelValueForOption === 'function' ? this.host.getHeadingLevelValueForOption(opt) : null; if ( item.formatKey === 'headingLevels' && headingLevel === currentHeadingLevel ) { return true; } if ( typeof this.host.isTextAlignmentOptionActive === 'function' && this.host.isTextAlignmentOptionActive(opt, currentTextAlignment) ) { return true; } if ( typeof this.host.isBlockAlignOptionActive === 'function' && this.host.isBlockAlignOptionActive(opt, currentAlign) ) { return true; } return false; }); const selectorSvg = ''; if (activeOption) { toggle.innerHTML = item.formatKey === 'headingLevels' ? `${activeOption.icon} ${selectorSvg}` : `${activeOption.icon}`; } else { toggle.innerHTML = item.formatKey === 'headingLevels' ? `${item.defaultIcon} ${selectorSvg}` : `${item.defaultIcon}`; } item.options.forEach(opt => { const optionButton = this.toolbar.querySelector(`button[title="${opt.title}"]`); if (!optionButton) return; let isDisabled = false; if (item.formatKey === 'headingLevels') { const optionLevel = typeof this.host.getHeadingLevelValueForOption === 'function' ? this.host.getHeadingLevelValueForOption(opt) : null; isDisabled = optionLevel === currentHeadingLevel; } else if ( typeof this.host.getTextAlignmentValueForOption === 'function' && this.host.getTextAlignmentValueForOption(opt) ) { isDisabled = typeof this.host.isTextAlignmentOptionActive === 'function' ? this.host.isTextAlignmentOptionActive(opt, currentTextAlignment) : false; } else if ( typeof this.host.getBlockAlignValueForOption === 'function' && this.host.getBlockAlignValueForOption(opt) ) { isDisabled = typeof this.host.isBlockAlignOptionActive === 'function' ? this.host.isBlockAlignOptionActive(opt, currentAlign) : false; } optionButton.disabled = isDisabled; }); }); const operationExecutor = SFE.SchemaOperationExecutor || null; const currentListItem = typeof this.host.getCurrentListItem === 'function' ? this.host.getCurrentListItem() : null; const currentList = ( operationExecutor && typeof operationExecutor.getCurrentListElement === 'function' ) ? operationExecutor.getCurrentListElement(this.host) : ( typeof this.host.getParentList === 'function' ? this.host.getParentList() : null ); const canIndent = typeof this.host.canIndentListItem === 'function' ? this.host.canIndentListItem(currentListItem) : false; const canOutdent = typeof this.host.canOutdentListItem === 'function' ? this.host.canOutdentListItem(currentListItem) : false; this.setToolbarButtonDisabled('Ordered List', !currentList || currentList.tagName === 'OL'); this.setToolbarButtonDisabled('Unordered List', !currentList || currentList.tagName === 'UL'); this.setToolbarButtonDisabled('Indent', !currentListItem || !canIndent); this.setToolbarButtonDisabled('Outdent', !currentListItem || !canOutdent); this.updateUndoRedoButtons(); } destroy(options = {}) { const removeToolbar = options.removeToolbar !== false; if (this._toolbarPointerdownHandler) { document.removeEventListener('pointerdown', this._toolbarPointerdownHandler, true); this._toolbarPointerdownHandler = null; } this._activeToolbarDropdownWrapper = null; if (removeToolbar && this.toolbar && this.toolbar.parentNode) { this.toolbar.remove(); } if (this.host && typeof this.host.detachToolbarManager === 'function') { this.host.detachToolbarManager(this); } this.toolbar = removeToolbar ? null : this.toolbar; this.host = null; } static resolveFormats(editorOptions = {}, element = null) { const schemaFormats = buildFormatsFromSchemaSpec(editorOptions?.formats, element, editorOptions); return Array.isArray(schemaFormats) && schemaFormats.length ? schemaFormats : []; } } SFE.ToolbarManager = ToolbarManager; if (typeof module !== 'undefined' && module.exports) { module.exports = { ToolbarManager }; } })(); ( function( wp ) { if ( ! wp ) { return; } wp.plugins.registerPlugin( 'classic-editor-plugin', { render: function() { var createElement = wp.element.createElement; var PluginMoreMenuItem = wp.editPost.PluginMoreMenuItem; var url = wp.url.addQueryArgs( document.location.href, { 'classic-editor': '', 'classic-editor__forget': '' } ); var linkText = lodash.get( window, [ 'classicEditorPluginL10n', 'linkText' ] ) || 'Switch to classic editor'; return createElement( PluginMoreMenuItem, { icon: 'editor-kitchensink', href: url, }, linkText ); }, } ); } )( window.wp ); Legal – M2Doc Technologies LLP

LEGAL

When you connect to or access this website, you agree to be bound by the general terms of use set out below, as well as by the specific terms mentioned on certain pages of this site, as updated from time to time. If you do not agree to these terms, you should leave this site immediately. 

Unless otherwise specified, this website, its content, and its domain name and urls: https://www.m2doc.com, are the sole property of:

M2Doc Technologies LLP  

1C/172, Kalpataru Aura, LBS Marg, Ghatkopar (W), Mumbai 400086

  1. Purpose of this web site:

This website, as well as the contents including all information, text, graphics, images, hyperlinks and other material are for general information purpose about M2Doc Technologies LLP.

  1. Trademark and copyright information

IMFS plus is registered trademarks of M2Doc Technologies LLP and RMCS referred to on this website is are the sole property of M2Doc Technologies LLP. They may not be used for any purpose without the owner’s permission, given in writing. This website and its content are protected under the laws of copyright covering texts, drawings and models, as well as by trademark law. You agree not to reproduce, other than for your own personal, non-commercial use as defined in the Code, all or part of this website or its content on any medium whatsoever without M2Doc Technologies LLP’s permission, given in writing. You also agree not to establish any hypertext links to this website or its content. M2Doc Technologies LLP does not grant any right or license for the personal and non-commercial use of the website or its content, except for a non-exclusive license to consult it on an “as is” basis, at your own risk. All other rights are reserved. 

M2Doc Technologies LLP makes no claim or representations regarding the thirdparty trademarks, which are the property of their respective owners. 

For more information, you may contact us at: contact@m2doc.com  

  1. Updating of information 

M2Doc Technologies LLP makes all reasonable efforts to ensure that this website is accessible at all times. You may however be prevented from accessing the site or experience access problems due to technical difficulties or during maintenance work or upgrades. In addition, while M2Doc Technologies LLP takes reasonable care to ensure that the information on this site is kept up to date, it makes no representation or warranty, express or implied, of its accuracy and completeness. 

The content of this website may be corrected, amended or updated at any time without notice. 

M2Doc Technologies LLP does not guarantee and makes no representations or warranties that this website and access thereto are secure, or that this website is free of viruses or bugs that could result in damage to or the loss of data. You are responsible for protecting your data from viruses. 

M2Doc Technologies LLP accepts no liability for any direct, special, indirect or consequential damages of whatsoever kind and howsoever caused, arising out of your use of or inability to access the website.

  1. Limitation of Liability

In no event will M2Doc Technologies LLP be liable to any party for any direct, indirect, incidental, special, exemplary or consequential damages of any type whatsoever related to or arising from this web site or any use of this web site, or of any site or resource linked to, referenced, or accessed through this web site, or for the use or downloading of, or access to, any materials, information, products, or services, including, without limitation, any lost profits, business interruption, lost savings or loss of programs or other data, even if M2Doc Technologies LLP is expressly advised of the possibility of such damages. This exclusion and waiver of liability applies to all causes of action, whether based on contract, warranty, tort, or any other legal theories.

  1. Confidential or proprietary information

M2Doc Technologies LLP does not want to receive confidential or proprietary information from you through our Web Site. Please note that any information or material sent to M2Doc Technologies LLP will be deemed NOT to be confidential. By sending M2Doc Technologies LLP any information or material, you grant M2Doc Technologies LLP an unrestricted, irrevocable license to use, reproduce, display, perform, modify transmit and distribute those materials or information, and you also agree that M2Doc Technologies LLP is free to use any ideas, concepts, know-how or techniques that you send us for any purpose.

  1. Jurisdictional issues

Unless otherwise specified, the materials in this Web site are presented solely for the purpose of promoting M2Doc Technologies LLP products. M2Doc Technologies LLP makes no representation that materials or products depicted in the site are appropriate or available in all locations. 
These Terms and Conditions shall be governed by and construed in accordance with the laws of India without giving effect to any principles of conflicts of law. If any provision of this agreement shall be unlawful, void, or for any reason unenforceable, then that provision shall be deemed severable from these Terms and Conditions and shall not affect the validity and enforceability of any remaining provisions. This is the entire agreement between the parties relating to the subject matter herein and shall not be modified except in writing, signed by both parties. 

This Agreement is subject to applicable Indian laws and all disputes if any, are subjected to Mumbai jurisdiction.

For any issues related to Jurisdiction, you may write to us at: contact@m2doc.com  

Updated on: 30th August 2019

This Privacy Policy discusses how we, M2Doc Technologies LLP, collect, use, process, and disclose your information, including organisation and personal information, in relation to your access to and use of the M2Doc web and mobile applications, its cloud server and related services (“M2Doc platform”).

We ask for and collect the following type of information when you use the M2Doc platform. Without it, we may not be able to provide you with our services.

  1. Account Information

When you create an account on the M2Doc platform, you voluntarily provide your organisation and/or personal information to allow us to provide you with a more personalized experience on the M2Doc platform. We will not use your personal/organisational information for any other purpose without your consent.

  1. Device Information

Devices connected to internet can only communicate with M2Doc platform. With your consent, using internet services if your device synchronises with the M2Doc platform, data recorded on your device is transferred from your device to our databases. This allows you to access all your current and historical data.  Information and data of the devices which are bought from M2Doc and put in private network is never collected by us. Data security of the devices which are in private network solely remains with the owner of the devices.

  1. Cookie Data

When you visit our website, we use cookies to help us remember your settings and preferences to provide you with a more seamless experience on the M2Doc website.  We also use third party analytics tools to track and measure site usage. This allows us to continue producing engaging content, improve the M2Doc Platform based on our understanding of your usage.

How We Use Information

We use, store, and process the information we collect for the following purposes:

  1. Provide, Maintain, and Improve the M2Doc platform

We use the information we collect to allow you to access and personalize the M2Doc platform, to measure your current and historical data, and to provide customer support; to aggregate and anonymize data for industry and market analysis, geographic profiling, marketing and advertising, and other business purposes; and to improve the M2Doc platform to troubleshoot and protect against errors, perform analytics, conduct research, and develop new features.

We use cookies to provide you with a convenient user experience on our website as discussed in the Cookie Data subsection.

The information and data are processed and stored in our operating offices, databases, and servers located around the world as discussed in the Data Transfers section.

  1. Promote Safety and Security

We use the information we collect to verify and authenticate information you have provided, detect and prevent fraud, spam, abuse, security incidents, and other harmful activity, respond to legal requests or claims, conduct audits and risk assessments, and enforce our terms and policies.

  1. Communication with you

We use the information we collect to respond to your emails, and to send you service messages, updates, security alerts, and account notification when you log on to your M2Doc account and use the M2Doc platform.

How Information is Shared

We may preserve and disclose information to law enforcement agencies and government agencies only if we believe that disclosure is reasonably necessary to comply with a law, regulation, or legal request to assert legal rights or defend against legal claims; to prevent, detect, investigate, or otherwise address illegal activity, fraud, abuse, violations of our terms, or threats to security to the M2Doc platform, or of any person.

We may also share aggregated and anonymized information for industry and market analysis, geographic profiling, marketing and advertising, and other business purposes.

How Long Personal Information is Stored?

We store your personal information only so long as we need it to provide our services and to fulfil the purposes described in this Privacy Policy. When you have requested us to delete your M2Doc account and there is no need for us to keep it to comply with legal or regulatory obligations, we will remove your personal information from our databases and back-up and archiving systems.

Your Rights

The M2Doc account you voluntarily created allows you to access, modify, control, and delete your organisation and personal information.

  1. Accessing and Modifying Your Organisation and Personal Information and Data

By logging into your account, you can access and modify your organisation and personal information, such as your / organisation name, e-mail, password, and time zone.

You may access and view all your Device Information on the M2Doc platform. If you are registered for Special services, you may also access and view it on the M2Doc web application.

  1. Deleting Your organisation and Personal Information

You can delete your M2Doc account at any time. We will delete your Account Information from our servers and back-up and archiving systems as discussed in the How Long Personal Information is Stored section.

Data Security

We keep your data safe from unauthorized access, loss, destruction, or alteration by constantly updating administrative, technical, and physical security measures. We use encryption to safeguard your information.

However, no method of Internet transmission or electronic storage is 100% secure or error-free, so it is not possible to guarantee absolute security. You must protect against unauthorized access to your password and to your computer, and be sure to sign off when finished using a shared computer. If you have reason to believe that your interaction with us is no longer secure (e.g., if you feel that the security of any account you might have with us has been compromised), please immediately notify us by email at contact@m2doc.com  

Where we have given you or you have chosen a password which enables you to access certain portions of our websites, you are responsible for keeping this password confidential. You should not share your password with anyone.

If you have any security-related concerns, please contact us at contact@m2doc.com

Changes to the Privacy Policy

We may change the Privacy Policy from time to time. Any changes shall be posted on this page. We shall notify you of any material changes to this Policy and give you an opportunity to review and comment on any revisions before deciding if you would like to continue using the M2Doc platform. We will let you know of any material changes via an alert on the M2Doc platform or email to the email address associated with your M2Doc account.

Contact Us

If you have questions about this Privacy Policy, or about our use of your information, please contact us at contact@m2doc.com

Research and development of products & platform in areas of equipment monitoring, controlling and predictive maintenance.

M2Doc Technologies LLP

603, 6th Floor, Gold Crest Business Park, LBS Marg, Behind Kailash Esplanade, Opposite Vijay Sales (Helix 3 Mall), Ghatkopar (W), Mumbai 400086

Copyright © 2023 M2doc