UNPKG

activator-oce-exporter

Version:

Extract Activator binder and convert it to valid OCE mono pacakge

410 lines (368 loc) 12 kB
import { FusionStore } from './services/fusion-store'; import { FusionAligner } from './services/fusion-aligner'; import { FusionNavigation } from './navigation'; import { freeze, unfreeze, setActivatorEnv, toggleState, } from './_actions/app.js'; import { themeApplier } from './theme-applier'; import { editorMessenger } from './editor-messenger'; import { monitoring } from './monitoring.js'; import { FusionLogger } from './services/fusion-logger'; import { FusionBase } from './base'; import { rootSelector } from '../config.json'; // This is the API accessible from browser via window.Fusion namespace /* Private methods start */ const setDefaultTemplate = ({ template = '', element }) => { element.innerHTML += template; }; const getComponentState = (el) => { let state = null; if (el.state) { const name = `${el.state}-${el.id}`; const { currentState } = FusionStore.store.getState().app; const isActive = currentState.includes(name); state = { name, isActive }; } return state; }; const generateAddedElementData = ({ element, query, options }) => ({ name: 'fusion/saveAddedComponent', data: { query, options, state: getComponentState(element), template: element.outerHTML, selectorId: element.id, tagName: element.tagName, }, }); const generateRemovedElementData = ({ query, deselectedElement, state }) => ({ name: 'fusion/saveRemovedComponent', data: { query, state, deselectedElement, }, }); const generateUpdateContentData = data => ({ name: 'actions/updateContent', data, }); const generateSaveAttributesData = data => ({ name: 'actions/saveAttributes', data, }); const generateSaveStylesData = data => ({ name: 'actions/saveStyles', data, }); const shouldAdjustState = (state, enforcedState) => { const { currentState } = FusionStore.store.getState().app; return currentState.includes(state) !== enforcedState; }; const checkElementPublish = async ({ parent, query }) => { const element = parent || document.querySelector(query); if (element.untilPublished instanceof Promise) { await element.untilPublished; } }; const shouldToggle = (state, enforcedState) => enforcedState === null || shouldAdjustState(state, enforcedState); const triggerPublishedEventRecursively = (element) => { // const children = element.querySelectorAll(':not([slot="mo-system"])'); if (element instanceof FusionBase) { element.emitCustomEvent('published'); } [...element.children].forEach(el => triggerPublishedEventRecursively(el)); }; /* Private methods end */ class FusionApi { // @todo: need remove and update editor static get isEditMode() { return FusionStore.isEditMode; } static getRootNode() { return document.querySelector(rootSelector); } /** * Add Fusion element to slide or email. * @private * @static * @param name * @param properties * @param template * @param parent * @returns {any} */ static addElement(name, properties, template, parent) { const element = document.createElement(name); element.setAttribute('id', this.generateId()); this.setAttributes({ properties, element }); setDefaultTemplate({ template, element }); parent.appendChild(element); return element; } /** * Private method for save API. Use only in class * @private * @static * */ static async saveElement(element, query, parent, options) { return this.sendSaveRequest('saveAddedComponent', { element, query: query || parent.id, options, }); } /** + * Private method for update content API. Use only in class + * @static + * @param data.query {string} Query for save element + * @param data.element {HTMLElement} Element that will be saved + * @param data.options {object} Additional params for saving + * */ static async updateContent(data) { await checkElementPublish({ query: data.query }); return this.sendSaveRequest('updateContent', data); } /** Private method for send request to Activator from Fusion part * @private * @static * @param name * @param options */ static sendSaveRequest(name, options) { let requestObj = null; const map = { saveAddedComponent: generateAddedElementData, saveRemovedComponent: generateRemovedElementData, saveAttributes: generateSaveAttributesData, saveStyles: generateSaveStylesData, updateContent: generateUpdateContentData, }; if (Object.prototype.hasOwnProperty.call(map, name)) { requestObj = map[name](options); requestObj.data.ignoreUndo = options.ignoreUndo; this.request(requestObj); } else { throw new Error(`Unknown save request name ${name}`); } } /** Private method to delete actions, connected to the removed element * @private * @static * @param {string} id * @returns {boolean} */ static checkActionsDeletion(id) { const selectorAttrs = ['on', 'to']; const relatedActionsSelector = selectorAttrs .map(attr => `fusion-action[${attr}*="#${id}"]`).join(', '); const actionElements = [...document.querySelectorAll(relatedActionsSelector)]; actionElements.forEach(el => this.deleteElement(el.id, false, true)); return actionElements.length > 0; } // Are we in iRep, Accelerator, Activator, or Fusion Presentation? static get env() { return false; } static get isTouchSupported() { return 'ontouchstart' in window; } static get subscribe() { return FusionStore.subscribe; } static getEventsPreset() { return this.isTouchSupported ? { upEvent: 'touchend', startEvent: 'touchstart', moveEvent: 'touchmove', } : { upEvent: 'mouseup', startEvent: 'mousedown', moveEvent: 'mousemove', }; } static goTo(slide, presentation, direction, isDocId) { const navigationData = FusionNavigation.goTo(slide, presentation, direction, isDocId); if (FusionNavigation.isActivatorGoTo(FusionStore.isActivator, FusionStore.isEditMode)) { this.request(navigationData); } } static trackCustomData(data) { monitoring.trackCustomData(data); } static async getThemeConfig() { return themeApplier.getConfig(); } /** * @description receiving array of fragments names from fragment folders * @returns {Array<String>} */ static getFragments() { // @todo: not the best solution, since they are included into main.js const customContext = require.context('../../fragments/', true, /\.html$/); return customContext.keys().reduce((array, key) => { // Expected to have folder with index.html and thumb.png (not required) files; const fragmentName = key.split('/')[1]; array.push(fragmentName); return array; }, []); } /** * @param {{ name: String, data: Object }} data */ static request(data) { return editorMessenger.request(data); } /** * * @param name - Component name * @param properties - Component static props * @param template - Template Component static template * @param parent - Parent element where we will append element * @param query - csspath for parent element * @param options - pack of params for behaviour * @returns {HTMLElement} */ static async createElement(name, properties = {}, template, parent, query, options = { setActive: true, setState: true }) { if (!name) throw new Error('[ERR:Components:UI] Unknown name'); await checkElementPublish({ parent }); const element = await this.addElement(name, properties, template, parent); await this.saveElement(element, query, parent, options); triggerPublishedEventRecursively(element); return element; } static createActivatorAttrs(element, attrs) { element.setAttribute(attrs.category, attrs.categories.component); element.setAttribute(attrs.type, attrs.categories.component); } static deleteElement(id, deselectedElement, ignoreUndo) { this.checkActionsDeletion(id); const element = document.getElementById(id); let parent = null; if (element) { const state = getComponentState(element); parent = element.parentNode; // @todo: would be better to make this method async, but in that case we will break stuff in the editor checkElementPublish({ parent }).then(() => { parent.removeChild(element); this.sendSaveRequest('saveRemovedComponent', { state, element, ignoreUndo, deselectedElement, query: `#${id}`, }); }); } else { throw new Error(`[ERR:deleteElement:Fusion] Could not find element with ID ${id}`); } return parent; } /** * @description check DOM element by received query selector * @param {String} query - query selector of DOM element */ static async checkElement(query) { const element = document.querySelector(query); if (element) { await checkElementPublish({ parent: element.parentNode }); } else { FusionLogger.warn(`Element ${query} doesn't exist in DOM`, 'api'); } return element; } static async saveAttributes(query, attributes, ignoreUndo = true) { return await this.checkElement(query) && this.sendSaveRequest('saveAttributes', { query, attributes, ignoreUndo }); } static async saveStyles(query, styles, ignoreUndo = true) { return await this.checkElement(query) && this.sendSaveRequest('saveStyles', { query, styles, ignoreUndo }); } static getCurrentState() { return FusionStore.store.getState().app.currentState; } static getRegisteredStates() { return FusionStore.store.getState().app.registeredStates; } static getRegisteredComponents() { return FusionStore.store.getState().app.registeredComponents; } /** * @param {Object} data - configuration for elements align * @param {String} data.query - parent element selector * @param {Align} data.align */ static alignInnerElements(data) { const itemsToSave = FusionAligner.applyAlignToItems(data); if (itemsToSave.length) { itemsToSave.forEach(({ id, styles }) => { this.saveStyles(`#${id}`, styles); }); } else { FusionLogger.warn('Nothing to align', 'FusionApi'); } } static freeze() { return FusionStore.store.dispatch(freeze()); } static unfreeze() { return FusionStore.store.dispatch(unfreeze()); } static setActivatorEnv() { editorMessenger.initPublishing(); return FusionStore.store.dispatch(setActivatorEnv()); } static generateId() { return (Date.now().toString(36) + Math.random().toString(36).substr(2, 5)).toUpperCase(); } static toggleState(state, enforcedState = null) { if (shouldToggle(state, enforcedState)) { FusionStore.store.dispatch(toggleState(state)); document.body.classList.toggle(state); } } static updateLevelById(id) { const el = document.getElementById(id); if (el && el.removeLevel) { el.removeLevel(); el.addLevel(); } } /** * Attributes option object. * @typedef {Object} UpdateAttrOptions * @property {Boolean} isComponent - value of attribute * @property {String} selectorId - id of DOM element * @property {AttrConfig[]} attrList */ /** * @param {UpdateAttrOptions} data */ static updateAttributeList(data) { return this.request({ name: 'actions/updateAttributeList', data, }); } static getMoData() { return this.request({ name: 'actions/getMoData', }); } /** * * @param {object} properties of component * @param {HTMLElement} component */ static setAttributes({ properties = {}, element }) { Object .keys(properties) .forEach((propertyName) => { const { value } = properties[propertyName]; value !== false ? element.setAttribute(propertyName, value) : element.removeAttribute(propertyName); }); } } export { FusionApi };