Browse Source

Merge pull request #3167 from artf/dev

Merge dev
pull/3550/head
Artur Arseniev 6 years ago
committed by GitHub
parent
commit
d9cdb671e6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      dist/css/grapes.min.css
  2. 7669
      dist/grapes.js
  3. 6
      dist/grapes.min.js
  4. 2
      dist/grapes.min.js.map
  5. 2
      docs/README.md
  6. 2
      docs/modules/Components.md
  7. 667
      package-lock.json
  8. 10
      package.json
  9. 9
      src/asset_manager/index.js
  10. 10
      src/block_manager/index.js
  11. 20
      src/canvas/index.js
  12. 4
      src/canvas/view/CanvasView.js
  13. 6
      src/canvas/view/FrameView.js
  14. 4
      src/canvas/view/FrameWrapView.js
  15. 6
      src/code_manager/index.js
  16. 57
      src/commands/index.js
  17. 5
      src/commands/view/CopyComponent.js
  18. 19
      src/commands/view/SelectComponent.js
  19. 26
      src/css_composer/index.js
  20. 28
      src/css_composer/model/CssRule.js
  21. 2
      src/css_composer/model/CssRules.js
  22. 8
      src/device_manager/index.js
  23. 20
      src/dom_components/index.js
  24. 205
      src/dom_components/model/Component.js
  25. 60
      src/dom_components/model/Components.js
  26. 2
      src/dom_components/view/ComponentTextView.js
  27. 5
      src/domain_abstract/ui/Input.js
  28. 5
      src/domain_abstract/ui/InputColor.js
  29. 26
      src/domain_abstract/ui/InputNumber.js
  30. 25
      src/editor/model/Editor.js
  31. 5
      src/i18n/index.js
  32. 198
      src/i18n/locale/ca.js
  33. 8
      src/keymaps/index.js
  34. 6
      src/modal_dialog/index.js
  35. 3
      src/navigator/index.js
  36. 7
      src/navigator/view/ItemView.js
  37. 8
      src/panels/index.js
  38. 20
      src/panels/view/ButtonView.js
  39. 11
      src/panels/view/ButtonsView.js
  40. 5
      src/parser/index.js
  41. 2
      src/parser/model/ParserHtml.js
  42. 5
      src/rich_text_editor/index.js
  43. 9
      src/selector_manager/index.js
  44. 35
      src/selector_manager/model/Selector.js
  45. 11
      src/storage_manager/index.js
  46. 10
      src/style_manager/index.js
  47. 11
      src/style_manager/model/Sectors.js
  48. 6
      src/style_manager/view/PropertyColorView.js
  49. 6
      src/style_manager/view/PropertyCompositeView.js
  50. 4
      src/style_manager/view/PropertyStackView.js
  51. 15
      src/style_manager/view/PropertyView.js
  52. 12
      src/style_manager/view/SectorsView.js
  53. 10
      src/styles/scss/_gjs_inputs.scss
  54. 6
      src/styles/scss/main.scss
  55. 5
      src/trait_manager/index.js
  56. 14
      src/trait_manager/view/TraitNumberView.js
  57. 8
      src/undo_manager/index.js
  58. 2
      src/utils/index.js
  59. 2
      src/utils/mixins.js
  60. 11
      test/specs/css_composer/e2e/CssComposer.js
  61. 6
      test/specs/dom_components/model/Component.js
  62. 17
      test/specs/editor/index.js
  63. 2
      test/specs/grapesjs/index.js
  64. 2
      test/specs/panels/view/ButtonView.js
  65. 32
      test/specs/parser/model/ParserHtml.js
  66. 38
      test/specs/selector_manager/e2e/ClassManager.js

2
dist/css/grapes.min.css

File diff suppressed because one or more lines are too long

7669
dist/grapes.js

File diff suppressed because it is too large

6
dist/grapes.min.js

File diff suppressed because one or more lines are too long

2
dist/grapes.min.js.map

File diff suppressed because one or more lines are too long

2
docs/README.md

@ -11,7 +11,7 @@ GrapesJS ships with features and tools that enable you to craft easy to use buil
## Why GrapesJS? ## Why GrapesJS?
GrapesJS was designed primarily to for use inside Content Management Systems to speed up the creation of dynamic templates and replace common WYSIWYG editors. Which are good for content editing, but inappropriate for creating HTML structures. Instead of creating an application we decided to create an extensible framework that could be used by anyone for any purpose. GrapesJS was designed primarily for use inside Content Management Systems to speed up the creation of dynamic templates and replace common WYSIWYG editors. Which are good for content editing, but inappropriate for creating HTML structures. Instead of creating an application we decided to create an extensible framework that could be used by anyone for any purpose.

2
docs/modules/Components.md

