`);\n this.getJsContainer().appendChild(view.scriptContainer.get(0));\n }\n\n view.el.id = id;\n view.scriptContainer.html('');\n // In editor, I make use of setTimeout as during the append process of elements\n // those will not be available immediately, therefore 'item' variable\n const script = document.createElement('script');\n const scriptFn = model.getScriptString();\n const scriptFnStr = model.get('script-props')\n ? scriptFn\n : `function(){\\n${scriptFn}\\n;}`;\n const scriptProps = JSON.stringify(model.__getScriptProps());\n script.innerHTML = `\n setTimeout(function() {\n var item = document.getElementById('${id}');\n if (!item) return;\n (${scriptFnStr}.bind(item))(${scriptProps})\n }, 1);`;\n // #873\n // Adding setTimeout will make js components work on init of the editor\n setTimeout(() => {\n const scr = view.scriptContainer;\n scr && scr.get(0).appendChild(script);\n }, 0);\n },\n\n /**\n * Get javascript container\n * @private\n */\n getJsContainer(view) {\n const frameView = this.getFrameView(view);\n return frameView && frameView.getJsContainer();\n },\n\n getFrameView(view) {\n return (view && view._getFrame()) || this.em.get('currentFrame');\n },\n\n _renderFrames() {\n if (!this.ready) return;\n const { model, frames, em, framesArea } = this;\n const frms = model.get('frames');\n frms.listenToLoad();\n frames.render();\n const mainFrame = frms.at(0);\n const currFrame = mainFrame && mainFrame.view;\n em.setCurrentFrame(currFrame);\n framesArea && framesArea.appendChild(frames.el);\n this.frame = currFrame;\n },\n\n render() {\n const { el, $el, ppfx, config, em } = this;\n $el.html(this.template());\n const $frames = $el.find('[data-frames]');\n this.framesArea = $frames.get(0);\n\n const toolsWrp = $el.find('[data-tools]');\n this.toolsWrapper = toolsWrp.get(0);\n toolsWrp.append(`\n
\n
\n `);\n const toolsEl = el.querySelector(`#${ppfx}tools`);\n this.hlEl = el.querySelector(`.${ppfx}highlighter`);\n this.badgeEl = el.querySelector(`.${ppfx}badge`);\n this.placerEl = el.querySelector(`.${ppfx}placeholder`);\n this.ghostEl = el.querySelector(`.${ppfx}ghost`);\n this.toolbarEl = el.querySelector(`.${ppfx}toolbar`);\n this.resizerEl = el.querySelector(`.${ppfx}resizer`);\n this.offsetEl = el.querySelector(`.${ppfx}offset-v`);\n this.fixedOffsetEl = el.querySelector(`.${ppfx}offset-fixed-v`);\n this.toolsGlobEl = el.querySelector(`.${ppfx}tools-gl`);\n this.toolsEl = toolsEl;\n this.el.className = getUiClass(em, this.className);\n this.ready = 1;\n this._renderFrames();\n\n return this;\n }\n});\n","/**\n * You can customize the initial state of the module from the editor initialization, by passing the following [Configuration Object](https://github.com/artf/grapesjs/blob/master/src/canvas/config/config.js)\n * ```js\n * const editor = grapesjs.init({\n * canvas: {\n * // options\n * }\n * })\n * ```\n *\n * Once the editor is instantiated you can use its API and listen to its events. Before using these methods, you should get the module from the instance.\n *\n * ```js\n * // Listen to events\n * editor.on('canvas:drop', () => { ... });\n *\n * // Use the API\n * const canvas = editor.Canvas;\n * canvas.setCoords(...);\n * ```\n * ## Available Events\n * * `canvas:dragenter` - When something is dragged inside the canvas, `DataTransfer` instance passed as an argument\n * * `canvas:dragover` - When something is dragging on canvas, `DataTransfer` instance passed as an argument\n * * `canvas:drop` - Something is dropped in canvas, `DataTransfer` instance and the dropped model are passed as arguments\n * * `canvas:dragend` - When a drag operation is ended, `DataTransfer` instance passed as an argument\n * * `canvas:dragdata` - On any dataTransfer parse, `DataTransfer` instance and the `result` are passed as arguments.\n * By changing `result.content` you're able to customize what is dropped\n *\n * ## Methods\n * * [getConfig](#getconfig)\n * * [getElement](#getelement)\n * * [getFrameEl](#getframeel)\n * * [getWindow](#getwindow)\n * * [getDocument](#getdocument)\n * * [getBody](#getbody)\n * * [setCustomBadgeLabel](#setcustombadgelabel)\n * * [hasFocus](#hasfocus)\n * * [scrollTo](#scrollto)\n * * [setZoom](#setzoom)\n * * [getZoom](#getzoom)\n * * [getCoords](#getcoords)\n * * [setCoords](#setcoords)\n *\n * [Component]: component.html\n * [Frame]: frame.html\n *\n * @module Canvas\n */\n\nimport { isUndefined } from 'underscore';\nimport { getElement, getViewEl } from 'utils/mixins';\nimport defaults from './config/config';\nimport Canvas from './model/Canvas';\nimport canvasView from './view/CanvasView';\n\nexport default () => {\n let c = {};\n let canvas;\n let CanvasView;\n\n return {\n /**\n * Used inside RTE\n * @private\n */\n getCanvasView() {\n return CanvasView;\n },\n\n name: 'Canvas',\n\n /**\n * Initialize module. Automatically called with a new instance of the editor\n * @param {Object} config Configurations\n * @private\n */\n init(config = {}) {\n c = {\n ...defaults,\n ...config,\n module: this,\n };\n\n this.em = c.em;\n const { scripts, styles } = c;\n const ppfx = c.pStylePrefix;\n if (ppfx) c.stylePrefix = ppfx + c.stylePrefix;\n canvas = new Canvas({ scripts, styles }, config);\n this.model = canvas;\n this.startAutoscroll = this.startAutoscroll.bind(this);\n this.stopAutoscroll = this.stopAutoscroll.bind(this);\n return this;\n },\n\n onLoad() {\n this.model.init();\n },\n\n getModel() {\n return canvas;\n },\n\n /**\n * Get the configuration object\n * @returns {Object} Configuration object\n * @example\n * console.log(canvas.getConfig())\n */\n getConfig() {\n return c;\n },\n\n /**\n * Get the canvas element\n * @returns {HTMLElement}\n */\n getElement() {\n return CanvasView.el;\n },\n\n getFrame(index) {\n return this.getFrames()[index || 0];\n },\n\n /**\n * Get the main frame element of the canvas\n * @returns {HTMLIFrameElement}\n */\n getFrameEl() {\n const { frame } = CanvasView || {};\n return frame && frame.el;\n },\n\n getFramesEl() {\n return CanvasView.framesArea;\n },\n\n /**\n * Get the main frame window instance\n * @returns {Window}\n */\n getWindow() {\n return this.getFrameEl().contentWindow;\n },\n\n /**\n * Get the main frame document element\n * @returns {HTMLDocument}\n */\n getDocument() {\n const frame = this.getFrameEl();\n return frame && frame.contentDocument;\n },\n\n /**\n * Get the main frame body element\n * @return {HTMLBodyElement}\n */\n getBody() {\n const doc = this.getDocument();\n return doc && doc.body;\n },\n\n _getCompFrame(compView) {\n return compView && compView._getFrame();\n },\n\n _getLocalEl(globalEl, compView, method) {\n let result = globalEl;\n const frameView = this._getCompFrame(compView);\n result = frameView ? frameView[method]() : result;\n\n return result;\n },\n\n /**\n * Returns element containing all global canvas tools\n * @returns {HTMLElement}\n * @private\n */\n getGlobalToolsEl() {\n return CanvasView.toolsGlobEl;\n },\n\n /**\n * Returns element containing all canvas tools\n * @returns {HTMLElement}\n * @private\n */\n getToolsEl(compView) {\n return this._getLocalEl(CanvasView.toolsEl, compView, 'getToolsEl');\n },\n\n /**\n * Returns highlighter element\n * @returns {HTMLElement}\n * @private\n */\n getHighlighter(compView) {\n return this._getLocalEl(CanvasView.hlEl, compView, 'getHighlighter');\n },\n\n /**\n * Returns badge element\n * @returns {HTMLElement}\n * @private\n */\n getBadgeEl(compView) {\n return this._getLocalEl(CanvasView.badgeEl, compView, 'getBadgeEl');\n },\n\n /**\n * Returns placer element\n * @returns {HTMLElement}\n * @private\n */\n getPlacerEl() {\n return CanvasView.placerEl;\n },\n\n /**\n * Returns ghost element\n * @returns {HTMLElement}\n * @private\n */\n getGhostEl() {\n return CanvasView.ghostEl;\n },\n\n /**\n * Returns toolbar element\n * @returns {HTMLElement}\n * @private\n */\n getToolbarEl() {\n return CanvasView.toolbarEl;\n },\n\n /**\n * Returns resizer element\n * @returns {HTMLElement}\n * @private\n */\n getResizerEl() {\n return CanvasView.resizerEl;\n },\n\n /**\n * Returns offset viewer element\n * @returns {HTMLElement}\n * @private\n */\n getOffsetViewerEl(compView) {\n return this._getLocalEl(CanvasView.offsetEl, compView, 'getOffsetViewerEl');\n },\n\n /**\n * Returns fixed offset viewer element\n * @returns {HTMLElement}\n * @private\n */\n getFixedOffsetViewerEl() {\n return CanvasView.fixedOffsetEl;\n },\n\n render() {\n CanvasView && CanvasView.remove();\n CanvasView = new canvasView({\n model: canvas,\n config: c,\n });\n return CanvasView.render().el;\n },\n\n /**\n * Get frame position\n * @returns {Object}\n * @private\n */\n getOffset() {\n var frameOff = this.offset(this.getFrameEl());\n var canvasOff = this.offset(this.getElement());\n return {\n top: frameOff.top - canvasOff.top,\n left: frameOff.left - canvasOff.left,\n };\n },\n\n /**\n * Get the offset of the passed component element\n * @param {HTMLElement} el\n * @returns {Object}\n * @private\n */\n offset(el) {\n return CanvasView.offset(el);\n },\n\n /**\n * Set custom badge naming strategy\n * @param {Function} f\n * @example\n * canvas.setCustomBadgeLabel(function(component){\n * return component.getName();\n * });\n */\n setCustomBadgeLabel(f) {\n c.customBadgeLabel = f;\n },\n\n /**\n * Get element position relative to the canvas\n * @param {HTMLElement} el\n * @returns {Object}\n * @private\n */\n getElementPos(el, opts) {\n return CanvasView.getElementPos(el, opts);\n },\n\n /**\n * Returns element's offsets like margins and paddings\n * @param {HTMLElement} el\n * @returns {Object}\n * @private\n */\n getElementOffsets(el) {\n return CanvasView.getElementOffsets(el);\n },\n\n /**\n * Get canvas rectangular data\n * @returns {Object}\n */\n getRect() {\n const { top, left } = CanvasView.getPosition();\n return {\n ...CanvasView.getCanvasOffset(),\n topScroll: top,\n leftScroll: left,\n };\n },\n\n /**\n * This method comes handy when you need to attach something like toolbars\n * to elements inside the canvas, dealing with all relative position,\n * offsets, etc. and returning as result the object with positions which are\n * viewable by the user (when the canvas is scrolled the top edge of the element\n * is not viewable by the user anymore so the new top edge is the one of the canvas)\n *\n * The target should be visible before being passed here as invisible elements\n * return empty string as width\n * @param {HTMLElement} target The target in this case could be the toolbar\n * @param {HTMLElement} element The element on which I'd attach the toolbar\n * @param {Object} options Custom options\n * @param {Boolean} options.toRight Set to true if you want the toolbar attached to the right\n * @return {Object}\n * @private\n */\n getTargetToElementDim(target, element, options = {}) {\n var opts = options || {};\n var canvasPos = CanvasView.getPosition();\n if (!canvasPos) return;\n var pos = opts.elPos || CanvasView.getElementPos(element);\n var toRight = options.toRight || 0;\n var targetHeight = opts.targetHeight || target.offsetHeight;\n var targetWidth = opts.targetWidth || target.offsetWidth;\n var eventToTrigger = opts.event || null;\n\n var elTop = pos.top - targetHeight;\n var elLeft = pos.left;\n elLeft += toRight ? pos.width : 0;\n elLeft = toRight ? elLeft - targetWidth : elLeft;\n\n var leftPos = elLeft < canvasPos.left ? canvasPos.left : elLeft;\n var topPos = elTop < canvasPos.top ? canvasPos.top : elTop;\n topPos = topPos > pos.top + pos.height ? pos.top + pos.height : topPos;\n\n var result = {\n top: topPos,\n left: leftPos,\n elementTop: pos.top,\n elementLeft: pos.left,\n elementWidth: pos.width,\n elementHeight: pos.height,\n targetWidth: target.offsetWidth,\n targetHeight: target.offsetHeight,\n canvasTop: canvasPos.top,\n canvasLeft: canvasPos.left,\n canvasWidth: canvasPos.width,\n canvasHeight: canvasPos.height,\n };\n\n // In this way I can catch data and also change the position strategy\n if (eventToTrigger && c.em) {\n c.em.trigger(eventToTrigger, result);\n }\n\n return result;\n },\n\n canvasRectOffset(el, pos, opts = {}) {\n const getFrameElFromDoc = doc => {\n const { defaultView } = doc;\n return defaultView && defaultView.frameElement;\n };\n\n const rectOff = (el, top = 1, pos) => {\n const zoom = this.em.getZoomDecimal();\n const side = top ? 'top' : 'left';\n const doc = el.ownerDocument;\n const { offsetTop = 0, offsetLeft = 0 } = opts.offset ? getFrameElFromDoc(doc) : {};\n const { scrollTop = 0, scrollLeft = 0 } = doc.body || {};\n const scroll = top ? scrollTop : scrollLeft;\n const offset = top ? offsetTop : offsetLeft;\n\n // if (!top) {\n // console.log('LEFT', { posLeft: pos[side], scroll, offset }, el);\n // }\n\n return pos[side] - (scroll - offset) * zoom;\n };\n\n return {\n top: rectOff(el, 1, pos),\n left: rectOff(el, 0, pos),\n };\n },\n\n getTargetToElementFixed(el, elToMove, opts = {}) {\n const pos = opts.pos || this.getElementPos(el);\n const cvOff = opts.canvasOff || this.canvasRectOffset(el, pos);\n const toolbarH = elToMove.offsetHeight || 0;\n const toolbarW = elToMove.offsetWidth || 0;\n const elRight = pos.left + pos.width;\n const cv = this.getCanvasView();\n const frCvOff = cv.getPosition();\n const frameOffset = cv.getFrameOffset(el);\n const { event } = opts;\n\n let top = -toolbarH;\n let left = !isUndefined(opts.left) ? opts.left : pos.width - toolbarW;\n left = pos.left < -left ? -pos.left : left;\n left = elRight > frCvOff.width ? left - (elRight - frCvOff.width) : left;\n\n // Scroll with the window if the top edge is reached and the\n // element is bigger than the canvas\n const fullHeight = pos.height + toolbarH;\n const elIsShort = fullHeight < frameOffset.height;\n\n if (cvOff.top < toolbarH) {\n if (elIsShort) {\n top = top + fullHeight;\n } else {\n top = -cvOff.top < pos.height ? -cvOff.top : pos.height;\n }\n }\n\n const result = {\n top,\n left,\n canvasOffsetTop: cvOff.top,\n canvasOffsetLeft: cvOff.left,\n };\n\n // In this way I can catch data and also change the position strategy\n event && this.em.trigger(event, result);\n\n return result;\n },\n\n /**\n * Instead of simply returning e.clientX and e.clientY this function\n * calculates also the offset based on the canvas. This is helpful when you\n * need to get X and Y position while moving between the editor area and\n * canvas area, which is in the iframe\n * @param {Event} e\n * @return {Object}\n * @private\n */\n getMouseRelativePos(e, options) {\n var opts = options || {};\n var addTop = 0;\n var addLeft = 0;\n var subWinOffset = opts.subWinOffset;\n var doc = e.target.ownerDocument;\n var win = doc.defaultView || doc.parentWindow;\n var frame = win.frameElement;\n var yOffset = subWinOffset ? win.pageYOffset : 0;\n var xOffset = subWinOffset ? win.pageXOffset : 0;\n\n if (frame) {\n var frameRect = frame.getBoundingClientRect();\n addTop = frameRect.top || 0;\n addLeft = frameRect.left || 0;\n }\n\n return {\n y: e.clientY + addTop - yOffset,\n x: e.clientX + addLeft - xOffset,\n };\n },\n\n /**\n * X and Y mouse position relative to the canvas\n * @param {Event} ev\n * @return {Object}\n * @private\n */\n getMouseRelativeCanvas(ev, opts) {\n const zoom = this.getZoomDecimal();\n const { top, left } = CanvasView.getPosition(opts);\n\n return {\n y: ev.clientY * zoom + top,\n x: ev.clientX * zoom + left,\n };\n },\n\n /**\n * Check if the canvas is focused\n * @returns {Boolean}\n */\n hasFocus() {\n return this.getDocument().hasFocus();\n },\n\n /**\n * Detects if some input is focused (input elements, text components, etc.)\n * @return {Boolean}\n * @private\n */\n isInputFocused() {\n const doc = this.getDocument();\n const frame = this.getFrameEl();\n const toIgnore = ['body', ...this.getConfig().notTextable];\n const docActive = frame && document.activeElement === frame;\n const focused = docActive ? doc && doc.activeElement : document.activeElement;\n\n return focused && !toIgnore.some(item => focused.matches(item));\n },\n\n /**\n * Scroll canvas to the element if it's not visible. The scrolling is\n * executed via `scrollIntoView` API and options of this method are\n * passed to it. For instance, you can scroll smoothly by using\n * `{ behavior: 'smooth' }`.\n * @param {HTMLElement|[Component]} el\n * @param {Object} [opts={}] Options, same as options for `scrollIntoView`\n * @param {Boolean} [opts.force=false] Force the scroll, even if the element is already visible\n * @example\n * const selected = editor.getSelected();\n * // Scroll smoothly (this behavior can be polyfilled)\n * canvas.scrollTo(selected, { behavior: 'smooth' });\n * // Force the scroll, even if the element is alredy visible\n * canvas.scrollTo(selected, { force: true });\n */\n scrollTo(el, opts = {}) {\n const elem = getElement(el);\n const view = elem && getViewEl(elem);\n view && view.scrollIntoView(opts);\n },\n\n /**\n * Start autoscroll\n * @private\n */\n startAutoscroll(frame) {\n const fr = (frame && frame.view) || this.em.getCurrentFrame();\n fr && fr.startAutoscroll();\n },\n\n /**\n * Stop autoscroll\n * @private\n */\n stopAutoscroll(frame) {\n const fr = (frame && frame.view) || this.em.getCurrentFrame();\n fr && fr.stopAutoscroll();\n },\n\n /**\n * Set canvas zoom value\n * @param {Number} value The zoom value, from 0 to 100\n * @returns {this}\n * @example\n * canvas.setZoom(50); // set zoom to 50%\n */\n setZoom(value) {\n canvas.set('zoom', parseFloat(value));\n return this;\n },\n\n /**\n * Get canvas zoom value\n * @returns {Number}\n * @example\n * canvas.setZoom(50); // set zoom to 50%\n * const zoom = canvas.getZoom(); // 50\n */\n getZoom() {\n return parseFloat(canvas.get('zoom'));\n },\n\n /**\n * Set canvas position coordinates\n * @param {Number} x Horizontal position\n * @param {Number} y Vertical position\n * @returns {this}\n * @example\n * canvas.setCoords(100, 100);\n */\n setCoords(x, y) {\n canvas.set({ x: parseFloat(x), y: parseFloat(y) });\n return this;\n },\n\n /**\n * Get canvas position coordinates\n * @returns {Object} Object containing coordinates\n * @example\n * canvas.setCoords(100, 100);\n * const coords = canvas.getCoords();\n * // { x: 100, y: 100 }\n */\n getCoords() {\n const { x, y } = canvas.attributes;\n return { x, y };\n },\n\n getZoomDecimal() {\n return this.getZoom() / 100;\n },\n\n getZoomMultiplier() {\n const zoom = this.getZoomDecimal();\n return zoom ? 1 / zoom : 1;\n },\n\n toggleFramesEvents(on) {\n const { style } = this.getFramesEl();\n style.pointerEvents = on ? '' : 'none';\n },\n\n getFrames() {\n return canvas.get('frames').map(item => item);\n },\n\n /**\n * Add new frame to the canvas\n * @param {Object} props Frame properties\n * @returns {[Frame]}\n * @example\n * canvas.addFrame({\n * name: 'Mobile home page',\n * x: 100, // Position in canvas\n * y: 100,\n * width: 500, // Frame dimensions\n * height: 600,\n * // device: 'DEVICE-ID',\n * components: [\n * '
Title frame
',\n * '
Paragraph frame
',\n * ],\n * styles: `\n * .testh { color: red; }\n * .testp { color: blue; }\n * `,\n * });\n */\n addFrame(props = {}, opts = {}) {\n return canvas.get('frames').add(\n {\n ...props,\n },\n {\n ...opts,\n em: this.em,\n }\n );\n },\n\n destroy() {\n canvas.stopListening();\n CanvasView && CanvasView.remove();\n [c, canvas, CanvasView].forEach(i => (i = {}));\n ['em', 'model', 'droppable'].forEach(i => (this[i] = {}));\n },\n };\n};\n","export default {\n // Style prefix\n stylePrefix: 'cm-',\n\n inlineCss: false\n};\n","import { Model } from 'backbone';\n\nexport default class HTMLGenerator extends Model {\n build(model, opts = {}) {\n const models = model.components();\n const htmlOpts = {};\n const { em } = opts;\n\n // Remove unnecessary IDs\n if (opts.cleanId && em) {\n const rules = em.get('CssComposer').getAll();\n const idRules = rules\n .toJSON()\n .map(rule => {\n const sels = rule.selectors;\n const sel = sels && sels.length === 1 && sels.models[0];\n return sel && sel.isId() && sel.get('name');\n })\n .filter(i => i);\n\n htmlOpts.attributes = (mod, attrs) => {\n const { id } = attrs;\n if (\n id &&\n id[0] === 'i' && // all autogenerated IDs start with 'i'\n !mod.get('script') && // if the component has script, we have to leave the ID\n !mod.get('attributes').id && // id is not intentionally in attributes\n idRules.indexOf(id) < 0 // we shouldn't have any rule with this ID\n ) {\n delete attrs.id;\n }\n return attrs;\n };\n }\n\n if (opts.exportWrapper) {\n return model.toHTML({\n ...htmlOpts,\n ...(opts.wrapperIsBody && model.is('wrapper') && { tag: 'body' })\n });\n }\n\n return this.buildModels(models, htmlOpts);\n }\n\n buildModels(models, opts = {}) {\n let code = '';\n models.forEach(mod => (code += mod.toHTML(opts)));\n return code;\n }\n}\n","import { each } from 'underscore';\nimport Backbone from 'backbone';\n\nexport default Backbone.Model.extend({\n /** @inheritdoc */\n build(model) {\n var json = model.toJSON();\n this.beforeEach(json);\n\n each(\n json,\n function(v, attr) {\n var obj = json[attr];\n if (obj instanceof Backbone.Model) {\n json[attr] = this.build(obj);\n } else if (obj instanceof Backbone.Collection) {\n var coll = obj;\n json[attr] = [];\n if (coll.length) {\n coll.each(function(el, index) {\n json[attr][index] = this.build(el);\n }, this);\n }\n }\n },\n this\n );\n\n return json;\n },\n\n /**\n * Execute on each object\n * @param {Object} obj\n */\n beforeEach(obj) {\n delete obj.status;\n }\n});\n","import { extend } from 'underscore';\nimport Backbone from 'backbone';\n\nfunction isFunctionEmpty(fn) {\n const content = fn.toString().match(/\\{([\\s\\S]*)\\}/m)[1]; // content between first and last { }\n return content.replace(/^\\s*\\/\\/.*$/gm, '').trim().length === 0; // remove comments\n}\n\nexport default Backbone.Model.extend({\n mapModel(model) {\n var code = '';\n var script = model.get('script-export') || model.get('script');\n var type = model.get('type');\n var comps = model.get('components');\n var id = model.getId();\n\n if (script) {\n // If the component has scripts we need to expose his ID\n var attr = model.get('attributes');\n attr = extend({}, attr, { id });\n model.set('attributes', attr, { silent: 1 });\n var scrStr = model.getScriptString(script);\n const scrProps = model.get('script-props');\n\n // If the script was updated, I'll put its code in a separate container\n if (model.get('scriptUpdated') && !scrProps) {\n this.mapJs[type + '-' + id] = { ids: [id], code: scrStr };\n } else {\n let props;\n const mapType = this.mapJs[type];\n\n if (scrProps) {\n props = model.__getScriptProps();\n }\n\n if (mapType) {\n mapType.ids.push(id);\n if (props) mapType.props[id] = props;\n } else {\n const res = { ids: [id], code: scrStr };\n if (props) res.props = { [id]: props };\n this.mapJs[type] = res;\n }\n }\n }\n\n comps.each(function (model) {\n code += this.mapModel(model);\n }, this);\n\n return code;\n },\n\n build(model) {\n this.mapJs = {};\n this.mapModel(model);\n let code = '';\n\n for (let type in this.mapJs) {\n const mapType = this.mapJs[type];\n\n if (!mapType.code) {\n continue;\n }\n\n if (mapType.props) {\n if (isFunctionEmpty(mapType.code)) {\n continue;\n }\n\n code += `\n var props = ${JSON.stringify(mapType.props)};\n var ids = Object.keys(props).map(function(id) { return '#'+id }).join(',');\n var els = document.querySelectorAll(ids);\n for (var i = 0, len = els.length; i < len; i++) {\n var el = els[i];\n (${mapType.code}.bind(el))(props[el.id]);\n }`;\n } else {\n // Deprecated\n const ids = '#' + mapType.ids.join(', #');\n code += `\n var items = document.querySelectorAll('${ids}');\n for (var i = 0, len = items.length; i < len; i++) {\n (function(){\\n${mapType.code}\\n}.bind(items[i]))();\n }`;\n }\n }\n\n return code;\n },\n});\n","import { bindAll } from 'underscore';\nimport Backbone from 'backbone';\nimport { hasWin } from 'utils/mixins';\n\nlet CodeMirror;\n\nif (hasWin()) {\n CodeMirror = require('codemirror/lib/codemirror');\n require('codemirror/mode/htmlmixed/htmlmixed');\n require('codemirror/mode/css/css');\n require('codemirror-formatting');\n}\n\nexport default Backbone.Model.extend({\n CodeMirror,\n\n defaults: {\n input: '',\n label: '',\n codeName: '',\n theme: 'hopscotch',\n readOnly: true,\n lineNumbers: true\n },\n\n /** @inheritdoc */\n init(el) {\n bindAll(this, 'onChange');\n this.editor = CodeMirror.fromTextArea(el, {\n dragDrop: false,\n lineWrapping: true,\n mode: this.get('codeName'),\n ...this.attributes\n });\n this.element = el;\n this.editor.on('change', this.onChange);\n\n return this;\n },\n\n onChange() {\n this.trigger('update', this);\n },\n\n getEditor() {\n return this.editor;\n },\n\n /**\n * The element where the viewer is attached\n * @return {HTMLElement}\n */\n getElement() {\n return this.element;\n },\n\n /**\n * Set the element which contains the viewer attached.\n * Generally, it should be just a textarea, but some editor might require\n * a container for it some in that case this method can be used\n * @param {HTMLElement} el\n * @return {self}\n */\n setElement(el) {\n this.element = el;\n return this;\n },\n\n /**\n * Refresh the viewer\n * @return {self}\n */\n refresh() {\n this.getEditor().refresh();\n return this;\n },\n\n /**\n * Focus the viewer\n * @return {self}\n */\n focus() {\n this.getEditor().focus();\n return this;\n },\n\n getContent() {\n const ed = this.getEditor();\n return ed && ed.getValue();\n },\n\n /** @inheritdoc */\n setContent(v, opts = {}) {\n const { editor } = this;\n if (!editor) return;\n editor.setValue(v);\n\n if (editor.autoFormatRange) {\n CodeMirror.commands.selectAll(editor);\n editor.autoFormatRange(editor.getCursor(true), editor.getCursor(false));\n CodeMirror.commands.goDocStart(editor);\n }\n\n !opts.noRefresh && setTimeout(() => this.refresh());\n }\n});\n","import html from 'utils/html';\nimport { View } from 'backbone';\n\nexport default class EditorView extends View {\n template({ pfx, codeName, label }) {\n return html`\n
\n `;\n }\n\n initialize(o) {\n this.config = o.config || {};\n this.pfx = this.config.stylePrefix;\n }\n\n render() {\n const { model, pfx, $el } = this;\n const obj = model.toJSON();\n obj.pfx = pfx;\n $el.html(this.template(obj));\n $el.attr('class', `${pfx}editor-c`);\n $el.find(`#${pfx}code`).append(model.get('input'));\n return this;\n }\n}\n","/**\n * - [addGenerator](#addgenerator)\n * - [getGenerator](#getgenerator)\n * - [getGenerators](#getgenerators)\n * - [addViewer](#addviewer)\n * - [getViewer](#getviewer)\n * - [getViewers](#getviewers)\n * - [updateViewer](#updateviewer)\n * - [getCode](#getcode)\n *\n *\n * Before using methods you should get first the module from the editor instance, in this way:\n *\n * ```js\n * var codeManager = editor.CodeManager;\n * ```\n *\n * @module CodeManager\n */\nimport { isUndefined } from 'underscore';\nimport defaults from './config/config';\nimport gHtml from './model/HtmlGenerator';\nimport gCss from './model/CssGenerator';\nimport gJson from './model/JsonGenerator';\nimport gJs from './model/JsGenerator';\nimport eCM from './model/CodeMirrorEditor';\nimport editorView from './view/EditorView';\n\nexport default () => {\n var c = {};\n var generators = {},\n defGenerators = {},\n viewers = {},\n defViewers = {};\n\n const defaultViewer = 'CodeMirror';\n\n return {\n getConfig() {\n return c;\n },\n\n config: c,\n\n EditorView: editorView,\n\n /**\n * Name of the module\n * @type {String}\n * @private\n */\n name: 'CodeManager',\n\n /**\n * Initialize module. Automatically called with a new instance of the editor\n * @param {Object} config Configurations\n */\n init(config) {\n c = config || {};\n for (var name in defaults) {\n if (!(name in c)) c[name] = defaults[name];\n }\n\n var ppfx = c.pStylePrefix;\n if (ppfx) c.stylePrefix = ppfx + c.stylePrefix;\n\n defGenerators.html = new gHtml();\n defGenerators.css = new gCss();\n defGenerators.json = new gJson();\n defGenerators.js = new gJs();\n defViewers.CodeMirror = new eCM();\n this.loadDefaultGenerators().loadDefaultViewers();\n\n return this;\n },\n\n /**\n * Add new code generator to the collection\n * @param {string} id Code generator ID\n * @param {Object} generator Code generator wrapper\n * @param {Function} generator.build Function that builds the code\n * @return {this}\n * @example\n * codeManager.addGenerator('html7',{\n * build: function(model){\n * return 'myCode';\n * }\n * });\n * */\n addGenerator(id, generator) {\n generators[id] = generator;\n return this;\n },\n\n /**\n * Get code generator by id\n * @param {string} id Code generator ID\n * @return {Object|null}\n * @example\n * var generator = codeManager.getGenerator('html7');\n * generator.build = function(model){\n * //extend\n * };\n * */\n getGenerator(id) {\n return generators[id] || null;\n },\n\n /**\n * Returns all code generators\n * @return {Array