UNPKG

drapcode-builder

Version:

Drapcode Builder Library

214 lines (184 loc) 5.54 kB
import { on, off } from 'utils/mixins'; import ComponentView from './ComponentView'; const compProt = ComponentView.prototype; export default ComponentView.extend({ events: { dblclick: 'onActive', input: 'onInput' }, initialize(o) { compProt.initialize.apply(this, arguments); this.disableEditing = this.disableEditing.bind(this); const model = this.model; const em = this.em; this.listenTo(model, 'focus', this.onActive); this.listenTo(model, 'change:content', this.updateContentText); this.listenTo(model, 'sync:content', this.syncContent); this.rte = em && em.get('RichTextEditor'); }, updateContentText(m, v, opts = {}) { !opts.fromDisable && this.disableEditing(); }, /** * Enable element content editing * @private * */ onActive(e) { // We place this before stopPropagation in case of nested // text components will not block the editing (#1394) if (this.rteEnabled || !this.model.get('editable')) { return; } e && e.stopPropagation && e.stopPropagation(); const { rte, em } = this; if (rte) { try { this.activeRte = rte.enable(this, this.activeRte); } catch (err) { em.logError(err); } } this.toggleEvents(1); }, onDisable() { this.disableEditing(); }, /** * Disable element content editing * @private * */ disableEditing() { const { model, rte, activeRte, em } = this; const editable = model.get('editable'); if (rte && editable) { try { rte.disable(this, activeRte); } catch (err) { em.logError(err); } this.syncContent(); } this.toggleEvents(); }, /** * get content from RTE * @return string */ getContent() { const { activeRte } = this; const canGetRteContent = activeRte && typeof activeRte.getContent === 'function'; return canGetRteContent ? activeRte.getContent() : this.getChildrenContainer().innerHTML; }, /** * Merge content from the DOM to the model */ syncContent(opts = {}) { const { model, rte, rteEnabled } = this; if (!rteEnabled && !opts.force) return; const content = this.getContent(); const comps = model.components(); const contentOpt = { fromDisable: 1, ...opts }; comps.length && comps.reset(null, opts); model.set('content', '', contentOpt); // If there is a custom RTE the content is just baked staticly // inside 'content' if (rte.customRte) { model.set('content', content, contentOpt); } else { const clean = model => { const textable = !!model.get('textable'); const selectable = !['text', 'default', ''].some(type => model.is(type)) || textable; model.set( { _innertext: !selectable, editable: selectable && model.get('editable'), selectable: selectable, hoverable: selectable, removable: textable, draggable: textable, highlightable: 0, copyable: textable, ...(!textable && { toolbar: '' }) }, opts ); model.get('components').each(model => clean(model)); }; // Avoid re-render on reset with silent option !opts.silent && model.trigger('change:content', model, '', contentOpt); comps.add(content, opts); comps.each(model => clean(model)); comps.trigger('resetNavigator'); } }, getModelsFromEl(el) { const result = []; const children = (el || this.el).childNodes; for (let index = 0; index < children.length; index++) { const child = children[index]; const model = child.__cashData && child.__cashData.model; if (model) { model.components = this.getModelsFromEl(child); if (model.get('content')) { model.attributes.content = child.textContent; } // TODO add attributes; result.push(model); } } return result; }, /** * Callback on input event * @param {Event} e */ onInput() { const { em } = this; const evPfx = 'component'; const ev = [`${evPfx}:update`, `${evPfx}:input`].join(' '); // Update toolbars em && em.trigger(ev, this.model); }, /** * Isolate disable propagation method * @param {Event} * @private * */ disablePropagation(e) { e.stopPropagation(); }, /** * Enable/Disable events * @param {Boolean} enable */ toggleEvents(enable) { const { em } = this; const mixins = { on, off }; const method = enable ? 'on' : 'off'; em.setEditing(enable); this.rteEnabled = !!enable; // The ownerDocument is from the frame var elDocs = [this.el.ownerDocument, document]; mixins.off(elDocs, 'mousedown', this.disableEditing); mixins[method](elDocs, 'mousedown', this.disableEditing); em[method]('toolbar:run:before', this.disableEditing); // Avoid closing edit mode on component click this.$el.off('mousedown', this.disablePropagation); this.$el[method]('mousedown', this.disablePropagation); // Fixes #2210 but use this also as a replacement // of this fix: bd7b804f3b46eb45b4398304b2345ce870f232d2 if (this.config.draggableComponents) { let { el } = this; while (el) { el.draggable = enable ? !1 : !0; // Note: el.parentNode is sometimes null here el = el.parentNode; el && el.tagName == 'BODY' && (el = 0); } } } });