@ -79,7 +79,7 @@ In the first step, the HTML string is parsed and transformed to what is called *
} }
``` ```
The real **Component Definition** would be a little bit bigger so so we'd reduced the JSON for the sake of simplicity. The real **Component Definition** would be a little bit bigger so we've reduced the JSON for the sake of simplicity.
You might notice the result is similar to what is generally called a **Virtual DOM**, a lightweight representation of the DOM element. This actually helps the editor to keep track of the state of our elements and make performance-friendly changes/updates. You might notice the result is similar to what is generally called a **Virtual DOM**, a lightweight representation of the DOM element. This actually helps the editor to keep track of the state of our elements and make performance-friendly changes/updates.
The meaning of properties like `tagName`, `attributes` and `components` are quite obvious, but what about `type`?! This particular property specifies the **Component Type** of our **Component Definition** (you check the list of default components [below](#built-in-component-types)) and if it's omitted, the default one will be used `type: 'default'`. The meaning of properties like `tagName`, `attributes` and `components` are quite obvious, but what about `type`?! This particular property specifies the **Component Type** of our **Component Definition** (you check the list of default components [below](#built-in-component-types)) and if it's omitted, the default one will be used `type: 'default'`.

667
package-lock.json

@ -1,6 +1,6 @@
{ {
"name": "grapesjs", "name": "grapesjs",
"version": "0.16.27", "version": "0.16.30",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -3502,12 +3502,6 @@
"integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
"dev": true "dev": true
}, },
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"dev": true
},
"ansi-align": { "ansi-align": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
@ -3643,16 +3637,6 @@
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true "dev": true
}, },
"are-we-there-yet": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"dev": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
}
},
"argparse": { "argparse": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@ -3686,12 +3670,6 @@
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
"dev": true "dev": true
}, },
"array-find-index": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
"integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
"dev": true
},
"array-flatten": { "array-flatten": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
@ -3811,12 +3789,6 @@
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
"dev": true "dev": true
}, },
"async-foreach": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
"integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
"dev": true
},
"async-limiter": { "async-limiter": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
@ -5218,15 +5190,6 @@
"file-uri-to-path": "1.0.0" "file-uri-to-path": "1.0.0"
} }
}, },
"block-stream": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
"dev": true,
"requires": {
"inherits": "~2.0.0"
}
},
"bluebird": { "bluebird": {
"version": "3.7.2", "version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@ -5844,24 +5807,6 @@
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
"dev": true "dev": true
}, },
"camelcase-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
"integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
"dev": true,
"requires": {
"camelcase": "^2.0.0",
"map-obj": "^1.0.0"
},
"dependencies": {
"camelcase": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
"integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
"dev": true
}
}
},
"caniuse-api": { "caniuse-api": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@ -6162,9 +6107,9 @@
"dev": true "dev": true
}, },
"codemirror": { "codemirror": {
"version": "5.53.2", "version": "5.58.3",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.53.2.tgz", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.58.3.tgz",
"integrity": "sha512-wvSQKS4E+P8Fxn/AQ+tQtJnF1qH5UOlxtugFLpubEZ5jcdH2iXTVinb+Xc/4QjshuOxRm4fUsU2QPF1JJKiyXA==" "integrity": "sha512-KBhB+juiyOOgn0AqtRmWyAT3yoElkuvWTI6hsHa9E6GQrl6bk/fdAYcvuqW1/upO9T9rtEtapWdw4XYcNiVDEA=="
}, },
"codemirror-formatting": { "codemirror-formatting": {
"version": "1.0.0", "version": "1.0.0",
@ -6372,12 +6317,6 @@
"integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
"dev": true "dev": true
}, },
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true
},
"consolidate": { "consolidate": {
"version": "0.15.1", "version": "0.15.1",
"resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz",
@ -6939,15 +6878,6 @@
"cssom": "0.3.x" "cssom": "0.3.x"
} }
}, },
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
"integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
"dev": true,
"requires": {
"array-find-index": "^1.0.1"
}
},
"cyclist": { "cyclist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@ -7223,12 +7153,6 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true
},
"depd": { "depd": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@ -9191,18 +9115,6 @@
} }
} }
}, },
"fstream": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"inherits": "~2.0.0",
"mkdirp": ">=0.5 0",
"rimraf": "2"
}
},
"function-bind": { "function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@ -9226,31 +9138,6 @@
"simple-git": "^1.85.0" "simple-git": "^1.85.0"
} }
}, },
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dev": true,
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.0",
"object-assign": "^4.1.0",
"signal-exit": "^3.0.0",
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wide-align": "^1.1.0"
}
},
"gaze": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
"integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
"dev": true,
"requires": {
"globule": "^1.0.0"
}
},
"gensync": { "gensync": {
"version": "1.0.0-beta.1", "version": "1.0.0-beta.1",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
@ -9476,17 +9363,6 @@
} }
} }
}, },
"globule": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
"integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
"dev": true,
"requires": {
"glob": "~7.1.1",
"lodash": "~4.17.12",
"minimatch": "~3.0.2"
}
},
"good-listener": { "good-listener": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
@ -9613,12 +9489,6 @@
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true "dev": true
}, },
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true
},
"has-value": { "has-value": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
@ -9744,9 +9614,9 @@
"dev": true "dev": true
}, },
"highlight.js": { "highlight.js": {
"version": "9.18.1", "version": "9.18.5",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.5.tgz",
"integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", "integrity": "sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA==",
"dev": true "dev": true
}, },
"hmac-drbg": { "hmac-drbg": {
@ -10354,12 +10224,6 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true "dev": true
}, },
"in-publish": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
"integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==",
"dev": true
},
"indent-string": { "indent-string": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
@ -11964,12 +11828,6 @@
} }
} }
}, },
"js-base64": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
"integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==",
"dev": true
},
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -12759,16 +12617,6 @@
"js-tokens": "^3.0.0 || ^4.0.0" "js-tokens": "^3.0.0 || ^4.0.0"
} }
}, },
"loud-rejection": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
"integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
"dev": true,
"requires": {
"currently-unhandled": "^0.4.1",
"signal-exit": "^3.0.0"
}
},
"lower-case": { "lower-case": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
@ -12825,12 +12673,6 @@
"integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
"dev": true "dev": true
}, },
"map-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
"integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
"dev": true
},
"map-visit": { "map-visit": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
@ -13062,114 +12904,6 @@
"integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
"dev": true "dev": true
}, },
"meow": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
"integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
"dev": true,
"requires": {
"camelcase-keys": "^2.0.0",
"decamelize": "^1.1.2",
"loud-rejection": "^1.0.0",
"map-obj": "^1.0.1",
"minimist": "^1.1.3",
"normalize-package-data": "^2.3.4",
"object-assign": "^4.0.1",
"read-pkg-up": "^1.0.1",
"redent": "^1.0.0",
"trim-newlines": "^1.0.0"
},
"dependencies": {
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
"dev": true,
"requires": {
"path-exists": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
},
"load-json-file": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^2.2.0",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0",
"strip-bom": "^2.0.0"
}
},
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"dev": true,
"requires": {
"error-ex": "^1.2.0"
}
},
"path-exists": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
"dev": true,
"requires": {
"pinkie-promise": "^2.0.0"
}
},
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
},
"read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
"dev": true,
"requires": {
"load-json-file": "^1.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^1.0.0"
}
},
"read-pkg-up": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
"dev": true,
"requires": {
"find-up": "^1.0.0",
"read-pkg": "^1.0.0"
}
},
"strip-bom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
"dev": true,
"requires": {
"is-utf8": "^0.2.0"
}
}
}
},
"merge-descriptors": { "merge-descriptors": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@ -13542,7 +13276,8 @@
"version": "2.14.1", "version": "2.14.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
"dev": true "dev": true,
"optional": true
}, },
"nanomatch": { "nanomatch": {
"version": "1.2.13", "version": "1.2.13",
@ -13626,34 +13361,6 @@
"integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==",
"dev": true "dev": true
}, },
"node-gyp": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
"integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
"dev": true,
"requires": {
"fstream": "^1.0.0",
"glob": "^7.0.3",
"graceful-fs": "^4.1.2",
"mkdirp": "^0.5.0",
"nopt": "2 || 3",
"npmlog": "0 || 1 || 2 || 3 || 4",
"osenv": "0",
"request": "^2.87.0",
"rimraf": "2",
"semver": "~5.3.0",
"tar": "^2.0.0",
"which": "1"
},
"dependencies": {
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
"dev": true
}
}
},
"node-int64": { "node-int64": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@ -13724,83 +13431,6 @@
"integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==", "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==",
"dev": true "dev": true
}, },
"node-sass": {
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
"integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
"dev": true,
"requires": {
"async-foreach": "^0.1.3",
"chalk": "^1.1.1",
"cross-spawn": "^3.0.0",
"gaze": "^1.0.0",
"get-stdin": "^4.0.1",
"glob": "^7.0.3",
"in-publish": "^2.0.0",
"lodash": "^4.17.15",
"meow": "^3.7.0",
"mkdirp": "^0.5.1",
"nan": "^2.13.2",
"node-gyp": "^3.8.0",
"npmlog": "^4.0.0",
"request": "^2.88.0",
"sass-graph": "^2.2.4",
"stdout-stream": "^1.4.0",
"true-case-path": "^1.0.2"
},
"dependencies": {
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"dev": true
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"cross-spawn": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
"integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
"dev": true,
"requires": {
"lru-cache": "^4.0.1",
"which": "^1.2.9"
}
},
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
"integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
"dev": true
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"dev": true
}
}
},
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
"integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
"dev": true,
"requires": {
"abbrev": "1"
}
},
"normalize-package-data": { "normalize-package-data": {
"version": "2.5.0", "version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@ -13915,18 +13545,6 @@
} }
} }
}, },
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
"gauge": "~2.7.3",
"set-blocking": "~2.0.0"
}
},
"nprogress": { "nprogress": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
@ -14210,16 +13828,6 @@
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true "dev": true
}, },
"osenv": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
}
},
"p-cancelable": { "p-cancelable": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
@ -15742,27 +15350,6 @@
"util.promisify": "^1.0.0" "util.promisify": "^1.0.0"
} }
}, },
"redent": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
"integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
"dev": true,
"requires": {
"indent-string": "^2.1.0",
"strip-indent": "^1.0.1"
},
"dependencies": {
"indent-string": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
"integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
"dev": true,
"requires": {
"repeating": "^2.0.0"
}
}
}
},
"reduce": { "reduce": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/reduce/-/reduce-1.0.2.tgz", "resolved": "https://registry.npmjs.org/reduce/-/reduce-1.0.2.tgz",
@ -16401,157 +15988,13 @@
} }
} }
}, },
"sass-graph": { "sass": {
"version": "2.2.4", "version": "1.29.0",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.29.0.tgz",
"integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", "integrity": "sha512-ZpwAUFgnvAUCdkjwPREny+17BpUj8nh5Yr6zKPGtLNTLrmtoRYIjm7njP24COhjJldjwW1dcv52Lpf4tNZVVRA==",
"dev": true, "dev": true,
"requires": { "requires": {
"glob": "^7.0.0", "chokidar": ">=2.0.0 <4.0.0"
"lodash": "^4.0.0",
"scss-tokenizer": "^0.2.3",
"yargs": "^7.0.0"
},
"dependencies": {
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"dev": true
},
"find-up": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
"dev": true,
"requires": {
"path-exists": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
},
"load-json-file": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^2.2.0",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0",
"strip-bom": "^2.0.0"
}
},
"os-locale": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
"dev": true,
"requires": {
"lcid": "^1.0.0"
}
},
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"dev": true,
"requires": {
"error-ex": "^1.2.0"
}
},
"path-exists": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
"dev": true,
"requires": {
"pinkie-promise": "^2.0.0"
}
},
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"pify": "^2.0.0",
"pinkie-promise": "^2.0.0"
}
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
},
"read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
"dev": true,
"requires": {
"load-json-file": "^1.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^1.0.0"
}
},
"read-pkg-up": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
"dev": true,
"requires": {
"find-up": "^1.0.0",
"read-pkg": "^1.0.0"
}
},
"strip-bom": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
"dev": true,
"requires": {
"is-utf8": "^0.2.0"
}
},
"which-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
"dev": true
},
"yargs": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
"dev": true,
"requires": {
"camelcase": "^3.0.0",
"cliui": "^3.2.0",
"decamelize": "^1.1.1",
"get-caller-file": "^1.0.1",
"os-locale": "^1.4.0",
"read-pkg-up": "^1.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^1.0.2",
"which-module": "^1.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^5.0.0"
}
},
"yargs-parser": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
"dev": true,
"requires": {
"camelcase": "^3.0.0"
}
}
} }
}, },
"sax": { "sax": {
@ -16570,27 +16013,6 @@
"ajv-keywords": "^3.4.1" "ajv-keywords": "^3.4.1"
} }
}, },
"scss-tokenizer": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
"integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
"dev": true,
"requires": {
"js-base64": "^2.1.8",
"source-map": "^0.4.2"
},
"dependencies": {
"source-map": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"dev": true,
"requires": {
"amdefine": ">=0.0.4"
}
}
}
},
"section-matter": { "section-matter": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
@ -17374,15 +16796,6 @@
} }
} }
}, },
"stdout-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
"integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
"dev": true,
"requires": {
"readable-stream": "^2.0.1"
}
},
"stealthy-require": { "stealthy-require": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
@ -17653,23 +17066,6 @@
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true "dev": true
}, },
"strip-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
"integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
"dev": true,
"requires": {
"get-stdin": "^4.0.1"
},
"dependencies": {
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
"integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
"dev": true
}
}
},
"strip-json-comments": { "strip-json-comments": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
@ -17907,17 +17303,6 @@
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
"dev": true "dev": true
}, },
"tar": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"dev": true,
"requires": {
"block-stream": "*",
"fstream": "^1.0.12",
"inherits": "2"
}
},
"term-size": { "term-size": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz",
@ -18261,12 +17646,6 @@
"integrity": "sha512-E0ZosSWYK2mkSu+KEtQ9/KqarVjA9HztOSX+9FDdNacRAq29RRV6ZQNgob3iuW8Htar9vAfEa6yyt5qBAHZDBA==", "integrity": "sha512-E0ZosSWYK2mkSu+KEtQ9/KqarVjA9HztOSX+9FDdNacRAq29RRV6ZQNgob3iuW8Htar9vAfEa6yyt5qBAHZDBA==",
"dev": true "dev": true
}, },
"trim-newlines": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
"integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
"dev": true
},
"trim-right": { "trim-right": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
@ -18285,15 +17664,6 @@
"integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==",
"dev": true "dev": true
}, },
"true-case-path": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
"integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
"dev": true,
"requires": {
"glob": "^7.1.2"
}
},
"tslib": { "tslib": {
"version": "1.11.1", "version": "1.11.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
@ -20249,15 +19619,6 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true "dev": true
}, },
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"dev": true,
"requires": {
"string-width": "^1.0.2 || 2"
}
},
"widest-line": { "widest-line": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",

10
package.json

@ -1,7 +1,7 @@
{ {
"name": "grapesjs", "name": "grapesjs",
"description": "Free and Open Source Web Builder Framework", "description": "Free and Open Source Web Builder Framework",
"version": "0.16.27", "version": "0.16.30",
"author": "Artur Arseniev", "author": "Artur Arseniev",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"homepage": "http://grapesjs.com", "homepage": "http://grapesjs.com",
@ -20,7 +20,7 @@
"backbone": "1.3.3", "backbone": "1.3.3",
"backbone-undo": "^0.2.5", "backbone-undo": "^0.2.5",
"cash-dom": "^2.3.9", "cash-dom": "^2.3.9",
"codemirror": "^5.49.2", "codemirror": "^5.58.2",
"codemirror-formatting": "^1.0.0", "codemirror-formatting": "^1.0.0",
"keymaster": "^1.6.2", "keymaster": "^1.6.2",
"promise-polyfill": "^8.1.3", "promise-polyfill": "^8.1.3",
@ -40,9 +40,9 @@
"husky": "^2.7.0", "husky": "^2.7.0",
"jest": "^24.9.0", "jest": "^24.9.0",
"lint-staged": "^8.2.1", "lint-staged": "^8.2.1",
"node-sass": "^4.13.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^1.18.2", "prettier": "^1.18.2",
"sass": "^1.29.0",
"sinon": "^7.5.0", "sinon": "^7.5.0",
"string-replace-loader": "^2.2.0", "string-replace-loader": "^2.2.0",
"vuepress": "^1.5.4", "vuepress": "^1.5.4",
@ -124,11 +124,11 @@
"build-n": "npm run check && npm run build:css && npm run build-prod", "build-n": "npm run check && npm run build:css && npm run build-prod",
"build-dev": "webpack --env=dev && npm run build:css && npm run build:locale", "build-dev": "webpack --env=dev && npm run build:css && npm run build:locale",
"build-prod": "webpack --env=prod", "build-prod": "webpack --env=prod",
"build:css": "node-sass src/styles/scss/main.scss dist/css/grapes.min.css --output-style compressed", "build:css": "sass src/styles/scss/main.scss dist/css/grapes.min.css --no-source-map --style=compressed --load-path=node_modules",
"build:locale": "rm -rf ./locale && node scripts/build-locale.js && babel locale -d locale --copy-files --no-comments", "build:locale": "rm -rf ./locale && node scripts/build-locale.js && babel locale -d locale --copy-files --no-comments",
"v:patch": "npm version --no-git-tag-version patch", "v:patch": "npm version --no-git-tag-version patch",
"start": "run-p start:*", "start": "run-p start:*",
"start:css": "npm run build:css -- -w", "start:css": "npm run build:css -- --watch",
"start:js": "webpack-dev-server --open --progress --colors", "start:js": "webpack-dev-server --open --progress --colors",
"stats": "webpack --env=prod --profile --json > stats.json && webpack-bundle-analyzer stats.json", "stats": "webpack --env=prod --profile --json > stats.json && webpack-bundle-analyzer stats.json",
"format": "prettier --single-quote --write './{src,test}/**/*.js'", "format": "prettier --single-quote --write './{src,test}/**/*.js'",

9
src/asset_manager/index.js

@ -347,6 +347,15 @@ export default () => {
*/ */
onDblClick(func) { onDblClick(func) {
c.onDblClick = func; c.onDblClick = func;
},
destroy() {
assets.reset();
fu.collection.reset();
fu.remove();
am.remove();
[assets, am, fu].forEach(i => (i = null));
c = {};
} }
}; };
}; };

10
src/block_manager/index.js

@ -255,6 +255,16 @@ export default () => {
} }
return this.getContainer(); return this.getContainer();
},
destroy() {
blocks.reset();
blocks.stopListening();
blocksVisible.reset();
categories.reset();
blocksView && blocksView.remove();
[blocks, blocksVisible, categories, blocksView].forEach(i => (i = null));
c = {};
} }
}; };
}; };

20
src/canvas/index.js

@ -30,26 +30,15 @@
* @module Canvas * @module Canvas
*/ */
import { import { hasDnd, getElement, getViewEl } from 'utils/mixins';
on,
off,
hasDnd,
getElement,
getPointerEvent,
getViewEl
} from 'utils/mixins';
import { debounce } from 'underscore';
import Droppable from 'utils/Droppable'; import Droppable from 'utils/Droppable';
import defaults from './config/config'; import defaults from './config/config';
import Canvas from './model/Canvas'; import Canvas from './model/Canvas';
import canvasView from './view/CanvasView'; import canvasView from './view/CanvasView';
const { requestAnimationFrame } = window;
export default () => { export default () => {
let c = {}; let c = {};
let canvas; let canvas;
let frameRect;
let CanvasView; let CanvasView;
return { return {
@ -684,6 +673,13 @@ export default () => {
em: this.em em: this.em
} }
); );
},
destroy() {
canvas.stopListening();
CanvasView.remove();
[c, canvas, CanvasView].forEach(i => (i = {}));
['em', 'model', 'droppable'].forEach(i => (this[i] = {}));
} }
}; };
}; };

