Browse Source

Fix conflicts

pull/4313/head
Artur Arseniev 4 years ago
parent
commit
04bc2876a7
  1. 213
      src/abstract/Module.ts
  2. 8
      src/abstract/index.ts
  3. 11
      src/canvas/index.js
  4. 62
      src/canvas/model/Canvas.js
  5. 71
      src/canvas/model/Canvas.ts
  6. 120
      src/canvas/model/Frame.ts
  7. 47
      src/canvas/model/Frames.js
  8. 45
      src/canvas/model/Frames.ts
  9. 41
      src/editor/index.ts
  10. 330
      src/editor/model/Editor.ts
  11. 299
      src/pages/index.js
  12. 290
      src/pages/index.ts
  13. 51
      src/pages/model/Page.ts
  14. 26
      src/pages/model/Pages.js
  15. 18
      src/pages/model/Pages.ts

213
src/abstract/Module.ts

@ -1,6 +1,10 @@
import EditorModel from '../editor/model/Editor'; import { isElement, isUndefined } from "underscore";
import { Collection } from "../common";
import EditorModel from "../editor/model/Editor";
import { createId, isDef } from "../utils/mixins";
export interface IModule<TConfig extends any = any> extends IBaseModule<TConfig> { export interface IModule<TConfig extends any = any>
extends IBaseModule<TConfig> {
init(cfg: any): void; init(cfg: any): void;
destroy(): void; destroy(): void;
postLoad(key: any): any; postLoad(key: any): any;
@ -15,26 +19,41 @@ export interface IBaseModule<TConfig extends any> {
config: TConfig; config: TConfig;
} }
interface ModuleConfig{ export interface ModuleConfig {
name: string; name: string;
stylePrefix?: string; stylePrefix?: string;
} }
export interface IStorableModule extends IModule {
storageKey: string[] | string;
store(result: any): any;
load(keys: string[]): void;
postLoad(key: any): any;
}
export default abstract class Module<T extends ModuleConfig = ModuleConfig> export default abstract class Module<T extends ModuleConfig = ModuleConfig>
implements IModule<T> implements IModule<T>
{ {
//conf: CollectionCollectionModuleConfig;
private _em: EditorModel; private _em: EditorModel;
private _config: T; private _config: T;
private _name: string;
cls: any[] = []; cls: any[] = [];
events: any; events: any;
constructor( constructor(em: EditorModel, moduleName: string) {
em: EditorModel,
config: T
) {
this._em = em; this._em = em;
this._config = config; this._name = moduleName;
const name = this.name.charAt(0).toLowerCase() + this.name.slice(1);
const cfgParent = !isUndefined(em.config[name])
? em.config[name]
: em.config[this.name];
const cfg = cfgParent === true ? {} : cfgParent || {};
cfg.pStylePrefix = em.config.pStylePrefix || "";
if (!isUndefined(cfgParent) && !cfgParent) {
cfg._disable = 1;
}
this._config = cfg;
} }
public get em() { public get em() {
@ -51,16 +70,186 @@ export default abstract class Module<T extends ModuleConfig = ModuleConfig>
postLoad(key: any): void {} postLoad(key: any): void {}
get name(): string { get name(): string {
return this.config.name; return this._name;
} }
getConfig() { getConfig() {
return this.config; return this.config;
} }
__logWarn(str: string) { __logWarn(str: string, opts = {}) {
this.em.logWarning(`[${this.name}]: ${str}`); this.em.logWarning(`[${this.name}]: ${str}`, opts);
} }
postRender?(view: any): void; postRender?(view: any): void;
} }
export abstract class ItemManagerModule<
TConf extends ModuleConfig = any,
TCollection extends Collection = Collection
> extends Module<TConf> {
cls: any[] = [];
protected all: TCollection;
constructor(em: EditorModel, moduleName: string, all: any, events: any) {
super(em, moduleName);
this.all = all;
this.events = events;
this.__initListen();
}
private: boolean = false;
abstract storageKey: string;
abstract destroy(): void;
postLoad(key: any): void {}
render() {}
getProjectData(data?: any) {
const obj: any = {};
const key = this.storageKey;
if (key) {
obj[key] = data || this.getAll();
}
return obj;
}
loadProjectData(
data: any = {},
param: { all?: TCollection; onResult?: Function; reset?: boolean } = {}
) {
const { all, onResult, reset } = param;
const key = this.storageKey;
const opts: any = { action: "load" };
const coll = all || this.all;
let result = data[key];
if (typeof result == "string") {
try {
result = JSON.parse(result);
} catch (err) {
this.__logWarn("Data parsing failed", { input: result });
}
}
reset && result && coll.reset(undefined, opts);
if (onResult) {
result && onResult(result, opts);
} else if (result && isDef(result.length)) {
coll.reset(result, opts);
}
return result;
}
clear(opts = {}) {
const { all } = this;
all && all.reset(undefined, opts);
return this;
}
getAll(): TCollection extends Collection<infer C> ? C[] : unknown[] {
return [...this.all.models] as any;
}
getAllMap(): {
[key: string]: TCollection extends Collection<infer C> ? C : unknown;
} {
return this.getAll().reduce((acc, i) => {
acc[i.get(i.idAttribute)] = i;
return acc;
}, {} as any);
}
__initListen(opts: any = {}) {
const { all, em, events } = this;
all &&
em &&
all
.on("add", (m: any, c: any, o: any) => em.trigger(events.add, m, o))
.on("remove", (m: any, c: any, o: any) =>
em.trigger(events.remove, m, o)
)
.on("change", (p: any, c: any) =>
em.trigger(events.update, p, p.changedAttributes(), c)
)
.on("all", this.__catchAllEvent, this);
// Register collections
this.cls = [all].concat(opts.collections || []);
// Propagate events
((opts.propagate as any[]) || []).forEach(({ entity, event }) => {
entity.on("all", (ev: any, model: any, coll: any, opts: any) => {
const options = opts || coll;
const opt = { event: ev, ...options };
[em, all].map((md) => md.trigger(event, model, opt));
});
});
}
__remove(model: any, opts: any = {}) {
const { em } = this;
//@ts-ignore
const md = isString(model) ? this.get(model) : model;
const rm = () => {
md && this.all.remove(md, opts);
return md;
};
!opts.silent && em?.trigger(this.events.removeBefore, md, rm, opts);
return !opts.abort && rm();
}
__catchAllEvent(event: any, model: any, coll: any, opts: any) {
const { em, events } = this;
const options = opts || coll;
em && events.all && em.trigger(events.all, { event, model, options });
this.__onAllEvent();
}
__appendTo() {
//@ts-ignore
const elTo = this.config.appendTo;
if (elTo) {
const el = isElement(elTo) ? elTo : document.querySelector(elTo);
if (!el) return this.__logWarn('"appendTo" element not found');
el.appendChild(this.render());
}
}
__onAllEvent() {}
_createId(len = 16) {
const all = this.getAll();
const ln = all.length + len;
const allMap = this.getAllMap();
let id;
do {
id = createId(ln);
} while (allMap[id]);
return id;
}
__listenAdd(model: TCollection, event: string) {
model.on("add", (m, c, o) => this.em.trigger(event, m, o));
}
__listenRemove(model: TCollection, event: string) {
model.on("remove", (m, c, o) => this.em.trigger(event, m, o));
}
__listenUpdate(model: TCollection, event: string) {
model.on("change", (p, c) =>
this.em.trigger(event, p, p.changedAttributes(), c)
);
}
__destroy() {
this.cls.forEach((coll) => {
coll.stopListening();
coll.reset();
});
}
}

8
src/abstract/index.ts

@ -1,4 +1,4 @@
export { default as Model } from './Model'; export { default as Model } from "./Model";
export { default as Collection } from './Collection'; export { default as Collection } from "./Collection";
export { default as View } from './View'; export { default as View } from "./View";
export { default as Module } from './moduleLegacy'; export { default as Module } from "./Module";

11
src/canvas/index.js

@ -51,6 +51,7 @@ import { isUndefined } from 'underscore';
import { getElement, getViewEl } from '../utils/mixins'; import { getElement, getViewEl } from '../utils/mixins';
import defaults from './config/config'; import defaults from './config/config';
import Canvas from './model/Canvas'; import Canvas from './model/Canvas';
import Frame from './model/Frame';
import CanvasView from './view/CanvasView'; import CanvasView from './view/CanvasView';
export default class CanvasModule { export default class CanvasModule {
@ -667,15 +668,7 @@ export default class CanvasModule {
* }); * });
*/ */
addFrame(props = {}, opts = {}) { addFrame(props = {}, opts = {}) {
return this.canvas.get('frames').add( return this.canvas.frames.add(new Frame({ ...props }, { em: this.em }), opts);
{
...props,
},
{
...opts,
em: this.em,
}
);
} }
destroy() { destroy() {

62
src/canvas/model/Canvas.js

@ -1,62 +0,0 @@
import { Model } from '../../common';
import { evPageSelect } from '../../pages';
import Frames from './Frames';
export default class Canvas extends Model {
defaults() {
return {
frame: '',
frames: '',
rulers: false,
zoom: 100,
x: 0,
y: 0,
// Scripts to apply on all frames
scripts: [],
// Styles to apply on all frames
styles: [],
};
}
initialize(props, config = {}) {
const { em } = config;
this.config = config;
this.em = em;
this.set('frames', new Frames());
this.listenTo(this, 'change:zoom', this.onZoomChange);
this.listenTo(em, 'change:device', this.updateDevice);
this.listenTo(em, evPageSelect, this._pageUpdated);
}
init() {
const { em } = this;
const mainPage = em.get('PageManager').getMain();
const frame = mainPage.getMainFrame();
this.set('frames', mainPage.getFrames());
this.updateDevice({ frame });
}
_pageUpdated(page, prev) {
const { em } = this;
em.setSelected();
em.get('readyCanvas') && em.stopDefault(); // We have to stop before changing current frames
prev && prev.getFrames().map(frame => frame.disable());
this.set('frames', page.getFrames());
}
updateDevice(opts = {}) {
const { em } = this;
const device = em.getDeviceModel();
const model = opts.frame || em.getCurrentFrameModel();
if (model && device) {
const { width, height } = device.attributes;
model.set({ width, height }, { noUndo: 1 });
}
}
onZoomChange() {
const zoom = this.get('zoom');
zoom < 1 && this.set('zoom', 1);
}
}

71
src/canvas/model/Canvas.ts

@ -0,0 +1,71 @@
import { Model } from "../../common";
import Backbone from "backbone";
import { evPageSelect } from "../../pages";
import Frames from "./Frames";
import EditorModel from "../../editor/model/Editor";
import Page from "../../pages/model/Page";
export default class Canvas extends Backbone.Model {
defaults() {
return {
frame: "",
frames: new Frames(),
rulers: false,
zoom: 100,
x: 0,
y: 0,
// Scripts to apply on all frames
scripts: [],
// Styles to apply on all frames
styles: [],
};
}
em: EditorModel;
config: any;
constructor(props: any, config: any = {}) {
super(props);
const { em } = config;
this.config = config;
this.em = em;
this.listenTo(this, "change:zoom", this.onZoomChange);
this.listenTo(em, "change:device", this.updateDevice);
this.listenTo(em, evPageSelect, this._pageUpdated);
}
get frames(): Frames {
return this.get("frames");
}
init() {
const { em } = this;
const mainPage = em.get("PageManager").getMain();
const frame = mainPage.getMainFrame();
this.set("frames", mainPage.getFrames());
this.updateDevice({ frame });
}
_pageUpdated(page: Page, prev?: Page) {
const { em } = this;
em.setSelected();
em.get("readyCanvas") && em.stopDefault(); // We have to stop before changing current frames
//@ts-ignore
prev?.getFrames().map((frame) => frame.disable());
this.set("frames", page.getFrames());
}
updateDevice(opts: any = {}) {
const { em } = this;
const device = em.getDeviceModel();
const model = opts.frame || em.getCurrentFrameModel();
if (model && device) {
const { width, height } = device.attributes;
model.set({ width, height }, { noUndo: 1 });
}
}
onZoomChange() {
const zoom = this.get("zoom");
zoom < 1 && this.set("zoom", 1);
}
}

120
src/canvas/model/Frame.js → src/canvas/model/Frame.ts

@ -1,9 +1,15 @@
import { result, forEach, isEmpty, isString } from 'underscore'; import { result, forEach, isEmpty, isString } from "underscore";
import { Model } from '../../common'; import { Model } from "../../common";
import { isComponent, isObject } from '../../utils/mixins'; import { Component } from "../../dom_components/model/Component";
import Components from "../../dom_components/model/Components";
const keyAutoW = '__aw'; import ComponentWrapper from "../../dom_components/model/ComponentWrapper";
const keyAutoH = '__ah'; import EditorModel from "../../editor/model/Editor";
import { isComponent, isObject } from "../../utils/mixins";
import FrameView from "../view/FrameView";
import Frames from "./Frames";
const keyAutoW = "__aw";
const keyAutoH = "__ah";
/** /**
* @property {Object|String} component Wrapper component definition. You can also pass an HTML string as components of the default wrapper component. * @property {Object|String} component Wrapper component definition. You can also pass an HTML string as components of the default wrapper component.
@ -23,45 +29,47 @@ export default class Frame extends Model {
width: null, width: null,
height: null, height: null,
head: [], head: [],
component: '', component: "",
styles: '', styles: "",
_undo: true, _undo: true,
_undoexc: ['changesCount'], _undoexc: ["changesCount"],
}; };
} }
em: EditorModel;
view?: FrameView;
initialize(props, opts = {}) { constructor(props: any, opts: any) {
const { config } = opts; super(props);
const { em } = config; const { em } = opts;
const { styles, component } = this.attributes; const { styles, component } = this.attributes;
const domc = em.get('DomComponents'); const domc = em.get("DomComponents");
const conf = domc.getConfig(); const conf = domc.getConfig();
const allRules = em.get('CssComposer').getAll(); const allRules = em.get("CssComposer").getAll();
const idMap = {}; const idMap: any = {};
this.em = em; this.em = em;
const modOpts = { em, config: conf, frame: this, idMap }; const modOpts = { em, config: conf, frame: this, idMap };
if (!isComponent(component)) { if (!isComponent(component)) {
const wrp = isObject(component) ? component : { components: component }; const wrp = isObject(component) ? component : { components: component };
!wrp.type && (wrp.type = 'wrapper'); !wrp.type && (wrp.type = "wrapper");
const Wrapper = domc.getType('wrapper').model; const Wrapper = domc.getType("wrapper").model;
this.set('component', new Wrapper(wrp, modOpts)); this.set("component", new Wrapper(wrp, modOpts));
} }
if (!styles) { if (!styles) {
this.set('styles', allRules); this.set("styles", allRules);
} else if (!isObject(styles)) { } else if (!isObject(styles)) {
// Avoid losing styles on remapped components // Avoid losing styles on remapped components
const idMapKeys = Object.keys(idMap); const idMapKeys = Object.keys(idMap);
if (idMapKeys.length && Array.isArray(styles)) { if (idMapKeys.length && Array.isArray(styles)) {
styles.forEach(style => { styles.forEach((style) => {
const sel = style.selectors; const sel = style.selectors;
if (sel && sel.length == 1) { if (sel && sel.length == 1) {
const sSel = sel[0]; const sSel = sel[0];
const idSel = sSel.name && sSel.type === 2 && sSel; const idSel = sSel.name && sSel.type === 2 && sSel;
if (idSel && idMap[idSel.name]) { if (idSel && idMap[idSel.name]) {
idSel.name = idMap[idSel.name]; idSel.name = idMap[idSel.name];
} else if (isString(sSel) && sSel[0] === '#') { } else if (isString(sSel) && sSel[0] === "#") {
const prevId = sSel.substring(1); const prevId = sSel.substring(1);
if (prevId && idMap[prevId]) { if (prevId && idMap[prevId]) {
sel[0] = `#${idMap[prevId]}`; sel[0] = `#${idMap[prevId]}`;
@ -72,7 +80,7 @@ export default class Frame extends Model {
} }
allRules.add(styles); allRules.add(styles);
this.set('styles', allRules); this.set("styles", allRules);
} }
!props.width && this.set(keyAutoW, 1); !props.width && this.set(keyAutoW, 1);
@ -83,52 +91,57 @@ export default class Frame extends Model {
this.getComponent().remove({ root: 1 }); this.getComponent().remove({ root: 1 });
} }
changesUp(opt = {}) { changesUp(opt: any = {}) {
if (opt.temporary || opt.noCount || opt.avoidStore) { if (opt.temporary || opt.noCount || opt.avoidStore) {
return; return;
} }
this.set('changesCount', this.get('changesCount') + 1); this.set("changesCount", this.get("changesCount") + 1);
} }
getComponent() { getComponent(): ComponentWrapper {
return this.get('component'); return this.get("component");
} }
getStyles() { getStyles() {
return this.get('styles'); return this.get("styles");
} }
disable() { disable() {
this.trigger('disable'); this.trigger("disable");
} }
remove() { remove() {
this.view = 0; this.view = undefined;
const coll = this.collection; const coll = this.collection;
return coll && coll.remove(this); return coll && coll.remove(this);
} }
getHead() { getHead() {
const head = this.get('head') || []; const head = this.get("head") || [];
return [...head]; return [...head];
} }
setHead(value) { setHead(value: any) {
return this.set('head', [...value]); return this.set("head", [...value]);
} }
addHeadItem(item) { addHeadItem(item: any) {
const head = this.getHead(); const head = this.getHead();
head.push(item); head.push(item);
this.setHead(head); this.setHead(head);
} }
getHeadByAttr(attr, value, tag) { getHeadByAttr(attr: string, value: any, tag: string) {
const head = this.getHead(); const head = this.getHead();
return head.filter(item => item.attributes && item.attributes[attr] == value && (!tag || tag === item.tag))[0]; return head.filter(
(item) =>
item.attributes &&
item.attributes[attr] == value &&
(!tag || tag === item.tag)
)[0];
} }
removeHeadByAttr(attr, value, tag) { removeHeadByAttr(attr: string, value: any, tag: string) {
const head = this.getHead(); const head = this.getHead();
const item = this.getHeadByAttr(attr, value, tag); const item = this.getHeadByAttr(attr, value, tag);
const index = head.indexOf(item); const index = head.indexOf(item);
@ -139,47 +152,46 @@ export default class Frame extends Model {
} }
} }
addLink(href) { addLink(href: string) {
const tag = 'link'; const tag = "link";
!this.getHeadByAttr('href', href, tag) && !this.getHeadByAttr("href", href, tag) &&
this.addHeadItem({ this.addHeadItem({
tag, tag,
attributes: { attributes: {
href, href,
rel: 'stylesheet', rel: "stylesheet",
}, },
}); });
} }
removeLink(href) { removeLink(href: string) {
this.removeHeadByAttr('href', href, 'link'); this.removeHeadByAttr("href", href, "link");
} }
addScript(src) { addScript(src: string) {
const tag = 'script'; const tag = "script";
!this.getHeadByAttr('src', src, tag) && !this.getHeadByAttr("src", src, tag) &&
this.addHeadItem({ this.addHeadItem({
tag, tag,
attributes: { src }, attributes: { src },
}); });
} }
removeScript(src) { removeScript(src: string) {
this.removeHeadByAttr('src', src, 'script'); this.removeHeadByAttr("src", src, "script");
} }
getPage() { getPage() {
const coll = this.collection; return (this.collection as unknown as Frames)?.page;
return coll && coll.page;
} }
_emitUpdated(data = {}) { _emitUpdated(data = {}) {
this.em.trigger('frame:updated', { frame: this, ...data }); this.em.trigger("frame:updated", { frame: this, ...data });
} }
toJSON(opts = {}) { toJSON(opts: any = {}) {
const obj = Model.prototype.toJSON.call(this, opts); const obj = Model.prototype.toJSON.call(this, opts);
const defaults = result(this, 'defaults'); const defaults = result(this, "defaults");
if (opts.fromUndo) delete obj.component; if (opts.fromUndo) delete obj.component;
delete obj.styles; delete obj.styles;
@ -189,14 +201,14 @@ export default class Frame extends Model {
// Remove private keys // Remove private keys
forEach(obj, (value, key) => { forEach(obj, (value, key) => {
key.indexOf('_') === 0 && delete obj[key]; key.indexOf("_") === 0 && delete obj[key];
}); });
forEach(defaults, (value, key) => { forEach(defaults, (value, key) => {
if (obj[key] === value) delete obj[key]; if (obj[key] === value) delete obj[key];
}); });
forEach(['attributes', 'head'], prop => { forEach(["attributes", "head"], (prop) => {
if (isEmpty(obj[prop])) delete obj[prop]; if (isEmpty(obj[prop])) delete obj[prop];
}); });

47
src/canvas/model/Frames.js

@ -1,47 +0,0 @@
import { bindAll } from 'underscore';
import { Collection } from '../../common';
import Frame from './Frame';
export default class Frames extends Collection {
initialize(models, config = {}) {
bindAll(this, 'itemLoaded');
this.config = config;
this.on('reset', this.onReset);
this.on('remove', this.onRemove);
}
onReset(m, opts = {}) {
const prev = opts.previousModels || [];
prev.map(p => this.onRemove(p));
}
onRemove(removed) {
removed && removed.onRemove();
}
itemLoaded() {
this.loadedItems++;
if (this.loadedItems >= this.itemsToLoad) {
this.trigger('loaded:all');
this.listenToLoadItems(0);
}
}
listenToLoad() {
this.loadedItems = 0;
this.itemsToLoad = this.length;
this.listenToLoadItems(1);
}
listenToLoadItems(on) {
this.forEach(item => item[on ? 'on' : 'off']('loaded', this.itemLoaded));
}
add(m, o = {}) {
const { config } = this;
return Collection.prototype.add.call(this, m, { ...o, config });
}
}
Frames.prototype.model = Frame;

45
src/canvas/model/Frames.ts

@ -0,0 +1,45 @@
import { bindAll } from "underscore";
import { Collection } from "../../common";
import Page from "../../pages/model/Page";
import Frame from "./Frame";
export default class Frames extends Collection<Frame> {
loadedItems = 0;
itemsToLoad = 0;
page?: Page;
constructor(models?: Frame[]) {
super(models);
bindAll(this, "itemLoaded");
this.on("reset", this.onReset);
this.on("remove", this.onRemove);
}
onReset(m: Frame, opts?: { previousModels?: Frame[] }) {
const prev = opts?.previousModels || [];
prev.map((p) => this.onRemove(p));
}
onRemove(removed?: Frame) {
removed?.onRemove();
}
itemLoaded() {
this.loadedItems++;
if (this.loadedItems >= this.itemsToLoad) {
this.trigger("loaded:all");
this.listenToLoadItems(false);
}
}
listenToLoad() {
this.loadedItems = 0;
this.itemsToLoad = this.length;
this.listenToLoadItems(true);
}
listenToLoadItems(on: boolean) {
this.forEach((item) => item[on ? "on" : "off"]("loaded", this.itemLoaded));
}
}

41
src/editor/index.ts

@ -54,19 +54,24 @@
* ## Methods * ## Methods
* @module Editor * @module Editor
*/ */
import { EventHandler } from 'backbone'; import { EventHandler } from "backbone";
import { isUndefined } from 'underscore'; import { isUndefined } from "underscore";
import { IBaseModule } from '../abstract/Module'; import { IBaseModule } from "../abstract/Module";
import cash from '../utils/cash-dom'; import cash from "../utils/cash-dom";
import html from '../utils/html'; import html from "../utils/html";
import defaults from './config/config'; import defaults from "./config/config";
import EditorModel from './model/Editor'; import EditorModel from "./model/Editor";
import EditorView from './view/EditorView'; import EditorView from "./view/EditorView";
export default class EditorModule implements IBaseModule<typeof defaults> { export default class EditorModule implements IBaseModule<typeof defaults> {
constructor(config = {}, opts: any = {}) { constructor(config = {}, opts: any = {}) {
//@ts-ignore //@ts-ignore
this.config = { ...defaults, ...config, pStylePrefix: defaults.stylePrefix }; this.config = {
...defaults,
...config,
//@ts-ignore
pStylePrefix: defaults.stylePrefix,
};
this.em = new EditorModel(this.config); this.em = new EditorModel(this.config);
this.$ = opts.$; this.$ = opts.$;
this.em.init(this); this.em.init(this);
@ -206,7 +211,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
//@ts-ignore //@ts-ignore
get Devices(): DeviceManagerModule { get Devices(): DeviceManagerModule {
return this.em.get('DeviceManager'); return this.em.get('DeviceManager');
} }
//@ts-ignore //@ts-ignore
get DeviceManager(): DeviceManagerModule { get DeviceManager(): DeviceManagerModule {
return this.em.get('DeviceManager'); return this.em.get('DeviceManager');
@ -259,7 +264,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
* @return {Components} * @return {Components}
*/ */
getComponents() { getComponents() {
return this.em.get('DomComponents').getComponents(); return this.em.get("DomComponents").getComponents();
} }
/** /**
@ -267,7 +272,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
* @return {Component} * @return {Component}
*/ */
getWrapper() { getWrapper() {
return this.em.get('DomComponents').getWrapper(); return this.em.get("DomComponents").getWrapper();
} }
/** /**
@ -315,7 +320,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
* @return {Object} * @return {Object}
*/ */
getStyle() { getStyle() {
return this.em.get('CssComposer').getAll(); return this.em.get("CssComposer").getAll();
} }
/** /**
@ -453,7 +458,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
* editor.setDevice('Tablet'); * editor.setDevice('Tablet');
*/ */
setDevice(name: string) { setDevice(name: string) {
this.em.set('device', name); this.em.set("device", name);
return this; return this;
} }
@ -466,7 +471,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
* // 'Tablet' * // 'Tablet'
*/ */
getDevice() { getDevice() {
return this.em.get('device'); return this.em.get("device");
} }
/** /**
@ -478,7 +483,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
* editor.runCommand('myCommand', {someValue: 1}); * editor.runCommand('myCommand', {someValue: 1});
*/ */
runCommand(id: string, options = {}) { runCommand(id: string, options = {}) {
return this.em.get('Commands').run(id, options); return this.em.get("Commands").run(id, options);
} }
/** /**
@ -490,7 +495,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
* editor.stopCommand('myCommand', {someValue: 1}); * editor.stopCommand('myCommand', {someValue: 1});
*/ */
stopCommand(id: string, options = {}) { stopCommand(id: string, options = {}) {
return this.em.get('Commands').stop(id, options); return this.em.get("Commands").stop(id, options);
} }
/** /**
@ -779,7 +784,7 @@ export default class EditorModule implements IBaseModule<typeof defaults> {
* }); * });
*/ */
onReady(clb: EventHandler) { onReady(clb: EventHandler) {
this.em.get('ready') ? clb(this) : this.em.on('load', clb); this.em.get("ready") ? clb(this) : this.em.on("load", clb);
} }
/** /**

330
src/editor/model/Editor.ts

@ -1,41 +1,48 @@
import { isUndefined, isArray, contains, toArray, keys, bindAll } from 'underscore'; import {
import Backbone from 'backbone'; isUndefined,
import $ from '../../utils/cash-dom'; isArray,
import Extender from '../../utils/extender'; contains,
import { getModel, hasWin, isEmptyObj } from '../../utils/mixins'; toArray,
import { Model } from '../../common'; keys,
import Selected from './Selected'; bindAll,
import FrameView from '../../canvas/view/FrameView'; } from "underscore";
import EditorModule from '..'; import Backbone from "backbone";
import EditorView from '../view/EditorView'; import $ from "../../utils/cash-dom";
import { IModule } from '../../abstract/Module'; import Extender from "../../utils/extender";
import { getModel, hasWin, isEmptyObj } from "../../utils/mixins";
import { Model } from "../../common";
import Selected from "./Selected";
import FrameView from "../../canvas/view/FrameView";
import EditorModule from "..";
import EditorView from "../view/EditorView";
import { IModule } from "../../abstract/Module";
//@ts-ignore //@ts-ignore
Backbone.$ = $; Backbone.$ = $;
const deps = [ const deps = [
require('utils'), require("utils"),
require('i18n'), require("i18n"),
require('keymaps'), require("keymaps"),
require('undo_manager'), require("undo_manager"),
require('storage_manager'), require("storage_manager"),
require('device_manager'), require("device_manager"),
require('parser'), require("parser"),
require('style_manager'), require("style_manager"),
require('selector_manager'), require("selector_manager"),
require('modal_dialog'), require("modal_dialog"),
require('code_manager'), require("code_manager"),
require('panels'), require("panels"),
require('rich_text_editor'), require("rich_text_editor"),
require('asset_manager'), require("asset_manager"),
require('css_composer'), require("css_composer"),
require('pages'), require("pages"),
require('trait_manager'), require("trait_manager"),
require('dom_components'), require("dom_components"),
require('navigator'), require("navigator"),
require('canvas'), require("canvas"),
require('commands'), require("commands"),
require('block_manager'), require("block_manager"),
]; ];
const ts_deps: any[] = []; const ts_deps: any[] = [];
@ -67,7 +74,7 @@ export default class EditorModel extends Model {
modules: [], modules: [],
toLoad: [], toLoad: [],
opened: {}, opened: {},
device: '', device: "",
}; };
} }
@ -99,15 +106,15 @@ export default class EditorModel extends Model {
super(); super();
this._config = conf; this._config = conf;
const { config } = this; const { config } = this;
this.set('Config', conf); this.set("Config", conf);
this.set('modules', []); this.set("modules", []);
this.set('toLoad', []); this.set("toLoad", []);
this.set('storables', []); this.set("storables", []);
this.set('selected', new Selected()); this.set("selected", new Selected());
this.set('dmode', config.dragMode); this.set("dmode", config.dragMode);
const { el, log } = config; const { el, log } = config;
const toLog = log === true ? keys(logs) : isArray(log) ? log : []; const toLog = log === true ? keys(logs) : isArray(log) ? log : [];
bindAll(this, 'initBaseColorPicker'); bindAll(this, "initBaseColorPicker");
if (el && config.fromElement) { if (el && config.fromElement) {
config.components = el.innerHTML; config.components = el.innerHTML;
@ -118,7 +125,7 @@ export default class EditorModel extends Model {
res[next.nodeName] = next.nodeValue; res[next.nodeName] = next.nodeValue;
return res; return res;
}, {}) }, {})
: ''; : "";
// Move components to pages // Move components to pages
if (config.components && !config.pageManager) { if (config.components && !config.pageManager) {
@ -126,27 +133,35 @@ export default class EditorModel extends Model {
} }
// Load modules // Load modules
deps.forEach(name => this.loadModule(name)); deps.forEach((name) => this.loadModule(name));
ts_deps.forEach(name => this.tsLoadModule(name)); ts_deps.forEach((name) => this.tsLoadModule(name));
this.on('change:componentHovered', this.componentHovered, this); this.on("change:componentHovered", this.componentHovered, this);
this.on('change:changesCount', this.updateChanges, this); this.on("change:changesCount", this.updateChanges, this);
this.on('change:readyLoad change:readyCanvas', this._checkReady, this); this.on("change:readyLoad change:readyCanvas", this._checkReady, this);
toLog.forEach(e => this.listenLog(e)); toLog.forEach((e) => this.listenLog(e));
// Deprecations // Deprecations
[{ from: 'change:selectedComponent', to: 'component:toggled' }].forEach(event => { [{ from: "change:selectedComponent", to: "component:toggled" }].forEach(
const eventFrom = event.from; (event) => {
const eventTo = event.to; const eventFrom = event.from;
this.listenTo(this, eventFrom, (...args) => { const eventTo = event.to;
this.trigger(eventTo, ...args); this.listenTo(this, eventFrom, (...args) => {
this.logWarning(`The event '${eventFrom}' is deprecated, replace it with '${eventTo}'`); this.trigger(eventTo, ...args);
}); this.logWarning(
}); `The event '${eventFrom}' is deprecated, replace it with '${eventTo}'`
);
});
}
);
} }
_checkReady() { _checkReady() {
if (this.get('readyLoad') && this.get('readyCanvas') && !this.get('ready')) { if (
this.set('ready', true); this.get("readyLoad") &&
this.get("readyCanvas") &&
!this.get("ready")
) {
this.set("ready", true);
} }
} }
@ -180,15 +195,15 @@ export default class EditorModel extends Model {
*/ */
loadOnStart() { loadOnStart() {
const { projectData, headless } = this.config; const { projectData, headless } = this.config;
const sm = this.get('StorageManager'); const sm = this.get("StorageManager");
// In `onLoad`, the module will try to load the data from its configurations. // In `onLoad`, the module will try to load the data from its configurations.
this.toLoad.forEach(mdl => mdl.onLoad()); this.toLoad.forEach((mdl) => mdl.onLoad());
// Stuff to do post load // Stuff to do post load
const postLoad = () => { const postLoad = () => {
this.modules.forEach(mdl => mdl.postLoad && mdl.postLoad(this)); this.modules.forEach((mdl) => mdl.postLoad && mdl.postLoad(this));
this.set('readyLoad', 1); this.set("readyLoad", 1);
}; };
if (headless) { if (headless) {
@ -218,8 +233,8 @@ export default class EditorModel extends Model {
undoManager: false, undoManager: false,
}); });
// We only need to load a few modules // We only need to load a few modules
['PageManager', 'Canvas'].forEach(key => shallow.get(key).onLoad()); ["PageManager", "Canvas"].forEach((key) => shallow.get(key).onLoad());
this.set('shallow', shallow); this.set("shallow", shallow);
} }
/** /**
@ -228,18 +243,18 @@ export default class EditorModel extends Model {
* @private * @private
*/ */
updateChanges() { updateChanges() {
const stm = this.get('StorageManager'); const stm = this.get("StorageManager");
const changes = this.getDirtyCount(); const changes = this.getDirtyCount();
this.updateItr && clearTimeout(this.updateItr); this.updateItr && clearTimeout(this.updateItr);
//@ts-ignore //@ts-ignore
this.updateItr = setTimeout(() => this.trigger('update')); this.updateItr = setTimeout(() => this.trigger("update"));
if (this.config.noticeOnUnload) { if (this.config.noticeOnUnload) {
window.onbeforeunload = changes ? () => true : null; window.onbeforeunload = changes ? () => true : null;
} }
if (stm.isAutosave() && changes >= stm.getStepsBeforeSave()) { if (stm.isAutosave() && changes >= stm.getStepsBeforeSave()) {
this.store().catch(err => this.logError(err)); this.store().catch((err) => this.logError(err));
} }
} }
@ -252,11 +267,13 @@ export default class EditorModel extends Model {
loadModule(moduleName: any) { loadModule(moduleName: any) {
const { config } = this; const { config } = this;
const Module = moduleName.default || moduleName; const Module = moduleName.default || moduleName;
const Mod = new Module(); const Mod = new Module(this);
const name = Mod.name.charAt(0).toLowerCase() + Mod.name.slice(1); const name = Mod.name.charAt(0).toLowerCase() + Mod.name.slice(1);
const cfgParent = !isUndefined(config[name]) ? config[name] : config[Mod.name]; const cfgParent = !isUndefined(config[name])
? config[name]
: config[Mod.name];
const cfg = cfgParent === true ? {} : cfgParent || {}; const cfg = cfgParent === true ? {} : cfgParent || {};
cfg.pStylePrefix = config.pStylePrefix || ''; cfg.pStylePrefix = config.pStylePrefix || "";
if (!isUndefined(cfgParent) && !cfgParent) { if (!isUndefined(cfgParent) && !cfgParent) {
cfg._disable = 1; cfg._disable = 1;
@ -307,11 +324,11 @@ export default class EditorModel extends Model {
this.initialize(opts); this.initialize(opts);
this.destroyed = false; this.destroyed = false;
} }
this.set('Editor', editor); this.set("Editor", editor);
} }
getEditor() { getEditor() {
return this.get('Editor'); return this.get("Editor");
} }
/** /**
@ -324,7 +341,13 @@ export default class EditorModel extends Model {
* */ * */
handleUpdates(model: any, val: any, opt: any = {}) { handleUpdates(model: any, val: any, opt: any = {}) {
// Component has been added temporarily - do not update storage or record changes // Component has been added temporarily - do not update storage or record changes
if (this.__skip || opt.temporary || opt.noCount || opt.avoidStore || !this.get('ready')) { if (
this.__skip ||
opt.temporary ||
opt.noCount ||
opt.avoidStore ||
!this.get("ready")
) {
return; return;
} }
@ -333,7 +356,7 @@ export default class EditorModel extends Model {
this.timedInterval = setTimeout(() => { this.timedInterval = setTimeout(() => {
const curr = this.getDirtyCount() || 0; const curr = this.getDirtyCount() || 0;
const { unset, ...opts } = opt; const { unset, ...opts } = opt;
this.set('changesCount', curr + 1, opts); this.set("changesCount", curr + 1, opts);
}, 0); }, 0);
} }
@ -349,9 +372,9 @@ export default class EditorModel extends Model {
* @private * @private
* */ * */
componentHovered(editor: any, component: any, options: any) { componentHovered(editor: any, component: any, options: any) {
const prev = this.previous('componentHovered'); const prev = this.previous("componentHovered");
prev && this.trigger('component:unhovered', prev, options); prev && this.trigger("component:unhovered", prev, options);
component && this.trigger('component:hovered', component, options); component && this.trigger("component:hovered", component, options);
} }
/** /**
@ -392,17 +415,18 @@ export default class EditorModel extends Model {
const multiple = isArray(el); const multiple = isArray(el);
multiple && this.removeSelected(selected.filter(s => !contains(els, s))); multiple && this.removeSelected(selected.filter(s => !contains(els, s)));
els.forEach(el => { els.forEach((el) => {
let model = getModel(el, undefined); let model = getModel(el, undefined);
if (model) { if (model) {
this.trigger('component:select:before', model, opts); this.trigger("component:select:before", model, opts);
// Check for valid selectable // Check for valid selectable
if (!model.get('selectable') || opts.abort) { if (!model.get("selectable") || opts.abort) {
if (opts.useValid) { if (opts.useValid) {
let parent = model.parent(); let parent = model.parent();
while (parent && !parent.get('selectable')) parent = parent.parent(); while (parent && !parent.get("selectable"))
parent = parent.parent();
model = parent; model = parent;
} else { } else {
return; return;
@ -414,13 +438,13 @@ export default class EditorModel extends Model {
if (ctrlKey && mltSel) { if (ctrlKey && mltSel) {
return this.toggleSelected(model); return this.toggleSelected(model);
} else if (shiftKey && mltSel) { } else if (shiftKey && mltSel) {
this.clearSelection(this.get('Canvas').getWindow()); this.clearSelection(this.get("Canvas").getWindow());
const coll = model.collection; const coll = model.collection;
const index = model.index(); const index = model.index();
let min: number | undefined, max: number | undefined; let min: number | undefined, max: number | undefined;
// Fin min and max siblings // Fin min and max siblings
this.getSelectedAll().forEach(sel => { this.getSelectedAll().forEach((sel) => {
const selColl = sel.collection; const selColl = sel.collection;
const selIndex = sel.index(); const selIndex = sel.index();
if (selColl === coll) { if (selColl === coll) {
@ -451,7 +475,7 @@ export default class EditorModel extends Model {
return this.addSelected(model); return this.addSelected(model);
} }
!multiple && this.removeSelected(selected.filter(s => s !== model)); !multiple && this.removeSelected(selected.filter((s) => s !== model));
this.addSelected(model, opts); this.addSelected(model, opts);
added = model; added = model;
}); });
@ -472,7 +496,7 @@ export default class EditorModel extends Model {
const { selected } = this; const { selected } = this;
opts.forceChange && this.removeSelected(model, opts); opts.forceChange && this.removeSelected(model, opts);
selected.addComponent(model, opts); selected.addComponent(model, opts);
model && this.trigger('component:select', model, opts); model && this.trigger("component:select", model, opts);
}); });
} }
@ -512,21 +536,21 @@ export default class EditorModel extends Model {
* @private * @private
*/ */
setHovered(el: any, opts: any = {}) { setHovered(el: any, opts: any = {}) {
if (!el) return this.set('componentHovered', ''); if (!el) return this.set("componentHovered", "");
const ev = 'component:hover'; const ev = "component:hover";
let model = getModel(el, undefined); let model = getModel(el, undefined);
if (!model) return; if (!model) return;
opts.forceChange && this.set('componentHovered', ''); opts.forceChange && this.set("componentHovered", "");
this.trigger(`${ev}:before`, model, opts); this.trigger(`${ev}:before`, model, opts);
// Check for valid hoverable // Check for valid hoverable
if (!model.get('hoverable')) { if (!model.get("hoverable")) {
if (opts.useValid && !opts.abort) { if (opts.useValid && !opts.abort) {
let parent = model && model.parent(); let parent = model && model.parent();
while (parent && !parent.get('hoverable')) parent = parent.parent(); while (parent && !parent.get("hoverable")) parent = parent.parent();
model = parent; model = parent;
} else { } else {
return; return;
@ -534,13 +558,13 @@ export default class EditorModel extends Model {
} }
if (!opts.abort) { if (!opts.abort) {
this.set('componentHovered', model, opts); this.set("componentHovered", model, opts);
this.trigger(ev, model, opts); this.trigger(ev, model, opts);
} }
} }
getHovered() { getHovered() {
return this.get('componentHovered'); return this.get("componentHovered");
} }
/** /**
@ -551,7 +575,7 @@ export default class EditorModel extends Model {
* @public * @public
*/ */
setComponents(components: any, opt = {}) { setComponents(components: any, opt = {}) {
return this.get('DomComponents').setComponents(components, opt); return this.get("DomComponents").setComponents(components, opt);
} }
/** /**
@ -560,13 +584,13 @@ export default class EditorModel extends Model {
* @private * @private
*/ */
getComponents() { getComponents() {
var cmp = this.get('DomComponents'); var cmp = this.get("DomComponents");
var cm = this.get('CodeManager'); var cm = this.get("CodeManager");
if (!cmp || !cm) return; if (!cmp || !cm) return;
var wrp = cmp.getComponents(); var wrp = cmp.getComponents();
return cm.getCode(wrp, 'json'); return cm.getCode(wrp, "json");
} }
/** /**
@ -577,7 +601,7 @@ export default class EditorModel extends Model {
* @public * @public
*/ */
setStyle(style: any, opt = {}) { setStyle(style: any, opt = {}) {
const cssc = this.get('CssComposer'); const cssc = this.get("CssComposer");
cssc.clear(opt); cssc.clear(opt);
cssc.getAll().add(style, opt); cssc.getAll().add(style, opt);
return this; return this;
@ -600,7 +624,7 @@ export default class EditorModel extends Model {
* @private * @private
*/ */
getStyle() { getStyle() {
return this.get('CssComposer').getAll(); return this.get("CssComposer").getAll();
} }
/** /**
@ -609,7 +633,7 @@ export default class EditorModel extends Model {
* @returns {this} * @returns {this}
*/ */
setState(value: string) { setState(value: string) {
this.set('state', value); this.set("state", value);
return this; return this;
} }
@ -618,7 +642,7 @@ export default class EditorModel extends Model {
* @returns {String} * @returns {String}
*/ */
getState() { getState() {
return this.get('state') || ''; return this.get("state") || "";
} }
/** /**
@ -630,15 +654,15 @@ export default class EditorModel extends Model {
getHtml(opts: any = {}) { getHtml(opts: any = {}) {
const { config } = this; const { config } = this;
const { optsHtml } = config; const { optsHtml } = config;
const js = config.jsInHtml ? this.getJs(opts) : ''; const js = config.jsInHtml ? this.getJs(opts) : "";
const cmp = opts.component || this.get('DomComponents').getComponent(); const cmp = opts.component || this.get("DomComponents").getComponent();
let html = cmp let html = cmp
? this.get('CodeManager').getCode(cmp, 'html', { ? this.get("CodeManager").getCode(cmp, "html", {
...optsHtml, ...optsHtml,
...opts, ...opts,
}) })
: ''; : "";
html += js ? `<script>${js}</script>` : ''; html += js ? `<script>${js}</script>` : "";
return html; return html;
} }
@ -652,19 +676,21 @@ export default class EditorModel extends Model {
const config = this.config; const config = this.config;
const { optsCss } = config; const { optsCss } = config;
const avoidProt = opts.avoidProtected; const avoidProt = opts.avoidProtected;
const keepUnusedStyles = !isUndefined(opts.keepUnusedStyles) ? opts.keepUnusedStyles : config.keepUnusedStyles; const keepUnusedStyles = !isUndefined(opts.keepUnusedStyles)
const cssc = this.get('CssComposer'); ? opts.keepUnusedStyles
const wrp = opts.component || this.get('DomComponents').getComponent(); : config.keepUnusedStyles;
const protCss = !avoidProt ? config.protectedCss : ''; const cssc = this.get("CssComposer");
const wrp = opts.component || this.get("DomComponents").getComponent();
const protCss = !avoidProt ? config.protectedCss : "";
const css = const css =
wrp && wrp &&
this.get('CodeManager').getCode(wrp, 'css', { this.get("CodeManager").getCode(wrp, "css", {
cssc, cssc,
keepUnusedStyles, keepUnusedStyles,
...optsCss, ...optsCss,
...opts, ...opts,
}); });
return wrp ? (opts.json ? css : protCss + css) : ''; return wrp ? (opts.json ? css : protCss + css) : "";
} }
/** /**
@ -673,8 +699,8 @@ export default class EditorModel extends Model {
* @public * @public
*/ */
getJs(opts: any = {}) { getJs(opts: any = {}) {
var wrp = opts.component || this.get('DomComponents').getWrapper(); var wrp = opts.component || this.get("DomComponents").getWrapper();
return wrp ? this.get('CodeManager').getCode(wrp, 'js').trim() : ''; return wrp ? this.get("CodeManager").getCode(wrp, "js").trim() : "";
} }
/** /**
@ -683,7 +709,7 @@ export default class EditorModel extends Model {
*/ */
async store(options?: any) { async store(options?: any) {
const data = this.storeData(); const data = this.storeData();
await this.get('StorageManager').store(data, options); await this.get("StorageManager").store(data, options);
this.clearDirtyCount(); this.clearDirtyCount();
return data; return data;
} }
@ -693,7 +719,7 @@ export default class EditorModel extends Model {
* @public * @public
*/ */
async load(options?: any) { async load(options?: any) {
const result = await this.get('StorageManager').load(options); const result = await this.get("StorageManager").load(options);
this.loadData(result); this.loadData(result);
return result; return result;
} }
@ -702,9 +728,9 @@ export default class EditorModel extends Model {
let result = {}; let result = {};
// Sync content if there is an active RTE // Sync content if there is an active RTE
const editingCmp = this.getEditing(); const editingCmp = this.getEditing();
editingCmp && editingCmp.trigger('sync:content', { noCount: true }); editingCmp && editingCmp.trigger("sync:content", { noCount: true });
this.storables.forEach(m => { this.storables.forEach((m) => {
result = { ...result, ...m.store(1) }; result = { ...result, ...m.store(1) };
}); });
return JSON.parse(JSON.stringify(result)); return JSON.parse(JSON.stringify(result));
@ -712,8 +738,8 @@ export default class EditorModel extends Model {
loadData(data = {}) { loadData(data = {}) {
if (!isEmptyObj(data)) { if (!isEmptyObj(data)) {
this.storables.forEach(module => module.clear()); this.storables.forEach((module) => module.clear());
this.storables.forEach(module => module.load(data)); this.storables.forEach((module) => module.load(data));
} }
return data; return data;
} }
@ -724,8 +750,8 @@ export default class EditorModel extends Model {
* @private * @private
*/ */
getDeviceModel() { getDeviceModel() {
var name = this.get('device'); var name = this.get("device");
return this.get('DeviceManager').get(name); return this.get("DeviceManager").get(name);
} }
/** /**
@ -734,7 +760,7 @@ export default class EditorModel extends Model {
* @private * @private
*/ */
runDefault(opts = {}) { runDefault(opts = {}) {
var command = this.get('Commands').get(this.config.defaultCommand); var command = this.get("Commands").get(this.config.defaultCommand);
if (!command || this.defaultRunning) return; if (!command || this.defaultRunning) return;
command.stop(this, this, opts); command.stop(this, this, opts);
command.run(this, this, opts); command.run(this, this, opts);
@ -747,7 +773,7 @@ export default class EditorModel extends Model {
* @private * @private
*/ */
stopDefault(opts = {}) { stopDefault(opts = {}) {
const commands = this.get('Commands'); const commands = this.get("Commands");
const command = commands.get(this.config.defaultCommand); const command = commands.get(this.config.defaultCommand);
if (!command || !this.defaultRunning) return; if (!command || !this.defaultRunning) return;
command.stop(this, this, opts); command.stop(this, this, opts);
@ -759,9 +785,9 @@ export default class EditorModel extends Model {
* @public * @public
*/ */
refreshCanvas(opts: any = {}) { refreshCanvas(opts: any = {}) {
this.set('canvasOffset', null); this.set("canvasOffset", null);
this.set('canvasOffset', this.get('Canvas').getOffset()); this.set("canvasOffset", this.get("Canvas").getOffset());
opts.tools && this.trigger('canvas:updateTools'); opts.tools && this.trigger("canvas:updateTools");
} }
/** /**
@ -784,8 +810,8 @@ export default class EditorModel extends Model {
const device = this.getDeviceModel(); const device = this.getDeviceModel();
const condition = config.mediaCondition; const condition = config.mediaCondition;
const preview = config.devicePreviewMode; const preview = config.devicePreviewMode;
const width = device && device.get('widthMedia'); const width = device && device.get("widthMedia");
return device && width && !preview ? `(${condition}: ${width})` : ''; return device && width && !preview ? `(${condition}: ${width})` : "";
} }
/** /**
@ -793,15 +819,15 @@ export default class EditorModel extends Model {
* @return {Component} * @return {Component}
*/ */
getWrapper() { getWrapper() {
return this.get('DomComponents').getWrapper(); return this.get("DomComponents").getWrapper();
} }
setCurrentFrame(frameView: FrameView) { setCurrentFrame(frameView: FrameView) {
return this.set('currentFrame', frameView); return this.set("currentFrame", frameView);
} }
getCurrentFrame(): FrameView { getCurrentFrame(): FrameView {
return this.get('currentFrame'); return this.get("currentFrame");
} }
getCurrentFrameModel() { getCurrentFrameModel() {
@ -810,7 +836,7 @@ export default class EditorModel extends Model {
getIcon(icon: string) { getIcon(icon: string) {
const icons = this.config.icons || {}; const icons = this.config.icons || {};
return icons[icon] || ''; return icons[icon] || "";
} }
/** /**
@ -823,23 +849,23 @@ export default class EditorModel extends Model {
} }
clearDirtyCount() { clearDirtyCount() {
return this.set('changesCount', 0); return this.set("changesCount", 0);
} }
getZoomDecimal() { getZoomDecimal() {
return this.get('Canvas').getZoomDecimal(); return this.get("Canvas").getZoomDecimal();
} }
getZoomMultiplier() { getZoomMultiplier() {
return this.get('Canvas').getZoomMultiplier(); return this.get("Canvas").getZoomMultiplier();
} }
setDragMode(value: string) { setDragMode(value: string) {
return this.set('dmode', value); return this.set("dmode", value);
} }
t(...args: any[]) { t(...args: any[]) {
const i18n = this.get('I18n'); const i18n = this.get("I18n");
return i18n?.t(...args); return i18n?.t(...args);
} }
@ -848,7 +874,7 @@ export default class EditorModel extends Model {
* @returns {Boolean} * @returns {Boolean}
*/ */
inAbsoluteMode() { inAbsoluteMode() {
return this.get('dmode') === 'absolute'; return this.get("dmode") === "absolute";
} }
/** /**
@ -858,14 +884,14 @@ export default class EditorModel extends Model {
const { config, view } = this; const { config, view } = this;
const editor = this.getEditor(); const editor = this.getEditor();
const { editors = [] } = config.grapesjs || {}; const { editors = [] } = config.grapesjs || {};
const shallow = this.get('shallow'); const shallow = this.get("shallow");
shallow?.destroyAll(); shallow?.destroyAll();
this.stopListening(); this.stopListening();
this.stopDefault(); this.stopDefault();
this.modules this.modules
.slice() .slice()
.reverse() .reverse()
.forEach(mod => mod.destroy()); .forEach((mod) => mod.destroy());
view && view.remove(); view && view.remove();
this.clear({ silent: true }); this.clear({ silent: true });
this.destroyed = true; this.destroyed = true;
@ -879,22 +905,22 @@ export default class EditorModel extends Model {
} }
getEditing() { getEditing() {
const res = this.get('editing'); const res = this.get("editing");
return (res && res.model) || null; return (res && res.model) || null;
} }
setEditing(value: boolean) { setEditing(value: boolean) {
this.set('editing', value); this.set("editing", value);
return this; return this;
} }
isEditing() { isEditing() {
return !!this.get('editing'); return !!this.get("editing");
} }
log(msg: string, opts: any = {}) { log(msg: string, opts: any = {}) {
const { ns, level = 'debug' } = opts; const { ns, level = "debug" } = opts;
this.trigger('log', msg, opts); this.trigger("log", msg, opts);
level && this.trigger(`log:${level}`, msg, opts); level && this.trigger(`log:${level}`, msg, opts);
if (ns) { if (ns) {
@ -905,7 +931,7 @@ export default class EditorModel extends Model {
} }
logInfo(msg: string, opts?: any) { logInfo(msg: string, opts?: any) {
this.log(msg, { ...opts, level: 'info' }); this.log(msg, { ...opts, level: "info" });
} }
logWarning(msg: string, opts?: any) { logWarning(msg: string, opts?: any) {
@ -913,7 +939,7 @@ export default class EditorModel extends Model {
} }
logError(msg: string, opts?: any) { logError(msg: string, opts?: any) {
this.log(msg, { ...opts, level: 'error' }); this.log(msg, { ...opts, level: "error" });
} }
initBaseColorPicker(el: any, opts = {}) { initBaseColorPicker(el: any, opts = {}) {
@ -925,13 +951,13 @@ export default class EditorModel extends Model {
//@ts-ignore //@ts-ignore
return $(el).spectrum({ return $(el).spectrum({
containerClassName: `${ppfx}one-bg ${ppfx}two-color`, containerClassName: `${ppfx}one-bg ${ppfx}two-color`,
appendTo: elToAppend || 'body', appendTo: elToAppend || "body",
maxSelectionSize: 8, maxSelectionSize: 8,
showPalette: true, showPalette: true,
palette: [], palette: [],
showAlpha: true, showAlpha: true,
chooseText: 'Ok', chooseText: "Ok",
cancelText: '⨯', cancelText: "⨯",
...opts, ...opts,
...colorPicker, ...colorPicker,
}); });
@ -944,7 +970,7 @@ export default class EditorModel extends Model {
*/ */
skip(clb: Function) { skip(clb: Function) {
this.__skip = true; this.__skip = true;
const um = this.get('UndoManager'); const um = this.get("UndoManager");
um ? um.skip(clb) : clb(); um ? um.skip(clb) : clb();
this.__skip = false; this.__skip = false;
} }
@ -958,7 +984,7 @@ export default class EditorModel extends Model {
* @private * @private
*/ */
data(el: any, name: string, value: any) { data(el: any, name: string, value: any) {
const varName = '_gjs-data'; const varName = "_gjs-data";
if (!el[varName]) { if (!el[varName]) {
el[varName] = {}; el[varName] = {};

299
src/pages/index.js

@ -1,299 +0,0 @@
/**
* You can customize the initial state of the module from the editor initialization
* ```js
* const editor = grapesjs.init({
* ....
* pageManager: {
* pages: [
* {
* id: 'page-id',
* styles: `.my-class { color: red }`, // or a JSON of styles
* component: '<div class="my-class">My element</div>', // or a JSON of components
* }
* ]
* },
* })
* ```
*
* Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance
*
* ```js
* const pageManager = editor.Pages;
* ```
*
* ## Available Events
* * `page:add` - Added new page. The page is passed as an argument to the callback
* * `page:remove` - Page removed. The page is passed as an argument to the callback
* * `page:select` - New page selected. The newly selected page and the previous one, are passed as arguments to the callback
* * `page:update` - Page updated. The updated page and the object containing changes are passed as arguments to the callback
* * `page` - Catch-all event for all the events mentioned above. An object containing all the available data about the triggered event is passed as an argument to the callback
*
* ## Methods
* * [add](#add)
* * [get](#get)
* * [getAll](#getall)
* * [getAllWrappers](#getallwrappers)
* * [getMain](#getmain)
* * [remove](#remove)
* * [select](#select)
* * [getSelected](#getselected)
*
* [Page]: page.html
* [Component]: component.html
*
* @module Pages
*/
import { isString, bindAll, unique, flatten } from 'underscore';
import { createId } from '../utils/mixins';
import { Model, Module } from '../common';
import Pages from './model/Pages';
import Page from './model/Page';
export const evAll = 'page';
export const evPfx = `${evAll}:`;
export const evPageSelect = `${evPfx}select`;
export const evPageSelectBefore = `${evPageSelect}:before`;
export const evPageUpdate = `${evPfx}update`;
export const evPageAdd = `${evPfx}add`;
export const evPageAddBefore = `${evPageAdd}:before`;
export const evPageRemove = `${evPfx}remove`;
export const evPageRemoveBefore = `${evPageRemove}:before`;
const chnSel = 'change:selected';
const typeMain = 'main';
export default () => {
return {
...Module,
name: 'PageManager',
storageKey: 'pages',
Page,
Pages,
events: {
all: evAll,
select: evPageSelect,
selectBefore: evPageSelectBefore,
update: evPageUpdate,
add: evPageAdd,
addBefore: evPageAddBefore,
remove: evPageRemove,
removeBefore: evPageRemoveBefore,
},
/**
* Initialize module
* @param {Object} config Configurations
* @private
*/
init(opts = {}) {
bindAll(this, '_onPageChange');
const { em } = opts;
const cnf = { ...opts };
this.config = cnf;
this.em = em;
const pages = new Pages([], cnf);
this.pages = pages;
this.all = pages;
const model = new Model({ _undo: true });
this.model = model;
pages.on('add', (p, c, o) => em.trigger(evPageAdd, p, o));
pages.on('remove', (p, c, o) => em.trigger(evPageRemove, p, o));
pages.on('change', (p, c) => {
em.trigger(evPageUpdate, p, p.changedAttributes(), c);
});
pages.on('reset', coll => coll.at(0) && this.select(coll.at(0)));
pages.on('all', this.__onChange, this);
model.on(chnSel, this._onPageChange);
return this;
},
__onChange(event, page, coll, opts) {
const options = opts || coll;
this.em.trigger(evAll, { event, page, options });
},
onLoad() {
const { pages } = this;
const opt = { silent: true };
pages.add(this.config.pages || [], opt);
const mainPage = !pages.length ? this.add({ type: typeMain }, opt) : this.getMain();
this.select(mainPage, opt);
},
_onPageChange(m, page, opts) {
const { em } = this;
const lm = em.get('LayerManager');
const mainComp = page.getMainComponent();
lm && mainComp && lm.setRoot(mainComp);
em.trigger(evPageSelect, page, m.previous('selected'));
this.__onChange(chnSel, page, opts);
},
postLoad() {
const { em, model } = this;
const um = em.get('UndoManager');
um && um.add(model);
um && um.add(this.pages);
},
/**
* Add new page
* @param {Object} props Page properties
* @param {Object} [opts] Options
* @returns {[Page]}
* @example
* const newPage = pageManager.add({
* id: 'new-page-id', // without an explicit ID, a random one will be created
* styles: `.my-class { color: red }`, // or a JSON of styles
* component: '<div class="my-class">My element</div>', // or a JSON of components
* });
*/
add(props, opts = {}) {
const { em } = this;
props.id = props.id || this._createId();
const add = () => {
const page = this.pages.add(props, opts);
opts.select && this.select(page);
return page;
};
!opts.silent && em.trigger(evPageAddBefore, props, add, opts);
return !opts.abort && add();
},
/**
* Remove page
* @param {String|[Page]} page Page or page id
* @returns {[Page]} Removed Page
* @example
* const removedPage = pageManager.remove('page-id');
* // or by passing the page
* const somePage = pageManager.get('page-id');
* pageManager.remove(somePage);
*/
remove(page, opts = {}) {
const { em } = this;
const pg = isString(page) ? this.get(page) : page;
const rm = () => {
pg && this.pages.remove(pg, opts);
return pg;
};
!opts.silent && em.trigger(evPageRemoveBefore, pg, rm, opts);
return !opts.abort && rm();
},
/**
* Get page by id
* @param {String} id Page id
* @returns {[Page]}
* @example
* const somePage = pageManager.get('page-id');
*/
get(id) {
return this.pages.filter(p => p.get('id') === id)[0];
},
/**
* Get main page (the first one available)
* @returns {[Page]}
* @example
* const mainPage = pageManager.getMain();
*/
getMain() {
const { pages } = this;
return pages.filter(p => p.get('type') === typeMain)[0] || pages.at(0);
},
/**
* Get all pages
* @returns {Array<[Page]>}
* @example
* const arrayOfPages = pageManager.getAll();
*/
getAll() {
return [...this.pages.models];
},
/**
* Get wrapper components (aka body) from all pages and frames.
* @returns {Array<[Component]>}
* @example
* const wrappers = pageManager.getAllWrappers();
* // Get all `image` components from the project
* const allImages = wrappers.map(wrp => wrp.findType('image')).flat();
*/
getAllWrappers() {
const pages = this.getAll();
return unique(flatten(pages.map(page => page.getAllFrames().map(frame => frame.getComponent()))));
},
getAllMap() {
return this.getAll().reduce((acc, i) => {
acc[i.get('id')] = i;
return acc;
}, {});
},
/**
* Change the selected page. This will switch the page rendered in canvas
* @param {String|[Page]} page Page or page id
* @returns {this}
* @example
* pageManager.select('page-id');
* // or by passing the page
* const somePage = pageManager.get('page-id');
* pageManager.select(somePage);
*/
select(page, opts = {}) {
const pg = isString(page) ? this.get(page) : page;
if (pg) {
this.em.trigger(evPageSelectBefore, pg, opts);
this.model.set('selected', pg, opts);
}
return this;
},
/**
* Get the selected page
* @returns {[Page]}
* @example
* const selectedPage = pageManager.getSelected();
*/
getSelected() {
return this.model.get('selected');
},
destroy() {
this.pages.off().reset();
this.model.stopListening();
this.model.clear({ silent: true });
['selected', 'config', 'em', 'pages', 'model'].map(i => (this[i] = 0));
},
store() {
return this.getProjectData();
},
load(data) {
return this.loadProjectData(data, { all: this.pages, reset: true });
},
_createId() {
const pages = this.getAll();
const len = pages.length + 16;
const pagesMap = this.getAllMap();
let id;
do {
id = createId(len);
} while (pagesMap[id]);
return id;
},
};
};

290
src/pages/index.ts

@ -0,0 +1,290 @@
/**
* You can customize the initial state of the module from the editor initialization
* ```js
* const editor = grapesjs.init({
* ....
* pageManager: {
* pages: [
* {
* id: 'page-id',
* styles: `.my-class { color: red }`, // or a JSON of styles
* component: '<div class="my-class">My element</div>', // or a JSON of components
* }
* ]
* },
* })
* ```
*
* Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance
*
* ```js
* const pageManager = editor.Pages;
* ```
*
* ## Available Events
* * `page:add` - Added new page. The page is passed as an argument to the callback
* * `page:remove` - Page removed. The page is passed as an argument to the callback
* * `page:select` - New page selected. The newly selected page and the previous one, are passed as arguments to the callback
* * `page:update` - Page updated. The updated page and the object containing changes are passed as arguments to the callback
* * `page` - Catch-all event for all the events mentioned above. An object containing all the available data about the triggered event is passed as an argument to the callback
*
* ## Methods
* * [add](#add)
* * [get](#get)
* * [getAll](#getall)
* * [getAllWrappers](#getallwrappers)
* * [getMain](#getmain)
* * [remove](#remove)
* * [select](#select)
* * [getSelected](#getselected)
*
* [Page]: page.html
* [Component]: component.html
*
* @module Pages
*/
import { isString, bindAll, unique, flatten } from "underscore";
import { createId } from "../utils/mixins";
import { Model, Module } from "../abstract";
import { ItemManagerModule, ModuleConfig } from "../abstract/Module";
import Pages from "./model/Pages";
import Page from "./model/Page";
import EditorModel from "../editor/model/Editor";
export const evAll = "page";
export const evPfx = `${evAll}:`;
export const evPageSelect = `${evPfx}select`;
export const evPageSelectBefore = `${evPageSelect}:before`;
export const evPageUpdate = `${evPfx}update`;
export const evPageAdd = `${evPfx}add`;
export const evPageAddBefore = `${evPageAdd}:before`;
export const evPageRemove = `${evPfx}remove`;
export const evPageRemoveBefore = `${evPageRemove}:before`;
const chnSel = "change:selected";
const typeMain = "main";
const events = {
all: evAll,
select: evPageSelect,
selectBefore: evPageSelectBefore,
update: evPageUpdate,
add: evPageAdd,
addBefore: evPageAddBefore,
remove: evPageRemove,
removeBefore: evPageRemoveBefore,
};
interface Config extends ModuleConfig {
pages?: string[];
}
export default class PageManager extends ItemManagerModule<Config, Pages> {
storageKey = "pages";
get pages() {
return this.all;
}
model: Model;
/**
* Initialize module
* @param {Object} config Configurations
* @private
*/
constructor(em: EditorModel) {
super(em, "PageManager", new Pages([]), events);
bindAll(this, "_onPageChange");
const model = new Model({ _undo: true } as any);
this.model = model;
this.pages.on("reset", (coll) => coll.at(0) && this.select(coll.at(0)));
this.pages.on("all", this.__onChange, this);
model.on(chnSel, this._onPageChange);
return this;
}
__onChange(event: string, page: Page, coll: Pages, opts?: any) {
const options = opts || coll;
this.em.trigger(evAll, { event, page, options });
}
onLoad() {
const { pages } = this;
const opt = { silent: true };
pages.add(
this.config.pages?.map(
(page) => new Page(page, { em: this.em, config: this.config })
) || [],
opt
);
const mainPage = !pages.length
? this.add({ type: typeMain }, opt)
: this.getMain();
mainPage && this.select(mainPage, opt);
}
_onPageChange(m: any, page: Page, opts: any) {
const { em } = this;
const lm = em.get("LayerManager");
const mainComp = page.getMainComponent();
lm && mainComp && lm.setRoot(mainComp);
em.trigger(evPageSelect, page, m.previous("selected"));
this.__onChange(chnSel, page, opts);
}
postLoad() {
const { em, model } = this;
const um = em.get("UndoManager");
um && um.add(model);
um && um.add(this.pages);
}
/**
* Add new page
* @param {Object} props Page properties
* @param {Object} [opts] Options
* @returns {[Page]}
* @example
* const newPage = pageManager.add({
* id: 'new-page-id', // without an explicit ID, a random one will be created
* styles: `.my-class { color: red }`, // or a JSON of styles
* component: '<div class="my-class">My element</div>', // or a JSON of components
* });
*/
add(
props: any, //{ id?: string; styles: string; component: string },
opts: any = {}
) {
const { em } = this;
props.id = props.id || this._createId();
const add = () => {
const page = this.pages.add(
new Page(props, { em: this.em, config: this.config }),
opts
);
opts.select && this.select(page);
return page;
};
!opts.silent && em.trigger(evPageAddBefore, props, add, opts);
return !opts.abort && add();
}
/**
* Remove page
* @param {String|[Page]} page Page or page id
* @returns {[Page]} Removed Page
* @example
* const removedPage = pageManager.remove('page-id');
* // or by passing the page
* const somePage = pageManager.get('page-id');
* pageManager.remove(somePage);
*/
remove(page: string | Page, opts: any = {}) {
const { em } = this;
const pg = isString(page) ? this.get(page) : page;
const rm = () => {
pg && this.pages.remove(pg, opts);
return pg;
};
!opts.silent && em.trigger(evPageRemoveBefore, pg, rm, opts);
return !opts.abort && rm();
}
/**
* Get page by id
* @param {String} id Page id
* @returns {[Page]}
* @example
* const somePage = pageManager.get('page-id');
*/
get(id: string) {
return this.pages.filter((p) => p.get(p.idAttribute) === id)[0];
}
/**
* Get main page (the first one available)
* @returns {[Page]}
* @example
* const mainPage = pageManager.getMain();
*/
getMain() {
const { pages } = this;
return pages.filter((p) => p.get("type") === typeMain)[0] || pages.at(0);
}
/**
* Get wrapper components (aka body) from all pages and frames.
* @returns {Array<[Component]>}
* @example
* const wrappers = pageManager.getAllWrappers();
* // Get all `image` components from the project
* const allImages = wrappers.map(wrp => wrp.findType('image')).flat();
*/
getAllWrappers() {
const pages = this.getAll();
return unique(
flatten(
pages.map((page) =>
page.getAllFrames().map((frame) => frame.getComponent())
)
)
);
}
/**
* Change the selected page. This will switch the page rendered in canvas
* @param {String|[Page]} page Page or page id
* @returns {this}
* @example
* pageManager.select('page-id');
* // or by passing the page
* const somePage = pageManager.get('page-id');
* pageManager.select(somePage);
*/
select(page: string | Page, opts = {}) {
const pg = isString(page) ? this.get(page) : page;
if (pg) {
this.em.trigger(evPageSelectBefore, pg, opts);
this.model.set("selected", pg, opts);
}
return this;
}
/**
* Get the selected page
* @returns {[Page]}
* @example
* const selectedPage = pageManager.getSelected();
*/
getSelected() {
return this.model.get("selected");
}
destroy() {
this.pages.off().reset();
this.model.stopListening();
this.model.clear({ silent: true });
//@ts-ignore
["selected", "model"].map((i) => (this[i] = 0));
}
store() {
return this.getProjectData();
}
load(data: any) {
return this.loadProjectData(data, { all: this.pages, reset: true });
}
_createId() {
const pages = this.getAll();
const len = pages.length + 16;
const pagesMap = this.getAllMap();
let id;
do {
id = createId(len);
} while (pagesMap[id]);
return id;
}
}

51
src/pages/model/Page.js → src/pages/model/Page.ts

@ -1,6 +1,8 @@
import { result, forEach } from 'underscore'; import { result, forEach } from "underscore";
import { Model } from '../../common'; import { Model } from "../../common";
import Frames from '../../canvas/model/Frames'; import Frames from "../../canvas/model/Frames";
import Frame from "../../canvas/model/Frame";
import EditorModel from "../../editor/model/Editor";
export default class Page extends Model { export default class Page extends Model {
defaults() { defaults() {
@ -9,19 +11,24 @@ export default class Page extends Model {
_undo: true, _undo: true,
}; };
} }
em: EditorModel;
initialize(props, opts = {}) { constructor(props: any, opts: any = {}) {
super(props, opts);
const { config = {} } = opts; const { config = {} } = opts;
const { em } = config; const { em } = opts;
const defFrame = {}; const defFrame: any = {};
this.em = em; this.em = em;
if (!props.frames) { if (!props.frames) {
defFrame.component = props.component; defFrame.component = props.component;
defFrame.styles = props.styles; defFrame.styles = props.styles;
['component', 'styles'].map(i => this.unset(i)); ["component", "styles"].map((i) => this.unset(i));
} }
const frms = props.frames || [defFrame]; const frms: any[] = props.frames || [defFrame];
const frames = new Frames(frms, config); const frames = new Frames(
frms?.map((model) => new Frame(model, opts)),
opts
);
frames.page = this; frames.page = this;
this.set('frames', frames); this.set('frames', frames);
!this.getId() && this.set('id', em?.get('PageManager')._createId()); !this.getId() && this.set('id', em?.get('PageManager')._createId());
@ -30,11 +37,11 @@ export default class Page extends Model {
} }
onRemove() { onRemove() {
this.get('frames').reset(); this.get("frames").reset();
} }
getFrames() { getFrames(): Frames {
return this.get('frames'); return this.get("frames");
} }
/** /**
@ -49,8 +56,8 @@ export default class Page extends Model {
* Get page name * Get page name
* @returns {String} * @returns {String}
*/ */
getName() { getName(): string {
return this.get('name'); return this.get("name");
} }
/** /**
@ -59,8 +66,8 @@ export default class Page extends Model {
* @example * @example
* page.setName('New name'); * page.setName('New name');
*/ */
setName(name) { setName(name: string) {
return this.get({ name }); return this.set({ name });
} }
/** /**
@ -69,7 +76,8 @@ export default class Page extends Model {
* @example * @example
* const arrayOfFrames = page.getAllFrames(); * const arrayOfFrames = page.getAllFrames();
*/ */
getAllFrames() { getAllFrames(): Frame[] {
//@ts-ignore
return this.getFrames().models || []; return this.getFrames().models || [];
} }
@ -79,7 +87,8 @@ export default class Page extends Model {
* @example * @example
* const mainFrame = page.getMainFrame(); * const mainFrame = page.getMainFrame();
*/ */
getMainFrame() { getMainFrame(): Frame {
//@ts-ignore
return this.getFrames().at(0); return this.getFrames().at(0);
} }
@ -92,16 +101,16 @@ export default class Page extends Model {
*/ */
getMainComponent() { getMainComponent() {
const frame = this.getMainFrame(); const frame = this.getMainFrame();
return frame && frame.getComponent(); return frame?.getComponent();
} }
toJSON(opts = {}) { toJSON(opts = {}) {
const obj = Model.prototype.toJSON.call(this, opts); const obj = Model.prototype.toJSON.call(this, opts);
const defaults = result(this, 'defaults'); const defaults = result(this, "defaults");
// Remove private keys // Remove private keys
forEach(obj, (value, key) => { forEach(obj, (value, key) => {
key.indexOf('_') === 0 && delete obj[key]; key.indexOf("_") === 0 && delete obj[key];
}); });
forEach(defaults, (value, key) => { forEach(defaults, (value, key) => {

26
src/pages/model/Pages.js

@ -1,26 +0,0 @@
import { Collection } from '../../common';
import Page from './Page';
export default class Pages extends Collection {
initialize(models, config = {}) {
this.config = config;
this.on('reset', this.onReset);
this.on('remove', this.onRemove);
}
onReset(m, opts = {}) {
const prev = opts.previousModels || [];
prev.map(p => this.onRemove(p));
}
onRemove(removed) {
removed && removed.onRemove();
}
add(m, o = {}) {
const { config } = this;
return Collection.prototype.add.call(this, m, { ...o, config });
}
}
Pages.prototype.model = Page;

18
src/pages/model/Pages.ts

@ -0,0 +1,18 @@
import { Collection } from "../../common";
import Page from "./Page";
export default class Pages extends Collection<Page> {
constructor(models: any) {
super(models);
this.on("reset", this.onReset);
this.on("remove", this.onRemove);
}
onReset(m: Page, opts?: { previousModels?: Pages }) {
opts?.previousModels?.map((p) => this.onRemove(p));
}
onRemove(removed?: Page) {
removed?.onRemove();
}
}
Loading…
Cancel
Save