import Backbone from 'backbone'; import { bindAll } from 'underscore'; import CssRulesView from 'css_composer/view/CssRulesView'; import ComponentView from 'dom_components/view/ComponentView'; import { appendVNodes, empty, append, createEl, createCustomEvent } from 'utils/dom'; import { on, off } from 'utils/mixins'; const motionsEv = 'transitionend oTransitionEnd transitionend webkitTransitionEnd'; export default Backbone.View.extend({ tagName: 'iframe', attributes: { allowfullscreen: 'allowfullscreen' }, initialize(o) { bindAll(this, 'updateOffset'); const { model } = this; this.config = o.config || {}; this.ppfx = this.config.pStylePrefix || ''; this.em = this.config.em; this.listenTo(model, 'change:head', this.updateHead); this.listenTo(model, 'change:x change:y', this.updatePos); this.listenTo(this.em, 'change:device', this.updateDim); this.updatePos(); }, updatePos(md) { const { model, el } = this; const { x, y } = model.attributes; const { style } = el; style.left = isNaN(x) ? x : `${x}px`; style.top = isNaN(y) ? y : `${y}px`; md && this.updateOffset(); }, /** * Update `` content of the frame */ updateHead() { const headEl = this.getHead(); empty(headEl); appendVNodes(headEl, this.model.getHead()); }, /** * Update dimensions of the frame * @private */ updateDim() { const { em, el, $el } = this; const { style } = el; const device = em.getDeviceModel(); const currW = style.width || ''; const currH = style.height || ''; const newW = device ? device.get('width') : ''; const newH = device ? device.get('height') : ''; const noChanges = currW == newW && currH == newH; style.width = newW; style.height = newH; this.updateOffset(); // Prevent fixed highlighting box which appears when on // component hover during the animation em.stopDefault({ preserveSelected: 1 }); noChanges ? this.updateOffset() : $el.on(motionsEv, this.updateOffset); }, updateOffset() { const { em } = this; const cv = em.get('Canvas'); if (!cv) return; const offset = cv.getOffset(); em.set('canvasOffset', offset); em.runDefault({ preserveSelected: 1 }); this.$el.off(motionsEv, this.updateOffset); }, getWindow() { return this.$el.get(0).contentWindow; }, getDoc() { return this.$el.get(0).contentDocument; }, getHead() { return this.getDoc().querySelector('head'); }, getBody() { return this.getDoc().querySelector('body'); }, getWrapper() { return this.$el.contents().find('body > div'); }, getJsContainer() { if (!this.jsContainer) { this.jsContainer = createEl('div', { class: `${this.ppfx}js-cont` }); } return this.jsContainer; }, render() { const { el, $el, ppfx, config } = this; $el.attr({ class: ppfx + 'frame' }); if (config.renderContent) { el.onload = this.renderBody.bind(this); } return this; }, renderBody() { const { config, model, ppfx } = this; const root = model.get('root'); const styles = model.get('styles'); const { em } = config; const win = this.getWindow(); const doc = this.getDoc(); const body = this.getBody(); const conf = em.get('Config'); // Should be handled by `head` // config.styles.forEach(style => { // externalStyles += ``; // }); // externalStyles && head.append(externalStyles); const colorWarn = '#ffca6f'; // I need all this styles to make the editor work properly // Remove `html { height: 100%;}` from the baseCss as it gives jumpings // effects (on ENTER) with RTE like CKEditor (maybe some bug there?!?) // With `body {height: auto;}` jumps in CKEditor are removed but in // Firefox is impossible to drag stuff in empty canvas, so bring back // `body {height: 100%;}`. // For the moment I give the priority to Firefox as it might be // CKEditor's issue append( body, `` ); append(body, new ComponentView({ model: root, config }).render().el); append(body, new CssRulesView({ collection: styles, config }).render().el); append(body, this.getJsContainer()); // em.trigger('loaded'); // I need to manage only the first one maybe // win.onscroll = this.onFrameScroll; // TODO this.updateOffset(); // TOFIX (check if I need it) // Avoid some default behaviours on( body, 'click', ev => ev && ev.target.tagName == 'A' && ev.preventDefault() ); on(body, 'submit', ev => ev && ev.preventDefault()); // When the iframe is focused the event dispatcher is not the same so // I need to delegate all events to the parent document [ { event: 'keydown keyup keypress', class: 'KeyboardEvent' }, { event: 'wheel', class: 'WheelEvent' } ].forEach(obj => obj.event.split(' ').forEach(event => { doc.addEventListener(event, ev => this.el.dispatchEvent(createCustomEvent(ev, obj.class)) ); }) ); } });