4
src/canvas/view/CanvasView.js

@ -63,6 +63,10 @@ export default Backbone.View.extend({
}, },
remove() { remove() {
const frm = this.model.get('frames');
frm.remove(frm.models);
this.frames.remove();
this.frames = {};
Backbone.View.prototype.remove.apply(this, arguments); Backbone.View.prototype.remove.apply(this, arguments);
this.toggleListeners(); this.toggleListeners();
}, },

6
src/canvas/view/FrameView.js

@ -1,5 +1,5 @@
import Backbone from 'backbone'; import Backbone from 'backbone';
import { bindAll, isString, debounce } from 'underscore'; import { bindAll, isString, debounce, isUndefined } from 'underscore';
import CssRulesView from 'css_composer/view/CssRulesView'; import CssRulesView from 'css_composer/view/CssRulesView';
import ComponentView from 'dom_components/view/ComponentView'; import ComponentView from 'dom_components/view/ComponentView';
import { import {
@ -162,11 +162,12 @@ export default Backbone.View.extend({
autoscroll() { autoscroll() {
if (this.dragging) { if (this.dragging) {
const { lastClientY } = this;
const canvas = this.em.get('Canvas'); const canvas = this.em.get('Canvas');
const win = this.getWindow(); const win = this.getWindow();
const body = this.getBody(); const body = this.getBody();
const actualTop = body.scrollTop; const actualTop = body.scrollTop;
const clientY = this.lastClientY || 0; const clientY = lastClientY || 0;
const limitTop = canvas.getConfig().autoscrollLimit; const limitTop = canvas.getConfig().autoscrollLimit;
const limitBottom = this.getRect().height - limitTop; const limitBottom = this.getRect().height - limitTop;
let nextTop = actualTop; let nextTop = actualTop;
@ -180,6 +181,7 @@ export default Backbone.View.extend({
} }
if ( if (
!isUndefined(lastClientY) && // Fixes #3134
nextTop !== actualTop && nextTop !== actualTop &&
nextTop > 0 && nextTop > 0 &&
nextTop < this.lastMaxHeight nextTop < this.lastMaxHeight

4
src/canvas/view/FrameWrapView.js

@ -35,6 +35,7 @@ export default Backbone.View.extend({
this.listenTo(model, 'loaded', this.frameLoaded); this.listenTo(model, 'loaded', this.frameLoaded);
this.listenTo(model, 'change:x change:y', this.updatePos); this.listenTo(model, 'change:x change:y', this.updatePos);
this.listenTo(model, 'change:width change:height', this.updateSize); this.listenTo(model, 'change:width change:height', this.updateSize);
this.listenTo(model, 'destroy remove', this.remove);
this.updatePos(); this.updatePos();
this.setupDragger(); this.setupDragger();
}, },
@ -69,8 +70,9 @@ export default Backbone.View.extend({
}, },
remove() { remove() {
Backbone.View.prototype.remove.apply(this, arguments);
this.frame.remove(); this.frame.remove();
this.frame = {};
Backbone.View.prototype.remove.apply(this, arguments);
return this; return this;
}, },

6
src/code_manager/index.js

@ -219,6 +219,12 @@ export default () => {
for (var id in defViewers) this.addViewer(id, defViewers[id]); for (var id in defViewers) this.addViewer(id, defViewers[id]);
return this; return this;
},
destroy() {
[c, generators, defGenerators, viewers, defViewers].forEach(
i => (i = {})
);
} }
}; };
}; };

57
src/commands/index.js

@ -32,38 +32,39 @@ import CommandAbstract from './view/CommandAbstract';
import defaults from './config/config'; import defaults from './config/config';
import { eventDrag } from 'dom_components/model/Component'; import { eventDrag } from 'dom_components/model/Component';
const commandsDef = [
['preview', 'Preview', 'preview'],
['resize', 'Resize', 'resize'],
['fullscreen', 'Fullscreen', 'fullscreen'],
['copy', 'CopyComponent'],
['paste', 'PasteComponent'],
['canvas-move', 'CanvasMove'],
['canvas-clear', 'CanvasClear'],
['open-code', 'ExportTemplate', 'export-template'],
['open-layers', 'OpenLayers', 'open-layers'],
['open-styles', 'OpenStyleManager', 'open-sm'],
['open-traits', 'OpenTraitManager', 'open-tm'],
['open-blocks', 'OpenBlocks', 'open-blocks'],
['open-assets', 'OpenAssets', 'open-assets'],
['component-select', 'SelectComponent', 'select-comp'],
['component-outline', 'SwitchVisibility', 'sw-visibility'],
['component-offset', 'ShowOffset', 'show-offset'],
['component-move', 'MoveComponent', 'move-comp'],
['component-next', 'ComponentNext'],
['component-prev', 'ComponentPrev'],
['component-enter', 'ComponentEnter'],
['component-exit', 'ComponentExit', 'select-parent'],
['component-delete', 'ComponentDelete'],
['component-style-clear', 'ComponentStyleClear'],
['component-drag', 'ComponentDrag']
];
export default () => { export default () => {
let em; let em;
let c = {}; let c = {};
const commands = {}; const commands = {};
const defaultCommands = {}; const defaultCommands = {};
const active = {}; const active = {};
const commandsDef = [
['preview', 'Preview', 'preview'],
['resize', 'Resize', 'resize'],
['fullscreen', 'Fullscreen', 'fullscreen'],
['copy', 'CopyComponent'],
['paste', 'PasteComponent'],
['canvas-move', 'CanvasMove'],
['canvas-clear', 'CanvasClear'],
['open-code', 'ExportTemplate', 'export-template'],
['open-layers', 'OpenLayers', 'open-layers'],
['open-styles', 'OpenStyleManager', 'open-sm'],
['open-traits', 'OpenTraitManager', 'open-tm'],
['open-blocks', 'OpenBlocks', 'open-blocks'],
['open-assets', 'OpenAssets', 'open-assets'],
['component-select', 'SelectComponent', 'select-comp'],
['component-outline', 'SwitchVisibility', 'sw-visibility'],
['component-offset', 'ShowOffset', 'show-offset'],
['component-move', 'MoveComponent', 'move-comp'],
['component-next', 'ComponentNext'],
['component-prev', 'ComponentPrev'],
['component-enter', 'ComponentEnter'],
['component-exit', 'ComponentExit', 'select-parent'],
['component-delete', 'ComponentDelete'],
['component-style-clear', 'ComponentStyleClear'],
['component-drag', 'ComponentDrag']
];
// Need it here as it would be used below // Need it here as it would be used below
const add = function(id, obj) { const add = function(id, obj) {
@ -424,6 +425,10 @@ export default () => {
if (!command.stop) command.noStop = 1; if (!command.stop) command.noStop = 1;
const cmd = CommandAbstract.extend(command); const cmd = CommandAbstract.extend(command);
return new cmd(c); return new cmd(c);
},
destroy() {
[em, c, commands, defaultCommands, active].forEach(i => (i = {}));
} }
}; };
}; };

5
src/commands/view/CopyComponent.js

@ -2,9 +2,6 @@ export default {
run(ed) { run(ed) {
const em = ed.getModel(); const em = ed.getModel();
const models = [...ed.getSelectedAll()]; const models = [...ed.getSelectedAll()];
models.length && em.set('clipboard', models);
if (models.length) {
em.set('clipboard', models);
}
} }
}; };

19
src/commands/view/SelectComponent.js

@ -157,6 +157,15 @@ export default {
if (el.ownerDocument === this.currentDoc) this.elHovered = result; if (el.ownerDocument === this.currentDoc) this.elHovered = result;
}); });
} else {
this.currentDoc = null;
this.elHovered = 0;
this.updateToolsLocal();
this.canvas.getFrames().forEach(frame => {
const { view } = frame;
const el = view && view.getToolsEl();
el && this.toggleToolsEl(0, 0, { el });
});
} }
}, },
@ -209,15 +218,7 @@ export default {
}, },
onOut() { onOut() {
this.currentDoc = null;
this.em.setHovered(0); this.em.setHovered(0);
this.elHovered = 0;
this.updateToolsLocal();
this.canvas.getFrames().forEach(frame => {
const { view } = frame;
const el = view && view.getToolsEl();
el && this.toggleToolsEl(0, 0, { el });
});
}, },
toggleToolsEl(on, view, opts = {}) { toggleToolsEl(on, view, opts = {}) {
@ -730,9 +731,9 @@ export default {
stop(ed, sender, opts = {}) { stop(ed, sender, opts = {}) {
const { em, editor } = this; const { em, editor } = this;
this.onHovered(); // force to hide toolbar
this.stopSelectComponent(); this.stopSelectComponent();
!opts.preserveSelected && em.setSelected(null); !opts.preserveSelected && em.setSelected(null);
this.onOut();
this.toggleToolsEl(); this.toggleToolsEl();
editor && editor.stopCommand('resize'); editor && editor.stopCommand('resize');
} }

26
src/css_composer/index.js

@ -373,6 +373,24 @@ export default () => {
); );
}, },
/**
* Find rules, in different states (eg. like `:hover`) and media queries, matching the selector.
* @param {string} selector Selector, eg. '.myclass'
* @returns {Array<CssRule>}
* @example
* // Common scenario, take all the component specific rules
* const id = someComponent.getId();
* const rules = cc.getRules(`#${id}`);
* console.log(rules.map(rule => rule.toCSS()))
*/
getRules(selector) {
const rules = this.getAll();
const result = rules.filter(
r => r.getSelectors().getFullString() === selector
);
return result;
},
/** /**
* Add/update the CSS rule with id selector * Add/update the CSS rule with id selector
* @param {string} name Id selector name, eg. 'my-id' * @param {string} name Id selector name, eg. 'my-id'
@ -462,6 +480,14 @@ export default () => {
*/ */
render() { render() {
return rulesView.render().el; return rulesView.render().el;
},
destroy() {
rules.reset();
rules.stopListening();
rulesView.remove();
[em, rules, rulesView].forEach(i => (i = null));
c = {};
} }
}; };
}; };

28
src/css_composer/model/CssRule.js

@ -41,18 +41,36 @@ export default Backbone.Model.extend(Styleable).extend({
initialize(c, opt = {}) { initialize(c, opt = {}) {
this.config = c || {}; this.config = c || {};
this.opt = opt;
this.em = opt.em; this.em = opt.em;
this.ensureSelectors(); this.ensureSelectors();
}, },
clone() {
const opts = { ...this.opt };
const attr = { ...this.attributes };
attr.selectors = this.get('selectors').map(s => s.clone());
return new this.constructor(attr, opts);
},
ensureSelectors() { ensureSelectors() {
const { em } = this; const { em } = this;
const result = [];
const sm = em && em.get('SelectorManager'); const sm = em && em.get('SelectorManager');
const selectors = this.getSelectors(); const toListen = [this, 'change:selectors', this.ensureSelectors];
const toInit = Array.isArray(selectors); let sels = this.getSelectors();
selectors.forEach(sel => result.push(sm ? sm.add(sel) : sel)); this.stopListening(...toListen);
toInit && this.set('selectors', new Selectors(result));
if (sels.models) {
sels = [...sels.models];
}
if (Array.isArray(sels)) {
const res = sels.filter(i => i).map(i => (sm ? sm.add(i) : i));
sels = new Selectors(res);
}
this.set('selectors', sels);
this.listenTo(...toListen);
}, },
/** /**

2
src/css_composer/model/CssRules.js

@ -16,7 +16,7 @@ export default Backbone.Collection.extend({
}, },
onAdd(model) { onAdd(model) {
model.ensureSelectors(); model.ensureSelectors(); // required for undo
}, },
onRemove(removed) { onRemove(removed) {

8
src/device_manager/index.js

@ -124,6 +124,14 @@ export default () => {
*/ */
render() { render() {
return view.render().el; return view.render().el;
},
destroy() {
devices.reset();
devices.stopListening();
view.remove();
[devices, view].forEach(i => (i = null));
c = {};
} }
}; };
}; };

20
src/dom_components/index.js

@ -344,12 +344,13 @@ export default () => {
const handleChanges = this.handleChanges.bind(this); const handleChanges = this.handleChanges.bind(this);
const handleRemoves = this.handleRemoves.bind(this); const handleRemoves = this.handleRemoves.bind(this);
um.add(coll); um.add(coll);
[[coll, 'add', handleChanges], [coll, 'remove', handleRemoves]].forEach( [
els => { [coll, 'add', handleChanges],
em.stopListening(els[0], els[1], els[2]); [coll, 'remove', handleRemoves]
em.listenTo(els[0], els[1], els[2]); ].forEach(els => {
} em.stopListening(els[0], els[1], els[2]);
); em.listenTo(els[0], els[1], els[2]);
});
} }
}, },
@ -733,6 +734,13 @@ export default () => {
allById() { allById() {
return componentsById; return componentsById;
},
destroy() {
this.clear();
componentView.remove();
[c, em, componentsById, component, componentView].forEach(i => (i = {}));
this.em = {};
} }
}; };
}; };

205
src/dom_components/model/Component.js

@ -9,9 +9,10 @@ import {
isString, isString,
forEach, forEach,
result, result,
bindAll,
keys keys
} from 'underscore'; } from 'underscore';
import { shallowDiff, capitalize } from 'utils/mixins'; import { shallowDiff, capitalize, isEmptyObj } from 'utils/mixins';
import Styleable from 'domain_abstract/model/Styleable'; import Styleable from 'domain_abstract/model/Styleable';
import Backbone from 'backbone'; import Backbone from 'backbone';
import Components from './Components'; import Components from './Components';
@ -142,6 +143,7 @@ const Component = Backbone.Model.extend(Styleable).extend(
removed() {}, removed() {},
initialize(props = {}, opt = {}) { initialize(props = {}, opt = {}) {
bindAll(this, '__upSymbProps', '__upSymbCls', '__upSymbComps');
const em = opt.em; const em = opt.em;
// Propagate properties from parent if indicated // Propagate properties from parent if indicated
@ -190,6 +192,7 @@ const Component = Backbone.Model.extend(Styleable).extend(
this.listenTo(this, 'change:attributes:id', this._idUpdated); this.listenTo(this, 'change:attributes:id', this._idUpdated);
this.set('status', ''); this.set('status', '');
this.views = []; this.views = [];
this.__isSymbol() && this.__initSymb();
// Register global updates for collection properties // Register global updates for collection properties
['classes', 'traits', 'components'].forEach(name => { ['classes', 'traits', 'components'].forEach(name => {
@ -320,6 +323,26 @@ const Component = Backbone.Model.extend(Styleable).extend(
return parent; return parent;
}, },
/**
* The method returns a Boolean value indicating whether the passed
* component is a descendant of a given component
* @param {Component} component Component to check
* @returns {Boolean}
*/
contains(component) {
let result = !1;
if (!component) return result;
const contains = components => {
!result &&
components.forEach(item => {
if (item === component) result = !0;
!result && contains(item.components());
});
};
contains(this.components());
return result;
},
/** /**
* Once the tag is updated I have to remove the node and replace it * Once the tag is updated I have to remove the node and replace it
* @private * @private
@ -552,6 +575,95 @@ const Component = Backbone.Model.extend(Styleable).extend(
return classStr ? classStr.split(' ') : []; return classStr ? classStr.split(' ') : [];
}, },
__initSymb() {
if (this.__symbReady) return;
this.on('change', this.__upSymbProps);
this.__symbReady = 1;
},
__isSymbol() {
return isArray(this.get('__symbol'));
},
__isSymbolTop() {
const parent = this.parent();
return this.__isSymbol() && parent && !parent.__isSymbol();
},
__getSymbolOf() {
return this.get('__symbolOf');
},
__getSymbToUp() {
const symbol = this.get('__symbol');
return !this.__isSymbol()
? []
: symbol.filter(item => item.collection || item.prevColl);
},
__getSymbTop(opts) {
const isSymbol = this.__isSymbol();
let result = this;
let parent = this.parent(opts);
while (
parent &&
(isSymbol ? parent.__isSymbol() : parent.__getSymbolOf())
) {
result = parent;
parent = parent.parent(opts);
}
return result;
},
__upSymbProps() {
const changed = this.changedAttributes();
const attrs = changed.attributes || {};
delete changed.status;
delete changed.open;
delete changed.__symbol;
delete changed.__symbolOf;
delete changed.attributes;
delete attrs.id;
if (!isEmptyObj(attrs)) changed.attributes = attrs;
!isEmptyObj(changed) &&
this.__getSymbToUp().forEach(child => child.set(changed));
},
__upSymbCls() {
this.__getSymbToUp().forEach(child => {
child.set({ classes: this.get('classes') });
});
},
__upSymbComps(m, c, o) {
if (!o) {
// Reset
this.__getSymbToUp().forEach(item => {
const newMods = m.models.map(mod => mod.clone({ symbol: 1 }));
item.components().reset(newMods, c);
});
} else if (o.add) {
// Add
const addedInstances = m.__getSymbToUp();
// console.log('Added', m.getId(), m.toHTML(), o, 'toUp', addedInstances);
this.__getSymbToUp().forEach(symbInst => {
const symbTop = symbInst.__getSymbTop();
const inner = addedInstances.filter(addedInst => {
const addedTop = addedInst.__getSymbTop({ prev: 1 });
return symbTop && addedTop && addedTop === symbTop;
})[0];
const toAppend = inner || m.clone({ symbol: 1 });
// console.log('Added inner', toAppend.getId(), toAppend.toHTML(), inner);
symbInst.append(toAppend, o);
});
} else {
// Remove
m.__getSymbToUp().forEach(item => item.remove(o));
}
},
initClasses() { initClasses() {
const event = 'change:classes'; const event = 'change:classes';
const attrCls = this.get('attributes').class || []; const attrCls = this.get('attributes').class || [];
@ -563,6 +675,7 @@ const Component = Backbone.Model.extend(Styleable).extend(
const selectors = new Selectors([]); const selectors = new Selectors([]);
this.set('classes', selectors); this.set('classes', selectors);
selectors.add(classes); selectors.add(classes);
selectors.on('add remove reset', this.__upSymbCls);
this.listenTo(...toListen); this.listenTo(...toListen);
return this; return this;
}, },
@ -579,7 +692,11 @@ const Component = Backbone.Model.extend(Styleable).extend(
const addChild = !this.opt.avoidChildren; const addChild = !this.opt.avoidChildren;
this.set('components', comps); this.set('components', comps);
addChild && addChild &&
comps.add(isFunction(components) ? components(this) : components); comps.add(
isFunction(components) ? components(this) : components,
this.opt
);
comps.on('add remove reset', this.__upSymbComps);
this.listenTo(...toListen); this.listenTo(...toListen);
return this; return this;
}, },
@ -664,8 +781,8 @@ const Component = Backbone.Model.extend(Styleable).extend(
* component.parent(); * component.parent();
* // -> Component * // -> Component
*/ */
parent() { parent(opts = {}) {
const coll = this.collection; const coll = this.collection || (opts.prev && this.prevColl);
return coll && coll.parent; return coll && coll.parent;
}, },
@ -788,7 +905,7 @@ const Component = Backbone.Model.extend(Styleable).extend(
*/ */
getTraitIndex(id) { getTraitIndex(id) {
const trait = this.getTrait(id); const trait = this.getTrait(id);
return trait ? this.get('traits').indexOf(trait) : trait; return trait ? this.get('traits').indexOf(trait) : -1;
}, },
/** /**
@ -835,23 +952,12 @@ const Component = Backbone.Model.extend(Styleable).extend(
* @private * @private
*/ */
normalizeClasses(arr) { normalizeClasses(arr) {
var res = []; const res = [];
const em = this.em; const { em } = this;
const clm = em && em.get('SelectorManager');
if (!em) return;
var clm = em.get('SelectorManager');
if (!clm) return; if (!clm) return;
if (arr.models) return [...arr.models];
arr.forEach(val => { arr.forEach(val => res.push(clm.add(val)));
var name = '';
if (typeof val === 'string') name = val;
else name = val.name;
var model = clm.add(name);
res.push(model);
});
return res; return res;
}, },
@ -859,19 +965,24 @@ const Component = Backbone.Model.extend(Styleable).extend(
* Override original clone method * Override original clone method
* @private * @private
*/ */
clone() { clone(opt = {}) {
const em = this.em; const em = this.em;
const style = this.getStyle();
const attr = { ...this.attributes }; const attr = { ...this.attributes };
const opts = { ...this.opt }; const opts = { ...this.opt };
const id = this.getId();
const cssc = em && em.get('CssComposer');
attr.attributes = { ...attr.attributes }; attr.attributes = { ...attr.attributes };
delete attr.attributes.id; delete attr.attributes.id;
attr.components = []; attr.components = [];
attr.classes = []; attr.classes = [];
attr.traits = []; attr.traits = [];
if (this.__isSymbolTop()) {
opt.symbol = 1;
}
this.get('components').each((md, i) => { this.get('components').each((md, i) => {
attr.components[i] = md.clone(); attr.components[i] = md.clone({ ...opt, _inner: 1 });
}); });
this.get('traits').each((md, i) => { this.get('traits').each((md, i) => {
attr.traits[i] = md.clone(); attr.traits[i] = md.clone();
@ -884,11 +995,29 @@ const Component = Backbone.Model.extend(Styleable).extend(
attr.view = ''; attr.view = '';
opts.collection = null; opts.collection = null;
if (em && em.getConfig('avoidInlineStyle') && !isEmpty(style)) { const cloned = new this.constructor(attr, opts);
attr.style = style;
// Clone component specific rules
const newId = `#${cloned.getId()}`;
const rulesToClone = cssc ? cssc.getRules(`#${id}`) : [];
rulesToClone.forEach(rule => {
const newRule = rule.clone();
newRule.set('selectors', [newId]);
cssc.getAll().add(newRule);
});
// Symbols
// If I clone an inner symbol, I have to reset it
cloned.unset('__symbol');
if (opt.symbol) {
// TODO Check if trying to clone a Symbol (check if parent is symbol)
const symbols = this.get('__symbol') || [];
symbols.push(cloned);
this.set('__symbol', symbols);
this.__initSymb();
cloned.set('__symbolOf', this);
} }
const cloned = new this.constructor(attr, opts);
const event = 'component:clone'; const event = 'component:clone';
em && em.trigger(event, cloned); em && em.trigger(event, cloned);
this.trigger(event, cloned); this.trigger(event, cloned);
@ -1021,12 +1150,22 @@ const Component = Backbone.Model.extend(Styleable).extend(
* @return {Object} * @return {Object}
* @private * @private
*/ */
toJSON(...args) { toJSON(opts = {}) {
const obj = Backbone.Model.prototype.toJSON.apply(this, args); const obj = Backbone.Model.prototype.toJSON.call(this, opts);
obj.attributes = this.getAttributes(); obj.attributes = this.getAttributes();
delete obj.attributes.class; delete obj.attributes.class;
delete obj.toolbar; delete obj.toolbar;
delete obj.traits; delete obj.traits;
delete obj.status;
if (!opts.keepSymbols) {
if (obj.__symbol) {
obj.__symbol = this.__getSymbToUp().map(i => i.getId());
}
if (obj.__symbolOf) {
obj.__symbolOf = obj.__symbolOf.getId();
}
}
if (this.em.getConfig('avoidDefaults')) { if (this.em.getConfig('avoidDefaults')) {
this.getChangedProps(obj); this.getChangedProps(obj);
@ -1043,7 +1182,7 @@ const Component = Backbone.Model.extend(Styleable).extend(
const defaults = result(this, 'defaults'); const defaults = result(this, 'defaults');
forEach(defaults, (value, key) => { forEach(defaults, (value, key) => {
if (['type', 'content'].indexOf(key) === -1 && obj[key] === value) { if (['type'].indexOf(key) === -1 && obj[key] === value) {
delete obj[key]; delete obj[key];
} }
}); });
@ -1195,9 +1334,9 @@ const Component = Backbone.Model.extend(Styleable).extend(
* Remove the component * Remove the component
* @return {this} * @return {this}
*/ */
remove() { remove(opts = {}) {
const coll = this.collection; const coll = this.collection;
return coll && coll.remove(this); return coll && coll.remove(this, opts);
}, },
/** /**
@ -1238,7 +1377,7 @@ const Component = Backbone.Model.extend(Styleable).extend(
const list = Component.getList(this); const list = Component.getList(this);
// If the ID already exists I need to rollback to the old one // If the ID already exists I need to rollback to the old one
if (list[id]) { if (list[id] || (!id && idPrev)) {
return this.setId(idPrev, { idUpdate: 1 }); return this.setId(idPrev, { idUpdate: 1 });
} }

60
src/dom_components/model/Components.js

@ -6,14 +6,27 @@ import {
each, each,
includes, includes,
extend, extend,
flatten flatten,
debounce
} from 'underscore'; } from 'underscore';
let Component; let Component;
const getIdsToKeep = prev => { const getIdsToKeep = (prev, res = []) => {
const pr = prev || []; const pr = prev || [];
return pr.map(comp => comp.getId()); pr.forEach(comp => {
res.push(comp.getId());
getIdsToKeep(comp.components(), res);
});
return res;
};
const getNewIds = (items, res = []) => {
items.map(item => {
res.push(item.getId());
getNewIds(item.components(), res);
});
return res;
}; };
export default Backbone.Collection.extend({ export default Backbone.Collection.extend({
@ -31,7 +44,7 @@ export default Backbone.Collection.extend({
const coll = this; const coll = this;
const prev = opts.previousModels || []; const prev = opts.previousModels || [];
const toRemove = prev.filter(prev => !models.get(prev.cid)); const toRemove = prev.filter(prev => !models.get(prev.cid));
const newIds = models.map(i => i.getId()); const newIds = getNewIds(models);
opts.keepIds = getIdsToKeep(prev).filter(pr => newIds.indexOf(pr) >= 0); opts.keepIds = getIdsToKeep(prev).filter(pr => newIds.indexOf(pr) >= 0);
toRemove.forEach(md => this.removeChildren(md, coll, opts)); toRemove.forEach(md => this.removeChildren(md, coll, opts));
models.each(model => this.onAdd(model)); models.each(model => this.onAdd(model));
@ -46,8 +59,10 @@ export default Backbone.Collection.extend({
const { domc, em } = this; const { domc, em } = this;
const allByID = domc ? domc.allById() : {}; const allByID = domc ? domc.allById() : {};
const isTemp = opts.temporary;
removed.prevColl = this; // This one is required for symbols
if (!opts.temporary) { if (!isTemp) {
// Remove the component from the global list // Remove the component from the global list
const id = removed.getId(); const id = removed.getId();
const sels = em.get('SelectorManager').getAll(); const sels = em.get('SelectorManager').getAll();
@ -159,8 +174,9 @@ export default Backbone.Collection.extend({
.filter(i => i) .filter(i => i)
.map(model => this.processDef(model)); .map(model => this.processDef(model));
models = isMult ? flatten(models, 1) : models[0]; models = isMult ? flatten(models, 1) : models[0];
const result = Backbone.Collection.prototype.add.apply(this, [models, opt]);
return Backbone.Collection.prototype.add.apply(this, [models, opt]); this.__firstAdd = result;
return result;
}, },
/** /**
@ -235,5 +251,33 @@ export default Backbone.Collection.extend({
model.setStyle({}); model.setStyle({});
model.addClass(name); model.addClass(name);
} }
}
this.__onAddEnd();
},
__onAddEnd: debounce(function() {
const { domc } = this;
const allComp = (domc && domc.allById()) || {};
const firstAdd = this.__firstAdd;
const toCheck = isArray(firstAdd) ? firstAdd : [firstAdd];
const silent = { silent: true };
const onAll = comps => {
comps.forEach(comp => {
const symbol = comp.get('__symbol');
const symbolOf = comp.get('__symbolOf');
if (symbol && isArray(symbol) && isString(symbol[0])) {
comp.set(
'__symbol',
symbol.map(smb => allComp[smb]).filter(i => i),
silent
);
}
if (isString(symbolOf)) {
comp.set('__symbolOf', allComp[symbolOf], silent);
}
onAll(comp.components());
});
};
onAll(toCheck);
})
}); });

2
src/dom_components/view/ComponentTextView.js

@ -131,8 +131,6 @@ export default ComponentView.extend({
model.get('components').each(model => clean(model)); model.get('components').each(model => clean(model));
}; };
// Avoid re-render on reset with silent option
!opts.silent && model.trigger('change:content', model, '', contentOpt);
comps.reset(content, opts); comps.reset(content, opts);
comps.each(model => clean(model)); comps.each(model => clean(model));
comps.trigger('resetNavigator'); comps.trigger('resetNavigator');

5
src/domain_abstract/ui/Input.js

@ -68,13 +68,14 @@ export default Backbone.View.extend({
*/ */
getInputEl() { getInputEl() {
if (!this.inputEl) { if (!this.inputEl) {
const { model } = this; const { model, opts } = this;
const type = opts.type || 'text';
const plh = const plh =
model.get('placeholder') || model.get('placeholder') ||
model.get('defaults') || model.get('defaults') ||
model.get('default') || model.get('default') ||
''; '';
this.inputEl = $(`<input type="text" placeholder="${plh}">`); this.inputEl = $(`<input type="${type}" placeholder="${plh}">`);
} }
return this.inputEl.get(0); return this.inputEl.get(0);

5
src/domain_abstract/ui/InputColor.js

@ -28,6 +28,11 @@ export default Input.extend({
return `${this.ppfx}input-holder`; return `${this.ppfx}input-holder`;
}, },
remove() {
Input.prototype.remove.apply(this, arguments);
this.colorEl.spectrum('destroy');
},
/** /**
* Set value to the model * Set value to the model
* @param {string} val * @param {string} val

26
src/domain_abstract/ui/InputNumber.js

@ -11,7 +11,8 @@ export default Input.extend({
'change select': 'handleUnitChange', 'change select': 'handleUnitChange',
'click [data-arrow-up]': 'upArrowClick', 'click [data-arrow-up]': 'upArrowClick',
'click [data-arrow-down]': 'downArrowClick', 'click [data-arrow-down]': 'downArrowClick',
'mousedown [data-arrows]': 'downIncrement' 'mousedown [data-arrows]': 'downIncrement',
keydown: 'handleKeyDown'
}, },
template() { template() {
@ -81,6 +82,21 @@ export default Input.extend({
this.elementUpdated(); this.elementUpdated();
}, },
/**
* Handled when user uses keyboard
*/
handleKeyDown(e) {
if (e.key === 'ArrowUp') {
e.preventDefault();
this.upArrowClick();
}
if (e.key === 'ArrowDown') {
e.preventDefault();
this.downArrowClick();
}
},
/** /**
* Fired when the element of the property is updated * Fired when the element of the property is updated
*/ */
@ -235,6 +251,8 @@ export default Input.extend({
var unit = model.get('unit') || (units.length && units[0]) || ''; var unit = model.get('unit') || (units.length && units[0]) || '';
var max = model.get('max'); var max = model.get('max');
var min = model.get('min'); var min = model.get('min');
var limitlessMax = !!model.get('limitlessMax');
var limitlessMin = !!model.get('limitlessMin');
if (opt.deepCheck) { if (opt.deepCheck) {
var fixed = model.get('fixedValues') || []; var fixed = model.get('fixedValues') || [];
@ -258,8 +276,10 @@ export default Input.extend({
} }
} }
if (!isUndefined(max) && max !== '') val = val > max ? max : val; if (!limitlessMax && !isUndefined(max) && max !== '')
if (!isUndefined(min) && min !== '') val = val < min ? min : val; val = val > max ? max : val;
if (!limitlessMax && !isUndefined(min) && min !== '')
val = val < min ? min : val;
return { return {
force, force,

25
src/editor/model/Editor.js

@ -750,29 +750,18 @@ export default Backbone.Model.extend({
const { config } = this; const { config } = this;
const editor = this.getEditor(); const editor = this.getEditor();
const { editors = [] } = config.grapesjs || {}; const { editors = [] } = config.grapesjs || {};
const {
DomComponents,
CssComposer,
UndoManager,
Panels,
Canvas,
Keymaps,
RichTextEditor,
LayerManager
} = this.attributes;
this.stopDefault(); this.stopDefault();
DomComponents.clear(); this.get('modules')
CssComposer.clear(); .slice()
UndoManager.clear().removeAll(); .reverse()
Panels.getPanels().reset(); .forEach(mod => mod.destroy());
Canvas.getCanvasView().remove();
Keymaps.removeAll();
RichTextEditor.destroy();
LayerManager.destroy();
this.view.remove(); this.view.remove();
this.stopListening(); this.stopListening();
this.clear({ silent: true }); this.clear({ silent: true });
this.destroyed = 1; this.destroyed = 1;
['config', 'view', '_previousAttributes', '_events', '_listeners'].forEach(
i => (this[i] = {})
);
editors.splice(editors.indexOf(editor), 1); editors.splice(editors.indexOf(editor), 1);
$(config.el) $(config.el)
.empty() .empty()

5
src/i18n/index.js

@ -242,6 +242,11 @@ export default () => {
_debug(str, opts = {}) { _debug(str, opts = {}) {
const { em, config } = this; const { em, config } = this;
(opts.debug || config.debug) && em && em.logWarning(str); (opts.debug || config.debug) && em && em.logWarning(str);
},
destroy() {
this.config = config;
this.em = {};
} }
}; };
}; };

198
src/i18n/locale/ca.js

@ -0,0 +1,198 @@
const traitInputAttr = { placeholder: 'ex. Text aquí' };
export default {
assetManager: {
addButton: 'Afegir imatge',
inputPlh: 'http://ruta/a/la/imatge.jpg',
modalTitle: 'Escollir imatge',
uploadTitle: 'Arrossega els fitxers aquí o fes clic per a pujar-ne'
},
// Here just as a reference, GrapesJS core doesn't contain any block,
// so this should be omitted from other local files
blockManager: {
labels: {
// 'block-id': 'Block Label',
},
categories: {
// 'category-id': 'Category Label',
}
},
domComponents: {
names: {
'': 'Capsa',
wrapper: 'Cos',
text: 'Text',
comment: 'Comentari',
image: 'Imatge',
video: 'Vídeo',
label: 'Etiqueta',
link: 'Enllaç',
map: 'Mapa',
tfoot: 'Peu de la taula',
tbody: 'Cos de la taula',
thead: 'Capçalera de la taula',
table: 'Taula',
row: 'Fila de la taula',
cell: 'Cel·la de la taula'
}
},
deviceManager: {
device: 'Dispositius',
devices: {
desktop: 'Escriptori',
tablet: 'Tauleta',
mobileLandscape: 'Mòbil en horitzontal',
mobilePortrait: 'Mòbil en vertical'
}
},
panels: {
buttons: {
titles: {
preview: 'Vista prèvia',
fullscreen: 'Pantalla sencera',
'sw-visibility': 'Veure components',
'export-template': 'Veure codi',
'open-sm': 'Obrir Administrador d\'estils',
'open-tm': 'Configuració',
'open-layers': 'Obrir Aministrador de capes',
'open-blocks': 'Obrir Blocs'
}
}
},
selectorManager: {
label: 'Classes',
selected: 'Seleccionat',
emptyState: '- Estat -',
states: {
hover: 'A sobre',
active: 'Clic',
'nth-of-type(2n)': 'Parell/Senar'
}
},
styleManager: {
empty: 'Escull un element abans d\'utilitzar l\'Administrador d\'estils',
layer: 'Capa',
fileButton: 'Imatges',
sectors: {
general: 'General',
layout: 'Disseny',
typography: 'Tipografia',
decorations: 'Decoracions',
extra: 'Extres',
flex: 'Flex',
dimension: 'Tamany'
},
// The core library generates the name by their `property` name
properties: {
float: 'Flotant',
display: 'Vista',
position: 'Posició',
top: 'Superior',
right: 'Dreta',
left: 'Esquerra',
bottom: 'Inferior',
width: 'Ample',
height: 'Alt',
'max-width': 'Ample màx.',
'max-height': 'Alt màx.',
margin: 'Marge',
'margin-top': 'Marge superior',
'margin-right': 'Marge dret',
'margin-left': 'Marge esquerra',
'margin-bottom': 'Marge inferior',
padding: 'Padding',
'padding-top': 'Padding superior',
'padding-left': 'Padding esquerra',
'padding-right': 'Padding dret',
'padding-bottom': 'Padding inferior',
'font-family': 'Tipus de lletra',
'font-size': 'Tamany de la font',
'font-weight': 'Pes',
'letter-spacing': 'Espai entre lletres',
color: 'Color',
'line-height': 'Interlineat',
'text-align': 'Alineació del text',
'text-shadow': 'Ombra del text',
'text-shadow-h': 'Ombra del text: horizontal',
'text-shadow-v': 'Ombra del text: vertical',
'text-shadow-blur': 'Desenfocament de l\'ombra del text',
'text-shadow-color': 'Color de l\'ombra del text',
'border-top-left': 'Marc superior esquerra',
'border-top-right': 'Marc superior dret',
'border-bottom-left': 'Marc inferior esquerra',
'border-bottom-right': 'Marc inferior dret',
'border-radius-top-left': 'Radi del marc superior esquerra',
'border-radius-top-right': 'Radi del marc superior dret',
'border-radius-bottom-left': 'Radi del marc inferior esquerra',
'border-radius-bottom-right': 'Radi del marc inferior dret',
'border-radius': 'Radi del marc',
border: 'Marc',
'border-width': 'Ample del marc',
'border-style': 'Estil del marc',
'border-color': 'Color del marc',
'box-shadow': 'Ombra de la capsa',
'box-shadow-h': 'Ombra de la capsa: horizontal',
'box-shadow-v': 'Ombra de la capsa: vertical',
'box-shadow-blur': 'Desenfocament de l\'ombra de la capsa',
'box-shadow-spread': 'Propagació de l\'ombra de la capsa',
'box-shadow-color': 'Color de l\'ombra de la capsa',
'box-shadow-type': 'Tipus de l\'ombra de la capsa',
background: 'Fons',
'background-image': 'Imatge de fons',
'background-repeat': 'Repetir fons',
'background-position': 'Posició del fons',
'background-attachment': 'Desplaçament del fons',
'background-size': 'Tamany del fons',
transition: 'Transició',
'transition-property': 'Tipus de transició',
'transition-duration': 'Temps de transició',
'transition-timing-function': 'Funció de temps de la transición',
perspective: 'Perspectiva',
transform: 'Transformació',
'transform-rotate-x': 'Rotació horitzontal',
'transform-rotate-y': 'Rotació vertical',
'transform-rotate-z': 'Rotació profunditat',
'transform-scale-x': 'Escalar horitzontalment',
'transform-scale-y': 'Escalar verticalment',
'transform-scale-z': 'Escalar profunditat',
'flex-direction': 'Direcció flex',
'flex-wrap': 'Distribució flex',
'justify-content': 'Justificar contingut',
'align-items': 'Alinear elements',
'align-content': 'Alinear contingut',
order: 'Ordre',
'flex-basis': 'Base flex',
'flex-grow': 'Creixement flex',
'flex-shrink': 'Contracció flex',
'align-self': 'Alineació pròpia',
'background-color': 'Color de fons'
}
},
traitManager: {
empty: 'Escull un element abans d\'usar l\'Administrador de característiques',
label: 'Configuració de components',
traits: {
// The core library generates the name by their `name` property
labels: {
id: 'Identificador',
alt: 'Títol alternatiu',
title: 'Títol',
href: 'Enllaç'
},
// In a simple trait, like text input, these are used on input attributes
attributes: {
id: traitInputAttr,
alt: traitInputAttr,
title: traitInputAttr,
href: { placeholder: 'ex. https://google.com' }
},
// In a trait like select, these are used to translate option names
options: {
target: {
false: 'Mateixa pestanya/finestra',
_blank: 'Nova pestanya/finestra'
}
}
}
}
};

8
src/keymaps/index.js

@ -200,7 +200,7 @@ export default () => {
if (keymap) { if (keymap) {
delete keymaps[id]; delete keymaps[id];
keymaster.unbind(keymap.keys); keymap.keys.split(', ').forEach(k => keymaster.unbind(k.trim()));
em && em.trigger('keymap:remove', keymap); em && em.trigger('keymap:remove', keymap);
return keymap; return keymap;
} }
@ -213,6 +213,12 @@ export default () => {
removeAll() { removeAll() {
Object.keys(keymaps).forEach(keymap => this.remove(keymap)); Object.keys(keymaps).forEach(keymap => this.remove(keymap));
return this; return this;
},
destroy() {
this.removeAll();
[em, config, keymaps].forEach(i => (i = {}));
this.em = {};
} }
}; };
}; };

6
src/modal_dialog/index.js

@ -202,6 +202,12 @@ export default () => {
*/ */
render() { render() {
return modal.render().$el; return modal.render().$el;
},
destroy() {
modal.remove();
[c, model, modal].forEach(i => (i = {}));
this.em = {};
} }
}; };
}; };

3
src/navigator/index.js

@ -99,7 +99,8 @@ export default () => {
}, },
destroy() { destroy() {
return layers && layers.remove(); layers && layers.remove();
[em, layers, config].forEach(i => (i = {}));
} }
}; };
}; };

7
src/navigator/view/ItemView.js

@ -15,6 +15,7 @@ export default Backbone.View.extend({
'click [data-toggle-visible]': 'toggleVisibility', 'click [data-toggle-visible]': 'toggleVisibility',
'click [data-toggle-select]': 'handleSelect', 'click [data-toggle-select]': 'handleSelect',
'mouseover [data-toggle-select]': 'handleHover', 'mouseover [data-toggle-select]': 'handleHover',
'mouseout [data-toggle-select]': 'handleHoverOut',
'click [data-toggle-open]': 'toggleOpening', 'click [data-toggle-open]': 'toggleOpening',
'dblclick [data-name]': 'handleEdit', 'dblclick [data-name]': 'handleEdit',
'focusout [data-name]': 'handleEditEnd' 'focusout [data-name]': 'handleEditEnd'
@ -249,6 +250,12 @@ export default Backbone.View.extend({
em && config.showHover && em.setHovered(model, { fromLayers: 1 }); em && config.showHover && em.setHovered(model, { fromLayers: 1 });
}, },
handleHoverOut(ev) {
ev.stopPropagation();
const { em, config } = this;
em && config.showHover && em.setHovered(0, { fromLayers: 1 });
},
/** /**
* Delegate to sorter * Delegate to sorter
* @param Event * @param Event

8
src/panels/index.js

@ -28,7 +28,6 @@
import defaults from './config/config'; import defaults from './config/config';
import Panel from './model/Panel'; import Panel from './model/Panel';
import Panels from './model/Panels'; import Panels from './model/Panels';
import PanelView from './view/PanelView';
import PanelsView from './view/PanelsView'; import PanelsView from './view/PanelsView';
export default () => { export default () => {
@ -233,6 +232,13 @@ export default () => {
}); });
}, },
destroy() {
panels.reset();
panels.stopListening();
PanelsViewObj.remove();
[c, panels, PanelsViewObj].forEach(i => (i = {}));
},
Panel Panel
}; };
}; };

20
src/panels/view/ButtonView.js

@ -89,6 +89,8 @@ export default Backbone.View.extend({
const commandName = model.get('command'); const commandName = model.get('command');
let command = {}; let command = {};
if (!commandName) return;
if (commands && isString(commandName)) { if (commands && isString(commandName)) {
command = commands.get(commandName) || {}; command = commands.get(commandName) || {};
} else if (isFunction(commandName)) { } else if (isFunction(commandName)) {
@ -133,15 +135,16 @@ export default Backbone.View.extend({
* @return void * @return void
* */ * */
clicked(e) { clicked(e) {
if (this.model.get('bntsVis')) return; const { model } = this;
if (this.model.get('disable')) return; if (model.get('bntsVis') || model.get('disable') || !model.get('command'))
return;
this.toggleActive(); this.toggleActive();
}, },
toggleActive() { toggleActive() {
const { model } = this; const { model, em } = this;
const { active, togglable } = model.attributes; const { active, togglable } = model.attributes;
if (active && !togglable) return; if (active && !togglable) return;
@ -149,19 +152,18 @@ export default Backbone.View.extend({
model.set('active', !active); model.set('active', !active);
// If the stop is requested // If the stop is requested
var command = this.em.get('Commands').get('select-comp');
if (active) { if (active) {
if (model.get('runDefaultCommand')) this.em.runDefault(); if (model.get('runDefaultCommand')) em.runDefault();
} else { } else {
if (model.get('stopDefaultCommand')) this.em.stopDefault(); if (model.get('stopDefaultCommand')) em.stopDefault();
} }
}, },
render() { render() {
const label = this.model.get('label'); const { model } = this;
const label = model.get('label');
const { $el } = this; const { $el } = this;
$el.empty(); !model.get('el') && $el.empty();
this.updateAttributes(); this.updateAttributes();
label && $el.append(label); label && $el.append(label);
this.checkActive(); this.checkActive();

11
src/panels/view/ButtonsView.js

@ -31,15 +31,16 @@ export default Backbone.View.extend({
* @return Object Object created * @return Object Object created
* */ * */
addToCollection(model, fragmentEl) { addToCollection(model, fragmentEl) {
var fragment = fragmentEl || null; const fragment = fragmentEl || null;
var viewObject = ButtonView; const viewObject = ButtonView;
const el = model.get('el');
var view = new viewObject({ const view = new viewObject({
el,
model, model,
config: this.config, config: this.config,
parentM: this.parentM parentM: this.parentM
}); });
var rendered = view.render().el; const rendered = view.render().el;
if (fragment) { if (fragment) {
fragment.appendChild(rendered); fragment.appendChild(rendered);

5
src/parser/index.js

@ -72,6 +72,11 @@ export default () => {
*/ */
parseCss(str) { parseCss(str) {
return pCss.parse(str); return pCss.parse(str);
},
destroy() {
[conf, pHtml, pCss].forEach(i => (i = {}));
['em', 'parserCss', 'parserHtml'].forEach(i => (this[i] = {}));
} }
}; };
}; };

2
src/parser/model/ParserHtml.js

@ -202,7 +202,7 @@ export default config => {
// just make it content of the current node // just make it content of the current node
if (nodeChild === 1 && firstChild.nodeType === 3) { if (nodeChild === 1 && firstChild.nodeType === 3) {
!model.type && (model.type = 'text'); !model.type && (model.type = 'text');
model.content = firstChild.nodeValue; model.components = firstChild.nodeValue;
} else { } else {
model.components = this.parseNode(node); model.components = this.parseNode(node);
} }

5
src/rich_text_editor/index.js

@ -87,10 +87,11 @@ export default () => {
const { customRte } = this; const { customRte } = this;
globalRte && globalRte.destroy(); globalRte && globalRte.destroy();
customRte && customRte.destroy && customRte.destroy(); customRte && customRte.destroy && customRte.destroy();
toolbar = 0;
globalRte = 0;
this.actionbar = 0; this.actionbar = 0;
this.actions = 0; this.actions = 0;
[config, toolbar, actions, lastEl, lastElPos, globalRte].forEach(
i => (i = {})
);
}, },
/** /**

9
src/selector_manager/index.js

@ -312,6 +312,15 @@ export default config => {
}); });
return this.selectorTags.render().el; return this.selectorTags.render().el;
} else return this.selectorTags.render().el; } else return this.selectorTags.render().el;
},
destroy() {
selectors.reset();
selectors.stopListening();
this.selectorTags.remove();
[c, selectors].forEach(i => (i = {}));
this.em = {};
this.selectorTags = {};
} }
}; };
}; };

35
src/selector_manager/model/Selector.js

@ -1,9 +1,11 @@
import Backbone from 'backbone'; import Backbone from 'backbone';
import { result, forEach, keys } from 'underscore';
const TYPE_CLASS = 1; const TYPE_CLASS = 1;
const TYPE_ID = 2; const TYPE_ID = 2;
const { Model } = Backbone;
const Selector = Backbone.Model.extend( const Selector = Model.extend(
{ {
idAttribute: 'name', idAttribute: 'name',
@ -43,6 +45,7 @@ const Selector = Backbone.Model.extend(
? escapeName(namePreEsc) ? escapeName(namePreEsc)
: Selector.escapeName(namePreEsc); : Selector.escapeName(namePreEsc);
this.set('name', nameEsc); this.set('name', nameEsc);
this.em = config.em;
}, },
/** /**
@ -64,6 +67,36 @@ const Selector = Backbone.Model.extend(
} }
return init + (escape ? escape(name) : name); return init + (escape ? escape(name) : name);
},
toJSON(opts = {}) {
const { em } = this;
let obj = Model.prototype.toJSON.call(this, [opts]);
const defaults = result(this, 'defaults');
if (em && em.getConfig('avoidDefaults')) {
forEach(defaults, (value, key) => {
if (obj[key] === value) {
delete obj[key];
}
});
if (obj.label === obj.name) {
delete obj.label;
}
const objLen = keys(obj).length;
if (objLen === 1 && obj.name) {
obj = obj.name;
}
if (objLen === 2 && obj.name && obj.type) {
obj = this.getFullName();
}
}
return obj;
} }
}, },
{ {

11
src/storage_manager/index.js

@ -36,14 +36,15 @@ import defaults from './config/config';
import LocalStorage from './model/LocalStorage'; import LocalStorage from './model/LocalStorage';
import RemoteStorage from './model/RemoteStorage'; import RemoteStorage from './model/RemoteStorage';
const eventStart = 'storage:start';
const eventEnd = 'storage:end';
const eventError = 'storage:error';
export default () => { export default () => {
var c = {}; var c = {};
let em; let em;
var storages = {}; var storages = {};
var defaultStorages = {}; var defaultStorages = {};
const eventStart = 'storage:start';
const eventEnd = 'storage:end';
const eventError = 'storage:error';
return { return {
/** /**
@ -331,6 +332,10 @@ export default () => {
canAutoload() { canAutoload() {
const storage = this.getCurrentStorage(); const storage = this.getCurrentStorage();
return storage && this.getConfig().autoload; return storage && this.getConfig().autoload;
},
destroy() {
[c, em, storages, defaultStorages].forEach(i => (i = {}));
} }
}; };
}; };

10
src/style_manager/index.js

@ -414,6 +414,16 @@ export default () => {
_logNoSector(sectorId) { _logNoSector(sectorId) {
const { em } = this; const { em } = this;
em && em.logWarning(`'${sectorId}' sector not found`); em && em.logWarning(`'${sectorId}' sector not found`);
},
destroy() {
[properties, sectors].forEach(coll => {
coll.reset();
coll.stopListening();
});
SectView.remove();
[c, properties, sectors, SectView].forEach(i => (i = {}));
this.em = {};
} }
}; };
}; };

11
src/style_manager/model/Sectors.js

@ -2,5 +2,14 @@ import Backbone from 'backbone';
import Sector from './Sector'; import Sector from './Sector';
export default Backbone.Collection.extend({ export default Backbone.Collection.extend({
model: Sector model: Sector,
initialize() {
this.listenTo(this, 'reset', this.onReset);
},
onReset(models, opts = {}) {
const prev = opts.previousModels || [];
prev.forEach(sect => sect.get('properties').reset());
}
}); });

6
src/style_manager/view/PropertyColorView.js

@ -7,6 +7,12 @@ export default PropertyIntegerView.extend({
this.inputInst.setValue(value, opts); this.inputInst.setValue(value, opts);
}, },
remove() {
PropertyIntegerView.prototype.remove.apply(this, arguments);
this.inputInst.remove();
['inputInst', '$color'].forEach(i => (this[i] = {}));
},
onRender() { onRender() {
if (!this.input) { if (!this.input) {
const ppfx = this.ppfx; const ppfx = this.ppfx;

6
src/style_manager/view/PropertyCompositeView.js

@ -27,6 +27,12 @@ export default PropertyView.extend({
PropertyView.prototype.clear.apply(this, arguments); PropertyView.prototype.clear.apply(this, arguments);
}, },
remove() {
this.model.get('properties').reset();
PropertyView.prototype.remove.apply(this, arguments);
['properties', 'props', '$props'].forEach(i => (this[i] = {}));
},
/** /**
* Renders input * Renders input
* */ * */

4
src/style_manager/view/PropertyStackView.js

@ -191,7 +191,7 @@ export default PropertyCompositeView.extend({
const layers = this.getLayers(); const layers = this.getLayers();
const detached = model.get('detached'); const detached = model.get('detached');
const property = model.get('property'); const property = model.get('property');
const target = this.getTarget(); const target = this.getFirstTarget();
const valueComput = this.getComputedValue(); const valueComput = this.getComputedValue();
const selected = em.getSelected(); const selected = em.getSelected();
const updateOpts = { fromTarget: 1 }; const updateOpts = { fromTarget: 1 };
@ -281,7 +281,7 @@ export default PropertyCompositeView.extend({
getTargetValue(opts = {}) { getTargetValue(opts = {}) {
const { model } = this; const { model } = this;
const { detached } = model.attributes; const { detached } = model.attributes;
const target = this.getTarget(); const target = this.getFirstTarget();
let result = PropertyCompositeView.prototype.getTargetValue.call( let result = PropertyCompositeView.prototype.getTargetValue.call(
this, this,
opts opts

15
src/style_manager/view/PropertyView.js

@ -95,6 +95,9 @@ export default Backbone.View.extend({
remove() { remove() {
Backbone.View.prototype.remove.apply(this, arguments); Backbone.View.prototype.remove.apply(this, arguments);
['em', 'target', 'input', '$input', 'propTarget', 'sector'].forEach(
i => (this[i] = {})
);
this.__destroyFn(this._getClbOpts()); this.__destroyFn(this._getClbOpts());
}, },
@ -167,6 +170,10 @@ export default Backbone.View.extend({
return targets || [this.getTarget()]; return targets || [this.getTarget()];
}, },
getFirstTarget() {
return this.getTargets()[0];
},
/** /**
* Returns Styleable model * Returns Styleable model
* @return {Model|null} * @return {Model|null}
@ -209,7 +216,7 @@ export default Backbone.View.extend({
emitUpdateTarget: debounce(function() { emitUpdateTarget: debounce(function() {
const em = this.config.em; const em = this.config.em;
em && em.trigger('styleManager:update:target', this.getTarget()); em && em.trigger('styleManager:update:target', this.getFirstTarget());
}), }),
_getTargetData() { _getTargetData() {
@ -342,7 +349,7 @@ export default Backbone.View.extend({
getTargetValue(opts = {}) { getTargetValue(opts = {}) {
let result; let result;
const { model } = this; const { model } = this;
const target = this.getTargetModel(); const target = this.getFirstTarget();
const customFetchValue = this.customValue; const customFetchValue = this.customValue;
if (!target) { if (!target) {
@ -460,7 +467,7 @@ export default Backbone.View.extend({
*/ */
updateTargetStyle(value, name = '', opts = {}) { updateTargetStyle(value, name = '', opts = {}) {
const property = name || this.model.get('property'); const property = name || this.model.get('property');
const target = opts.target || this.getTarget(); const target = opts.target || this.getFirstTarget();
const style = target.getStyle(); const style = target.getStyle();
if (value) { if (value) {
@ -489,7 +496,7 @@ export default Backbone.View.extend({
* @return {Boolean} * @return {Boolean}
*/ */
isTargetStylable(target) { isTargetStylable(target) {
const trg = target || this.getTarget(); const trg = target || this.getFirstTarget();
const model = this.model; const model = this.model;
const id = model.get('id'); const id = model.get('id');
const property = model.get('property'); const property = model.get('property');

12
src/style_manager/view/SectorsView.js

@ -31,6 +31,11 @@ export default Backbone.View.extend({
this.listenTo(this.target, events, this.targetUpdated); this.listenTo(this.target, events, this.targetUpdated);
}, },
remove() {
Backbone.View.prototype.remove.apply(this, arguments);
['target', 'config', 'propTarget'].forEach(i => (this[i] = {}));
},
/** /**
* Add to collection * Add to collection
* @param {Object} model Model * @param {Object} model Model
@ -95,7 +100,8 @@ export default Backbone.View.extend({
pt.helper = helperRule; pt.helper = helperRule;
}; };
model = em.get('StyleManager').getModelToStyle(model); const sm = em.get('StyleManager');
model = sm.getModelToStyle(model);
if (state) { if (state) {
appendStateRule(model.getStyle()); appendStateRule(model.getStyle());
@ -103,7 +109,9 @@ export default Backbone.View.extend({
} }
pt.model = model; pt.model = model;
if (componentFirst) pt.targets = targets; if (componentFirst) {
pt.targets = targets.map(t => sm.getModelToStyle(t)).filter(Boolean);
}
pt.trigger('update'); pt.trigger('update');
}, },

10
src/styles/scss/_gjs_inputs.scss

@ -68,6 +68,16 @@
} }
} }
input[type=number] {
-moz-appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
&-range { &-range {
flex: 9 1 auto; flex: 9 1 auto;
} }

6
src/styles/scss/main.scss

@ -1,7 +1,7 @@
/* stylelint-disable */ /* stylelint-disable */
@import "../../../node_modules/spectrum-colorpicker/spectrum"; @import "spectrum-colorpicker/spectrum";
@import "../../../node_modules/codemirror/lib/codemirror"; @import "codemirror/lib/codemirror";
@import "../../../node_modules/codemirror/theme/hopscotch"; @import "codemirror/theme/hopscotch";
@import "gjs_variables.scss"; @import "gjs_variables.scss";

5
src/trait_manager/index.js

@ -81,6 +81,11 @@ export default () => {
render() { render() {
return TraitsViewer.render().el; return TraitsViewer.render().el;
},
destroy() {
TraitsViewer.remove();
[c, TraitsViewer].forEach(i => (i = {}));
} }
}; };
}; };

14
src/trait_manager/view/TraitNumberView.js

@ -18,16 +18,18 @@ export default TraitView.extend({
*/ */
getInputEl() { getInputEl() {
if (!this.input) { if (!this.input) {
var value = this.getModelValue(); const { ppfx, model } = this;
var inputNumber = new InputNumber({ const value = this.getModelValue();
contClass: this.ppfx + 'field-int', const inputNumber = new InputNumber({
model: this.model, contClass: `${ppfx}field-int`,
ppfx: this.ppfx type: 'number',
model: model,
ppfx
}); });
this.input = inputNumber.render(); this.input = inputNumber.render();
this.$input = this.input.inputEl; this.$input = this.input.inputEl;
this.$unit = this.input.unitEl; this.$unit = this.input.unitEl;
this.model.set('value', value, { fromTarget: 1 }); model.set('value', value, { fromTarget: 1 });
this.$input.val(value); this.$input.val(value);
this.input = inputNumber.el; this.input = inputNumber.el;
} }

8
src/undo_manager/index.js

@ -81,7 +81,7 @@ export default () => {
const result = { const result = {
object, object,
before: beforeCache, before: beforeCache,
after: object.toJSON() after: object.toJSON({ keepSymbols: 1 })
}; };
beforeCache = null; beforeCache = null;
return result; return result;
@ -293,6 +293,12 @@ export default () => {
getInstance() { getInstance() {
return um; return um;
},
destroy() {
this.clear().removeAll();
[em, um, config, beforeCache].forEach(i => (i = {}));
this.em = {};
} }
}; };
}; };

2
src/utils/index.js

@ -18,6 +18,8 @@ export default () => {
return this; return this;
}, },
destroy() {},
Sorter, Sorter,
Resizer, Resizer,
Dragger Dragger

2
src/utils/mixins.js

@ -212,6 +212,7 @@ const getKeyChar = ev => String.fromCharCode(getKeyCode(ev));
const isEscKey = ev => getKeyCode(ev) === 27; const isEscKey = ev => getKeyCode(ev) === 27;
const isObject = val => const isObject = val =>
val !== null && !Array.isArray(val) && typeof val === 'object'; val !== null && !Array.isArray(val) && typeof val === 'object';
const isEmptyObj = val => Object.keys(val).length <= 0;
const capitalize = str => str && str.charAt(0).toUpperCase() + str.substring(1); const capitalize = str => str && str.charAt(0).toUpperCase() + str.substring(1);
const isComponent = obj => obj && obj.toHTML; const isComponent = obj => obj && obj.toHTML;
@ -245,6 +246,7 @@ export {
setViewEl, setViewEl,
appendStyles, appendStyles,
isObject, isObject,
isEmptyObj,
isComponent, isComponent,
isRule isRule
}; };

11
test/specs/css_composer/e2e/CssComposer.js

@ -128,16 +128,7 @@ describe('E2E tests', () => {
// ruleOut is a Model // ruleOut is a Model
ruleOut = JSON.parse(JSON.stringify(ruleOut)); ruleOut = JSON.parse(JSON.stringify(ruleOut));
var ruleResult = { var ruleResult = {
selectors: [ selectors: ['test1'],
{
active: true,
label: 'test1',
name: 'test1',
type: clsm.Selector.TYPE_CLASS,
private: false,
protected: false
}
],
style: { style: {
color: 'red', color: 'red',
width: '10px' width: '10px'

6
test/specs/dom_components/model/Component.js

@ -274,9 +274,9 @@ describe('Component', () => {
}); });
test('setAttributes overwrites correctly', () => { test('setAttributes overwrites correctly', () => {
obj.setAttributes({ id: 'test', 'data-test': 'value', a: 'b' }); obj.setAttributes({ id: 'test', 'data-test': 'value', a: 'b', b: 'c' });
obj.setAttributes({ 'data-test': 'value2' }); obj.setAttributes({ id: 'test2', 'data-test': 'value2' });
expect(obj.getAttributes()).toEqual({ 'data-test': 'value2' }); expect(obj.getAttributes()).toEqual({ id: 'test2', 'data-test': 'value2' });
}); });
test('append() returns always an array', () => { test('append() returns always an array', () => {

17
test/specs/editor/index.js

@ -4,9 +4,10 @@ const { keys } = Object;
const initComps = 1; const initComps = 1;
describe('Editor', () => { describe('Editor', () => {
const editor = new Editor(); let editor;
beforeEach(() => { beforeEach(() => {
editor = new Editor();
editor.init(); editor.init();
}); });
@ -47,8 +48,8 @@ describe('Editor', () => {
test('Components are correctly tracked on add', () => { test('Components are correctly tracked on add', () => {
const all = editor.Components.allById(); const all = editor.Components.allById();
const wrapper = editor.getWrapper(); const wrapper = editor.getWrapper();
wrapper.append('<div>Component</div>'); wrapper.append('<div>Component</div>'); // Div component + textnode
expect(keys(all).length).toBe(1 + initComps); expect(keys(all).length).toBe(2 + initComps);
}); });
test('Components are correctly tracked on add and remove', () => { test('Components are correctly tracked on add and remove', () => {
@ -58,13 +59,13 @@ describe('Editor', () => {
<div>Component 1</div> <div>Component 1</div>
<div></div> <div></div>
`); `);
expect(keys(all).length).toBe(2 + initComps); expect(keys(all).length).toBe(3 + initComps);
const secComp = added[1]; const secComp = added[1];
secComp.append(` secComp.append(`
<div>Component 2</div> <div>Component 2</div>
<div>Component 3</div> <div>Component 3</div>
`); `);
expect(keys(all).length).toBe(4 + initComps); expect(keys(all).length).toBe(7 + initComps);
wrapper.empty(); wrapper.empty();
expect(wrapper.components().length).toBe(0); expect(wrapper.components().length).toBe(0);
expect(keys(all).length).toBe(initComps); expect(keys(all).length).toBe(initComps);
@ -83,7 +84,7 @@ describe('Editor', () => {
expect(umStack.length).toBe(2); expect(umStack.length).toBe(2);
expect(keys(all).length).toBe(initComps); expect(keys(all).length).toBe(initComps);
um.undo(false); um.undo(false);
expect(keys(all).length).toBe(1 + initComps); expect(keys(all).length).toBe(2 + initComps);
}); });
test('Components are correctly tracked with UndoManager and mutiple operations', () => { test('Components are correctly tracked with UndoManager and mutiple operations', () => {
@ -98,7 +99,7 @@ describe('Editor', () => {
<div>Component 2</div> <div>Component 2</div>
</div>`); </div>`);
expect(umStack.length).toBe(1); // UM counts first children expect(umStack.length).toBe(1); // UM counts first children
expect(keys(all).length).toBe(3 + initComps); expect(keys(all).length).toBe(5 + initComps);
wrapper wrapper
.components() .components()
.at(0) .at(0)
@ -108,7 +109,7 @@ describe('Editor', () => {
// UM registers 2 identical remove undoTypes as Backbone triggers remove from the // UM registers 2 identical remove undoTypes as Backbone triggers remove from the
// collection and the model // collection and the model
expect(umStack.length).toBe(3); expect(umStack.length).toBe(3);
expect(keys(all).length).toBe(2 + initComps); expect(keys(all).length).toBe(3 + initComps);
wrapper.empty(); wrapper.empty();
expect(umStack.length).toBe(4); expect(umStack.length).toBe(4);
expect(keys(all).length).toBe(initComps); expect(keys(all).length).toBe(initComps);

2
test/specs/grapesjs/index.js

@ -85,7 +85,7 @@ describe('GrapesJS', () => {
expect(editor.getStyle().length).toEqual(0); expect(editor.getStyle().length).toEqual(0);
}); });
test.only('Editor canvas baseCSS can be overwritten', () => { test('Editor canvas baseCSS can be overwritten', () => {
config.components = htmlString; config.components = htmlString;
config.baseCss = '#wrapper { background-color: #eee; }'; config.baseCss = '#wrapper { background-color: #eee; }';
config.protectedCss = ''; config.protectedCss = '';

2
test/specs/panels/view/ButtonView.js

@ -8,7 +8,7 @@ describe('ButtonView', () => {
var btnClass = 'btn'; var btnClass = 'btn';
beforeEach(() => { beforeEach(() => {
model = new Button(); model = new Button({ command: 'fake-command' });
view = new ButtonView({ view = new ButtonView({
model: model model: model
}); });

32
test/specs/parser/model/ParserHtml.js

@ -130,7 +130,7 @@ describe('ParserHtml', () => {
tagName: 'div', tagName: 'div',
attributes: { id: 'test1' }, attributes: { id: 'test1' },
type: 'text', type: 'text',
content: 'test2 ' components: 'test2 '
} }
]; ];
expect(obj.parse(str).html).toEqual(result); expect(obj.parse(str).html).toEqual(result);
@ -158,7 +158,7 @@ describe('ParserHtml', () => {
tagName: '' tagName: ''
}, },
{ {
content: 'b', components: 'b',
type: 'text', type: 'text',
tagName: 'b' tagName: 'b'
}, },
@ -168,7 +168,7 @@ describe('ParserHtml', () => {
tagName: '' tagName: ''
}, },
{ {
content: 'i', components: 'i',
tagName: 'i', tagName: 'i',
type: 'text' type: 'text'
}, },
@ -178,7 +178,7 @@ describe('ParserHtml', () => {
tagName: '' tagName: ''
}, },
{ {
content: 'u', components: 'u',
tagName: 'u', tagName: 'u',
type: 'text' type: 'text'
}, },
@ -208,7 +208,7 @@ describe('ParserHtml', () => {
tagName: '' tagName: ''
}, },
{ {
content: 'b', components: 'b',
tagName: 'b', tagName: 'b',
type: 'text' type: 'text'
}, },
@ -218,7 +218,7 @@ describe('ParserHtml', () => {
tagName: '' tagName: ''
}, },
{ {
content: 'i', components: 'i',
tagName: 'i', tagName: 'i',
type: 'text' type: 'text'
}, },
@ -230,7 +230,7 @@ describe('ParserHtml', () => {
{ {
tagName: 'div', tagName: 'div',
type: 'text', type: 'text',
content: 'ABC' components: 'ABC'
}, },
{ {
content: ' ', content: ' ',
@ -238,7 +238,7 @@ describe('ParserHtml', () => {
tagName: '' tagName: ''
}, },
{ {
content: 'i', components: 'i',
tagName: 'i', tagName: 'i',
type: 'text' type: 'text'
}, },
@ -248,7 +248,7 @@ describe('ParserHtml', () => {
tagName: '' tagName: ''
}, },
{ {
content: 'u', components: 'u',
tagName: 'u', tagName: 'u',
type: 'text' type: 'text'
}, },
@ -313,7 +313,7 @@ describe('ParserHtml', () => {
{ {
tagName: 'div', tagName: 'div',
type: 'text', type: 'text',
content: 'nested' components: 'nested'
}, },
{ {
tagName: '', tagName: '',
@ -343,7 +343,7 @@ describe('ParserHtml', () => {
{ {
tagName: 'span', tagName: 'span',
type: 'text', type: 'text',
content: 'nested' components: 'nested'
} }
] ]
}, },
@ -448,7 +448,7 @@ describe('ParserHtml', () => {
}, },
{ {
tagName: 'p', tagName: 'p',
content: 'TestText', components: 'TestText',
type: 'text' type: 'text'
}, },
{ {
@ -475,7 +475,7 @@ describe('ParserHtml', () => {
'data-test': 'test-value' 'data-test': 'test-value'
}, },
type: 'text', type: 'text',
content: 'test2 ' components: 'test2 '
} }
]; ];
expect(obj.parse(str).html).toEqual(result); expect(obj.parse(str).html).toEqual(result);
@ -494,7 +494,7 @@ describe('ParserHtml', () => {
'data-test': 'test-value' 'data-test': 'test-value'
}, },
type: 'text', type: 'text',
content: 'test2 ' components: 'test2 '
} }
]; ];
expect(obj.parse(str).html).toEqual(result); expect(obj.parse(str).html).toEqual(result);
@ -512,7 +512,7 @@ describe('ParserHtml', () => {
prop2: 10, prop2: 10,
prop3: true prop3: true
}, },
content: 'test2 ' components: 'test2 '
} }
]; ];
expect(obj.parse(str).html).toEqual(result); expect(obj.parse(str).html).toEqual(result);
@ -526,7 +526,7 @@ describe('ParserHtml', () => {
attributes: {}, attributes: {},
type: 'text', type: 'text',
test: ['value1', 'value2'], test: ['value1', 'value2'],
content: 'test2 ' components: 'test2 '
} }
]; ];
expect(obj.parse(str).html).toEqual(result); expect(obj.parse(str).html).toEqual(result);

38
test/specs/selector_manager/e2e/ClassManager.js

@ -1,3 +1,4 @@
import Selector from 'selector_manager/model/Selector';
import Selectors from 'selector_manager/model/Selectors'; import Selectors from 'selector_manager/model/Selectors';
import ClassTagsView from 'selector_manager/view/ClassTagsView'; import ClassTagsView from 'selector_manager/view/ClassTagsView';
@ -117,5 +118,42 @@ describe('E2E tests', () => {
tagEl.addNewTag('test2'); tagEl.addNewTag('test2');
expect(spy.called).toEqual(true); expect(spy.called).toEqual(true);
}); });
test('Selectors are properly transformed to JSON', () => {
const model = components.add({
classes: [
'test1',
'.test1a',
'#test2',
{ name: 'test3', label: 'test3' },
{ name: 'test4', label: 'test4a' },
{ name: 'test5' },
{ name: 'test6', type: Selector.TYPE_CLASS },
{ name: 'test7', type: Selector.TYPE_ID },
{ name: 'test8', type: Selector.TYPE_CLASS, protected: 1 },
{ name: 'test9', type: Selector.TYPE_ID, protected: 1 },
{ label: 'test10' },
{ label: 'test11', type: Selector.TYPE_ID },
{ label: 'test12', protected: 1 }
]
});
const modelTr = JSON.parse(JSON.stringify(model));
expect(modelTr.classes).toEqual([
'test1',
'test1a',
'#test2',
'test3',
{ name: 'test4', label: 'test4a' },
'test5',
'test6',
'#test7',
{ name: 'test8', protected: 1 },
{ name: 'test9', type: Selector.TYPE_ID, protected: 1 },
'test10',
'#test11',
{ name: 'test12', protected: 1 }
]);
});
}); });
}); });

Loading…
Cancel
Save