Browse Source

Merge from dev

pull/311/head
Artur Arseniev 10 years ago
parent
commit
04bf37ae2f
  1. 4
      .gitignore
  2. 2
      .travis.yml
  3. 46
      Gruntfile.js
  4. 113
      README.md
  5. 2
      bower.json
  6. 4
      dist/css/grapes.min.css
  7. BIN
      dist/fonts/main-fonts.eot
  8. 246
      dist/fonts/main-fonts.svg
  9. BIN
      dist/fonts/main-fonts.ttf
  10. BIN
      dist/fonts/main-fonts.woff
  11. 30
      dist/grapes.min.js
  12. 790
      index.html
  13. 17
      package.json
  14. 45
      src/asset_manager/config/config.js
  15. 252
      src/asset_manager/main.js
  16. 41
      src/asset_manager/model/Asset.js
  17. 25
      src/asset_manager/model/AssetImage.js
  18. 74
      src/asset_manager/model/Assets.js
  19. 5
      src/asset_manager/template/assetImage.html
  20. 15
      src/asset_manager/template/assets.html
  21. 19
      src/asset_manager/view/AssetImageView.js
  22. 1
      src/asset_manager/view/AssetView.js
  23. 114
      src/asset_manager/view/AssetsView.js
  24. 14
      src/asset_manager/view/FileUploader.js
  25. 7
      src/block_manager/config/config.js
  26. 122
      src/block_manager/main.js
  27. 13
      src/block_manager/model/Block.js
  28. 9
      src/block_manager/model/Blocks.js
  29. 48
      src/block_manager/view/BlockView.js
  30. 140
      src/block_manager/view/BlocksView.js
  31. 10
      src/canvas/config/config.js
  32. 213
      src/canvas/main.js
  33. 22
      src/canvas/model/Canvas.js
  34. 14
      src/canvas/model/Frame.js
  35. 156
      src/canvas/view/CanvasView.js
  36. 54
      src/canvas/view/FrameView.js
  37. 63
      src/class_manager/main.js
  38. 29
      src/class_manager/model/ClassTag.js
  39. 11
      src/class_manager/model/ClassTags.js
  40. 3
      src/class_manager/template/classTag.html
  41. 359
      src/code_manager/main.js
  42. 6
      src/code_manager/model/CodeMirrorEditor.js
  43. 100
      src/code_manager/model/CssGenerator.js
  44. 31
      src/code_manager/model/EditorInterface.js
  45. 26
      src/code_manager/model/GeneratorInterface.js
  46. 9
      src/code_manager/model/HtmlGenerator.js
  47. 6
      src/code_manager/model/JsonGenerator.js
  48. 222
      src/commands/main.js
  49. 94
      src/commands/view/CommandAbstract.js
  50. 307
      src/commands/view/CreateComponent.js
  51. 5
      src/commands/view/DeleteComponent.js
  52. 34
      src/commands/view/ExportTemplate.js
  53. 86
      src/commands/view/Fullscreen.js
  54. 17
      src/commands/view/ImageComponent.js
  55. 68
      src/commands/view/InsertCustom.js
  56. 224
      src/commands/view/MoveComponent.js
  57. 28
      src/commands/view/OpenBlocks.js
  58. 19
      src/commands/view/OpenLayers.js
  59. 51
      src/commands/view/OpenStyleManager.js
  60. 32
      src/commands/view/OpenTraitManager.js
  61. 54
      src/commands/view/Preview.js
  62. 59
      src/commands/view/ResizeComponent.js
  63. 343
      src/commands/view/SelectComponent.js
  64. 420
      src/commands/view/SelectPosition.js
  65. 18
      src/commands/view/SwitchVisibility.js
  66. 15
      src/commands/view/TextComponent.js
  67. 15
      src/config/require-config.js
  68. 2
      src/css_composer/config/config.js
  69. 302
      src/css_composer/main.js
  70. 19
      src/css_composer/model/CssRule.js
  71. 13
      src/css_composer/model/CssRules.js
  72. 6
      src/css_composer/model/Selectors.js
  73. 35
      src/css_composer/view/CssRuleView.js
  74. 6
      src/css_composer/view/CssRulesView.js
  75. 1072
      src/demo.js
  76. 9
      src/device_manager/config/config.js
  77. 115
      src/device_manager/main.js
  78. 14
      src/device_manager/model/Device.js
  79. 9
      src/device_manager/model/Devices.js
  80. 10
      src/device_manager/template/devices.html
  81. 82
      src/device_manager/view/DevicesView.js
  82. 11
      src/dom_components/config/config.js
  83. 311
      src/dom_components/main.js
  84. 90
      src/dom_components/model/Component.js
  85. 17
      src/dom_components/model/ComponentImage.js
  86. 13
      src/dom_components/model/ComponentLink.js
  87. 12
      src/dom_components/model/ComponentText.js
  88. 47
      src/dom_components/model/Components.js
  89. 41
      src/dom_components/view/ComponentImageView.js
  90. 15
      src/dom_components/view/ComponentLinkView.js
  91. 65
      src/dom_components/view/ComponentTextView.js
  92. 88
      src/dom_components/view/ComponentView.js
  93. 41
      src/dom_components/view/ComponentsView.js
  94. 68
      src/domain_abstract/view/DomainViews.js
  95. 181
      src/editor/config/config.js
  96. 415
      src/editor/main.js
  97. 613
      src/editor/model/Editor.js
  98. 38
      src/editor/view/EditorView.js
  99. 37
      src/grapesjs/config/config.js
  100. 79
      src/grapesjs/main.js

4
.gitignore

@ -2,13 +2,13 @@
.settings/
.sass-cache/
.project
.eslintrc
npm-debug.log
style/.sass-cache/
grapes.sublime-project
grapes.sublime-workspace
img/
private/
docs/
vendor/
coverage/
node_modules/

2
.travis.yml

@ -1,5 +1,5 @@
language: node_js
node_js:
- "4.0"
- "5.6"
before_script:
- npm run build

46
Gruntfile.js

@ -48,7 +48,7 @@ module.exports = function(grunt) {
"else "+
"root.<%= pkg.name %> = root.GrapesJS = factory();"+
"}(this, function () {",
end: "return require('editor/main'); }));"
end: "return require('grapesjs/main'); }));"
},
paths: {
@ -197,12 +197,56 @@ module.exports = function(grunt) {
grunt.file.copy(appPath + '/' + configPath, buildPath + '/' + appPath + '/' + configPath);
});
grunt.registerTask('webfont-custom', function() {
var dir = './styles/fonts/';
var destName = 'main-fonts';
var donePromise = this.async();
var svg2ttf = {
cmd: 'svg2ttf',
args: [dir + destName + '.svg', dir + destName + '.ttf'],
};
var ttf2woff = {
cmd: 'ttf2woff',
args: [dir + destName + '.ttf', dir + destName + '.woff'],
};
var ttf2woff2 = {
cmd: 'cat',
//args: [dir + destName + '.ttf', dir + destName + '.woff2'],
args: [dir + destName + '.ttf', '|', 'ttf2woff2', '>>', dir + destName + '.woff2'],
};
var ttf2eot = {
cmd: 'ttf2eot',
args: [dir + destName + '.ttf', dir + destName + '.eot'],
};
grunt.util.spawn(svg2ttf, function done(error, result, code) {
grunt.log.ok('.ttf file created');
grunt.util.spawn(ttf2woff, function done(error, result, code) {
grunt.log.ok('.woff file created');
grunt.util.spawn(ttf2eot, function done(error, result, code) {
grunt.log.ok('.eot file created');
donePromise();
/*
grunt.util.spawn(ttf2woff2, function done(error, result, code) {
grunt.log.ok('.woff2 file created');
donePromise();
});
*/
});
});
});
});
grunt.registerTask('bower', ['bowercopy']);
grunt.registerTask('dev', ['bowercopy', 'connect', 'watch']);
grunt.registerTask('test', ['jshint', 'mocha']);
grunt.registerTask('build:fonts', ['webfont-custom']);
grunt.registerTask('build', ['bowercopy', 'jshint', 'sass', 'before-rjs', 'requirejs', 'uglify', 'cssmin', 'concat', 'clean', 'copy']);
grunt.registerTask('default', ['dev']);

113
README.md

@ -3,15 +3,16 @@
[![Build Status](https://travis-ci.org/artf/grapesjs.svg?branch=master)](https://travis-ci.org/artf/grapesjs)
<p align="center"><img src="http://grapesjs.com/img/grapesjs-demo-template2.jpg" alt="GrapesJS" width="500" align="center"/></p>
<br/>
GrapesJS is a free and open source Web Template Editor for building HTML templates to be used inside sites, webapps, newsletters or anything else related with HTML.
Mainly GrapesJS was designed to be used inside a [CMS] to speed up creation of dynamic templates. To better understand this concept check the image below
GrapesJS is a free and open source Web Template Builder which helps you building HTML templates to be used inside sites, newsletters and mobile apps.
Mainly GrapesJS was designed to be used inside a [CMS] to speed up a creation of dynamic templates. To better understand this concept check the image below
<br/>
<p align="center"><img src="http://grapesjs.com/img/gjs-concept.png" alt="GrapesJS - Style Manager" height="400" align="center"/></p>
<br/>
Generally any 'template system', that you can find in various applications like CMS, is composed by the **structure** (HTML), **style** (CSS) and **variables**, which are then replaced with other templates and contents on server-side and rendered soon on client.
Generally any 'template system', that you'd find in various applications like CMS, is composed by the **structure** (HTML), **style** (CSS) and **variables**, which are then replaced with other templates and contents on server-side and rendered on client.
This demo shows an example of what is possible to achieve: http://grapesjs.com/demo.html
@ -67,7 +68,7 @@ $ npm run build
Launch server, which also gonna watch some files, and try out the demo on `localhost:8000`
```sh
$ npm run dev
$ npm start
```
Tests are already available inside browser on `localhost:8000/test`
@ -77,102 +78,68 @@ If [Grunt](http://gruntjs.com/) is already installed globally you could change t
## Usage
JQuery is the only hard dependency so you have to include it before use GrapesJS.
JQuery is the only hard dependency so you have to include it before using GrapesJS
```html
<script src="http://code.jquery.com/jquery-2.2.0.min.js"></script>
```
After that include scripts from GrapesJS with all your configurations and render it.
After that include scripts from GrapesJS with all your configurations
```html
<link rel="stylesheet" href="path/to/grapes.min.css">
<script src="path/to/grapes.min.js"></script>
<div id="gjs"></div>
<script type="text/javascript">
var gjs = new GrapesJS({
var editor = grapesjs.init({
container : '#gjs',
components: '<div class="txt-red">Hello world!</div>',
style: '.txt-red{color: red}',
});
gjs.render();
</script>
```
Unfortunately with the configuration above you wouldn't see a lot. This because GrapesJS it self is simply empty, adding panels, buttons and other stuff will be your job (actually it's not empty but you need buttons to show them up).
The section below will explain some basic configurations but for a more practical example I suggest to look up the code inside this demo: http://grapesjs.com/demo.html
Documentation is under construction here: [wiki]
## Configuration
For now I only show up some general settings, for more details check source or demo. Examples will be available soon
```js
var config = {
// Prefix to use inside local storage name
storagePrefix: 'wte-',
// Where to render editor (eg. #myId)
container: '',
// Enable/Disable the possibility to copy (ctrl + c) and paste (ctrl + v) elements
copyPaste: true,
// Enable/Disable undo manager
undoManager: true,
//Indicates which storage to use. Available: local | remote | none
storageType: 'local',
//Configurations for Asset Manager (check src/asset_manager/config/config.js)
assetManager: {},
You could also grab the content directly from the element with `fromElement` property
//Configurations for Style Manager (check src/style_manager/config/config.js)
styleManager: {},
//Configurations for Layers (check src/navigator/config/config.js)
layers: {},
//Configurations for Storage Manager (check src/storage_manager/config/config.js)
storageManager: {},
```html
<div id="gjs">
<div class="txt-red">Hello world!</div>
<style>.txt-red{color: red}</style>
</div>
//Configurations for Rich Text Editor (check src/rich_text_editor/config/config.js)
rte: {},
<script type="text/javascript">
var editor = grapesjs.init({
container : '#gjs',
fromElement: true,
});
</script>
```
//Configurations for Components (check src/dom_components/config/config.js)
components: {},
For more practical example I suggest to look up the code inside this demo: http://grapesjs.com/demo.html
//Configurations for Panels (check src/panels/config/config.js)
panels: {},
//Configurations for Commands (check src/commands/config/config.js)
commands: {},
## Configuration
};
```
Check the getting started guide here: [wiki]
## API
At the moment `render()` is the only available method but others will be public very soon...
API References (draft) could be found here: [API-Reference]
## Testing
**ATTENTION: tests are pretty far away from being complete**
Tests are run by [PhantomJS](http://phantomjs.org/) using [Mocha](https://mochajs.org/) (with [Chai](http://chaijs.com/) and [Sinon](http://sinonjs.org/) help)
```sh
$ npm run test
$ npm test
```
## Todos before beta release
## TODOs before beta release
* **Class Manager** (*in development*) - Ability to assign different classes to components and style them (because CSS with only ids is pretty much a pain)
* **Breakpoint Manager** - Resize canvas according to breakpoints established by user (in simple terms, for responsive templates). Will be put into development immediately after Class Manager
* **Style Manager improvements** - Mainly `stack` type is not yet complete
* **Component traits** - traits are some some kind of `attributes` of the component. For example, the `Link`
will get `href` for the url/resource address and it's exactly as we see `<a>`, but in case of `Map` component
I'd attach `address`, `city` and `zoom` to manage marker position.
## Acknowledgements
@ -189,16 +156,11 @@ GrapesJS is built on top of this amazing open source projects:
## Support
A star/fork is already a huge motivational support and I'd like to thank all of you for that, but if you want to contribute the project economically and you have this possibility you could use the link below :heart:
If you like the project support it with a donation of your choice.
[![PayPalMe](http://grapesjs.com/img/ppme.png)](https://paypal.me/grapesjs)
## Contributing
Any kind of help is welcome. At the moment there is no generic guidelines so use usual pull requests and push to `dev` branch
## License
BSD 3-clause
@ -211,4 +173,5 @@ BSD 3-clause
[Spectrum]: <https://github.com/bgrins/spectrum>
[FontAwesome]: <https://fortawesome.github.io/Font-Awesome/>
[wiki]: <https://github.com/artf/grapesjs/wiki>
[CMS]: <https://it.wikipedia.org/wiki/Content_management_system>
[API-Reference]: <https://github.com/artf/grapesjs/wiki/API-Reference>
[CMS]: <https://it.wikipedia.org/wiki/Content_management_system>

2
bower.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Open source Web Template Editor",
"version": "0.1.7",
"version": "0.3.20",
"author": "Artur Arseniev",
"homepage": "http://grapesjs.com",
"main": [

4
dist/css/grapes.min.css

File diff suppressed because one or more lines are too long

BIN
dist/fonts/main-fonts.eot

Binary file not shown.

246
dist/fonts/main-fonts.svg

@ -0,0 +1,246 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000"
height="1000"
viewBox="0 0 999.99999 999.99999"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="main-fonts.svg">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 500 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="999.99999 : 500 : 1"
inkscape:persp3d-origin="500 : 333.33333 : 1"
id="perspective4924" />
<font
horiz-adv-x="1024"
id="font3336"
inkscape:label="font 1"
horiz-origin-x="0"
horiz-origin-y="0"
vert-origin-x="45"
vert-origin-y="90"
vert-adv-y="90">
<font-face
units-per-em="1024"
id="font-face3338"
font-family="SVGFont 1" />
<missing-glyph
d="M0,0h1000v1024h-1000z"
id="missing-glyph3340" />
<glyph
glyph-name="b100"
id="glyph3342"
unicode="q"
d="M 122.06174,747.63773 C 110.00813,747.63773 100,737.71025 100,725.75379 l 0,-556.23236 c 0,-11.9564 10.00814,-21.8837 22.06174,-21.8837 l 755.87652,0 c 12.0536,0 22.06174,9.9275 22.06174,21.8837 l 0,556.23236 c 0,11.95646 -10.00812,21.88394 -22.06174,21.88394 l -755.87652,0 z m 0,-19.35484 755.87652,0 c 1.58135,0 2.54954,-0.96039 2.54954,-2.5291 l 0,-556.23236 c 0,-1.5685 -0.96817,-2.5289 -2.54954,-2.5289 l -755.87652,0 c -1.58136,0 -2.54955,0.9604 -2.54955,2.5289 l 0,556.23236 c 0,1.56871 0.96821,2.5291 2.54955,2.5291 z" />
<glyph
glyph-name="b33"
id="glyph4306"
unicode="e"
d="m 122.12332,747.63773 c -12.08627,0 -22.123322,-8.54906 -22.123322,-18.84764 l 0,-562.30156 c 0,-10.2985 10.037082,-18.8508 22.123322,-18.8508 l 186.60985,0 c 12.08628,0 22.12332,8.5523 22.12332,18.8508 l 0,562.30156 c 0,10.29858 -10.03702,18.84764 -22.12332,18.84764 l -186.60985,0 z m 284.56982,0 c -12.08625,0 -22.11949,-8.54906 -22.11949,-18.84764 l 0,-562.30156 c 0,-10.2985 10.03324,-18.8508 22.11949,-18.8508 l 186.61372,0 c 12.08622,0 22.1195,8.5523 22.1195,18.8508 l 0,562.30156 c 0,10.29858 -10.03328,18.84764 -22.1195,18.84764 l -186.61372,0 z m 284.57362,0 c -12.08622,0 -22.1233,-8.54906 -22.1233,-18.84764 l 0,-562.30156 c 0,-10.2985 10.03709,-18.8508 22.1233,-18.8508 l 186.61374,0 c 12.0862,0 22.1195,8.5523 22.1195,18.8508 l 0,562.30156 c 0,10.29858 -10.0333,18.84764 -22.1195,18.84764 l -186.61374,0 z m -569.14344,-16.66661 186.60985,0 c 1.58838,0 2.56343,-0.82761 2.56343,-2.18103 l 0,-562.30156 c 0,-1.3533 -0.97502,-2.1843 -2.56343,-2.1843 l -186.60985,0 c -1.58841,0 -2.56341,0.831 -2.56341,2.1843 l 0,562.30156 c 0,1.35342 0.97503,2.18103 2.56341,2.18103 z m 284.56982,0 186.61372,0 c 1.58837,0 2.5596,-0.82761 2.5596,-2.18103 l 0,-562.30156 c 0,-1.3533 -0.97123,-2.1843 -2.5596,-2.1843 l -186.61372,0 c -1.58837,0 -2.55961,0.831 -2.55961,2.1843 l 0,562.30156 c 0,1.35342 0.97127,2.18103 2.55961,2.18103 z m 284.57362,0 186.61374,0 c 1.5884,0 2.5595,-0.82761 2.5595,-2.18103 l 0,-562.30156 c 0,-1.3533 -0.9711,-2.1843 -2.5595,-2.1843 l -186.61374,0 c -1.58837,0 -2.56341,0.831 -2.56341,2.1843 l 0,562.30156 c 0,1.35342 0.97504,2.18103 2.56341,2.18103 z" />
<glyph
glyph-name="b50"
id="glyph4889"
unicode="w"
d="m 541.98335,747.63769 c -11.9982,0 -21.95893,-9.85244 -21.95893,-21.72025 l 0,-555.59715 c 0,-11.86783 9.96071,-21.71632 21.95893,-21.71632 l 336.05772,0 c 11.99822,0 21.95894,9.84849 21.95894,21.71632 l 0,555.59714 c 0,11.86781 -9.96074,21.72025 -21.95894,21.72025 l -336.05772,0 z m 0,-19.05571 336.05772,0 c 1.65972,0 2.69763,-1.02288 2.69763,-2.66454 l 0,-555.59715 c 0,-1.64155 -1.03793,-2.66453 -2.69763,-2.66453 l -336.05772,0 c -1.6597,0 -2.69763,1.02299 -2.69763,2.66453 l 0,555.59714 c 0,1.64166 1.03791,2.66454 2.69763,2.66454 z m -420.02441,18.08953 c -11.99821,0 -21.958948,-9.85245 -21.958948,-21.72025 l 0,-555.59716 c 0,-11.86782 9.960718,-21.71633 21.958948,-21.71633 l 336.0577,0 c 11.99822,0 21.95894,9.84851 21.95894,21.71633 l 0,555.59715 c 0,11.8678 -9.96074,21.72025 -21.95894,21.72025 l -336.0577,0 z m 0,-19.05571 336.0577,0 c 1.65972,0 2.69763,-1.02288 2.69763,-2.66454 l 0,-555.59716 c 0,-1.64154 -1.03793,-2.66454 -2.69763,-2.66454 l -336.0577,0 c -1.65973,0 -2.69765,1.023 -2.69765,2.66454 l 0,555.59715 c 0,1.64168 1.0379,2.66454 2.69765,2.66454 z" />
<glyph
glyph-name="b37"
id="glyph4916"
unicode="r"
d="m 116.28515,747.63775 c -8.8492,0 -16.285153,-7.43672 -16.285153,-16.28516 l 0,-567.42968 c 0,-8.8484 7.435973,-16.2852 16.285153,-16.2852 l 231.17968,0 c 8.84916,0 16.28517,7.4368 16.28517,16.2852 l 0,567.42968 c 0,8.84844 -7.43597,16.28516 -16.28517,16.28516 l -231.17967,0 z m 341.24999,0 c -8.84919,0 -16.28516,-7.43672 -16.28516,-16.28516 l 0,-567.42968 c 0,-8.8484 7.43599,-16.2852 16.28516,-16.2852 l 426.17966,0 c 8.84921,0 16.2852,7.4368 16.2852,16.2852 l 0,567.42968 c 0,8.84844 -7.43599,16.28516 -16.2852,16.28516 l -426.17966,0 z m -337.53516,-20 223.75,0 0,-560.00004 -223.75,0 0,560.00004 z m 341.25,0 418.75002,0 0,-560.00004 -418.75,0 0,560.00004 z" />
<glyph
glyph-name="hero"
id="glyph4986"
unicode="t"
d="m 122.06248,747.63773 c -12.0536,0 -22.062495,-9.9284 -22.062495,-21.8848 l 0,-556.2324 c 0,-11.9564 10.008895,-21.8828 22.062495,-21.8828 l 755.87503,0 c 12.0536,0 22.0625,9.9266 22.0625,21.8828 l 0,556.2324 c 0,11.9564 -10.0088,21.8848 -22.0625,21.8848 z m 0,-19.3555 755.87503,0 c 1.5814,0 2.55078,-0.9606 2.55078,-2.5293 l 0,-556.2324 c 0,-1.5685 -0.96938,-2.5274 -2.55078,-2.5274 l -755.87503,0 c -1.5813,0 -2.5507,0.9589 -2.5507,2.5274 l 0,556.2324 c 0,1.5687 0.9694,2.5293 2.5507,2.5293 z m 86.2227,-66.6445 c -3.4816,0 -6.2852,-2.8032 -6.2852,-6.2852 l 0,-37.4297 c 0,-3.482 2.8036,-6.2851 6.2852,-6.2851 l 87.42967,0 c 3.4816,0 6.28516,2.8031 6.28516,6.2851 l 0,37.4297 c 0,3.482 -2.80356,6.2852 -6.28516,6.2852 z m 247.99999,0 c -3.4816,0 -6.28516,-2.8032 -6.28516,-6.2852 l 0,-37.4297 c 0,-3.482 2.80356,-6.2851 6.28516,-6.2851 l 337.42968,0 c 3.4816,0 6.28516,2.8031 6.28516,6.2851 l 0,37.4297 c 0,3.482 -2.80356,6.2852 -6.28516,6.2852 z m -202,-126 c -3.4816,0 -6.28516,-2.8032 -6.28516,-6.2852 l 0,-135.4297 c 0,-3.482 2.80356,-6.2851 6.28516,-6.2851 l 491.42968,0 c 3.48161,0 6.28516,2.8031 6.28516,6.2851 l 0,135.4297 c 0,3.482 -2.80355,6.2852 -6.28516,6.2852 z m 152,-222 c -3.4816,0 -6.28516,-2.8032 -6.28516,-6.2852 l 0,-37.4297 c 0,-3.482 2.80356,-6.2851 6.28516,-6.2851 l 187.42968,0 c 3.48161,0 6.28516,2.8031 6.28516,6.2851 l 0,37.4297 c 0,3.482 -2.80355,6.2852 -6.28516,6.2852 z" />
<glyph
glyph-name="h1p"
id="glyph5007"
unicode="y"
d="m 108.77344,747.13773 c -3.4816,0 -6.28321,-2.80319 -6.28321,-6.28516 l 0,-93.25976 c 0,-3.48196 2.80161,-6.28516 6.28321,-6.28516 l 506.45117,0 c 3.48161,0 6.28516,2.8032 6.28516,6.28516 l 0,93.25976 c 0,3.48197 -2.80355,6.28516 -6.28516,6.28516 l -506.45117,0 z m 0,-245.55469 c -3.4816,0 -6.28321,-2.80319 -6.28321,-6.28515 l 0,-23.41211 c 0,-3.48197 2.80161,-6.28516 6.28321,-6.28516 l 782.45117,0 c 3.4816,0 6.28516,2.80319 6.28516,6.28516 l 0,23.41211 c 0,3.48196 -2.80356,6.28515 -6.28516,6.28515 l -782.45117,0 z m 0,-105.46289 c -3.4816,0 -6.28321,-2.80319 -6.28321,-6.28515 l 0,-23.41211 c 0,-3.48197 2.80161,-6.28516 6.28321,-6.28516 l 782.45117,0 c 3.4816,0 6.28516,2.80319 6.28516,6.28516 l 0,23.41211 c 0,3.48196 -2.80356,6.28515 -6.28516,6.28515 l -782.45117,0 z m 0,-106.53711 c -3.4816,0 -6.28321,-2.80319 -6.28321,-6.28515 l 0,-23.41211 c 0,-3.48197 2.80161,-6.28516 6.28321,-6.28516 l 782.45117,0 c 3.4816,0 6.28516,2.80319 6.28516,6.28516 l 0,23.41211 c 0,3.48196 -2.80356,6.28515 -6.28516,6.28515 l -782.45117,0 z m 0,-105.46289 c -3.4816,0 -6.28321,-2.80319 -6.28321,-6.28515 l 0,-23.41211 c 0,-3.48197 2.80161,-6.28516 6.28321,-6.28516 l 782.45117,0 c 3.4816,0 6.28516,2.80319 6.28516,6.28516 l 0,23.41211 c 0,3.48196 -2.80356,6.28515 -6.28516,6.28515 l -782.45117,0 z" />
<glyph
glyph-name="3badges"
id="glyph5077"
unicode="u"
d="m 685.72852,681.99609 c -8.6183,0 -15.82618,-7.2085 -15.82618,-15.82812 l 0,-330.03906 c 0,-8.61963 7.20788,-15.82813 15.82618,-15.82813 l 197.94726,0 c 8.61818,0 15.82422,7.2085 15.82422,15.82813 l 0,330.03906 c 0,8.61962 -7.20592,15.82812 -15.82422,15.82812 l -197.94726,0 z m -569.40235,-2.29687 c -8.61829,0 -15.82617,-7.2085 -15.82617,-15.82813 l 0,-330.03906 c 0,-8.61962 7.20788,-15.82812 15.82617,-15.82812 l 197.94727,0 c 8.61818,0 15.82422,7.2085 15.82422,15.82812 l 0,330.03906 c 0,8.61963 -7.20593,15.82813 -15.82422,15.82813 l -197.94727,0 z m 282.4043,0 c -8.6183,0 -15.82617,-7.2085 -15.82617,-15.82813 l 0,-330.03906 c 0,-8.61962 7.20787,-15.82812 15.82617,-15.82812 l 197.94726,0 c 8.61818,0 15.82422,7.2085 15.82422,15.82812 l 0,330.03906 c 0,8.61963 -7.20592,15.82813 -15.82422,15.82813 l -197.94726,0 z m 288.39258,-14.92578 195.15625,0 0,-327.25 -195.15625,0 0,327.25 z m -569.40235,-2.29688 195.15625,0 0,-327.25 -195.15625,0 0,327.25 z m 282.4043,0 195.1582,0 0,-327.25 -195.1582,0 0,327.25 z m 382.2793,-43.06054 c -26.9457,0 -48.78907,-21.84913 -48.78907,-48.80079 0,-26.95166 21.84337,-48.79882 48.78907,-48.79882 26.9457,0 48.79101,21.84716 48.79101,48.79882 0,26.95166 -21.84531,48.80079 -48.79101,48.80079 z m -569.40235,-2.29688 c -26.9457,0 -48.78906,-21.84912 -48.78906,-48.80078 0,-26.95166 21.84336,-48.79883 48.78906,-48.79883 26.9457,0 48.79102,21.84717 48.79102,48.79883 0,26.95166 -21.84532,48.80078 -48.79102,48.80078 z m 282.40625,0 c -26.9457,0 -48.78906,-21.84912 -48.78906,-48.80078 0,-26.95166 21.84336,-48.79883 48.78906,-48.79883 26.9457,0 48.78907,21.84717 48.78907,48.79883 0,26.95166 -21.84337,48.80078 -48.78907,48.80078 z M 733.94336,472.44141 c -3.99684,0 -7.21484,-3.21874 -7.21484,-7.2168 l 0,-7.38281 c 0,-3.99818 3.218,-7.2168 7.21484,-7.2168 l 100.36719,0 c 3.99683,0 7.21679,3.21862 7.21679,7.2168 l 0,7.38281 c 0,3.99806 -3.21996,7.2168 -7.21679,7.2168 l -100.36719,0 z m -569.40234,-2.29688 c -3.99684,0 -7.21485,-3.21873 -7.21485,-7.2168 l 0,-7.38281 c 0,-3.99818 3.21801,-7.2168 7.21485,-7.2168 l 100.36718,0 c 3.99684,0 7.2168,3.21862 7.2168,7.2168 l 0,7.38281 c 0,3.99807 -3.21996,7.2168 -7.2168,7.2168 l -100.36718,0 z m 282.40429,0 c -3.99683,0 -7.21484,-3.21873 -7.21484,-7.2168 l 0,-7.38281 c 0,-3.99818 3.21801,-7.2168 7.21484,-7.2168 l 100.36914,0 c 3.99684,0 7.21485,3.21862 7.21485,7.2168 l 0,7.38281 c 0,3.99807 -3.21801,7.2168 -7.21485,7.2168 l -100.36914,0 z m 286.9961,-36.74414 c -3.99684,0 -7.21289,-3.21862 -7.21289,-7.2168 l 0,-7.38281 c 0,-3.99818 3.21605,-7.2168 7.21289,-7.2168 l 100.37109,0 c 3.99684,0 7.21484,3.21862 7.21484,7.2168 l 0,7.38281 c 0,3.99818 -3.218,7.2168 -7.21484,7.2168 l -100.37109,0 z m -569.40235,-2.29687 c -3.99683,0 -7.21289,-3.21862 -7.21289,-7.2168 l 0,-7.38281 c 0,-3.99818 3.21606,-7.2168 7.21289,-7.2168 l 100.3711,0 c 3.99683,0 7.21484,3.21862 7.21484,7.2168 l 0,7.38281 c 0,3.99818 -3.21801,7.2168 -7.21484,7.2168 l -100.3711,0 z m 282.40625,0 c -3.99683,0 -7.21484,-3.21862 -7.21484,-7.2168 l 0,-7.38281 c 0,-3.99818 3.21801,-7.2168 7.21484,-7.2168 l 100.36914,0 c 3.99684,0 7.21485,3.21862 7.21485,7.2168 l 0,7.38281 c 0,3.99818 -3.21801,7.2168 -7.21485,7.2168 l -100.36914,0 z m 286.9961,-36.74219 c -3.99684,0 -7.21289,-3.21862 -7.21289,-7.2168 l 0,-7.38281 c 0,-3.99818 3.21605,-7.2168 7.21289,-7.2168 l 100.37109,0 c 3.99684,0 7.21484,3.21862 7.21484,7.2168 l 0,7.38281 c 0,3.99818 -3.218,7.2168 -7.21484,7.2168 l -100.37109,0 z m -569.40235,-2.29688 c -3.99683,0 -7.21289,-3.21861 -7.21289,-7.21679 l 0,-7.38282 c 0,-3.99818 3.21606,-7.21679 7.21289,-7.21679 l 100.3711,0 c 3.99683,0 7.21484,3.21861 7.21484,7.21679 l 0,7.38282 c 0,3.99818 -3.21801,7.21679 -7.21484,7.21679 l -100.3711,0 z m 282.40625,0 c -3.99683,0 -7.21484,-3.21861 -7.21484,-7.21679 l 0,-7.38282 c 0,-3.99818 3.21801,-7.21679 7.21484,-7.21679 l 100.36914,0 c 3.99684,0 7.21485,3.21861 7.21485,7.21679 l 0,7.38282 c 0,3.99818 -3.21801,7.21679 -7.21485,7.21679 l -100.36914,0 z" />
<glyph
glyph-name="image"
id="glyph5147"
unicode="i"
d="m 122.06252,747.63773 c -12.0536,0 -22.0625,-9.92827 -22.0625,-21.88477 l 0,-556.23243 c 0,-11.9564 10.0089,-21.8828 22.0625,-21.8828 l 755.87495,0 c 12.0536,0 22.0625,9.9266 22.0625,21.8828 l 0,556.23243 c 0,11.9565 -10.0088,21.88477 -22.0625,21.88477 l -755.87495,0 z m 0,-19.35547 755.87495,0 c 1.5814,0 2.5508,-0.9606 2.5508,-2.5293 l 0,-556.23243 c 0,-1.5685 -0.9694,-2.5273 -2.5508,-2.5273 l -755.87495,0 c -1.5813,0 -2.55078,0.9588 -2.55078,2.5273 l 0,556.23243 c 0,1.5687 0.96948,2.5293 2.55078,2.5293 z m 603.43745,-37.64453 a 67.5,67.5 0 0 1 -67.5,-67.5 67.5,67.5 0 0 1 67.5,-67.5 67.5,67.5 0 0 1 67.5,67.5 67.5,67.5 0 0 1 -67.5,67.5 z m -327.33589,-58.78516 -235.3789,-426.32224 259.25586,0 -0.54883,-0.9922 366.19137,0 -183.09571,331.11718 -76.7148,-138.73438 -129.70899,234.93164 z" />
<glyph
glyph-name="text"
id="glyph5149"
unicode="o"
d="m 241.54297,800 -7.47656,-140.70703 17.75976,0 c 2.1811,27.13864 9.03393,48.67237 20.5625,64.60156 11.52843,15.92921 25.39542,26.69607 41.59766,32.30078 12.46317,4.12981 33.49551,6.19336 63.0957,6.19336 l 76.18164,0 0,-458.40625 c 0,-33.62831 -3.42842,-55.16202 -10.2832,-64.60156 -11.2169,-15.33922 -30.22357,-23.00977 -57.01953,-23.00977 l -22.4336,0 0,-16.37109 268.74024,0 0,16.37109 -21.9668,0 c -24.61491,0 -42.84157,6.19463 -54.68164,18.58399 -8.4127,9.14454 -12.62109,32.15418 -12.62109,69.02734 l 0,458.40625 89.26953,0 c 26.17279,0 47.2031,-4.12909 63.09375,-12.38867 16.2022,-7.9646 29.13201,-20.50219 38.79101,-37.61133 5.92001,-10.61945 10.59409,-28.31694 14.02149,-53.0957 l 17.75976,0 L 758.92383,800 241.54297,800 Z" />
<glyph
glyph-name="quotes"
id="glyph5248"
unicode="p"
d="m 205.0332,800 0,-10 0,-252.15234 122.83594,0 c 0.19856,-1.5552 0.35938,-3.0785 0.35938,-4.44336 l 0,-0.99024 0.19336,-0.96875 c 0.56358,-2.8203 0.91406,-6.65611 0.91406,-11.36719 0,-49.69706 -10.37622,-93.07559 -30.95703,-130.7207 l -0.0352,-0.0644 -0.0352,-0.0645 C 278.74416,352.1644 251.28807,331.76304 213.41602,325.56055 l -8.38282,-1.37305 0,-124.1875 11.19532,1.34961 c 82.40701,9.92779 145.45236,49.82688 186.08398,118.11523 l 0.0117,0.0215 0.0137,0.0215 c 40.26558,68.43351 60.20313,151.05581 60.20313,247.22266 l 0,233.26953 -257.50782,0 z m 332.42578,0 0,-10 0,-252.15234 122.83399,0 c 0.19946,-1.55621 0.36133,-3.07993 0.36133,-4.44336 l 0,-0.99024 0.19336,-0.96875 c 0.56358,-2.82026 0.91406,-6.6561 0.91406,-11.36719 0,-49.69706 -10.37622,-93.07559 -30.95703,-130.7207 l -0.0352,-0.0644 -0.0352,-0.0645 C 611.16993,352.1644 583.71386,331.76304 545.8418,325.56055 l -8.38282,-1.37305 0,-124.1875 11.19532,1.34961 c 82.40702,9.92779 145.45236,49.82688 186.08398,118.11523 l 0.0117,0.0215 0.0137,0.0215 c 40.26567,68.43338 60.20313,151.05581 60.20313,247.22266 l 0,233.26953 -257.50782,0 z m -312.42578,-20 217.50782,0 0,-213.26953 c 0,-93.39687 -19.27335,-172.19948 -57.42774,-237.05664 C 349.1695,269.27492 296.65772,234.14551 225.0332,222.82031 l 0,84.86719 c 39.43166,8.90932 70.49983,33.48573 90.92578,72.14453 22.30915,40.83549 33.37696,87.77857 33.37696,140.24609 0,5.3977 -0.38074,10.20735 -1.20899,14.62305 -0.1288,5.15097 -0.94385,10.4327 -2.50195,15.89063 l -2.07031,7.25586 -118.52149,0 0,222.15234 z m 332.42578,0 217.50782,0 0,-213.26953 c 0,-93.39687 -19.27334,-172.19948 -57.42774,-237.05664 -35.94378,-60.39891 -88.45555,-95.52832 -160.08008,-106.85352 l 0,84.86719 c 39.43166,8.90932 70.49982,33.48573 90.92579,72.14453 22.30914,40.83549 33.37695,87.77857 33.37695,140.24609 0,5.40154 -0.3814,10.21455 -1.21094,14.63282 -0.13032,5.15253 -0.94794,10.43031 -2.5039,15.88086 l -2.07032,7.25586 -118.51758,0 0,222.15234 z" />
</font>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.1767767"
inkscape:cx="334.86414"
inkscape:cy="521.53582"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1440"
inkscape:window-height="800"
inkscape:window-x="268"
inkscape:window-y="79"
inkscape:window-maximized="0"
guidetolerance="10"
objecttolerance="10"
gridtolerance="10"
inkscape:snap-bbox="false"
inkscape:bbox-nodes="false"
inkscape:bbox-paths="false"
inkscape:snap-bbox-edge-midpoints="false"
inkscape:snap-bbox-midpoints="false"
inkscape:snap-grids="true"
inkscape:snap-global="true"
inkscape:object-nodes="true"
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid4211"
color="#ff00ff"
opacity="0.1254902"
empcolor="#0000ff"
empopacity="0.25098039"
spacingx="9.9999999"
spacingy="9.9999999"
units="px" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Livello 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-52.36227)">
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -737.93827,1092.3623 c -12.0536,0 -22.0617,9.9274 -22.0617,21.8839 l 0,556.2324 c 0,11.9564 10.0081,21.8837 22.0617,21.8837 l 755.876498,0 c 12.0536,0 22.0618,-9.9275 22.0618,-21.8837 l 0,-556.2324 c 0,-11.9565 -10.0081,-21.8839 -22.0618,-21.8839 l -755.876498,0 z m 0,19.3548 755.876498,0 c 1.5814,0 2.5496,0.9604 2.5496,2.5291 l 0,556.2324 c 0,1.5685 -0.9682,2.5289 -2.5496,2.5289 l -755.876498,0 c -1.5813,0 -2.5495,-0.9604 -2.5495,-2.5289 l 0,-556.2324 c 0,-1.5687 0.9682,-2.5291 2.5495,-2.5291 z"
id="rect4221"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
d="m 1545.9833,300.36231 c -11.9982,0 -21.9589,9.85244 -21.9589,21.72025 l 0,555.59715 c 0,11.86783 9.9607,21.71632 21.9589,21.71632 l 336.0577,0 c 11.9983,0 21.959,-9.84849 21.959,-21.71632 l 0,-555.59714 c 0,-11.86781 -9.9608,-21.72025 -21.959,-21.72025 l -336.0577,0 z m 0,19.05571 336.0577,0 c 1.6598,0 2.6977,1.02288 2.6977,2.66454 l 0,555.59715 c 0,1.64155 -1.038,2.66453 -2.6977,2.66453 l -336.0577,0 c -1.6597,0 -2.6976,-1.02299 -2.6976,-2.66453 l 0,-555.59714 c 0,-1.64166 1.0379,-2.66454 2.6976,-2.66454 z m -420.0244,-18.08953 c -11.9982,0 -21.9589,9.85245 -21.9589,21.72025 l 0,555.59716 c 0,11.86782 9.9607,21.71633 21.9589,21.71633 l 336.0577,0 c 11.9982,0 21.959,-9.84851 21.959,-21.71633 l 0,-555.59715 c 0,-11.8678 -9.9608,-21.72025 -21.959,-21.72025 l -336.0577,0 z m 0,19.05571 336.0577,0 c 1.6597,0 2.6976,1.02288 2.6976,2.66454 l 0,555.59716 c 0,1.64154 -1.0379,2.66454 -2.6976,2.66454 l -336.0577,0 c -1.6597,0 -2.6976,-1.023 -2.6976,-2.66454 l 0,-555.59715 c 0,-1.64168 1.0379,-2.66454 2.6976,-2.66454 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.86022854;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4238" />
<path
inkscape:connector-curvature="0"
id="rect4298"
d="m 1114.1233,990.36225 c -12.0863,0 -22.1233,8.54906 -22.1233,18.84765 l 0,562.3015 c 0,10.2985 10.0371,18.8508 22.1233,18.8508 l 186.6098,0 c 12.0863,0 22.1234,-8.5523 22.1234,-18.8508 l 0,-562.3015 c 0,-10.29859 -10.0371,-18.84765 -22.1234,-18.84765 l -186.6098,0 z m 284.5698,0 c -12.0862,0 -22.1195,8.54906 -22.1195,18.84765 l 0,562.3015 c 0,10.2985 10.0333,18.8508 22.1195,18.8508 l 186.6137,0 c 12.0863,0 22.1195,-8.5523 22.1195,-18.8508 l 0,-562.3015 c 0,-10.29859 -10.0332,-18.84765 -22.1195,-18.84765 l -186.6137,0 z m 284.5736,0 c -12.0862,0 -22.1233,8.54906 -22.1233,18.84765 l 0,562.3015 c 0,10.2985 10.0371,18.8508 22.1233,18.8508 l 186.6138,0 c 12.0862,0 22.1195,-8.5523 22.1195,-18.8508 l 0,-562.3015 c 0,-10.29859 -10.0333,-18.84765 -22.1195,-18.84765 l -186.6138,0 z m -569.1434,16.66665 186.6098,0 c 1.5884,0 2.5635,0.8276 2.5635,2.181 l 0,562.3015 c 0,1.3533 -0.975,2.1843 -2.5635,2.1843 l -186.6098,0 c -1.5884,0 -2.5634,-0.831 -2.5634,-2.1843 l 0,-562.3015 c 0,-1.3534 0.975,-2.181 2.5634,-2.181 z m 284.5698,0 186.6137,0 c 1.5884,0 2.5596,0.8276 2.5596,2.181 l 0,562.3015 c 0,1.3533 -0.9712,2.1843 -2.5596,2.1843 l -186.6137,0 c -1.5884,0 -2.5596,-0.831 -2.5596,-2.1843 l 0,-562.3015 c 0,-1.3534 0.9713,-2.181 2.5596,-2.181 z m 284.5736,0 186.6138,0 c 1.5884,0 2.5595,0.8276 2.5595,2.181 l 0,562.3015 c 0,1.3533 -0.9711,2.1843 -2.5595,2.1843 l -186.6138,0 c -1.5883,0 -2.5634,-0.831 -2.5634,-2.1843 l 0,-562.3015 c 0,-1.3534 0.9751,-2.181 2.5634,-2.181 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 130.28515,1206.3622 c -8.8492,0 -16.28515,7.4367 -16.28515,16.2852 l 0,567.4297 c 0,8.8484 7.43597,16.2852 16.28515,16.2852 l 231.17968,0 c 8.84916,0 16.28517,-7.4368 16.28517,-16.2852 l 0,-567.4297 c 0,-8.8485 -7.43597,-16.2852 -16.28517,-16.2852 l -231.17967,0 z m 341.24999,0 c -8.84919,0 -16.28516,7.4367 -16.28516,16.2852 l 0,567.4297 c 0,8.8484 7.43599,16.2852 16.28516,16.2852 l 426.17966,0 c 8.84921,0 16.2852,-7.4368 16.2852,-16.2852 l 0,-567.4297 c 0,-8.8485 -7.43599,-16.2852 -16.2852,-16.2852 l -426.17966,0 z m -337.53516,20 223.75,0 0,560.0001 -223.75,0 0,-560.0001 z m 341.25,0 418.75002,0 0,560.0001 -418.75,0 0,-560.0001 z"
id="rect4897"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -1606.1064,1092.4051 c -12.0536,0 -22.0625,9.9284 -22.0625,21.8848 l 0,556.2324 c 0,11.9564 10.0089,21.8828 22.0625,21.8828 l 755.87498,0 c 12.0536,0 22.0625,-9.9266 22.0625,-21.8828 l 0,-556.2324 c 0,-11.9564 -10.0088,-21.8848 -22.0625,-21.8848 z m 0,19.3555 755.87498,0 c 1.5814,0 2.55078,0.9606 2.55078,2.5293 l 0,556.2324 c 0,1.5685 -0.96938,2.5274 -2.55078,2.5274 l -755.87498,0 c -1.5813,0 -2.5507,-0.9589 -2.5507,-2.5274 l 0,-556.2324 c 0,-1.5687 0.9694,-2.5293 2.5507,-2.5293 z m 86.2227,66.6445 c -3.4816,0 -6.2852,2.8032 -6.2852,6.2852 l 0,37.4297 c 0,3.482 2.8036,6.2851 6.2852,6.2851 l 87.4296,0 c 3.4816,0 6.2852,-2.8031 6.2852,-6.2851 l 0,-37.4297 c 0,-3.482 -2.8036,-6.2852 -6.2852,-6.2852 z m 247.9999,0 c -3.4816,0 -6.2851,2.8032 -6.2851,6.2852 l 0,37.4297 c 0,3.482 2.8035,6.2851 6.2851,6.2851 l 337.42972,0 c 3.4816,0 6.28516,-2.8031 6.28516,-6.2851 l 0,-37.4297 c 0,-3.482 -2.80356,-6.2852 -6.28516,-6.2852 z m -202,126 c -3.4816,0 -6.2851,2.8032 -6.2851,6.2852 l 0,135.4297 c 0,3.482 2.8035,6.2851 6.2851,6.2851 l 491.42972,0 c 3.48161,0 6.28516,-2.8031 6.28516,-6.2851 l 0,-135.4297 c 0,-3.482 -2.80355,-6.2852 -6.28516,-6.2852 z m 152,222 c -3.4816,0 -6.2851,2.8032 -6.2851,6.2852 l 0,37.4297 c 0,3.482 2.8035,6.2851 6.2851,6.2851 l 187.4297,0 c 3.4816,0 6.2852,-2.8031 6.2852,-6.2851 l 0,-37.4297 c 0,-3.482 -2.8036,-6.2852 -6.2852,-6.2852 z"
id="path4918"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssssssssssssssssssssssssssssssssssssssssssssssssssssss" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -954.71513,261.34755 c -3.4816,0 -6.28321,2.80319 -6.28321,6.28516 l 0,93.25976 c 0,3.48196 2.80161,6.28516 6.28321,6.28516 l 506.45117,0 c 3.48161,0 6.28516,-2.8032 6.28516,-6.28516 l 0,-93.25976 c 0,-3.48197 -2.80355,-6.28516 -6.28516,-6.28516 l -506.45117,0 z m 0,245.55469 c -3.4816,0 -6.28321,2.80319 -6.28321,6.28515 l 0,23.41211 c 0,3.48197 2.80161,6.28516 6.28321,6.28516 l 782.45117,0 c 3.4816,0 6.28516,-2.80319 6.28516,-6.28516 l 0,-23.41211 c 0,-3.48196 -2.80356,-6.28515 -6.28516,-6.28515 l -782.45117,0 z m 0,105.46289 c -3.4816,0 -6.28321,2.80319 -6.28321,6.28515 l 0,23.41211 c 0,3.48197 2.80161,6.28516 6.28321,6.28516 l 782.45117,0 c 3.4816,0 6.28516,-2.80319 6.28516,-6.28516 l 0,-23.41211 c 0,-3.48196 -2.80356,-6.28515 -6.28516,-6.28515 l -782.45117,0 z m 0,106.53711 c -3.4816,0 -6.28321,2.80319 -6.28321,6.28515 l 0,23.41211 c 0,3.48197 2.80161,6.28516 6.28321,6.28516 l 782.45117,0 c 3.4816,0 6.28516,-2.80319 6.28516,-6.28516 l 0,-23.41211 c 0,-3.48196 -2.80356,-6.28515 -6.28516,-6.28515 l -782.45117,0 z m 0,105.46289 c -3.4816,0 -6.28321,2.80319 -6.28321,6.28515 l 0,23.41211 c 0,3.48197 2.80161,6.28516 6.28321,6.28516 l 782.45117,0 c 3.4816,0 6.28516,-2.80319 6.28516,-6.28516 l 0,-23.41211 c 0,-3.48196 -2.80356,-6.28515 -6.28516,-6.28515 l -782.45117,0 z"
id="rect4990"
inkscape:connector-curvature="0" />
<g
id="g5100"
transform="matrix(1.1479885,0,0,1.1482424,-1626.635,-2493.2328)">
<path
style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.87099254px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2231.0895,1774.6238 c -7.5073,0 -13.786,6.2778 -13.786,13.7846 l 0,287.4298 c 0,7.5068 6.2787,13.7847 13.786,13.7847 l 172.4296,0 c 7.5072,0 13.7843,-6.2779 13.7843,-13.7847 l 0,-287.4298 c 0,-7.5068 -6.277,-13.7846 -13.7843,-13.7846 l -172.4296,0 z m -496.0001,2.0003 c -7.5073,0 -13.786,6.2779 -13.786,13.7847 l 0,287.4298 c 0,7.5068 6.2787,13.7846 13.786,13.7846 l 172.4297,0 c 7.5072,0 13.7843,-6.2778 13.7843,-13.7846 l 0,-287.4298 c 0,-7.5068 -6.277,-13.7847 -13.7843,-13.7847 l -172.4297,0 z m 245.9993,0 c -7.5073,0 -13.786,6.2779 -13.786,13.7847 l 0,287.4298 c 0,7.5068 6.2787,13.7846 13.786,13.7846 l 172.4296,0 c 7.5072,0 13.7843,-6.2778 13.7843,-13.7846 l 0,-287.4298 c 0,-7.5068 -6.277,-13.7847 -13.7843,-13.7847 l -172.4296,0 z m 251.2155,12.9988 169.9985,0 0,285.0008 -169.9985,0 0,-285.0008 z m -496,2.0004 169.9984,0 0,285.0008 -169.9984,0 0,-285.0008 z m 245.9992,0 170.0002,0 0,285.0008 -170.0002,0 0,-285.0008 z m 332.9993,37.5012 c -23.4721,0 -42.4996,19.0284 -42.4996,42.5005 0,23.4721 19.0275,42.4987 42.4996,42.4987 23.4721,0 42.5013,-19.0266 42.5013,-42.4987 0,-23.4721 -19.0292,-42.5005 -42.5013,-42.5005 z m -496.0001,2.0004 c -23.4721,0 -42.4996,19.0283 -42.4996,42.5004 0,23.4721 19.0275,42.4987 42.4996,42.4987 23.4721,0 42.5013,-19.0266 42.5013,-42.4987 0,-23.4721 -19.0292,-42.5004 -42.5013,-42.5004 z m 246.001,0 c -23.4721,0 -42.4996,19.0283 -42.4996,42.5004 0,23.4721 19.0275,42.4987 42.4996,42.4987 23.4721,0 42.4996,-19.0266 42.4996,-42.4987 0,-23.4721 -19.0275,-42.5004 -42.4996,-42.5004 z m 207.7853,125.9993 c -3.4816,0 -6.2848,2.8032 -6.2848,6.2851 l 0,6.4296 c 0,3.482 2.8032,6.2851 6.2848,6.2851 l 87.4287,0 c 3.4816,0 6.2865,-2.8031 6.2865,-6.2851 l 0,-6.4296 c 0,-3.4819 -2.8049,-6.2851 -6.2865,-6.2851 l -87.4287,0 z m -496.0001,2.0003 c -3.4816,0 -6.2847,2.8032 -6.2847,6.2851 l 0,6.4297 c 0,3.482 2.8031,6.2851 6.2847,6.2851 l 87.4288,0 c 3.4816,0 6.2865,-2.8031 6.2865,-6.2851 l 0,-6.4297 c 0,-3.4819 -2.8049,-6.2851 -6.2865,-6.2851 l -87.4288,0 z m 245.9993,0 c -3.4816,0 -6.2848,2.8032 -6.2848,6.2851 l 0,6.4297 c 0,3.482 2.8032,6.2851 6.2848,6.2851 l 87.4304,0 c 3.4816,0 6.2848,-2.8031 6.2848,-6.2851 l 0,-6.4297 c 0,-3.4819 -2.8032,-6.2851 -6.2848,-6.2851 l -87.4304,0 z m 249.9991,32.0004 c -3.4816,0 -6.2831,2.803 -6.2831,6.285 l 0,6.4297 c 0,3.482 2.8015,6.2851 6.2831,6.2851 l 87.4321,0 c 3.4816,0 6.2848,-2.8031 6.2848,-6.2851 l 0,-6.4297 c 0,-3.482 -2.8032,-6.285 -6.2848,-6.285 l -87.4321,0 z m -496.0001,2.0003 c -3.4816,0 -6.283,2.8031 -6.283,6.2851 l 0,6.4296 c 0,3.482 2.8014,6.2851 6.283,6.2851 l 87.4322,0 c 3.4816,0 6.2848,-2.8031 6.2848,-6.2851 l 0,-6.4296 c 0,-3.482 -2.8032,-6.2851 -6.2848,-6.2851 l -87.4322,0 z m 246.001,0 c -3.4816,0 -6.2848,2.8031 -6.2848,6.2851 l 0,6.4296 c 0,3.482 2.8032,6.2851 6.2848,6.2851 l 87.4304,0 c 3.4816,0 6.2848,-2.8031 6.2848,-6.2851 l 0,-6.4296 c 0,-3.482 -2.8032,-6.2851 -6.2848,-6.2851 l -87.4304,0 z m 249.9991,31.9986 c -3.4816,0 -6.2831,2.8031 -6.2831,6.2851 l 0,6.4297 c 0,3.482 2.8015,6.2851 6.2831,6.2851 l 87.4321,0 c 3.4816,0 6.2848,-2.8031 6.2848,-6.2851 l 0,-6.4297 c 0,-3.482 -2.8032,-6.2851 -6.2848,-6.2851 l -87.4321,0 z m -496.0001,2.0004 c -3.4816,0 -6.283,2.803 -6.283,6.285 l 0,6.4297 c 0,3.482 2.8014,6.2851 6.283,6.2851 l 87.4322,0 c 3.4816,0 6.2848,-2.8031 6.2848,-6.2851 l 0,-6.4297 c 0,-3.482 -2.8032,-6.285 -6.2848,-6.285 l -87.4322,0 z m 246.001,0 c -3.4816,0 -6.2848,2.803 -6.2848,6.285 l 0,6.4297 c 0,3.482 2.8032,6.2851 6.2848,6.2851 l 87.4304,0 c 3.4816,0 6.2848,-2.8031 6.2848,-6.2851 l 0,-6.4297 c 0,-3.482 -2.8032,-6.285 -6.2848,-6.285 l -87.4304,0 z"
id="path5110"
inkscape:connector-curvature="0" />
</g>
<flowRoot
xml:space="preserve"
id="flowRoot5131"
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Sans;font-style:normal;font-weight:normal;font-size:40px;line-height:125%;letter-spacing:0px;word-spacing:0px"><flowRegion
id="flowRegion5133"><rect
id="rect5135"
width="400"
height="150"
x="100"
y="400" /></flowRegion><flowPara
id="flowPara5137" /></flowRoot> <path
inkscape:connector-curvature="0"
id="path5152"
d="m -1647.8463,-177.55864 -7.4766,140.707027 17.7598,0 c 2.1811,-27.13864 9.0339,-48.67237 20.5625,-64.601557 11.5284,-15.92921 25.3954,-26.69607 41.5977,-32.30078 12.4631,-4.12981 33.4955,-6.19336 63.0957,-6.19336 l 76.1815,0 0,458.40625 c 0,33.6283 -3.4284,55.16201 -10.2831,64.60155 -11.2168,15.33923 -30.2236,23.00977 -57.0195,23.00977 l -22.4336,0 0,16.37109 268.7402,0 0,-16.37109 -21.9668,0 c -24.6149,0 -42.8416,-6.19462 -54.6816,-18.58398 -8.4127,-9.14455 -12.6211,-32.15418 -12.6211,-69.02734 l 0,-458.40625 89.2695,0 c 26.1728,0 47.2031,4.12909 63.0938,12.38867 16.2022,7.9646 29.132,20.50219 38.791,37.611327 5.92,10.61945 10.5941,28.31694 14.0215,53.0957 l 17.7597,0 -7.0097,-140.707027 -517.3809,0 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:500px;line-height:125%;font-family:'Times New Roman';-inkscape-font-specification:'Times New Roman, ';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<flowRoot
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Times New Roman, ';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="flowRoot5199"
xml:space="preserve"
transform="matrix(1.3825379,0,0,1.3923258,-0.27349449,-162.31425)"><flowRegion
id="flowRegion5201"><rect
y="150"
x="250"
height="650"
width="600"
id="rect5203" /></flowRegion><flowPara
id="flowPara5205" /></flowRoot> <path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:'Yuppy TC';-inkscape-font-specification:'Yuppy TC';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -1590.8717,-913.42067 0,10 0,252.15235 122.836,0 c 0.1985,1.5552 0.3594,3.0785 0.3594,4.44336 l 0,0.99024 0.1933,0.96875 c 0.5636,2.8203 0.9141,6.65611 0.9141,11.36719 0,49.69706 -10.3762,93.07559 -30.9571,130.7207 l -0.035,0.0645 -0.035,0.0645 c -19.5644,37.06412 -47.0205,57.46548 -84.8925,63.66797 l -8.3829,1.37305 0,124.18749 11.1954,-1.34961 c 82.407,-9.92779 145.4523,-49.82688 186.084,-118.11522 l 0.012,-0.0215 0.014,-0.0215 c 40.2656,-68.43358 60.2032,-151.05588 60.2032,-247.22273 l 0,-233.26954 -257.5079,0 z m 332.4258,0 0,10 0,252.15235 122.834,0 c 0.1995,1.55621 0.3613,3.07993 0.3613,4.44336 l 0,0.99024 0.1934,0.96875 c 0.5636,2.82026 0.9141,6.6561 0.9141,11.36719 0,49.69706 -10.3763,93.07559 -30.9571,130.7207 l -0.035,0.0645 -0.035,0.0645 c -19.5644,37.06412 -47.0205,57.46548 -84.8925,63.66797 l -8.3829,1.37305 0,124.18749 11.1954,-1.34961 c 82.407,-9.92779 145.4523,-49.82688 186.0839,-118.11522 l 0.012,-0.0215 0.014,-0.0215 c 40.2658,-68.43345 60.2032,-151.05588 60.2032,-247.22273 l 0,-233.26954 -257.5078,0 z m -312.4258,20 217.5079,0 0,213.26954 c 0,93.39687 -19.2734,172.19948 -57.4278,237.05664 -35.9438,60.39891 -88.4556,95.52831 -160.0801,106.85351 l 0,-84.86718 c 39.4317,-8.90932 70.4999,-33.48573 90.9258,-72.14453 22.3092,-40.83549 33.377,-87.77857 33.377,-140.24609 0,-5.3977 -0.3808,-10.20735 -1.209,-14.62305 -0.1288,-5.15097 -0.9439,-10.4327 -2.502,-15.89063 l -2.0703,-7.25586 -118.5215,0 0,-222.15235 z m 332.4258,0 217.5078,0 0,213.26954 c 0,93.39687 -19.2733,172.19948 -57.4277,237.05664 -35.9438,60.39891 -88.4555,95.52831 -160.0801,106.85351 l 0,-84.86718 c 39.4317,-8.90932 70.4998,-33.48573 90.9258,-72.14453 22.3092,-40.83549 33.377,-87.77857 33.377,-140.24609 0,-5.40154 -0.3814,-10.21455 -1.211,-14.63282 -0.1303,-5.15253 -0.9479,-10.43031 -2.5039,-15.88086 l -2.0703,-7.25586 -118.5176,0 0,-222.15235 z"
id="flowRoot5215"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -839.60268,-468.88663 c -12.0536,0 -22.0625,9.92827 -22.0625,21.88477 l 0,556.23243 c 0,11.9564 10.0089,21.8828 22.0625,21.8828 l 755.874954,0 c 12.0536,0 22.0625,-9.9266 22.0625,-21.8828 l 0,-556.23243 c 0,-11.9565 -10.0088,-21.88477 -22.0625,-21.88477 l -755.874954,0 z m 0,19.35547 755.874954,0 c 1.5814,0 2.5508,0.9606 2.5508,2.5293 l 0,556.23243 c 0,1.5685 -0.9694,2.5273 -2.5508,2.5273 l -755.874954,0 c -1.5813,0 -2.55078,-0.9588 -2.55078,-2.5273 l 0,-556.23243 c 0,-1.5687 0.96948,-2.5293 2.55078,-2.5293 z m 603.43745,37.64453 a 67.5,67.5 0 0 0 -67.5,67.5 67.5,67.5 0 0 0 67.5,67.5 67.5,67.5 0 0 0 67.5,-67.5 67.5,67.5 0 0 0 -67.5,-67.5 z m -327.33589,58.78516 -235.3789,426.322243 259.25586,0 -0.54883,0.9922 366.19137,0 -183.09571,-331.117183 -76.7148,138.73438 -129.70899,-234.93164 z"
id="path5156"
inkscape:connector-curvature="0" />
<flowRoot
xml:space="preserve"
id="flowRoot4167"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="translate(0,86.303395)"><flowRegion
id="flowRegion4169"><rect
id="rect4171"
width="2000"
height="650"
x="-450"
y="-1300" /></flowRegion><flowPara
style="font-size:125px"
id="flowPara4175">Borders: 20px</flowPara><flowPara
style="font-size:125px"
id="flowPara4179">Canvas: 1000x1000px</flowPara></flowRoot> </g>
</svg>

After

Width:  |  Height:  |  Size: 41 KiB

BIN
dist/fonts/main-fonts.ttf

Binary file not shown.

BIN
dist/fonts/main-fonts.woff

Binary file not shown.

30
dist/grapes.min.js

File diff suppressed because one or more lines are too long

790
index.html

@ -7,8 +7,796 @@
<link rel="stylesheet" href="vendor/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="vendor/codemirror/theme/hopscotch.css">
</head>
<style>
body, html{ height: 100%; margin: 0;}
</style>
<body>
<div id="wte-app"></div>
<div id="gjs" style="height:0px; overflow:hidden">
<header class="header-banner">
<div class="container-width">
<!--
<table>
<thead>
<tr> <th>Header1</th> <th>Header2</th> <th>Header3</th> </tr>
</thead>
<tbody>
<tr> <td>Row11</td> <td>Row12</td> <td>Row13</td> </tr>
<tr> <td>Row21</td> <td>Row22</td> <td>Row23</td> </tr>
<tr> <td>Row31</td> <td>Row32</td> <td>Row33</td> </tr>
</tbody>
</table>
-->
<div class="logo-container">
<div class="logo">GrapesJS</div>
</div>
<nav class="navbar">
<div class="menu-item">BUILDER</div>
<div class="menu-item">TEMPLATE</div>
<div class="menu-item">WEB</div>
</nav>
<div class="clearfix"></div>
<div class="lead-title">Build your templates without coding</div>
<div class="sub-lead-title">All text blocks could be edited easily with double clicking on it. You can create new text blocks with the command from the left panel</div>
<div class="lead-btn">Hover me</div>
</div>
</header>
<section class="flex-sect">
<div class="container-width">
<div class="flex-title">Flex is the new black</div>
<div class="flex-desc">With flexbox system you're able to build complex layouts easily and with free responsivity</div>
<div class="cards">
<div class="card">
<div class="card-header"></div>
<div class="card-body">
<div class="card-title">Title one</div>
<div class="card-sub-title">Subtitle one</div>
<div class="card-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore</div>
</div>
</div>
<div class="card">
<div class="card-header ch2"></div>
<div class="card-body">
<div class="card-title">Title two</div>
<div class="card-sub-title">Subtitle two</div>
<div class="card-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore</div>
</div>
</div>
<div class="card">
<div class="card-header ch3"></div>
<div class="card-body">
<div class="card-title">Title three</div>
<div class="card-sub-title">Subtitle three</div>
<div class="card-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore</div>
</div>
</div>
<div class="card">
<div class="card-header ch4"></div>
<div class="card-body">
<div class="card-title">Title four</div>
<div class="card-sub-title">Subtitle four</div>
<div class="card-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore</div>
</div>
</div>
<div class="card">
<div class="card-header ch5"></div>
<div class="card-body">
<div class="card-title">Title five</div>
<div class="card-sub-title">Subtitle five</div>
<div class="card-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore</div>
</div>
</div>
<div class="card">
<div class="card-header ch6"></div>
<div class="card-body">
<div class="card-title">Title six</div>
<div class="card-sub-title">Subtitle six</div>
<div class="card-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore</div>
</div>
</div>
</div>
</div>
</section>
<section class="am-sect">
<div class="container-width">
<div class="am-container">
<img class="img-phone" onmousedown="return false" src="./img/phone-app.png"/>
<div class="am-content">
<div class="am-pre">ASSET MANAGER</div>
<div class="am-title">Manage your images with Asset Manager</div>
<div class="am-desc">You can create image blocks with the command from the left panel and edit them with double click</div>
<div class="am-post">Image uploading is not allowed in this demo</div>
</div>
</div>
</div>
</section>
<section class="blk-sect">
<div class="container-width">
<div class="blk-title">Blocks</div>
<div class="blk-desc">Each element in HTML page could be seen as a block. On the left panel you could find different kind of blocks that you can create, move and style</div>
<div class="price-cards">
<div class="price-card-cont">
<div class="price-card">
<div class="pc-title">Starter</div>
<div class="pc-desc">Some random list</div>
<div class="pc-feature odd-feat">+ Starter feature 1</div>
<div class="pc-feature">+ Starter feature 2</div>
<div class="pc-feature odd-feat">+ Starter feature 3</div>
<div class="pc-feature">+ Starter feature 4</div>
<div class="pc-amount odd-feat">$ 9,90/mo</div>
</div>
</div>
<div class="price-card-cont">
<div class="price-card pc-regular">
<div class="pc-title">Regular</div>
<div class="pc-desc">Some random list</div>
<div class="pc-feature odd-feat">+ Regular feature 1</div>
<div class="pc-feature">+ Regular feature 2</div>
<div class="pc-feature odd-feat">+ Regular feature 3</div>
<div class="pc-feature">+ Regular feature 4</div>
<div class="pc-amount odd-feat">$ 19,90/mo</div>
</div>
</div>
<div class="price-card-cont">
<div class="price-card pc-enterprise">
<div class="pc-title">Enterprise</div>
<div class="pc-desc">Some random list</div>
<div class="pc-feature odd-feat">+ Enterprise feature 1</div>
<div class="pc-feature">+ Enterprise feature 2</div>
<div class="pc-feature odd-feat">+ Enterprise feature 3</div>
<div class="pc-feature">+ Enterprise feature 4</div>
<div class="pc-amount odd-feat">$ 29,90/mo</div>
</div>
</div>
</div>
</div>
</section>
<section class="bdg-sect">
<div class="container-width">
<h1 class="bdg-title">The team</h1>
<div class="badges">
<div class="badge">
<div class="badge-header"></div>
<img class="badge-avatar" src="img/team1.jpg">
<div class="badge-body">
<div class="badge-name">Adam Smith</div>
<div class="badge-role">CEO</div>
<div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>
</div>
<div class="badge-foot">
<span class="badge-link">f</span>
<span class="badge-link">t</span>
<span class="badge-link">ln</span>
</div>
</div>
<div class="badge">
<div class="badge-header"></div>
<img class="badge-avatar" src="img/team2.jpg">
<div class="badge-body">
<div class="badge-name">John Black</div>
<div class="badge-role">Software Engineer</div>
<div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>
</div>
<div class="badge-foot">
<span class="badge-link">f</span>
<span class="badge-link">t</span>
<span class="badge-link">ln</span>
</div>
</div>
<div class="badge">
<div class="badge-header"></div>
<img class="badge-avatar" src="img/team3.jpg">
<div class="badge-body">
<div class="badge-name">Jessica White</div>
<div class="badge-role">Web Designer</div>
<div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>
</div>
<div class="badge-foot">
<span class="badge-link">f</span>
<span class="badge-link">t</span>
<span class="badge-link">ln</span>
</div>
</div>
</div>
</div>
</section>
<footer class="footer-under">
<div class="container-width">
<div class="footer-container">
<div class="foot-lists">
<div class="foot-list">
<div class="foot-list-title">About us</div>
<div class="foot-list-item">Contact</div>
<div class="foot-list-item">Events</div>
<div class="foot-list-item">Company</div>
<div class="foot-list-item">Jobs</div>
<div class="foot-list-item">Blog</div>
</div>
<div class="foot-list">
<div class="foot-list-title">Services</div>
<div class="foot-list-item">Education</div>
<div class="foot-list-item">Partner</div>
<div class="foot-list-item">Community</div>
<div class="foot-list-item">Forum</div>
<div class="foot-list-item">Download</div>
<div class="foot-list-item">Upgrade</div>
</div>
<div class="clearfix"></div>
</div>
<div class="form-sub">
<div class="foot-form-cont">
<div class="foot-form-title">Subscribe</div>
<div class="foot-form-desc">Subscribe to our newsletter to receive exclusive offers and the latest news</div>
<input name="name" class="sub-input" placeholder="Name" />
<input name="email" class="sub-input" placeholder="Email"/>
<button class="sub-btn" type="button">Submit</button>
</div>
</div>
</div>
</div>
<div class="copyright">
<div class="container-width">
<div class="made-with">
made with GrapesJS
</div>
<div class="foot-social-btns">facebook twitter linkedin mail</div>
<div class="clearfix"></div>
</div>
</div>
</footer>
<style>
.clearfix{ clear:both}
.header-banner{
padding-top: 35px;
padding-bottom: 100px;
color: #ffffff;
font-family: Helvetica, serif;
font-weight: 100;
background-image:url("http://grapesjs.com/img/bg-gr-v.png"), url("http://grapesjs.com/img/work-desk.jpg");
background-attachment:scroll, scroll;
background-position:left top, center center;
background-repeat:repeat-y, no-repeat;
background-size: contain, cover;
}
.container-width{
width: 90%;
max-width: 1150px;
margin: 0 auto;
}
.logo-container{
float: left;
width: 50%;
}
.logo{
background-color: #fff;
border-radius: 5px;
width: 130px;
padding: 10px;
min-height: 30px;
text-align: center;
line-height: 30px;
color: #4d114f;
font-size: 23px;
}
.navbar{
float: right;
width: 50%;
}
.menu-item{
float:right;
font-size: 15px;
color:#eee;
width: 130px;
padding: 10px;
min-height: 50px;
text-align: center;
line-height: 30px;
font-weight: 400;
}
.lead-title{
margin: 150px 0 30px 0;
font-size: 40px;
}
.sub-lead-title{
max-width: 650px;
line-height:30px;
margin-bottom:30px;
color: #c6c6c6;
}
.lead-btn{
margin-top: 15px;
padding:10px;
width:190px;
min-height:30px;
font-size:20px;
text-align:center;
letter-spacing:3px;
line-height:30px;
background-color:#d983a6;
border-radius:5px;
transition: all 0.5s ease;
cursor: pointer;
}
.lead-btn:hover{
background-color:#ffffff;
color:#4c114e;
}
.lead-btn:active{
background-color:#4d114f;
color:#fff;
}
.flex-sect{
background-color: #fafafa;
padding: 100px 0;
font-family: Helvetica, serif;
}
.flex-title{
margin-bottom: 15px;
font-size: 2em;
text-align: center;
font-weight: 700;
color:#555;
padding: 5px;
}
.flex-desc{
margin-bottom: 55px;
font-size: 1em;
color: rgba(0, 0, 0, 0.5);
text-align: center;
padding: 5px;
}
.cards{
padding: 20px 0;
display: flex;
justify-content: space-around;
flex-flow: wrap;
}
.card{
background-color: white;
height: 300px;
width:300px;
margin-bottom:30px;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
border-radius: 2px;
transition: all 0.5s ease;
font-weight: 100;
overflow: hidden;
}
.card:hover{
margin-top: -5px;
box-shadow: 0 20px 30px 0 rgba(0, 0, 0, 0.2);
}
.card-header{
height: 155px;
background-image:url("http://placehold.it/350x250/78c5d6/fff/image1.jpg");
background-size:cover;
background-position:center center;
}
.card-header.ch2{
background-image:url("http://placehold.it/350x250/459ba8/fff/image2.jpg");
}
.card-header.ch3{
background-image:url("http://placehold.it/350x250/79c267/fff/image3.jpg");
}
.card-header.ch4{
background-image:url("http://placehold.it/350x250/c5d647/fff/image4.jpg");
}
.card-header.ch5{
background-image:url("http://placehold.it/350x250/f28c33/fff/image5.jpg");
}
.card-header.ch6{
background-image:url("http://placehold.it/350x250/e868a2/fff/image6.jpg");
}
.card-body{
padding: 15px 15px 5px 15px;
color: #555;
}
.card-title{
font-size: 1.4em;
margin-bottom: 5px;
}
.card-sub-title{
color: #b3b3b3;
font-size: 1em;
margin-bottom: 15px;
}
.card-desc{
font-size: 0.85rem;
line-height: 17px;
}
.am-sect{
padding-top: 100px;
padding-bottom: 100px;
font-family: Helvetica, serif;
}
.img-phone{
float: left;
}
.am-container{
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-around;
}
/*
.am-container{
perspective: 1000px;
}*/
.am-content{
float:left;
padding:7px;
width: 490px;
color: #444;
font-weight: 100;
margin-top: 50px;
/*transform: rotateX(0deg) rotateY(-20deg) rotateZ(0deg) scaleX(1) scaleY(1) scaleZ(1);*/
}
.am-pre{
padding:7px;
color:#b1b1b1;
font-size:15px;
}
.am-title{
padding:7px;
font-size:25px;
font-weight: 400;
}
.am-desc{
padding:7px;
font-size:17px;
line-height:25px;
}
.am-post{
padding:7px;
line-height:25px;
font-size:13px;
}
.blk-sect{
padding-top: 100px;
padding-bottom: 100px;
background-color: #222222;
font-family: Helvetica, serif;
}
.blk-title{
color:#fff;
font-size:25px;
text-align:center;
margin-bottom: 15px;
}
.blk-desc{
color:#b1b1b1;
font-size:15px;
text-align:center;
max-width:700px;
margin:0 auto;
font-weight:100;
}
.price-cards{
margin-top: 70px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-around;
}
.price-card-cont{
width: 300px;
padding: 7px;
float:left;
}
.price-card{
margin:0 auto;
min-height:350px;
background-color:#d983a6;
border-radius:5px;
font-weight: 100;
color: #fff;
width: 90%;
}
.pc-title{
font-weight:100;
letter-spacing:3px;
text-align:center;
font-size:25px;
background-color:rgba(0, 0, 0, 0.1);
padding:20px;
}
.pc-desc{
padding: 75px 0;
text-align: center;
}
.pc-feature{
color:rgba(255,255,255,0.5);
background-color:rgba(0, 0, 0, 0.1);
letter-spacing:2px;
font-size:15px;
padding:10px 20px;
}
.pc-feature:nth-of-type(2n){
background-color:transparent;
}
.pc-amount{
background-color:rgba(0, 0, 0, 0.1);
font-size: 35px;
text-align: center;
padding: 35px 0;
}
.pc-regular{
background-color: #da78a0;
}
.pc-enterprise{
background-color: #d66a96;
}
.footer-under{
background-color: #312833;
padding-bottom: 100px;
padding-top: 100px;
min-height: 500px;
color:#eee;
position: relative;
font-weight: 100;
font-family: Helvetica,serif;
}
.led{
border-radius: 100%;
width: 10px;
height: 10px;
background-color: rgba(0, 0, 0, 0.15);
float: left;
margin: 2px;
transition: all 5s ease;
}
.led:hover{
background-color: #c29fca;/* #eac229 */
box-shadow: 0 0 5px #9d7aa5, 0 0 10px #e6c3ee;/* 0 0 10px 0 #efc111 */
transition: all 0s ease;
}
.copyright {
background-color: rgba(0, 0, 0, 0.15);
color: rgba(238, 238, 238, 0.5);
bottom: 0;
padding: 1em 0;
position: absolute;
width: 100%;
font-size: 0.75em;
}
.made-with{
float: left;
width: 50%;
padding: 5px 0;
}
.foot-social-btns{
display: none;
float: right;
width: 50%;
text-align: right;
padding: 5px 0;
}
.footer-container{
display: flex;
flex-wrap: wrap;
align-items: stretch;
justify-content: space-around;
}
.foot-list {
float: left;
width: 200px;
}
.foot-list-title {
font-weight: 400;
margin-bottom: 10px;
padding: 0.5em 0;
}
.foot-list-item {
color: rgba(238, 238, 238, 0.8);
font-size: 0.8em;
padding: 0.5em 0;
}
.foot-list-item:hover {
color: rgba(238, 238, 238, 1);
}
.foot-form-cont{
width: 300px;
float: right;
}
.foot-form-title{
color: rgba(255,255,255,0.75);
font-weight: 400;
margin-bottom: 10px;
padding: 0.5em 0;
text-align: right;
font-size: 2em;
}
.foot-form-desc{
font-size: 0.8em;
color: rgba(255,255,255,0.55);
line-height: 20px;
text-align: right;
margin-bottom: 15px;
}
.sub-input{
width: 100%;
margin-bottom: 15px;
padding: 7px 10px;
border-radius: 2px;
color:#fff;
background-color: #554c57;
border: none;
}
.sub-btn{
width: 100%;
margin-bottom: 15px;
background-color: #785580;
border: none;
color:#fff;
border-radius: 2px;
padding: 7px 10px;
font-size: 1em;
cursor: pointer;
}
.sub-btn:hover{
background-color: #91699a;
}
.sub-btn:active{
background-color: #573f5c;
}
.blk-row::after{
content: "";
clear: both;
display: block;
}
.blk-row{
padding: 10px;
}
.blk1{
width: 100%;
padding: 10px;
min-height: 75px;
}
.blk2{
float: left;
width: 50%;
padding: 10px;
min-height: 75px;
}
.blk3{
float: left;
width: 33.3333%;
padding: 10px;
min-height: 75px;
}
.blk37l{
float: left;
width: 30%;
padding: 10px;
min-height: 75px;
}
.blk37r{
float: left;
width: 70%;
padding: 10px;
min-height: 75px;
}
.heading{padding: 10px;}
.paragraph{padding: 10px;}
.bdg-sect{
padding-top:100px;
padding-bottom:100px;
font-family: Helvetica, serif;
background-color: #fafafa;
}
.bdg-title{
text-align: center;
font-size: 2em;
margin-bottom: 55px;
color: #555555;
}
.badges{
padding:20px;
display: flex;
justify-content: space-around;
align-items: flex-start;
flex-wrap: wrap;
}
.badge{
width: 290px;
font-family: Helvetica, serif;
background-color: white;
margin-bottom:30px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.2);
border-radius: 3px;
font-weight: 100;
overflow: hidden;
text-align: center;
}
.badge-header{
height: 115px;
background-image:url("http://grapesjs.com/img/bg-gr-v.png"), url("http://grapesjs.com/img/work-desk.jpg");
background-position:left top, center center;
background-attachment:scroll, fixed;
overflow: hidden;
}
.blurer{
filter: blur(5px);
}
.badge-name{
font-size: 1.4em;
margin-bottom: 5px;
}
.badge-role{
color: #777;
font-size: 1em;
margin-bottom: 25px;
}
.badge-desc{
font-size: 0.85rem;
line-height: 20px;
}
.badge-avatar{
width:100px;
height:100px;
border-radius: 100%;
border: 5px solid #fff;
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.2);
margin-top: -75px;
position: relative;
}
.badge-body{
margin: 35px 10px;
}
.badge-foot{
color:#fff;
background-color:#a290a5;
padding-top:13px;
padding-bottom:13px;
display: flex;
justify-content: center;
}
.badge-link{
height: 35px;
width: 35px;
line-height: 35px;
font-weight: 700;
background-color: #fff;
color: #a290a5;
display: block;
border-radius: 100%;
margin: 0 10px;
}
.quote{
color:#777;
font-weight: 300;
padding: 10px;
box-shadow: -5px 0 0 0 #ccc;
font-style: italic;
margin: 20px 30px;
}
@media (max-width: 768px){
.foot-form-cont{
width:400px;
}
.foot-form-title{
width: auto;
}
}
@media (max-width: 480px){
.foot-lists{
display:none;
}
}
</style>
</div>
<script data-main="src/demo" src="vendor/require/require.js"></script>
</body>
</html>

17
package.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Open source Web Template Editor",
"version": "0.1.7",
"version": "0.3.20",
"author": "Artur Arseniev",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",
@ -14,6 +14,7 @@
"bower": "^1.7.2"
},
"devDependencies": {
"documentation": "^4.0.0-beta2",
"grunt": "^0.4.5",
"grunt-bowercopy": "^1.2.4",
"grunt-cli": "^0.1.13",
@ -29,13 +30,20 @@
"grunt-mocha": "^0.4.15",
"grunt-sass": "^1.1.0",
"istanbul": "^0.4.2",
"node-sass": "^3.4.2"
"bower": "^1.7.2",
"node-sass": "^3.4.2",
"svg2ttf": "^4.0.1",
"ttf2eot": "^2.0.0",
"ttf2woff": "^2.0.1",
"ttf2woff2": "^2.0.3"
},
"keywords": [
"wte",
"grapes",
"grapesjs",
"web template editor",
"web site builder",
"web template builder",
"site builder",
"newsletter builder",
"wysiwyg",
@ -43,9 +51,10 @@
"editor"
],
"scripts": {
"postinstall": "node ./node_modules/bower/bin/bower install --config.interactive=false",
"postinstall": "./node_modules/.bin/bower install --config.interactive=false",
"build": "./node_modules/.bin/grunt build",
"build:fonts": "./node_modules/.bin/grunt build:fonts",
"test": "./node_modules/.bin/grunt test",
"dev": "./node_modules/.bin/grunt dev"
"start": "./node_modules/.bin/grunt dev"
}
}

45
src/asset_manager/config/config.js

@ -1,52 +1,17 @@
define(function () {
return {
// Style prefix
stylePrefix : 'am-',
// Default assets
assets : [],
// Indicates which storage to use. Available: local | remote
storageType : 'local',
// The name that will be used to identify assets inside storage.
// If empty will be used: prefix + 'assets'
storageName : 'assets',
// Where store remote assets
urlStore : 'http://localhost/assets/store',
// Where fetch remote assets
urlLoad : 'http://localhost/assets/load',
// Custom parameters to pass with set request
paramsStore : {},
assets: [],
// Custom parameters to pass with get request
paramsLoad : {},
// Callback before request
beforeSend : function(jqXHR,settings){},
// Callback after request
onComplete : function(jqXHR,status){},
// Style prefix
stylePrefix: 'am-',
// Url where uploads will be send
urlUpload : 'http://localhost/assets/upload',
upload: 'http://localhost/assets/upload',
// Text on upload input
uploadText : 'Drop files here or click to upload',
// Disable upload input
disableUpload : false,
// Store assets data where the new one is added or deleted
storeOnChange : true,
// It could be useful avoid to send other requests, for saving assets,
// after each upload because the uploader script has already done it
storeAfterUpload : false,
uploadText: 'Drop files here or click to upload',
};
});

252
src/asset_manager/main.js

@ -1,74 +1,222 @@
/**
* * [add](#add)
* * [get](#get)
* * [getAll](#getall)
* * [remove](#remove)
* * [store](#store)
* * [load](#load)
*
* Before using this methods you should get first the module from the editor instance, in this way:
*
* ```js
* var assetManager = editor.AssetManager;
* ```
*
* @module AssetManager
* @param {Object} config Configurations
* @param {Array<Object>} [config.assets=[]] Default assets
* @param {String} [config.uploadText='Drop files here or click to upload'] Upload text
* @param {String} [config.upload=''] Where to send upload data. Expects as return a JSON with asset/s object
* as: {data: [{src:'...'}, {src:'...'}]}
* @return {this}
* @example
* ...
* {
* assets: [
* {src:'path/to/image.png'},
* ...
* ],
* upload: 'http://dropbox/path', // Set to false to disable it
* uploadText: 'Drop files here or click to upload',
* }
*/
define(function(require) {
/**
* @class AssetManager
* @param {Object} Configurations
*
* @return {Object}
* */
var AssetManager = function(config)
{
var c = config || {},
defaults = require('./config/config'),
Assets = require('./model/Assets'),
AssetsView = require('./view/AssetsView'),
FileUpload = require('./view/FileUploader');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.assets = new Assets(c.assets);
var obj = {
collection : this.assets,
config : c,
};
this.am = new AssetsView(obj);
this.fu = new FileUpload(obj);
};
return function() {
var c = {},
Assets = require('./model/Assets'),
AssetsView = require('./view/AssetsView'),
FileUpload = require('./view/FileUploader'),
assets, am, fu;
return {
/**
* Name of the module
* @type {String}
* @private
*/
name: 'AssetManager',
/**
* Mandatory for the storage manager
* @type {String}
* @private
*/
storageKey: 'assets',
/**
* Initialize module
* @param {Object} config Configurations
* @private
*/
init: function(config){
c = config || {};
var defaults = require('./config/config');
AssetManager.prototype = {
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
assets = new Assets(c.assets);
var obj = {
collection: assets,
config: c,
};
am = new AssetsView(obj);
fu = new FileUpload(obj);
return this;
},
/**
* Get collection of assets
* Add new asset/s to the collection. URLs are supposed to be unique
* @param {string|Object|Array<string>|Array<Object>} asset URL strings or an objects representing the resource.
* @return {Model}
* @example
* // In case of strings, would be interpreted as images
* assetManager.add('http://img.jpg');
* assetManager.add(['http://img.jpg', './path/to/img.png']);
*
* @return {Object}
* */
getAssets : function(){
return this.assets;
* // Using objects you could indicate the type and other meta informations
* assetManager.add({
* src: 'http://img.jpg',
* //type: 'image', //image is default
* height: 300,
* width: 200,
* });
* assetManager.add([{
* src: 'http://img.jpg',
* },{
* src: './path/to/img.png',
* }]);
*/
add: function(asset){
return assets.add(asset);
},
/**
* Set new target
* @param {Object} m Model
*
* @return void
* */
setTarget : function(m){
this.am.collection.target = m;
* Returns the asset by URL
* @param {string} src URL of the asset
* @return {Object} Object representing the asset
* @example
* var asset = assetManager.get('http://img.jpg');
*/
get: function(src){
return assets.where({src: src})[0];
},
/**
* Set callback after asset was selected
* @param {Object} f Callback function
* Return all assets
* @return {Collection}
*/
getAll: function(){
return assets;
},
/**
* Remove the asset by its URL
* @param {string} src URL of the asset
* @return {this}
* @example
* assetManager.remove('http://img.jpg');
*/
remove: function(src){
var asset = this.get(src);
this.getAll().remove(asset);
return this;
},
/**
* Store assets data to the selected storage
* @param {Boolean} noStore If true, won't store
* @return {Object} Data to store
* @example
* var assets = assetManager.store();
*/
store: function(noStore){
var obj = {};
var assets = JSON.stringify(this.getAll().toJSON());
obj[this.storageKey] = assets;
if(!noStore && c.stm)
c.stm.store(obj);
return obj;
},
/**
* Load data from the passed object, if the object is empty will try to fetch them
* autonomously from the storage manager.
* The fetched data will be added to the collection
* @param {Object} data Object of data to load
* @return {Object} Loaded assets
* @example
* var assets = assetManager.load();
* // The format below will be used by the editor model
* // to load automatically all the stuff
* var assets = assetManager.load({
* assets: [...]
* });
*
* @return void
* */
onSelect : function(f){
this.am.collection.onSelect = f;
*/
load: function(data){
var d = data || '';
var name = this.storageKey;
if(!d && c.stm)
d = c.stm.load(name);
var assets = [];
try{
assets = JSON.parse(d[name]);
}catch(err){}
this.getAll().add(assets);
return assets;
},
/**
* Render
* @param {Boolean} f Force to render
* Render assets
* @param {Boolean} f Force to render, otherwise cached version will be returned
* @return {HTMLElement}
* @private
*/
render : function(f){
render: function(f){
if(!this.rendered || f)
this.rendered = this.am.render().$el.add(this.fu.render().$el);
this.rendered = am.render().$el.add(fu.render().$el);
return this.rendered;
},
};
return AssetManager;
//-------
/**
* Set new target
* @param {Object} m Model
* @private
* */
setTarget: function(m){
am.collection.target = m;
},
/**
* Set callback after asset was selected
* @param {Object} f Callback function
* @private
* */
onSelect: function(f){
am.collection.onSelect = f;
},
};
};
});

41
src/asset_manager/model/Asset.js

@ -1,36 +1,31 @@
define(['backbone'],
define(['backbone'],
function (Backbone) {
/**
* @class Asset
* */
return Backbone.Model.extend({
return Backbone.Model.extend({
idAttribute: 'src',
defaults: {
type: 'none', //Type of the asset
src: '', //Location
type: '',
src: '',
},
initialize: function(options) {
this.options = options || {};
},
/**
* Get filename of the asset
*
* @return {String}
* Get filename of the asset
* @return {string}
* @private
* */
getFilename: function(){
return this.get('src').split('/').pop();
return this.get('src').split('/').pop();
},
/**
* Get extension of the asset
*
* @return {String}
* Get extension of the asset
* @return {string}
* @private
* */
getExtension: function(){
return this.getFilename().split('.').pop();
return this.getFilename().split('.').pop();
},
});
});

25
src/asset_manager/model/AssetImage.js

@ -1,18 +1,13 @@
define(['backbone', './Asset'],
define(['backbone', './Asset'],
function (Backbone, Asset) {
/**
* @class AssetImage
* */
return Asset.extend({
defaults: _.extend({},Asset.prototype.defaults,
{
type: 'image',
unitDim: 'px',
height: 0,
width: 0,
}
),
return Asset.extend({
defaults: _.extend({}, Asset.prototype.defaults, {
type: 'image',
unitDim: 'px',
height: 0,
width: 0,
}),
});
});

74
src/asset_manager/model/Assets.js

@ -1,11 +1,67 @@
define(['backbone','./Asset'],
function (Backbone, Asset) {
/**
* @class Assets
* */
return Backbone.Collection.extend({
model: Asset,
define(['backbone', './Asset', './AssetImage'],
function (Backbone, Asset, AssetImage) {
return Backbone.Collection.extend({
model: AssetImage,
initialize: function(models, opt){
this.model = function(attrs, options) {
var model;
switch(attrs.type){
default:
model = new AssetImage(attrs, options);
}
return model;
};
},
/**
* Add new image asset to the collection
* @param {string} url URL of the image
* @param {Object} opts Options
* @return {this}
* @private
*/
addImg: function(url, opts){
this.add({
type: 'image',
src: url,
}, opts);
return this;
},
/**
* Prevent inserting assets with the same 'src'
* Seems like idAttribute is not working with dynamic model assignament
* @private
*/
add: function(models, opt) {
var mods = [];
models = models instanceof Array ? models : [models];
for (var i = 0, len = models.length; i < len; i++) {
var model = models[i];
if(typeof model === 'string')
model = {src: model, type: 'image'};
if(!model || !model.src)
continue;
var found = this.where({src: model.src});
if(!found.length)
mods.push(model);
}
if(mods.length == 1)
mods = mods[0];
return Backbone.Collection.prototype.add.apply(this, [mods, opt]);
},
});
});

5
src/asset_manager/template/assetImage.html

@ -1,4 +1,7 @@
<div id="<%= pfx %>preview" style="background-image: url(<%= src %>);"></div>
<div id="<%= pfx %>preview-cont">
<div id="<%= pfx %>preview" style="background-image: url(<%= src %>);"></div>
<div id="<%= pfx %>preview-bg" class="<%= ppfx %>checker-bg"></div>
</div>
<div id="<%= pfx %>meta">
<div id="<%= pfx %>name"><%= name %></div>
<div id="<%= pfx %>dimensions"><%= dim %></div>

15
src/asset_manager/template/assets.html

@ -0,0 +1,15 @@
<div class="<%= pfx %>assets-cont">
<div class="<%= pfx %>assets-header">
<form id="login-form" class="<%= pfx %>add-asset">
<input class="<%= ppfx %>input" placeholder="http://path/to/the/image.jpg" />
<button class="<%= ppfx %>btn-prim">Add image</button>
<div style="clear:both"></div>
</form>
<div class="<%= pfx %>dips" style="display:none">
<button class="fa fa-th <%= ppfx %>btnt"></button>
<button class="fa fa-th-list <%= ppfx %>btnt"></button>
</div>
</div>
<div class="<%= pfx %>assets"></div>
<div style="clear:both"></div>
</div>

19
src/asset_manager/view/AssetImageView.js

@ -1,8 +1,5 @@
define(['./AssetView','text!./../template/assetImage.html'],
function (AssetView, assetTemplate) {
/**
* @class AssetImageView
* */
return AssetView.extend({
events:{
@ -21,8 +18,7 @@ define(['./AssetView','text!./../template/assetImage.html'],
/**
* Trigger when asset is been selected
*
* @return void
* @private
* */
selected: function(){
this.model.collection.trigger('deselectAll');
@ -33,8 +29,7 @@ define(['./AssetView','text!./../template/assetImage.html'],
/**
* Trigger when asset is been chosen (double clicked)
*
* @return void
* @private
* */
chosen: function(){
this.updateTarget(this.model.get('src'));
@ -47,14 +42,12 @@ define(['./AssetView','text!./../template/assetImage.html'],
/**
* Update target if exists
* @param {String} v Value
*
* @return void
* @private
* */
updateTarget: function(v){
var target = this.model.collection.target;
if(target && target.set){
var attr = _.clone( target.get('attributes') );
attr['class'] = [];
target.set('attributes', attr );
target.set('src', v );
}
@ -62,8 +55,7 @@ define(['./AssetView','text!./../template/assetImage.html'],
/**
* Remove asset from collection
*
* @return void
* @private
* */
removeItem: function(e){
e.stopPropagation();
@ -81,7 +73,8 @@ define(['./AssetView','text!./../template/assetImage.html'],
name: name,
src: this.model.get('src'),
dim: dim,
pfx: this.pfx
pfx: this.pfx,
ppfx: this.ppfx
}));
this.$el.attr('class', this.className);
return this;

1
src/asset_manager/view/AssetView.js

@ -9,6 +9,7 @@ define(['backbone'],
this.options = o;
this.config = o.config || {};
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.className = this.pfx + 'asset';
this.listenTo( this.model, 'destroy remove', this.remove );
},

114
src/asset_manager/view/AssetsView.js

@ -1,67 +1,71 @@
define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
function (Backbone, AssetView, AssetImageView, FileUploader) {
/**
* @class AssetsView
* */
define(['backbone', './AssetView', './AssetImageView', './FileUploader', 'text!./../template/assets.html'],
function (Backbone, AssetView, AssetImageView, FileUploader, assetsTemplate) {
return Backbone.View.extend({
template: _.template(assetsTemplate),
initialize: function(o) {
this.options = o;
this.config = o.config;
this.pfx = this.config.stylePrefix;
this.options = o;
this.config = o.config;
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.listenTo( this.collection, 'add', this.addToAsset );
this.listenTo( this.collection, 'deselectAll', this.deselectAll );
this.className = this.pfx + 'assets';
// Check if storage is required and if Storage Manager is available
if(this.config.stm && this.config.storageType !== ''){
var type = this.config.storageType;
this.provider = this.config.stm.getProvider(type);
this.storeName = this.config.storageName ? this.config.storageName : this.className;
if(this.provider){
// Create new instance of provider
this.storagePrv = this.provider.clone().set(this.config);
this.collection.reset();
this.collection.add(this.load());
if(this.config.storeOnChange){
var ev = 'remove' + (this.config.storeAfterUpload ? ' add' : '');
this.listenTo(this.collection, ev, this.store);
}
}
}
this.events = {};
this.events.submit = 'addFromStr';
this.delegateEvents();
},
/**
* Store collection
*
* @return void
* */
store: function(){
if(this.storagePrv)
this.storagePrv.store(this.storeName, JSON.stringify(this.collection.toJSON()) );
* Add new asset to the collection via string
* @param {Event} e Event object
* @return {this}
* @private
*/
addFromStr: function(e){
e.preventDefault();
var input = this.getInputUrl();
var url = input.value.trim();
if(!url)
return;
this.collection.addImg(url, {at: 0});
this.getAssetsEl().scrollTop = 0;
input.value = '';
return this;
},
/**
* Load collection
*
* @return {Object}
* */
load: function(){
var result = null;
if(this.storagePrv)
result = this.storagePrv.load(this.storeName);
if(typeof result !== 'object'){
try{
result = JSON.parse(result);
}catch(err){
console.warn(err);
}
}
return result;
* Returns assets element
* @return {HTMLElement}
* @private
*/
getAssetsEl: function(){
//if(!this.assets) // Not able to cache as after the rerender it losses the ref
this.assets = this.el.querySelector('.' + this.pfx + 'assets');
return this.assets;
},
/**
* Returns input url element
* @return {HTMLElement}
* @private
*/
getInputUrl: function(){
if(!this.inputUrl || !this.inputUrl.value)
this.inputUrl = this.el.querySelector('.'+this.pfx+'add-asset input');
return this.inputUrl;
},
/**
* Add asset to collection
* @private
* */
addToAsset: function(model){
this.addAsset(model);
@ -71,8 +75,8 @@ define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
* Add new asset to collection
* @param Object Model
* @param Object Fragment collection
*
* @return Object Object created
* @private
* */
addAsset: function(model, fragmentEl){
var fragment = fragmentEl || null;
@ -90,7 +94,9 @@ define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
if(fragment){
fragment.appendChild( rendered );
}else{
this.$el.prepend(rendered);
var assetsEl = this.getAssetsEl();
if(assetsEl)
assetsEl.insertBefore(rendered, assetsEl.firstChild);
}
return rendered;
@ -98,8 +104,7 @@ define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
/**
* Deselect all assets
*
* @return void
* @private
* */
deselectAll: function(){
this.$el.find('.' + this.pfx + 'highlight').removeClass(this.pfx + 'highlight');
@ -113,9 +118,12 @@ define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
this.addAsset(model, fragment);
},this);
this.$el.append(fragment);
this.$el.attr('class', this.className);
this.$el.html(this.template({
pfx: this.pfx,
ppfx: this.ppfx,
}));
this.$el.find('.'+this.pfx + 'assets').append(fragment);
return this;
}
});

14
src/asset_manager/view/FileUploader.js

@ -1,9 +1,5 @@
define(['backbone', 'text!./../template/fileUploader.html'],
function (Backbone, fileUploaderTemplate) {
/**
* @class FileUploader
* */
return Backbone.View.extend({
template: _.template(fileUploaderTemplate),
@ -16,7 +12,7 @@ define(['backbone', 'text!./../template/fileUploader.html'],
this.pfx = this.config.stylePrefix || '';
this.target = this.collection || {};
this.uploadId = this.pfx + 'uploadFile';
this.disabled = this.config.disableUpload;
this.disabled = !this.config.upload;
this.events['change #' + this.uploadId] = 'uploadFile';
this.delegateEvents();
},
@ -24,8 +20,7 @@ define(['backbone', 'text!./../template/fileUploader.html'],
/**
* Upload files
* @param {Object} e Event
*
* @return void
* @private
* */
uploadFile : function(e){
var files = e.dataTransfer ? e.dataTransfer.files : e.target.files,
@ -35,7 +30,7 @@ define(['backbone', 'text!./../template/fileUploader.html'],
}
var target = this.target;
$.ajax({
url : this.config.urlUpload, //this.config.urlUpload
url : this.config.upload,
type : 'POST',
data : formData,
beforeSend : this.config.beforeSend,
@ -60,8 +55,7 @@ define(['backbone', 'text!./../template/fileUploader.html'],
/**
* Make input file droppable
*
* @return void
* @private
* */
initDrop: function(){
var that = this;

7
src/block_manager/config/config.js

@ -0,0 +1,7 @@
define(function () {
return {
'blocks': [],
};
});

122
src/block_manager/main.js

@ -0,0 +1,122 @@
/**
* * [add](#add)
* * [get](#get)
* * [getAll](#getall)
* * [render](#render)
*
* Block manager helps managing various, draggable, piece of contents that could be easily reused inside templates.
*
* Before using methods you should get first the module from the editor instance, in this way:
*
* ```js
* var blockManager = editor.BlockManager;
* ```
*
* @module BlockManager
* @param {Object} config Configurations
* @param {Array<Object>} [config.blocks=[]] Default blocks
* @example
* ...
* {
* blocks: [
* {id:'h1-block' label: 'Heading', content:'<h1>...</h1>'},
* ...
* ],
* }
* ...
*/
define(function(require) {
return function() {
var c = {},
defaults = require('./config/config'),
Blocks = require('./model/Blocks'),
BlocksView = require('./view/BlocksView');
var blocks, view;
return {
/**
* Name of the module
* @type {String}
* @private
*/
name: 'BlockManager',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @return {this}
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
blocks = new Blocks(c.blocks);
view = new BlocksView({ collection: blocks }, c);
return this;
},
/**
* Add new block to the collection.
* @param {string} id Block id
* @param {Object} opts Options
* @param {string} opts.label Name of the block
* @param {string} opts.content HTML content
* @param {Object} [opts.attributes={}] Block attributes
* @return {Block} Added block
* @example
* blockManager.add('h1-block', {
* label: 'Heading',
* content: '<h1>Put your title here</h1>',
* attributes: {
* title: 'Insert h1 block'
* }
* });
*/
add: function(id, opts){
var obj = opts || {};
obj.id = id;
return blocks.add(obj);
},
/**
* Return block by id
* @param {string} id Block id
* @example
* var block = blockManager.get('h1-block');
* console.log(JSON.stringify(block));
* // {label: 'Heading', content: '<h1>Put your ...', ...}
*/
get: function(id){
return blocks.get(id);
},
/**
* Return all blocks
* @return {Collection}
* @example
* var blocks = blockManager.getAll();
* console.log(JSON.stringify(blocks));
* // [{label: 'Heading', content: '<h1>Put your ...'}, ...]
*/
getAll: function(){
return blocks;
},
/**
* Render blocks
* @return {HTMLElement}
*/
render: function(){
return view.render().el;
},
};
};
});

13
src/block_manager/model/Block.js

@ -0,0 +1,13 @@
define(['backbone'],
function(Backbone){
return Backbone.Model.extend({
defaults :{
label: '',
content: '',
attributes: {},
},
});
});

9
src/block_manager/model/Blocks.js

@ -0,0 +1,9 @@
define(['backbone','./Block'],
function (Backbone, Block) {
return Backbone.Collection.extend({
model: Block,
});
});

48
src/block_manager/view/BlockView.js

@ -0,0 +1,48 @@
define(['backbone'],
function(Backbone) {
return Backbone.View.extend({
events: {
mousedown: 'onDrag'
},
initialize: function(o, config) {
_.bindAll(this, 'onDrop');
this.config = config || {};
this.ppfx = this.config.pStylePrefix || '';
this.listenTo(this.model, 'destroy', this.remove);
this.doc = $(document);
},
/**
* Start block dragging
* @private
*/
onDrag: function(){
if(!this.config.getSorter)
return;
var sorter = this.config.getSorter();
//this.config.dragHelper(this.el.cloneNode(1));
sorter.startSort(this.el);
sorter.setDropContent(this.model.get('content'));
this.doc.on('mouseup', this.onDrop);
},
/**
* Drop block
* @private
*/
onDrop: function(){
this.doc.off('mouseup', this.onDrop);
this.config.getSorter().endMove();
},
render: function() {
this.el.innerHTML = this.model.get('label');
this.$el.addClass(this.ppfx + 'block');
return this;
},
});
});

140
src/block_manager/view/BlocksView.js

@ -0,0 +1,140 @@
define(['backbone', './BlockView'],
function(Backbone, BlockView) {
return Backbone.View.extend({
initialize: function(opts, config) {
_.bindAll(this, 'getSorter', 'onDrag', 'onDrop', 'dragHelper', 'moveHelper');
this.config = config || {};
this.ppfx = this.config.pStylePrefix || '';
this.listenTo(this.collection, 'add', this.addTo);
this.em = this.config.em;
this.tac = 'test-tac';
if(this.em){
this.config.getSorter = this.getSorter;
this.config.dragHelper = this.dragHelper;
this.canvas = this.em.get('Canvas');
}
},
/**
* Get sorter
* @private
*/
getSorter: function(){
if(!this.em)
return;
if(!this.sorter){
var utils = this.em.get('Utils');
var canvas = this.canvas;
this.sorter = new utils.Sorter({
container: canvas.getBody(),
placer: canvas.getPlacerEl(),
containerSel: '*',
itemSel: '*',
pfx: this.ppfx,
onStart: this.onDrag,
onEndMove: this.onDrop,
document: canvas.getFrameEl().contentDocument,
direction: 'a',
wmargin: 1,
nested: 1,
em: this.em
});
}
return this.sorter;
},
/*
updateOffset: function(){
if(!this.sorter)
return;
var sorter = this.sorter;
var offDim = this.canvas.getOffset();
sorter.offTop = offDim.top;
sorter.offLeft = offDim.left;
},
*/
/**
* Callback when block is on drag
* @private
*/
onDrag: function(){
this.em.stopDefault();
this.em.get('Canvas').getBody().style.cursor = 'grabbing';
document.body.style.cursor = 'grabbing';
},
dragHelper: function(el){
el.className += ' ' + this.ppfx + 'bdrag';
this.helper = el;
document.body.appendChild(el);
$(this.em.get('Canvas').getBody()).on('mousemove', this.moveHelper);
$(document).on('mousemove', this.moveHelper);
},
moveHelper: function(e){
this.helper.style.left = e.pageX + 'px';
this.helper.style.top = e.pageY + 'px';
},
/**
* Callback when block is dropped
* @private
*/
onDrop: function(model){
this.em.runDefault();
this.em.get('Canvas').getBody().style.cursor = '';
document.body.style.cursor = '';
if(model && model.get('activeOnRender')){
model.trigger('active');
model.set('activeOnRender', 0);
}
},
/**
* Add new model to the collection
* @param {Model} model
* @private
* */
addTo: function(model){
this.add(model);
},
/**
* Render new model inside the view
* @param {Model} model
* @param {Object} fragment Fragment collection
* @private
* */
add: function(model, fragment){
var frag = fragment || null;
var view = new BlockView({
model: model,
attributes: model.get('attributes'),
}, this.config);
var rendered = view.render().el;
if(frag)
frag.appendChild(rendered);
else
this.$el.append(rendered);
},
render: function() {
var frag = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.add(model, frag);
}, this);
this.$el.append(frag);
this.$el.addClass(this.ppfx + 'blocks-c');
return this;
},
});
});

10
src/canvas/config/config.js

@ -1,10 +1,10 @@
define(function () {
return {
stylePrefix : 'cv-',
stylePrefix: 'cv-',
// Coming soon
rulers : false,
rulers: false,
};
});

213
src/canvas/main.js

@ -1,60 +1,185 @@
define(function(require) {
/**
* @class Canvas
* @param {Object} Configurations
*
* @return {Object}
* @class Canvas}
* */
var Canvas = function(config)
{
var c = config || {},
defaults = require('./config/config'),
Canvas = require('./model/Canvas'),
CanvasView = require('./view/CanvasView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.canvas = new Canvas(config);
var obj = {
model : this.canvas,
config : c,
};
this.CanvasView = new CanvasView(obj);
};
Canvas.prototype = {
return function() {
var c = {},
defaults = require('./config/config'),
Canvas = require('./model/Canvas'),
CanvasView = require('./view/CanvasView');
var canvas;
return {
/**
* Used inside RTE
* @private
*/
getCanvasView: function() {
return CanvasView;
},
/**
* Name of the module
* @type {String}
* @private
*/
name: 'Canvas',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
canvas = new Canvas(config);
CanvasView = new CanvasView({
model: canvas,
config: c,
});
var cm = c.em.get('DomComponents');
if(cm)
this.setWrapper(cm);
return this;
},
/**
* Add wrapper
* @param {Object} wrp Wrapper
*
*
* */
setWrapper : function(wrp)
{
this.canvas.set('wrapper', wrp);
setWrapper: function(wrp) {
canvas.set('wrapper', wrp);
},
/**
* Get wrapper
*
* @return {Object}
* */
getWrapper : function()
{
return this.canvas.get('wrapper').getComponent();
* Returns canvas element
* @return {HTMLElement}
*/
getElement: function(){
return CanvasView.el;
},
/**
* Returns frame element of the canvas
* @return {HTMLElement}
*/
getFrameEl: function(){
return CanvasView.frame.el;
},
/**
* Returns body element of the frame
* @return {HTMLElement}
*/
getBody: function(){
return CanvasView.frame.el.contentDocument.body;
},
/**
* Returns body wrapper element of the frame
* @return {HTMLElement}
*/
getWrapperEl: function(){
return this.getBody().querySelector('#wrapper');
},
/**
* Returns element containing canvas tools
* @return {HTMLElement}
*/
getToolsEl: function(){
return CanvasView.toolsEl;
},
/**
* Returns highlighter element
* @return {HTMLElement}
*/
getHighlighter: function(){
return CanvasView.hlEl;
},
/**
* Returns badge element
* @return {HTMLElement}
*/
getBadgeEl: function(){
return CanvasView.badgeEl;
},
/**
* Returns placer element
* @return {HTMLElement}
*/
getPlacerEl: function(){
return CanvasView.placerEl;
},
/**
* Returns ghost element
* @return {HTMLElement}
* @private
*/
getGhostEl: function(){
return CanvasView.ghostEl;
},
/**
* Render canvas
* */
render : function()
{
return this.CanvasView.render().$el;
render: function() {
return CanvasView.render().el;
},
/**
* Get frame position
* @return {Object}
* @private
*/
getOffset: function() {
var frameOff = this.offset(this.getFrameEl());
var canvasOff = this.offset(this.getElement());
return {
top: frameOff.top - canvasOff.top,
left: frameOff.left - canvasOff.left
};
},
/**
* Get the offset of the element
* @param {HTMLElement} el
* @return {Object}
* @private
*/
offset: function(el){
var rect = el.getBoundingClientRect();
return {
top: rect.top + document.body.scrollTop,
left: rect.left + document.body.scrollLeft
};
},
/**
* Returns wrapper element
* @return {HTMLElement}
* ????
*/
getFrameWrapperEl: function(){
return CanvasView.frame.getWrapper();
},
};
};
return Canvas;
});

22
src/canvas/model/Canvas.js

@ -1,14 +1,18 @@
define(['backbone'],
function(Backbone){
/**
* @class Canvas
* */
define(['backbone', './Frame'],
function(Backbone, Frame){
return Backbone.Model.extend({
defaults :{
wrapper : '',
rulers : false,
frame: '',
wrapper: '',
rulers: false,
},
initialize: function(config) {
var conf = this.conf || {};
this.set('frame', new Frame(conf.frame));
},
});
});

14
src/canvas/model/Frame.js

@ -0,0 +1,14 @@
define(['backbone'],
function(Backbone){
return Backbone.Model.extend({
defaults :{
wrapper: '',
width: '',
height: '',
attributes: {},
},
});
});

156
src/canvas/view/CanvasView.js

@ -1,25 +1,159 @@
define(['backbone'],
function(Backbone) {
define(['backbone','./FrameView'],
function(Backbone, FrameView) {
/**
* @class CanvasView
* */
return Backbone.View.extend({
//id: 'canvas',
initialize: function(o) {
this.config = o.config;
_.bindAll(this, 'renderBody', 'onFrameScroll', 'clearOff');
this.config = o.config || {};
this.em = this.config.em || {};
this.ppfx = this.config.pStylePrefix || '';
this.className = this.config.stylePrefix + 'canvas';
this.listenTo(this.em, 'change:canvasOffset', this.clearOff);
this.frame = new FrameView({
model: this.model.get('frame'),
config: this.config
});
},
/**
* Update tools position
* @private
*/
onFrameScroll: function(){
var u = 'px';
var body = this.frame.el.contentDocument.body;
this.toolsEl.style.top = '-' + body.scrollTop + u;
this.toolsEl.style.left = '-' + body.scrollLeft + u;
},
/**
* Render inside frame's body
* @private
*/
renderBody: function(){
var wrap = this.model.get('frame').get('wrapper');
if(wrap){
var body = this.frame.$el.contents().find('body');
var cssc = this.config.em.get('CssComposer');
var conf = this.config.em.get('Config');
body.append(wrap.render()).append(cssc.render());
var protCss = conf.protectedCss;
var frameCss = '.' + this.ppfx + 'dashed *{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' +
'.' + this.ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' +
'.' + this.ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+
'.' + this.ppfx + 'freezed{opacity: 0.5; pointer-events: none}' +
'.' + this.ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}';
if(protCss)
body.append('<style>' + frameCss + protCss + '</style>');
this.config.em.trigger('loaded');
this.frame.el.contentWindow.onscroll = this.onFrameScroll;
this.frame.udpateOffset();
// When the iframe is focused the event dispatcher is not the same so
// I need to delegate all events to the parent document
var doc = document;
var fdoc = this.frame.el.contentDocument;
fdoc.addEventListener('keydown', function(e){
doc.dispatchEvent(new KeyboardEvent(e.type, e));
});
fdoc.addEventListener('keyup', function(e){
doc.dispatchEvent(new KeyboardEvent(e.type, e));
});
}
},
/**
* Get the offset of the element
* @param {HTMLElement} el
* @return {Object}
*/
offset: function(el){
var rect = el.getBoundingClientRect();
var docBody = el.ownerDocument.body;
return {
top: rect.top + docBody.scrollTop,
left: rect.left + docBody.scrollLeft
};
},
/**
* Cleare cached offsets
* @private
*/
clearOff: function(){
this.frmOff = null;
this.cvsOff = null;
},
/**
* Returns element's data info
* @param {HTMLElement} el
* @return {Object}
* @private
*/
getElementPos: function(el){
if(!this.frmOff)
this.frmOff = this.offset(this.frame.el);
if(!this.cvsOff)
this.cvsOff = this.offset(this.el);
var eo = this.offset(el);
var top = eo.top + this.frmOff.top - this.cvsOff.top;
var left = eo.left + this.frmOff.left - this.cvsOff.left;
return {
top: top,
left: left,
height: el.offsetHeight,
width: el.offsetWidth
};
},
/**
* Returns position data of the canvas element
* @return {Object} obj Position object
*/
getPosition: function() {
var bEl = this.frame.el.contentDocument.body;
var fo = this.frmOff;
var co = this.cvsOff;
return {
top: fo.top + bEl.scrollTop - co.top,
left: fo.left + bEl.scrollLeft - co.left
};
},
render: function() {
this.wrapper = this.model.get('wrapper');
if(this.wrapper && typeof this.wrapper.render == 'function'){
this.$el.append( this.wrapper.render() );
this.model.get('frame').set('wrapper', this.wrapper);
this.$el.append(this.frame.render().el);
var frame = this.frame;
frame.el.onload = this.renderBody;
}
this.$el.attr({class: this.className, id: this.config.canvasId});
this.toolsEl = $('<div>', { id: this.ppfx + 'tools' }).get(0);
this.hlEl = $('<div>', { class: this.ppfx + 'highlighter' }).get(0);
this.badgeEl = $('<div>', {class: this.ppfx + 'badge'}).get(0);
this.placerEl = $('<div>', {class: this.ppfx + 'placeholder'}).get(0);
this.placerIntEl = $('<div>', {class: this.ppfx + 'placeholder-int'}).get(0);
this.ghostEl = $('<div>', {class: this.ppfx + 'ghost'}).get(0);
this.placerEl.appendChild(this.placerIntEl);
this.toolsEl.appendChild(this.hlEl);
this.toolsEl.appendChild(this.badgeEl);
this.toolsEl.appendChild(this.placerEl);
this.toolsEl.appendChild(this.ghostEl);
this.$el.append(this.toolsEl);
var rte = this.em.get('rte');
if(rte)
this.toolsEl.appendChild(rte.render());
this.$el.attr({class: this.className});
return this;
},
});
});
});

54
src/canvas/view/FrameView.js

@ -0,0 +1,54 @@
define(['backbone'],
function(Backbone) {
/**
* @class CanvasView
* */
return Backbone.View.extend({
tagName: 'iframe',
attributes: {
src: 'about:blank'
},
initialize: function(o) {
_.bindAll(this, 'udpateOffset');
this.config = o.config || {};
this.ppfx = this.config.pStylePrefix || '';
this.em = this.config.em;
this.motionsEv = 'transitionend oTransitionEnd transitionend webkitTransitionEnd';
this.listenTo(this.em, 'change:device', this.updateWidth);
},
/**
* Update width of the frame
* @private
*/
updateWidth: function(model){
var device = this.em.getDeviceModel();
this.el.style.width = device ? device.get('width') : '';
this.udpateOffset();
this.$el.on(this.motionsEv, this.udpateOffset);
},
udpateOffset: function(){
var offset = this.em.get('Canvas').getOffset();
this.em.set('canvasOffset', offset);
this.$el.off(this.motionsEv, this.udpateOffset);
},
getBody: function(){
this.$el.contents().find('body');
},
getWrapper: function(){
return this.$el.contents().find('body > div');
},
render: function() {
this.$el.attr({class: this.ppfx + 'frame'});
return this;
},
});
});

63
src/class_manager/main.js

@ -1,63 +0,0 @@
define(function(require) {
/**
* @class ClassManager
* @param {Object} config Configurations
*
* */
var ClassManager = function(config)
{
var c = config || {},
def = require('./config/config');
this.ClassTags = require('./model/ClassTags');
this.ClassTagsView = require('./view/ClassTagsView');
for (var name in def) {
if (!(name in c))
c[name] = def[name];
}
this.classes = new this.ClassTags(c.defaults);
this.config = c;
};
ClassManager.prototype = {
/**
* Add new class to collection only if it's not already exists
* @param {String} name Class name
*
* @return {Object} Model class
* */
addClass: function(name){
var label = name;
var c = this.getClass(name);
if(!c)
return this.classes.add({name: name, label: label});
return c;
},
/**
* Get class by its name
* @param {String} id Class name
*
* @return {Object|null}
* */
getClass : function(id) {
var res = this.classes.where({name: id});
return res.length ? res[0] : null;
},
/**
* Get collection of classes
*
* @return {Object}
* */
getClasses : function() {
return this.classes;
},
};
return ClassManager;
});

29
src/class_manager/model/ClassTag.js

@ -1,29 +0,0 @@
define(['backbone'],
function (Backbone) {
/**
* @class ClassTag
* */
return Backbone.Model.extend({
defaults: {
label: '',
name: '',
active: true,
},
initialize: function(){
this.set('name', this.escapeName(this.get('name')));
},
/**
* Escape string
* @param {String} name
*
* @return {String}
*/
escapeName: function(name) {
return name.toLowerCase().replace(/([^a-z0-9\w]+)/gi, '-');
},
});
});

11
src/class_manager/model/ClassTags.js

@ -1,11 +0,0 @@
define(['backbone','./ClassTag'],
function (Backbone, ClassTag) {
/**
* @class ClassTags
* */
return Backbone.Collection.extend({
model: ClassTag,
});
});

3
src/class_manager/template/classTag.html

@ -1,3 +0,0 @@
<span id="<%= pfx %>checkbox" class="fa"></span>
<span id="<%= pfx %>tag-label"><%= label %></span>
<span id="<%= pfx %>close">&Cross;</span>

359
src/code_manager/main.js

@ -1,254 +1,225 @@
/**
* - [addGenerator](#addgenerator)
* - [getGenerator](#getgenerator)
* - [getGenerators](#getgenerators)
* - [addViewer](#addviewer)
* - [getViewer](#getviewer)
* - [getViewers](#getviewers)
* - [updateViewer](#updateviewer)
* - [getCode](#getcode)
*
*
* Before using methods you should get first the module from the editor instance, in this way:
*
* ```js
* var codeManager = editor.CodeManager;
* ```
*
* @module CodeManager
*/
define(function(require) {
/**
* @class CodeManager
* @param {Object} Configurations
*
* @return {Object}
* */
function CodeManager(config)
{
var c = config || {},
defaults = require('./config/config'),
gInterface = require('./model/GeneratorInterface'),
gHtml = require('./model/HtmlGenerator'),
gCss = require('./model/CssGenerator'),
gJson = require('./model/JsonGenerator'),
eInterface = require('./model/EditorInterface'),
eCM = require('./model/CodeMirrorEditor'),
editorView = require('./view/EditorView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.gi = new gInterface();
this.generators = {};
this.defaultGenerators = {};
this.currentGenerator = null;
this.ei = new eInterface();
this.editors = {};
this.defaultEditors = {};
this.currentEditor = null;
var geHtml = new gHtml(),
geCss = new gCss(),
geJson = new gJson(),
edCM = new eCM();
this.defaultGenerators[geHtml.getId()] = geHtml;
this.defaultGenerators[geCss.getId()] = geCss;
this.defaultGenerators[geJson.getId()] = geJson;
this.defaultEditors[edCM.getId()] = edCM;
this.EditorView = editorView;
this.config = c;
}
CodeManager.prototype = {
/**
* Add new code generator
* @param {GeneratorInterface} generator
*
* @return this
* */
addGenerator : function(generator)
{
// Check interface implementation
for (var method in this.gi)
if(!generator[method]){
console.warn("addGenerator: method '"+ method +"' was not found");
return;
}
var CodeManager = function() {
var id = generator.getId();
this.generators[id] = generator;
var c = {},
defaults = require('./config/config'),
gHtml = require('./model/HtmlGenerator'),
gCss = require('./model/CssGenerator'),
gJson = require('./model/JsonGenerator'),
eCM = require('./model/CodeMirrorEditor'),
editorView = require('./view/EditorView');
if(!this.currentGenerator)
this.currentGenerator = id;
var generators = {},
defGenerators = {},
viewers = {},
defViewers = {};
return this;
return {
getConfig: function() {
return c;
},
/**
* Returns generator
* @param {String}|{Integer} id Generator ID
*
* @return {GeneratorInterface}|null
* */
getGenerator : function(id)
{
if(id && this.generators[id])
generator = this.generators[id];
config: c,
return generator ? generator : null;
},
EditorView: editorView,
/**
* Returns generators
*
* @return {Array}
* */
getGenerators : function()
{
return this.generators;
},
* Name of the module
* @type {String}
* @private
*/
name: 'CodeManager',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
defGenerators.html = new gHtml();
defGenerators.css = new gCss();
defGenerators.json = new gJson();
defViewers.CodeMirror = new eCM();
return this;
},
/**
* Callback on load
*/
onLoad: function(){
this.loadDefaultGenerators().loadDefaultViewers();
},
/**
* Get current generator
*
* @return {GeneratorInterface}
* Add new code generator to the collection
* @param {string} id Code generator ID
* @param {Object} generator Code generator wrapper
* @param {Function} generator.build Function that builds the code
* @return {this}
* @example
* codeManager.addGenerator('html7',{
* build: function(model){
* return 'myCode';
* }
* });
* */
getCurrentGenerator : function()
{
if(!this.currentGenerator)
this.loadDefaultGenerators();
return this.getGenerator(this.currentGenerator);
addGenerator: function(id, generator) {
generators[id] = generator;
return this;
},
/**
* Set current generator
* @param {Integer} id Generator ID
*
* @return this
* Get code generator by id
* @param {string} id Code generator ID
* @return {Object|null}
* @example
* var generator = codeManager.getGenerator('html7');
* generator.build = function(model){
* //extend
* };
* */
setCurrentGenerator : function(id)
{
this.currentGenerator = id;
return this;
getGenerator: function(id) {
return generators[id] || null;
},
/**
* Load default generators
*
* @return this
* Returns all code generators
* @return {Array<Object>}
* */
loadDefaultGenerators : function()
{
for (var id in this.defaultGenerators) {
this.addGenerator(this.defaultGenerators[id]);
}
return this;
getGenerators: function() {
return generators;
},
/**
* Add new editor
* @param {EditorInterface} editor
*
* @return this
* Add new code viewer
* @param {string} id Code viewer ID
* @param {Object} viewer Code viewer wrapper
* @param {Function} viewer.init Set element on which viewer will be displayed
* @param {Function} viewer.setContent Set content to the viewer
* @return {this}
* @example
* codeManager.addViewer('ace',{
* init: function(el){
* var ace = require('ace-editor');
* this.editor = ace.edit(el.id);
* },
* setContent: function(code){
* this.editor.setValue(code);
* }
* });
* */
addEditor : function(editor)
{
// Check interface implementation
for (var method in this.ei)
if(!editor[method]){
console.warn("addEditor: method '"+ method +"' was not found");
return;
}
var id = editor.getId();
this.editors[id] = editor;
if(!this.currentEditor)
this.currentEditor = id;
addViewer: function(id, viewer) {
viewers[id] = viewer;
return this;
},
/**
* Returns editor
* @param {String}|{Integer} id Editor ID
*
* @return {EditorInterface}|null
* Get code viewer by id
* @param {string} id Code viewer ID
* @return {Object|null}
* @example
* var viewer = codeManager.getViewer('ace');
* */
getEditor : function(id)
{
if(id && this.editors[id])
editor = this.editors[id];
return editor ? editor : null;
getViewer: function(id) {
return viewers[id] || null;
},
/**
* Returns editors
*
* @return {Array}
* Returns all code viewers
* @return {Array<Object>}
* */
getEditors : function()
{
return this.editors;
getViewers: function() {
return viewers;
},
/**
* Get current editor
*
* @return {EditorInterface}
* Update code viewer content
* @param {Object} viewer Viewer instance
* @param {string} code Code string
* @example
* var AceViewer = codeManager.getViewer('ace');
* // ...
* var viewer = AceViewer.init(el);
* // ...
* codeManager.updateViewer(AceViewer, 'code');
* */
getCurrentEditor : function()
{
if(!this.currentEditor)
this.loadDefaultEditors();
return this.getEditor(this.currentEditor);
updateViewer: function(viewer, code) {
viewer.setContent(code);
},
/**
* Set current editor
* @param {Integer} id Editor ID
*
* @return this
* Get code from model
* @param {Object} model Any kind of model that will be passed to the build method of generator
* @param {string} genId Code generator id
* @param {Object} [opt] Options
* @return {string}
* @example
* var codeStr = codeManager.getCode(model, 'html');
* */
setCurrentEditor : function(id)
{
this.currentEditor = id;
return this;
getCode: function(model, genId, opt) {
var generator = this.getGenerator(genId);
return generator ? generator.build(model, opt) : '';
},
/**
* Load default editors
*
* @return this
* Load default code generators
* @return {this}
* @private
* */
loadDefaultEditors : function()
{
for (var id in this.defaultEditors) {
this.addEditor(this.defaultEditors[id]);
}
loadDefaultGenerators: function() {
for (var id in defGenerators)
this.addGenerator(id, defGenerators[id]);
return this;
},
/**
* Get code by name
* @param {Backbone.Model} model Model
* @param {String}|{Integer} v Id of code generator
*
* @return {String}|null
* Load default code viewers
* @return {this}
* @private
* */
getCode : function(model, v, em)
{
var id = v || this.currentGenerator,
generator = this.generators[id];
return generator ? generator.build(model, em) : null;
},
loadDefaultViewers: function() {
for (var id in defViewers)
this.addViewer(id, defViewers[id]);
/**
* Update editor content
* @param {EditorInteface} editor Editor
* @param {String} code Code value
*
* @return void
* */
updateEditor : function(editor, code)
{
editor.setContent(code);
return this;
},
};
};
return CodeManager;
});

6
src/code_manager/model/CodeMirrorEditor.js

@ -19,12 +19,6 @@ define(['backbone',
lineNumbers : true,
},
/** @inheritdoc */
getId : function()
{
return 'CodeMirror';
},
/** @inheritdoc */
init: function(el)
{

100
src/code_manager/model/CssGenerator.js

@ -9,17 +9,12 @@ define(['backbone'],
this.compCls = [];
},
/** @inheritdoc */
getId : function()
{
return 'css';
},
/**
* Get CSS from components
* @param {Model} model
* @return {String}
*/
buildFromComp: function(model){
buildFromComp: function(model) {
var coll = model.get('components') || model,
code = '';
@ -49,41 +44,84 @@ define(['backbone'],
},
/** @inheritdoc */
build: function(model, cssc)
{
build: function(model, cssc) {
this.compCls = [];
var code = this.buildFromComp(model);
var compCls = this.compCls;
if(cssc){
var rules = cssc.getRules();
var rules = cssc.getAll();
var mediaRules = {};
rules.each(function(rule){
var selectors = rule.get('selectors');
var ruleStyle = rule.get('style');
var strSel = '';
var found = 0;
selectors.each(function(selector){
strSel += '.' + selector.get('name');
if(compCls.indexOf(selector.get('name')) > -1)
found = 1;
});
if(strSel && found){
var strStyle = '';
if(ruleStyle && Object.keys(ruleStyle).length !== 0){
for(var prop2 in ruleStyle){
if(ruleStyle.hasOwnProperty(prop2))
strStyle += prop2 + ':' + ruleStyle[prop2] + ';';
}
}
if(strStyle)
code += strSel + '{' + strStyle + '}';
var width = rule.get('maxWidth');
// If width setted will render it later
if(width){
var mRule = mediaRules[width];
if(mRule)
mRule.push(rule);
else
mediaRules[width] = [rule];
return;
}
});
code += this.buildFromRule(rule);
}, this);
// Get media rules
for (var ruleW in mediaRules) {
var meRules = mediaRules[ruleW];
var ruleC = '';
for(var i = 0, len = meRules.length; i < len; i++){
ruleC += this.buildFromRule(meRules[i]);
}
if(ruleC)
code += '@media (max-width: ' + ruleW + '){' + ruleC + '}';
}
}
return code;
},
/**
* Get CSS from the rule model
* @param {Model} rule
* @return {string} CSS string
*/
buildFromRule: function(rule) {
var result = '';
var selectors = rule.get('selectors');
var ruleStyle = rule.get('style');
var state = rule.get('state');
var strSel = '';
var found = 0;
var compCls = this.compCls;
// Get string of selectors
selectors.each(function(selector){
strSel += '.' + selector.get('name');
if(compCls.indexOf(selector.get('name')) > -1)
found = 1;
});
if(strSel && found){
strSel += state ? ':' + state : '';
var strStyle = '';
// Get string of style properties
if(ruleStyle && Object.keys(ruleStyle).length !== 0){
for(var prop2 in ruleStyle){
if(ruleStyle.hasOwnProperty(prop2))
strStyle += prop2 + ':' + ruleStyle[prop2] + ';';
}
}
if(strStyle)
result += strSel + '{' + strStyle + '}';
}
return result;
},
});
});

31
src/code_manager/model/EditorInterface.js

@ -1,31 +0,0 @@
define(function() {
/**
* @class EditorInterface
* */
function EditorInterface() {}
EditorInterface.prototype = {
/**
* Get id
*
* @return {String}|{Integer}
* */
getId : function(){},
/**
* Set content
* @param {String} str
*
* */
setContent : function(str){},
/**
* Init editor
* @param {Object} el DOM element
* */
init : function(el){},
};
return EditorInterface;
});

26
src/code_manager/model/GeneratorInterface.js

@ -1,26 +0,0 @@
define(function() {
/**
* @class GeneratorInterface
* */
function GeneratorInterface() {}
GeneratorInterface.prototype = {
/**
* Get id
*
* @return {String}|{Integer}
* */
getId : function(){},
/**
* Generate code from model
* @param {Backbone.Model} model
*
* @return {String}
* */
build : function(model){},
};
return GeneratorInterface;
});

9
src/code_manager/model/HtmlGenerator.js

@ -5,11 +5,6 @@ define(['backbone'],
* */
return Backbone.Model.extend({
/** @inheritdoc */
getId : function(){
return 'html';
},
/** @inheritdoc */
build: function(model){
var coll = model.get('components') || model,
@ -17,12 +12,12 @@ define(['backbone'],
coll.each(function(m){
var tag = m.get('tagName'), // Tag name
sTag = 0, // Single tag
attr = '', // Attributes string
attrId = '',
strCls = '',
cln = m.get('components'), // Children
attrs = m.get('attributes'),
sTag = m.get('void'),
classes = m.get('classes');
_.each(attrs,function(value, prop){
if(prop == 'onmousedown')
@ -33,7 +28,7 @@ define(['backbone'],
if(m.get('type') == 'image'){
tag = 'img';
sTag = 1;
attr += 'src="' + m.get('src') + '"';
attr += ' src="' + m.get('src') + '"';
}
if(!_.isEmpty(m.get('style')))

6
src/code_manager/model/JsonGenerator.js

@ -5,12 +5,6 @@ define(['backbone'],
* */
return Backbone.Model.extend({
/** @inheritdoc */
getId : function()
{
return 'json';
},
/** @inheritdoc */
build: function(model)
{

222
src/commands/main.js

@ -1,78 +1,151 @@
/**
*
* * [add](#add)
* * [get](#get)
*
* You can init the editor with all necessary commands via configuration
*
* ```js
* var editor = grapesjs.init({
* ...
* commands: {...} // Check below for the properties
* ...
* });
* ```
*
* Before using methods you should get first the module from the editor instance, in this way:
*
* ```js
* var commands = editor.Commands;
* ```
*
* @module Commands
* @param {Object} config Configurations
* @param {Array<Object>} [config.defaults=[]] Array of possible commands
* @example
* ...
* commands: {
* defaults: [{
* id: 'helloWorld',
* run: function(editor, sender){
* alert('Hello world!');
* },
* stop: function(editor, sender){
* alert('Stop!');
* },
* }],
* },
* ...
*/
define(function(require) {
/**
* @class Commands
* @param {Object} Configurations
*
* @return {Object}
* */
function Commands(config)
{
var c = config || {},
defaults = require('./config/config'),
AbsCommands = require('./view/CommandAbstract');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.commands = {};
this.config = c;
this.Abstract = AbsCommands;
// Load commands passed by configuration
for( var k in c.defaults){
var obj = c.defaults[k];
if(obj.id)
this.add(obj.id, obj);
}
this.defaultCommands = {};
this.defaultCommands['select-comp'] = require('./view/SelectComponent');
this.defaultCommands['create-comp'] = require('./view/CreateComponent');
this.defaultCommands['delete-comp'] = require('./view/DeleteComponent');
//this.defaultCommands['resize-comp'] = require('./view/ResizeComponent');
this.defaultCommands['image-comp'] = require('./view/ImageComponent');
this.defaultCommands['move-comp'] = require('./view/MoveComponent');
this.defaultCommands['text-comp'] = require('./view/TextComponent');
this.defaultCommands['insert-custom'] = require('./view/InsertCustom');
this.defaultCommands['export-template'] = require('./view/ExportTemplate');
this.defaultCommands['sw-visibility'] = require('./view/SwitchVisibility');
this.defaultCommands['open-layers'] = require('./view/OpenLayers');
this.defaultCommands['open-sm'] = require('./view/OpenStyleManager');
this.config.model = this.config.em.get('Canvas');
}
Commands.prototype = {
return function() {
var c = {},
commands = {},
defaultCommands = {},
defaults = require('./config/config'),
AbsCommands = require('./view/CommandAbstract');
// Need it here as it would be used below
var add = function(id, obj){
delete obj.initialize;
commands[id] = AbsCommands.extend(obj);
return this;
};
return {
/**
* Name of the module
* @type {String}
* @private
*/
name: 'Commands',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
// Load commands passed via configuration
for( var k in c.defaults){
var obj = c.defaults[k];
if(obj.id)
this.add(obj.id, obj);
}
defaultCommands['select-comp'] = require('./view/SelectComponent');
defaultCommands['create-comp'] = require('./view/CreateComponent');
defaultCommands['delete-comp'] = require('./view/DeleteComponent');
defaultCommands['image-comp'] = require('./view/ImageComponent');
defaultCommands['move-comp'] = require('./view/MoveComponent');
defaultCommands['text-comp'] = require('./view/TextComponent');
defaultCommands['insert-custom'] = require('./view/InsertCustom');
defaultCommands['export-template'] = require('./view/ExportTemplate');
defaultCommands['sw-visibility'] = require('./view/SwitchVisibility');
defaultCommands['open-layers'] = require('./view/OpenLayers');
defaultCommands['open-sm'] = require('./view/OpenStyleManager');
defaultCommands['open-tm'] = require('./view/OpenTraitManager');
defaultCommands['open-blocks'] = require('./view/OpenBlocks');
defaultCommands.fullscreen = require('./view/Fullscreen');
defaultCommands.preview = require('./view/Preview');
//this.defaultCommands['resize-comp'] = require('./view/ResizeComponent');
if(c.em)
c.model = c.em.get('Canvas');
return this;
},
/**
* On load callback
* @private
*/
onLoad: function() {
this.loadDefaultCommands();
},
/**
* Add new command
* @param {String} id
* @param {Object} obj
*
* @return this
* Add new command to the collection
* @param {string} id Command's ID
* @param {Object} command Object representing you command. Methods `run` and `stop` are required
* @return {this}
* @example
* commands.add('myCommand', {
* run: function(editor, sender){
* alert('Hello world!');
* },
* stop: function(editor, sender){
* },
* });
* */
add : function(id, obj)
{
delete obj.initialize;
this.commands[id] = this.Abstract.extend(obj);
return this;
},
add: add,
/**
* Get command
* @param {String} id
*
* @return Command
* Get command by ID
* @param {string} id Command's ID
* @return {Object} Object representing the command
* @example
* var myCommand = commands.get('myCommand');
* myCommand.run();
* */
get : function(id)
{
var el = this.commands[id];
get: function(id){
var el = commands[id];
if(typeof el == 'function'){
el = new el(this.config);
this.commands[id] = el;
el = new el(c);
commands[id] = el;
}
return el;
@ -80,18 +153,17 @@ define(function(require) {
/**
* Load default commands
*
* @return this
* @return {this}
* @private
* */
loadDefaultCommands : function()
{
for (var id in this.defaultCommands) {
this.add(id, this.defaultCommands[id]);
loadDefaultCommands: function(){
for (var id in defaultCommands) {
this.add(id, defaultCommands[id]);
}
return this;
},
};
};
return Commands;
});
};
});

94
src/commands/view/CommandAbstract.js

@ -2,31 +2,97 @@ define(['backbone'],
function(Backbone) {
/**
* @class CommandAbstract
* @private
* */
return Backbone.View.extend({
/**
* Initialize method that can't be removed
* @param {Object} o Options
* @private
* */
initialize: function(o) {
this.config = o;
this.editorModel = this.em = o.em || {};
this.canvasId = o.canvasId || '';
this.wrapperId = o.wrapperId || 'wrapper';
this.pfx = o.stylePrefix;
this.config = o || {};
this.editorModel = this.em = this.config.em || {};
this.pfx = this.config.stylePrefix;
this.ppfx = this.config.pStylePrefix;
this.hoverClass = this.pfx + 'hover';
this.badgeClass = this.pfx + 'badge';
this.plhClass = this.pfx + 'placeholder';
this.setElement(this.editorModel.get('$editor').find('#'+this.canvasId));
this.$canvas = this.$el;
this.$wrapper = this.$canvas.find('#'+this.wrapperId);
this.init(o);
this.freezClass = this.ppfx + 'freezed';
this.canvas = this.em.get && this.em.get('Canvas');
if(this.em.get)
this.setElement(this.getCanvas());
if(this.canvas){
this.$canvas = this.$el;
this.$wrapper = $(this.getCanvasWrapper());
this.frameEl = this.canvas.getFrameEl();
this.canvasTool = this.getCanvasTools();
this.bodyEl = this.getCanvasBody();
}
this.init(this.config);
},
/**
* On frame scroll callback
* @param {[type]} e [description]
* @return {[type]} [description]
*/
onFrameScroll: function(e){},
/**
* Returns canval element
* @return {HTMLElement}
*/
getCanvas: function(){
return this.canvas.getElement();
},
/**
* Get canvas body element
* @return {HTMLElement}
*/
getCanvasBody: function(){
return this.canvas.getBody();
},
/**
* Get canvas wrapper element
* @return {HTMLElement}
*/
getCanvasWrapper: function(){
return this.canvas.getWrapperEl();
},
/**
* Get canvas wrapper element
* @return {HTMLElement}
*/
getCanvasTools: function(){
return this.canvas.getToolsEl();
},
/**
* Get the offset of the element
* @param {HTMLElement} el
* @return {Object}
*/
offset: function(el){
var rect = el.getBoundingClientRect();
return {
top: rect.top + el.ownerDocument.body.scrollTop,
left: rect.left + el.ownerDocument.body.scrollLeft
};
},
/**
* Callback triggered after initialize
* @param {Object} o Options
* @private
* */
init: function(o){},
@ -34,19 +100,17 @@ define(['backbone'],
* Method that run command
* @param {Object} em Editor model
* @param {Object} sender Button sender
* @private
* */
run: function(em, sender) {
console.warn("No run method found");
},
run: function(em, sender) {},
/**
* Method that stop command
* @param {Object} em Editor model
* @param {Object} sender Button sender
* @private
* */
stop: function(em, sender) {
console.warn("No stop method found");
}
stop: function(em, sender) {},
});
});

307
src/commands/view/CreateComponent.js

@ -1,96 +1,67 @@
define(['backbone','./SelectPosition'],
function(Backbone, SelectPosition) {
/**
* @class CreateComponent
* */
return _.extend({}, SelectPosition, {
newElement : null,
tempComponent: { style:{} },
return _.extend({}, SelectPosition, {
init: function(opt) {
SelectPosition.init.apply(this, arguments);
_.bindAll(this,'startDraw','draw','endDraw','rollback');
this.config = opt;
this.heightType = this.config.newFixedH ? 'height' : 'min-height';
},
/**
* Returns creation placeholder
*
* @return {Object}
* */
getCreationPlaceholder: function()
{
return this.newElem;
},
/**
* Removes creation placeholder
*
* @return void
* */
removeCreationPlaceholder: function()
{
this.newElem.remove();
this.config = opt || {};
this.hType = this.config.newFixedH ? 'height' : 'min-height';
this.allowDraw = 1;
},
/**
* Start with enabling to select position and listening to start drawning
* @return void
* @private
* */
enable: function()
{
enable: function() {
SelectPosition.enable.apply(this, arguments);
this.$el.css('cursor','crosshair');
this.enableToDraw();
},
/**
* Enable user to draw components
*
* @return void
* */
enableToDraw: function()
{
this.$el.on('mousedown', this.startDraw);
//Need to disable selection
this.$wr.css('cursor','crosshair');
if(this.allowDraw)
this.$wr.on('mousedown', this.startDraw);
this.ghost = this.canvas.getGhostEl();
},
/**
* Start drawing component
* @param {Object} e Event
*
* @return void
* @param {Object} e Event
* @private
* */
startDraw : function(e)
{
startDraw : function(e) {
e.preventDefault();
this.stopSelectPosition(); //Interrupt selecting position
this.tempComponent = { style: {} }; //Reset the helper
this.stopSelectPosition();
this.ghost.style.display = 'block';
this.frameOff = this.getOffsetDim();
this.startPos = {
top : e.pageY + this.frameOff.top,
left: e.pageX + this.frameOff.left
};
this.isDragged = false;
this.tempComponent = {style: {}};
this.beforeDraw(this.tempComponent);
this.getPositionPlaceholder().addClass('change-placeholder'); //Change color of the position placeholder
this.newElemOrig = { top : e.pageY, left: e.pageX };
this.newElem = $('<div>', {class: "tempComp"}).css(this.newElemOrig); //Create helper element with initial position
this.newElem.data('helper',1);
$('body').append(this.newElem); //Show helper component
this.parentElem=this.newElem.parent(); //For percent count
this.targetC = this.outsideElem;
$(document).mousemove(this.draw);
$(document).mouseup(this.endDraw);
$(document).keypress(this.rollback);
this.updateSize(this.startPos.top, this.startPos.left, 0, 0);
this.toggleEvents(1);
},
/**
* Enable/Disable events
* @param {Boolean} enable
*/
toggleEvents: function(enable) {
var method = enable ? 'on' : 'off';
this.$wr[method]('mousemove', this.draw);
this.$wr[method]('mouseup', this.endDraw);
this.$canvas[method]('mousemove', this.draw);
$(document)[method]('mouseup', this.endDraw);
$(document)[method]('keypress', this.rollback);
},
/**
* While drawing the component
* @param {Object} e Event
*
* @return void
* @private
* */
draw: function(e)
{
draw: function(e) {
this.isDragged = true;
this.updateComponentSize(e);
},
@ -98,51 +69,43 @@ define(['backbone','./SelectPosition'],
/**
* End drawing component
* @param {Object} e Event
*
* @return void
* @private
* */
endDraw : function(e)
{
$(document).off('mouseup', this.endDraw);
$(document).off('mousemove', this.draw);
$(document).off('keypress',this.rollback);
endDraw : function(e) {
this.toggleEvents();
var model = {};
if(this.isDragged){ //Only if the mouse was moved
// Only if the mouse was moved
if(this.isDragged){
this.updateComponentSize(e);
this.setRequirements(this.tempComponent);
model = this.create(null,this.tempComponent,this.posIndex,this.posMethod);
var lp = this.sorter.lastPos;
model = this.create(this.sorter.target, this.tempComponent, lp.index, lp.method);
this.sorter.prevTarget = null;
}
if(this.getPositionPlaceholder())
this.getPositionPlaceholder().removeClass('change-placeholder'); //Turn back the original color of the placeholder
this.startSelectPosition(); //Return with selecting new position
this.removeCreationPlaceholder(); //Remove the element used for size indication
this.ghost.style.display = 'none';
this.startSelectPosition();
this.afterDraw(model);
},
/**
* Create component
* @param {Object} target DOM of the target element which to push new component
* @param {Object} component New component to push
* @param {Integer} posIndex Index inside the collection, 0 if no children inside
* @param {String} method Before or after of the children
*
* @return {Object} Created model
* */
create: function(target, component, posIndex, method)
{
var index = posIndex || 0;
if(this.posTargetCollection && this.posTargetModel.get('droppable')){
//Check config parameters for center in wrapper
if(this.config.firstCentered && (this.$wrapper.get(0) == this.posTargetEl.get(0)) ){
component.style.margin = '0 auto';
}
if(this.nearToFloat()) //Set not in flow if the nearest is too
component.style.float = 'left';
this.beforeCreation(component);
var model = this.posTargetCollection.add(component, { at: index, silent:false });
this.afterCreation(model);
return model;
}else
* Create new component inside the target
* @param {Object} target Tha target collection
* @param {Object} component New component to create
* @param {number} index Index inside the collection, 0 if no children inside
* @param {string} method Before or after of the children
* @param {Object} opts Options
*/
create: function(target, component, index, method, opts) {
index = method === 'after' ? index + 1 : index;
var opt = opts || {};
var $trg = $(target);
var trgModel = $trg.data('model');
var trgCollection = $trg.data('collection');
var droppable = trgModel ? trgModel.get('droppable') : 1;
opt.at = index;
if(trgCollection && droppable)
return trgCollection.add(component, opt);
else
console.warn("Invalid target position");
},
@ -150,66 +113,83 @@ define(['backbone','./SelectPosition'],
* Check and set basic requirements for the component
* @param {Object} component New component to be created
* @return {Object} Component updated
* @private
* */
setRequirements: function(component)
{
setRequirements: function(component) {
var c = this.config;
if(component.style.width.replace(/\D/g,'') < c.minComponentW) //Check min width
component.style.width = c.minComponentW +'px';
if(component.style[this.heightType].replace(/\D/g,'') < c.minComponentH) //Check min height
component.style[this.heightType] = c.minComponentH +'px';
if(c.newFixedH) //Set overflow in case of fixed height
component.style.overflow = 'auto';
var compStl = component.style;
// Check min width
if(compStl.width.replace(/\D/g,'') < c.minComponentW)
compStl.width = c.minComponentW +'px';
// Check min height
if(compStl[this.hType].replace(/\D/g,'') < c.minComponentH)
compStl[this.hType] = c.minComponentH +'px';
// Set overflow in case of fixed height
if(c.newFixedH)
compStl.overflow = 'auto';
if(!this.absoluteMode){
delete component.style.left;
delete component.style.top;
delete compStl.left;
delete compStl.top;
}else
component.style.position = 'absolute';
compStl.position = 'absolute';
var lp = this.sorter.lastPos;
if(this.nearFloat(lp.index, lp.method, this.sorter.lastDims))
compStl.float = 'left';
if(this.config.firstCentered &&
this.getCanvasWrapper() == this.sorter.target){
compStl.margin = '0 auto';
}
return component;
},
/**
* Update new component size while drawing
* @param {Object} e Event
*
* @return void
* */
updateComponentSize : function (e)
{
var newLeft = e.pageX;
var newTop = e.pageY;
var startLeft = this.newElemOrig.left;
var startTop = this.newElemOrig.top;
var newWidth = newLeft - startLeft;//$(this.newElem).offset().left
var newHeight = newTop - startTop;//$(this.newElem).offset().top
if (newLeft < this.newElemOrig.left) {
startLeft = newLeft;
newWidth = this.newElemOrig.left - newLeft;
}
if (newTop < this.newElemOrig.top) {
startTop = newTop;
newHeight = this.newElemOrig.top - newTop;
}
newWidth = this.absoluteMode ? (newWidth/this.parentElem.width()*100+"%") : newWidth+'px';
this.newElem[0].style.left = startLeft+'px';
this.newElem[0].style.top = startTop+'px';
this.newElem[0].style.width = newWidth;
this.newElem[0].style['min-height'] = newHeight+'px';
this.tempComponent.style.width = newWidth;
this.tempComponent.style[this.heightType] = newHeight+"px";
this.tempComponent.style.left = startLeft + "px";
this.tempComponent.style.top = startTop + "px";
* @private
* */
updateComponentSize : function (e) {
var y = e.pageY + this.frameOff.top;
var x = e.pageX + this.frameOff.left;
var start = this.startPos;
var top = start.top;
var left = start.left;
var height = y - top;
var width = x - left;
if (x < left) {
left = x;
width = start.left - x;
}
if (y < top) {
top = y;
height = start.top - y;
}
this.updateSize(top, left, width, height);
},
/**
* Update size
* @private
*/
updateSize: function(top, left, width, height){
var u = 'px';
var ghStl = this.ghost.style;
var compStl = this.tempComponent.style;
ghStl.top = compStl.top = top + u;
ghStl.left = compStl.left = left + u;
ghStl.width = compStl.width = width + u;
ghStl[this.hType] = compStl[this.hType] = height + u;
},
/**
* Used to bring the previous situation before event started
* @param {Object} e Event
* @param {Boolean} forse Indicates if rollback in anycase
*
* @return void
* @private
* */
rollback: function(e, force)
{
rollback: function(e, force) {
var key = e.which || e.keyCode;
if(key == this.config.ESCAPE_KEY || force){
this.isDragged = false;
@ -221,8 +201,7 @@ define(['backbone','./SelectPosition'],
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
*
* @return void
* @private
* */
beforeDraw: function(component){
component.editable = false;//set this component editable
@ -231,42 +210,22 @@ define(['backbone','./SelectPosition'],
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
*
* @return void
* @private
* */
afterDraw: function(model){},
/**
* This event is triggered just before a create operation
* @param {Object} component Object component before creation
*
* @return void
* */
beforeCreation: function(component){},
/**
* This event is triggered at the end of a create operation
* @param {Object} model Component model created
*
* @return void
* */
afterCreation: function(model){},
/** Run method
* */
run: function(em, sender){
run: function(editor, sender, opts){
this.editor = editor;
this.sender = sender;
this.$el = this.$wrapper;
this.$wr = this.$wrapper;
this.enable();
},
/** Stop method
* */
stop: function(){
this.removePositionPlaceholder(); //Removes placeholder from eventSelectPosition
this.stopSelectPosition();
this.$el.css('cursor',''); //Changes back aspect of the cursor
this.$el.unbind(); //Removes all attached events
this.$wrapper.css('cursor','');
this.$wrapper.unbind();
}
});
});

5
src/commands/view/DeleteComponent.js

@ -2,6 +2,7 @@ define(['backbone', './SelectComponent'],
function(Backbone, SelectComponent) {
/**
* @class DeleteComponent
* @private
* */
return _.extend({},SelectComponent,{
@ -23,6 +24,7 @@ define(['backbone', './SelectComponent'],
/**
* Start command
* @param {Object} e
* @private
*/
startDelete: function(e)
{
@ -40,6 +42,7 @@ define(['backbone', './SelectComponent'],
/**
* Stop command
* @param {Object} e
* @private
*/
stopDelete: function(e)
{
@ -55,6 +58,7 @@ define(['backbone', './SelectComponent'],
/**
* Delete command
* @param {Object} e
* @private
*/
onDelete: function(e)
{
@ -73,6 +77,7 @@ define(['backbone', './SelectComponent'],
/**
* Updates badge label
* @param {Object} model
* @private
* */
updateBadgeLabel: function (model)
{

34
src/commands/view/ExportTemplate.js

@ -1,15 +1,18 @@
define(function() {
/**
* @class ExportTemplate
* @private
* */
return {
run: function(em, sender){
this.sender = sender;
this.components = em.get('Canvas').getWrapper().get('components');
this.modal = em.get('Modal') || null;
this.cm = em.get('CodeManager') || null;
this.cssc = em.get('CssComposer') || null;
run: function(editor, sender) {
this.sender = sender;
this.components = editor.DomComponents.getComponents();
this.modal = editor.Modal || null;
this.cm = editor.CodeManager || null;
this.cssc = editor.CssComposer || null;
this.protCss = editor.Config.protectedCss;
this.pfx = editor.Config.stylePrefix || '';
this.enable();
},
@ -20,11 +23,11 @@ define(function() {
* @param {String} label
*
* @return {Object} Editor
* @private
* */
buildEditor: function(codeName, theme, label)
{
buildEditor: function(codeName, theme, label) {
if(!this.codeMirror)
this.codeMirror = this.cm.getEditor('CodeMirror');
this.codeMirror = this.cm.getViewer('CodeMirror');
var $input = $('<textarea>'),
@ -37,7 +40,7 @@ define(function() {
$editor = new this.cm.EditorView({
model : editor,
config : this.cm.config
config : this.cm.getConfig()
}).render().$el;
editor.init( $input[0] );
@ -45,25 +48,24 @@ define(function() {
return { el: editor, $el: $editor };
},
enable: function()
{
enable: function() {
if(!this.$editors){
var oHtmlEd = this.buildEditor('htmlmixed', 'hopscotch', 'HTML'),
oCsslEd = this.buildEditor('css', 'hopscotch', 'CSS');
this.htmlEditor = oHtmlEd.el;
this.cssEditor = oCsslEd.el;
this.$editors = $('<div>');
this.$editors = $('<div>', {class: this.pfx + 'export-dl'});
this.$editors.append(oHtmlEd.$el).append(oCsslEd.$el);
}
if(this.modal){
this.modal.setTitle('Export template');
this.modal.setContent(this.$editors);
this.modal.show();
this.modal.open();
}
var addCss = this.protCss || '';
this.htmlEditor.setContent( this.cm.getCode(this.components, 'html') );
this.cssEditor.setContent( this.cm.getCode(this.components, 'css', this.cssc));
this.cssEditor.setContent( addCss + this.cm.getCode(this.components, 'css', this.cssc));
if(this.sender)
this.sender.set('active',false);

86
src/commands/view/Fullscreen.js

@ -0,0 +1,86 @@
define(function() {
return {
/**
* Check if fullscreen mode is enabled
* @return {Boolean}
*/
isEnabled: function(){
var d = document;
if(d.fullscreenElement || d.webkitFullscreenElement || d.mozFullScreenElement)
return 1;
else
return 0;
},
/**
* Enable fullscreen mode and return browser prefix
* @param {HTMLElement} el
* @return {string}
*/
enable: function(el){
var pfx = '';
if (el.requestFullscreen)
el.requestFullscreen();
else if (el.webkitRequestFullscreen) {
pfx = 'webkit';
el.webkitRequestFullscreen();
}else if (el.mozRequestFullScreen) {
pfx = 'moz';
el.mozRequestFullScreen();
}else if (el.msRequestFullscreen)
el.msRequestFullscreen();
else
console.warn('Fullscreen not supported');
return pfx;
},
/**
* Disable fullscreen mode
*/
disable: function(){
var d = document;
if (d.exitFullscreen)
d.exitFullscreen();
else if (d.webkitExitFullscreen)
d.webkitExitFullscreen();
else if (d.mozCancelFullScreen)
d.mozCancelFullScreen();
else if (d.msExitFullscreen)
d.msExitFullscreen();
},
/**
* Triggered when the state of the fullscreen is changed. Inside detects if
* it's enabled
* @param {strinf} pfx Browser prefix
* @param {Event} e
*/
fsChanged: function(pfx, e){
var d = document;
var ev = (pfx || '') + 'fullscreenchange';
if(!this.isEnabled()){
this.stop(null, this.sender);
document.removeEventListener(ev, this.fsChanged);
}
},
run: function(editor, sender){
this.sender = sender;
var pfx = this.enable(editor.getContainer());
this.fsChanged = this.fsChanged.bind(this, pfx);
document.addEventListener(pfx + 'fullscreenchange', this.fsChanged);
if(editor)
editor.trigger('change:canvasOffset');
},
stop: function(editor, sender){
if(sender && sender.set)
sender.set('active', false);
this.disable();
if(editor)
editor.trigger('change:canvasOffset');
}
};
});

17
src/commands/view/ImageComponent.js

@ -2,32 +2,31 @@ define(['backbone', './InsertCustom'],
function(Backbone, InsertCustom) {
/**
* @class ImageComponent
* @private
* */
return _.extend({}, InsertCustom, {
/**
* Trigger before insert
* @param {Object} object
* @private
*
* */
beforeInsert: function(object){
object.type = 'image';
object.style = {};
object.type = 'image';
object.style = {};
object.attributes = {};
if (!this.nearToFloat()) {
object.style.display = 'block';
}
// This allow to avoid 'ghosts' on drag
object.attributes.onmousedown = 'return false';
if (this.config.firstCentered && (this.$wp.get(0) == this.posTargetEl.get(0)) ) {
object.style.margin = '0 auto';
if (this.config.firstCentered &&
this.getCanvasWrapper() == this.sorter.target ) {
object.style.margin = '0 auto';
}
},
/**
* Trigger after insert
* @param {Object} model Model created after insert
*
* @private
* */
afterInsert: function(model){
model.trigger('dblclick');

68
src/commands/view/InsertCustom.js

@ -1,47 +1,57 @@
define(['backbone', './SelectPosition'],
function(Backbone, SelectPosition) {
define(['backbone', './CreateComponent'],
function(Backbone, CreateComponent) {
/**
* @class InsertCustom
* @private
* */
return _.extend({}, SelectPosition, {
return _.extend({}, CreateComponent, {
init: function(){
CreateComponent.init.apply(this, arguments);
_.bindAll(this, 'insertComponent');
this.allowDraw = 0;
},
/**
* Run method
* @private
* */
run: function(em, sender){
run: function(em, sender, options) {
this.em = em;
this.sender = sender;
this.opt = options || {};
this.$wr = this.$wrapper;
this.enable();
this.em = em;
this.sender = sender;
this.opt = sender.get('options') || {};
this.content = this.opt.content;
},
enable: function(){
SelectPosition.enable.apply(this, arguments);
_.bindAll(this,'insertComponent');
this.$wp = this.$wrapper;
this.$wp.on('click', this.insertComponent);
CreateComponent.enable.apply(this, arguments);
this.$wr.on('click', this.insertComponent);
},
/**
* Start insert event
*
* @return void
* @private
* */
insertComponent: function(){
this.$wp.off('click', this.insertComponent);
this.$wr.off('click', this.insertComponent);
this.stopSelectPosition();
this.removePositionPlaceholder();
var object = this.buildContent();
var object = this.buildContent();
this.beforeInsert(object);
var model = this.posTargetCollection.add(object, { at: this.posIndex, silent:false });
var index = this.sorter.lastPos.index;
// By default, collections do not trigger add event, so silent is used
var model = this.create(this.sorter.target, object, index, null, {silent: false});
if(this.opt.terminateAfterInsert && this.sender)
this.sender.set('active',false);
this.sender.set('active', false);
else
this.enable();
if(!model)
return;
if(this.em)
this.em.initChildrenComp(model);
this.em.editor.initChildrenComp(model);
this.afterInsert(model, this);
},
@ -49,16 +59,14 @@ define(['backbone', './SelectPosition'],
/**
* Trigger before insert
* @param {Object} obj
*
* @return void
* @private
* */
beforeInsert: function(obj){},
/**
* Trigger after insert
* @param {Object} model Model created after insert
*
* @return void
* @private
* */
afterInsert: function(model){},
@ -66,18 +74,10 @@ define(['backbone', './SelectPosition'],
* Create different object, based on content, to insert inside canvas
*
* @return {Object}
* @private
* */
buildContent: function(){
var result = {};
if(typeof this.content === 'string'){
result = {
content : this.content,
tagName : 'span',
};
}else if(typeof this.content === 'object'){
result = this.content;
}
return result;
return this.opt.content || {};
},
});
});

224
src/commands/view/MoveComponent.js

@ -1,185 +1,113 @@
define(['backbone', './SelectComponent','./SelectPosition'],
function(Backbone, SelectComponent, SelectPosition) {
/**
* @class MoveComponent
* */
return _.extend({},SelectComponent, SelectPosition,{
return _.extend({}, SelectPosition, SelectComponent, {
init: function(o){
SelectComponent.init.apply(this, arguments);
_.bindAll(this,'startMove','onMove','endMove','rollback','selectingPosition','itemLeft');
this.opt = o;
this.hoverClass = this.pfx + 'hover-move';
this.badgeClass = this.pfx + 'badge-yellow';
this.noSelClass = this.pfx + 'no-select';
_.bindAll(this, 'initSorter','rollback', 'onEndMove');
this.opt = o;
this.hoverClass = this.ppfx + 'highlighter-warning';
this.badgeClass = this.ppfx + 'badge-warning';
this.noSelClass = this.ppfx + 'no-select';
},
enable: function(){
this.canvasTop = this.$canvas.offset().top;
this.canvasLeft = this.$canvas.offset().left;
this.$el.css('cursor','move');
this.$el.on('mousedown', this.startMove);
this.startSelectComponent();
//Avoid strange moving behavior
this.$el.addClass(this.noSelClass);
SelectComponent.enable.apply(this, arguments);
this.getBadgeEl().addClass(this.badgeClass);
this.getHighlighterEl().addClass(this.hoverClass);
var wp = this.$wrapper;
wp.css('cursor','move');
wp.on('mousedown', this.initSorter);
// Avoid strange moving behavior
wp.addClass(this.noSelClass);
},
/**
* Hover command
* @param {Object} e
* Overwrite for doing nothing
* @private
*/
onHover: function(e)
{
e.stopPropagation();
toggleClipboard: function(){},
var $this = $(e.target);
if($this.data('model').get('movable')){ //Show badge if possible
$this.addClass(this.hoverClass);
this.attachBadge(e.target);
}
},
/** Say what to do after the component was selected
* - Method from selectComponent
* @param Event
* @param Object Selected element
* */
onSelect: function(e,el){},
/** Picking component to move
* @param event
/**
* Delegate sorting
* @param {Event} e
* @private
* */
startMove: function(e, el){
this.moved = false;
if( !$(e.target).data('model').get('movable') ){ return; } //In case the component selected is not movable
this.$el.off('mousedown', this.startMove);
this.stopSelectComponent(e);
this.$selectedEl = $(e.target);
this.freezeComponent(this.$selectedEl);
this.helperObj = $('<div>', {class: "tempComp"}).css({ //HELPER gray box
top : (e.pageY - this.canvasTop) + this.$canvas.scrollTop(),
left: (e.pageX - this.canvasLeft) + this.$canvas.scrollLeft(),
width: $(e.target).width(),
height: $(e.target).height(),
position : 'absolute',
'pointer-events': 'none', //disable events for the element
}).data('helper',1).appendTo(this.$el);
this.startSelectPosition();
this.$el.on('mousemove',this.onMove);
$(document).on('mouseup',this.endMove);
$(document).on('keypress',this.rollback);
},
/** During move
* @param event */
onMove: function(e){
this.moved = true;
var relativeY = (e.pageY - this.canvasTop) + this.$canvas.scrollTop();
var relativeX = (e.pageX - this.canvasLeft) + this.$canvas.scrollLeft();
this.helperObj[0].style.top = (relativeY)+'px';
this.helperObj[0].style.left = (relativeX)+'px';
initSorter: function(e){
var el = $(e.target).data('model');
if(!el.get('draggable'))
return;
// Avoid badge showing on move
this.cacheEl = null;
this.startSelectPosition(e.target, this.frameEl.contentDocument);
this.sorter.onEndMove = this.onEndMove.bind(this);
this.stopSelectComponent();
this.$wrapper.off('mousedown', this.initSorter);
this.getContentWindow().on('keydown', this.rollback);
},
/** Leave component
* @param event */
endMove: function(e){
this.$el.off('mousemove',this.onMove);
$(document).off('mouseup', this.endMove);
$(document).off('keypress', this.rollback);
this.helperObj.remove();
this.removePositionPlaceholder();
this.stopSelectPosition();
//this.highlightComponent(e,el) after end of move
if(this.moved)
this.move(null, this.$selectedEl, this.posIndex, this.posMethod);
this.unfreezeComponent(this.$selectedEl);
/**
* Callback after sorting
* @private
*/
onEndMove: function(){
this.enable();
this.getContentWindow().off('keydown', this.rollback);
},
/** Move component to new position
* @param object Component to move
* @param object Target component
* @param int Indicates the position inside the collection
* @param string Before of after component
*
* @return void
* */
move: function(target, el, posIndex, posMethod){
var index = posIndex || 0;
var model = el.data("model");
var collection = model.collection;
var targetCollection = this.posTargetCollection;
var targetModel = this.posTargetModel;
if(targetCollection && targetModel.get('droppable')){
var modelTemp = targetCollection.add({css:{}}, { at: index });
var modelRemoved = collection.remove(model);
targetCollection.add(modelRemoved, { at: index });
targetCollection.remove(modelTemp);//{ avoidStore: 1 }
}else
console.warn("Invalid target position");
},
/** Make component untouchable
* @param object Component
* @return void
* */
freezeComponent: function($component){
$component.css({'pointer-events':'none'});
$component.addClass('freezed');
},
/** Make component touchable
* @param object Component
* @return void
/**
* Say what to do after the component was selected (selectComponent)
* @param {Event} e
* @param {Object} Selected element
* @private
* */
unfreezeComponent: function($component){
$component.css({'pointer-events':'auto'});
$component.removeClass('freezed');
},
onSelect: function(e,el){},
/** Used to bring the previous situation before start moving the component
* @param Event
* @param Bool Indicates if rollback in anycase
* @return void
/**
* Used to bring the previous situation before start moving the component
* @param {Event} e
* @param {Boolean} Indicates if rollback in anycase
* @private
* */
rollback: function(e, force){
var key = e.which || e.keyCode;
if(key == this.opt.ESCAPE_KEY || force){
this.moved = false;
this.endMove();
this.sorter.moved = false;
this.sorter.endMove();
}
return;
},
/** Closing method
* */
last: function(){
this.placeholder.remove();
this.placeholderStart.remove();
this.helperObj.remove();
this.$el.off('mousemove',this.move);
$(document).off('mouseup', this.endMove);
$(document).off('keypress', this.rollback);
/**
* Returns badge element
* @return {HTMLElement}
* @private
*/
getBadgeEl: function(){
if(!this.$badge)
this.$badge = $(this.getBadge());
return this.$badge;
},
/* Run method */
run: function(){
this.enable();
/**
* Returns highlighter element
* @return {HTMLElement}
* @private
*/
getHighlighterEl: function(){
if(!this.$hl)
this.$hl = $(this.canvas.getHighlighter());
return this.$hl;
},
/* Stop method */
stop: function(){
this.stopSelectComponent();
this.$el.css('cursor','');//changes back aspect of the cursor
this.$el.unbind();//removes all attached events
this.$el.removeClass(this.noSelClass);
SelectComponent.stop.apply(this, arguments);
this.getBadgeEl().removeClass(this.badgeClass);
this.getHighlighterEl().removeClass(this.hoverClass);
var wp = this.$wrapper;
wp.css('cursor', '').unbind().removeClass(this.noSelClass);
}
});
});

28
src/commands/view/OpenBlocks.js

@ -0,0 +1,28 @@
define(function() {
return {
run: function(editor, sender) {
var config = editor.Config;
var pfx = config.stylePrefix;
var bm = editor.BlockManager;
if(!this.blocks){
this.blocks = $('<div/>').get(0);
this.blocks.appendChild(bm.render());
var panels = editor.Panels;
if(!panels.getPanel('views-container'))
panelC = panels.addPanel({id: 'views-container'});
else
panelC = panels.getPanel('views-container');
panelC.set('appendContent', this.blocks).trigger('change:appendContent');
}
this.blocks.style.display = 'block';
},
stop: function() {
if(this.blocks)
this.blocks.style.display = 'none';
}
};
});

19
src/commands/view/OpenLayers.js

@ -1,27 +1,28 @@
define(['Navigator'], function(Layers) {
/**
* @class OpenStyleManager
* @private
* */
return {
run: function(em, sender)
{
if(!this.$layers){
var collection = em.get('Components').getComponent().get('components'),
config = em.get('Config'),
panels = em.get('Panels'),
lyStylePfx = config.layers.stylePrefix || 'nv-';
var collection = em.DomComponents.getComponent().get('components'),
config = em.getConfig(),
panels = em.Panels,
lyStylePfx = config.layers.stylePrefix || 'nv-';
config.layers.stylePrefix = config.stylePrefix + lyStylePfx;
config.layers.em = em;
var layers = new Layers(collection, config.layers);
this.$layers = layers.render();
config.layers.em = em.editor;
var layers = new Layers(collection, config.layers);
this.$layers = layers.render();
// Check if panel exists otherwise crate it
if(!panels.getPanel('views-container'))
this.panel = panels.addPanel({ id: 'views-container'});
this.panel = panels.addPanel({id: 'views-container'});
else
this.panel = panels.getPanel('views-container');
this.panel = panels.getPanel('views-container');
this.panel.set('appendContent', this.$layers).trigger('change:appendContent');
}

51
src/commands/view/OpenStyleManager.js

@ -1,45 +1,39 @@
define(['StyleManager'], function(StyleManager) {
/**
* @class OpenStyleManager
* @private
* */
return {
run: function(em, sender)
{
run: function(em, sender) {
this.sender = sender;
if(!this.$cn){
var config = em.get('Config'),
panels = em.get('Panels'),
pfx = config.styleManager.stylePrefix || 'sm-';
config.styleManager.stylePrefix = config.stylePrefix + pfx;
config.styleManager.target = em;
var config = em.getConfig(),
panels = em.Panels;
// Main container
this.$cn = $('<div/>');
// Secondary container
this.$cn2 = $('<div/>');
this.$cn.append(this.$cn2);
// Class Manager container
this.clm = em.get('ClassManager');
if(this.clm){
this.$clm = new this.clm.ClassTagsView({
collection: new this.clm.ClassTags([]),
config: this.clm.config,
}).render().el;
this.$cn2.append(this.$clm);
// Device Manager
var dvm = em.DeviceManager;
if(dvm && config.showDevices){
var devicePanel = panels.addPanel({ id: 'devices-c'});
devicePanel.set('appendContent', dvm.render()).trigger('change:appendContent');
}
// Style Manager manager container
this.sm = new StyleManager(config.styleManager);
this.$sm = this.sm.render();
this.$cn2.append(this.$sm);
// Class Manager container
var clm = em.SelectorManager;
if(clm)
this.$cn2.append(clm.render([]));
this.$cn2.append(em.StyleManager.render());
var smConfig = em.StyleManager.getConfig();
// Create header
this.$header = $('<div>', {
class : config.styleManager.stylePrefix + 'header',
text : config.styleManager.textNoElement,
class: smConfig.stylePrefix + 'header',
text: smConfig.textNoElement,
});
//this.$cn = this.$cn.add(this.$header);
this.$cn.append(this.$header);
@ -53,7 +47,7 @@ define(['StyleManager'], function(StyleManager) {
// Add all containers to the panel
this.panel.set('appendContent', this.$cn).trigger('change:appendContent');
this.target = em;
this.target = em.editor;
this.listenTo( this.target ,'change:selectedComponent', this.toggleSm);
}
this.toggleSm();
@ -61,9 +55,9 @@ define(['StyleManager'], function(StyleManager) {
/**
* Toggle Style Manager visibility
* @private
*/
toggleSm: function()
{
toggleSm: function() {
if(!this.sender.get('active'))
return;
if(this.target.get('selectedComponent')){
@ -75,8 +69,7 @@ define(['StyleManager'], function(StyleManager) {
}
},
stop: function()
{
stop: function() {
// Hide secondary container if exists
if(this.$cn2)
this.$cn2.hide();
@ -86,4 +79,4 @@ define(['StyleManager'], function(StyleManager) {
this.$header.hide();
}
};
});
});

32
src/commands/view/OpenTraitManager.js

@ -0,0 +1,32 @@
define(function() {
return {
run: function(editor, sender) {
var config = editor.Config;
var pfx = config.stylePrefix;
var tm = editor.TraitManager;
if(!this.obj){
var tmView = tm.getTraitsViewer();
var confTm = tm.getConfig();
this.obj = $('<div/>')
.append('<div class="'+pfx+'traits-label">' + confTm.labelContainer + '</div>')
.get(0);
this.obj.appendChild(tmView.render().el);
var panels = editor.Panels;
if(!panels.getPanel('views-container'))
panelC = panels.addPanel({id: 'views-container'});
else
panelC = panels.getPanel('views-container');
panelC.set('appendContent', this.obj).trigger('change:appendContent');
}
this.obj.style.display = 'block';
},
stop: function() {
if(this.obj)
this.obj.style.display = 'none';
}
};
});

54
src/commands/view/Preview.js

@ -0,0 +1,54 @@
define(function() {
return {
getPanels: function(editor){
if(!this.panels)
this.panels = editor.Panels.getPanelsEl();
return this.panels;
},
run: function(editor, sender) {
if(sender)
sender.set('active',false);
editor.stopCommand('sw-visibility');
var that = this;
var panels = this.getPanels(editor);
var canvas = editor.Canvas.getElement();
var editorEl = editor.getEl();
var pfx = editor.Config.stylePrefix;
if(!this.helper) {
this.helper = document.createElement('span');
this.helper.className = pfx + 'off-prv fa fa-eye-slash';
editorEl.appendChild(this.helper);
this.helper.onclick = function(){
that.stop(editor);
};
}
this.helper.style.display = 'inline-block';
panels.style.display = 'none';
var canvasS = canvas.style;
canvasS.width = '100%';
canvasS.height = '100%';
canvasS.top = '0';
canvasS.left = '0';
canvasS.padding = '0';
canvasS.margin = '0';
editor.trigger('change:canvasOffset');
},
stop: function(editor, sender) {
var panels = this.getPanels(editor);
editor.runCommand('sw-visibility');
editor.getModel().runDefault();
panels.style.display = 'block';
var canvas = editor.Canvas.getElement();
canvas.setAttribute('style', '');
if(this.helper) {
this.helper.style.display = 'none';
}
editor.trigger('change:canvasOffset');
}
};
});

59
src/commands/view/ResizeComponent.js

@ -1,59 +0,0 @@
define(['backbone', 'jqueryUi', './MoveComponent'],
function(Backbone, jqueryUi, MoveComponent) {
/**
* @class ResizeComponent
* */
return _.extend({}, MoveComponent,{
enable: function(){
var $this = this;
this.startSelectComponent();
this.$el.find('*').resizable({
containment: 'parent',
start: function(event,ui){
ui.element[0].style.height = ui.element.height()+'px';
ui.element.css({'min-height':'', 'min-width':'' });
},
stop: function(event,ui){
ui.element.css('overflow','auto');
$this.updateModel(ui);
}
});
},
/**
* Update model of resized element
* @param object Component model
* */
updateModel: function(el){
var um = 'px',
model = el.element.data("model"),
style = _.clone(model.get('style'));
delete style['min-height'];
delete style['min-width'];
style.height = el.size.height + um;
style.width = el.size.width + um;
style.overflow = 'auto';
model.set('style', style);
},
/**
* Run method
* */
run: function(){
this.enable();
this.active = true;
},
/**
* Stop method
* */
stop: function(){
this.stopSelectComponent();
this.$el.find('.ui-resizable').resizable("destroy");
this.$el.unbind();//removes all attached events
this.active = false;
}
});
});

343
src/commands/view/SelectComponent.js

@ -1,205 +1,302 @@
define(function() {
/**
* @class SelectComponent
* @private
* */
return {
init: function(o){
_.bindAll(this, 'onHover', 'onOut', 'onClick');
_.bindAll(this, 'onHover', 'onOut', 'onClick', 'onKeyPress');
},
enable: function()
{
_.bindAll(this,'copyComp','pasteComp');
var confMain = this.config.em.get('Config');
enable: function() {
_.bindAll(this, 'copyComp', 'pasteComp', 'onFrameScroll');
this.frameOff = this.canvasOff = this.adjScroll = null;
var config = this.config.em.get('Config');
this.startSelectComponent();
if(confMain.copyPaste){
this.toggleClipboard(config.copyPaste);
},
/**
* Toggle clipboard function
* @param {Boolean} active
* @return {this}
* @private
*/
toggleClipboard: function(active){
var en = active || 0;
if(en){
key('⌘+c, ctrl+c', this.copyComp);
key('⌘+v, ctrl+v', this.pasteComp);
}else{
key.unbind('⌘+c, ctrl+c');
key.unbind('⌘+v, ctrl+v');
}
},
/**
* Copy component to clipboard
* Copy component to the clipboard
* @private
*/
copyComp: function()
{
var sel = this.editorModel.get('selectedComponent');
if(sel && sel.get('copyable'))
this.editorModel.set('clipboard', sel);
copyComp: function() {
var el = this.editorModel.get('selectedComponent');
if(el && el.get('copyable'))
this.editorModel.set('clipboard', el);
},
/**
* Paste component from clipboard
* @private
*/
pasteComp: function()
{
var clp = this.editorModel.get('clipboard'),
sel = this.editorModel.get('selectedComponent');
if(clp && sel && sel.collection){
var index = sel.collection.indexOf(sel),
clone = clp.clone();
sel.collection.add(clone, { at: index + 1 });
}
pasteComp: function() {
var clp = this.editorModel.get('clipboard'),
sel = this.editorModel.get('selectedComponent');
if(clp && sel && sel.collection){
var index = sel.collection.indexOf(sel),
clone = clp.clone();
sel.collection.add(clone, { at: index + 1 });
}
},
/**
* Start select component event
* @private
* */
startSelectComponent: function() {
this.selEl = $(this.getCanvasBody()).find('*');
this.selEl.on('mouseover', this.onHover)
.on('mouseout', this.onOut)
.on('click', this.onClick);
var cw = this.getContentWindow();
cw.on('scroll', this.onFrameScroll);
cw.on('keydown', this.onKeyPress);
},
/**
* Stop select component event
* @private
* */
stopSelectComponent: function() {
if(this.selEl)
this.selEl.trigger('mouseout').off('mouseover', this.onHover)
.off('mouseout', this.onOut)
.off('click', this.onClick);
this.selEl = null;
var cw = this.getContentWindow();
cw.off('scroll', this.onFrameScroll);
cw.off('keydown', this.onKeyPress);
},
/** Start select component event
* @return void
/**
* On key press event
* @private
* */
startSelectComponent: function(){
var that = this;
this.$el.find('*')
.on('mouseover',this.onHover)
.on('mouseout', this.onOut)
.on('click', this.onClick);
this.selEl = this.$el.find('*');
onKeyPress: function(e) {
var key = e.which || e.keyCode;
var comp = this.editorModel.get('selectedComponent');
var focused = this.frameEl.contentDocument.activeElement.tagName !== 'BODY';
if(key == 8 || key == 46) {
if(!focused)
e.preventDefault();
if(comp && !focused){
if(!comp.get('removable'))
return;
comp.set('status','');
comp.destroy();
this.hideBadge();
this.clean();
this.editorModel.set('selectedComponent',null);
}
}
},
/**
* Hover command
* @param {Object} e
* @private
*/
onHover: function(e)
{
onHover: function(e) {
e.stopPropagation();
$(e.target).addClass(this.hoverClass);
this.attachBadge(e.target);
var trg = e.target;
// Adjust tools scroll top
if(!this.adjScroll){
this.adjScroll = 1;
this.onFrameScroll(e);
}
var pos = this.getElementPos(trg);
this.updateBadge(trg, pos);
this.updateHighlighter(trg, pos);
},
/**
* Out command
* @param {Object} e
* @private
*/
onOut: function(e)
{
onOut: function(e) {
e.stopPropagation();
$(e.target).removeClass(this.hoverClass);
if(this.badge)
this.badge.css({ left: -10000, top:-10000 });
this.hideBadge();
if(this.hl)
this.hl.css({ left: -10000, top:-10000 });
},
/**
* Hover command
* @param {Object} e
* @private
*/
onClick: function(e)
{
var s = $(e.target).data('model').get('stylable');
onClick: function(e) {
var m = $(e.target).data('model');
if(!m)
return;
var s = m.get('stylable');
if(!(s instanceof Array) && !s)
return;
this.onSelect(e, e.target);
},
/** Stop select component event
* @param Event
* @return void
/**
* Update badge for the component
* @param {Object} Component
* @param {Object} pos Position object
* @private
* */
stopSelectComponent: function(e){
if(this.selEl)
this.selEl.trigger('mouseout').off('mouseover mouseout click');
this.selEl = null;
updateBadge: function(el, pos) {
var $el = $(el);
this.cacheEl = el;
var model = $el.data("model");
if(!model || !model.get('badgable'))
return;
var badge = this.getBadge();
badge.innerHTML = model.getName();
var bStyle = badge.style;
var u = 'px';
bStyle.display = 'block';
var canvasPos = this.canvas.getCanvasView().getPosition();
var badgeH = badge ? badge.offsetHeight : 0;
var badgeW = badge ? badge.offsetWidth : 0;
var top = pos.top - badgeH < canvasPos.top ? canvasPos.top : pos.top - badgeH;
var left = pos.left + badgeW < canvasPos.left ? canvasPos.left : pos.left;
bStyle.top = top + u;
bStyle.left = left + u;
},
/**
* Update highlighter element
* @param {HTMLElement} el
* @param {Object} pos Position object
* @private
*/
updateHighlighter: function(el, pos){
if(!this.hl)
this.hl = $(this.canvas.getHighlighter());
this.hl.css({
left: pos.left,
top: pos.top,
height: pos.height,
width: pos.width
});
},
/**
* Say what to do after the component was selected
* @param {Object} e
* @param {Object} el
* @param {Object} e
* @param {Object} el
* @private
* */
onSelect: function(e, el)
{
onSelect: function(e, el) {
e.stopPropagation();
var md = this.editorModel.get('selectedComponent');
if(md)
md.set('status','');
var nMd = $(el).data('model');
this.cleanPrevious(md);
var $el = $(el);
var nMd = $el.data('model');
if(nMd){
this.editorModel.set('selectedComponent', nMd);
nMd.set('status','selected');
}
},
/** Removes all highlighting effects on components
* @return void
/**
* Removes all highlighting effects on components
* @private
* */
clean: function(){
this.$el.find('*').removeClass(this.hoverClass);
clean: function() {
if(this.selEl)
this.selEl.removeClass(this.hoverClass);
},
/** Attach badge to component
* @param Object Component
* @return void
* */
attachBadge: function(el){
var model = $(el).data("model");
if(!model || !model.get('badgable'))
return;
if(!this.badge)
this.createBadge();
var badgeH = this.badge.outerHeight();
this.updateBadgeLabel(model);
var $el = $(el);
if(!this.wrapper)
this.wrapper = this.$wrapper;
if(!this.wrapperTop)
this.wrapperTop = this.wrapper.offset() ? this.wrapper.offset().top : 0;
if(!this.wrapperLeft)
this.wrapperLeft= this.wrapper.offset() ? this.wrapper.offset().left : 0;
var relativeT = ($el.offset().top - this.wrapperTop) + this.wrapper.scrollTop();
var relativeL = ($el.offset().left- this.wrapperLeft) + this.wrapper.scrollLeft();
if( (relativeT-badgeH) > this.wrapperTop) //If it's possible set badge to top
relativeT -= badgeH;
this.badge.css({ left: relativeL, top:relativeT });
},
/** Create badge for the component
* @return void
* */
createBadge: function (){
this.badge = $('<div>', {class: this.badgeClass + " no-dots"}).appendTo(this.$wrapper);
/**
* Returns badge element
* @return {HTMLElement}
* @private
*/
getBadge: function(){
return this.canvas.getBadgeEl();
},
/** Remove badge
* @return void
* */
removeBadge: function (){
if(this.badge){
this.badge.remove();
delete this.badge;
}
/**
* On frame scroll callback
* @private
*/
onFrameScroll: function(e){
var el = this.cacheEl;
if(el)
this.updateBadge(el, this.getElementPos(el));
},
/** Updates badge label
* @param Object Model
* @return void
/**
* Returns element's data info
* @param {HTMLElement} el
* @return {Object}
* @private
*/
getElementPos: function(el, badge){
return this.canvas.getCanvasView().getElementPos(el);
},
/**
* Hide badge
* @private
* */
updateBadgeLabel: function (model){
hideBadge: function () {
this.getBadge().style.display = 'none';
},
/**
* Clean previous model from different states
* @param {Component} model
* @private
*/
cleanPrevious: function(model) {
if(model)
this.badge.html( model.getName() );
model.set({
status: '',
state: '',
});
},
/**
* Run method
* */
run: function(em, sender){
* Returns content window
* @private
*/
getContentWindow: function(){
if(!this.contWindow)
this.contWindow = $(this.frameEl.contentWindow);
return this.contWindow;
},
run: function(em, sender) {
this.enable();
this.render();
},
/**
* Stop method
* */
stop: function(){
var sel = this.editorModel.get('selectedComponent');
if(sel)
sel.set('status','');
this.$el.unbind(); //removes all attached events
this.removeBadge();
stop: function() {
this.stopSelectComponent();
this.cleanPrevious(this.em.get('selectedComponent'));
this.clean();
this.$el.find('*').unbind('mouseover').unbind('mouseout').unbind('click');
this.editorModel.set('selectedComponent',null);
key.unbind('⌘+c, ctrl+c');
key.unbind('⌘+v, ctrl+v');
this.em.set('selectedComponent', null);
this.toggleClipboard();
this.hideBadge();
}
};
});
});

420
src/commands/view/SelectPosition.js

@ -1,368 +1,102 @@
define(function() {
/**
* @class SelectPosition
* */
return {
init: function(opt) {
_.bindAll(this,'selectingPosition','itemLeft');
this.config = opt;
},
/**
* Returns position placeholder
*
* @return {Object} Placeholder
* */
getPositionPlaceholder: function()
{
return this.$plh;
},
/**
* Creates position placeholder
*
* @return {Object} Placeholder
* */
createPositionPlaceholder: function()
{
this.$plh = $('<div>', { class: this.plhClass + " no-dots" })
.css({'pointer-events':'none'}).data('helper',1);
this.$plh.append( $('<div>', { class: this.plhClass + "-int no-dots" } ) );
this.$plh.appendTo( this.$wrapper );
return this.$plh;
},
enable: function()
{
this.startSelectPosition();
},
/**
/**
* Start select position event
*
* @return void
* @param {HTMLElement} trg
* @private
* */
startSelectPosition: function()
{
startSelectPosition: function(trg, doc) {
this.isPointed = false;
this.$wrapper.on('mousemove', this.selectingPosition);
},
/**
var utils = this.editorModel.get('Utils');
if(utils && !this.sorter)
this.sorter = new utils.Sorter({
container: this.getCanvasBody(),
placer: this.canvas.getPlacerEl(),
containerSel: '*',
itemSel: '*',
pfx: this.ppfx,
direction: 'a',
document: doc,
wmargin: 1,
nested: 1,
em: this.editorModel,
});
this.sorter.startSort(trg);
},
/**
* Get frame position
* @return {Object}
* @private
*/
getOffsetDim: function() {
var frameOff = this.offset(this.canvas.getFrameEl());
var canvasOff = this.offset(this.canvas.getElement());
var top = frameOff.top - canvasOff.top;
var left = frameOff.left - canvasOff.left;
return { top: top, left: left };
},
/**
* Stop select position event
* @return void
* @private
* */
stopSelectPosition: function()
{
this.$wrapper.off('mousemove',this.selectingPosition);
stopSelectPosition: function() {
this.posTargetCollection = null;
this.posIndex = this.posMethod=='after' && this.cDim.length!==0 ? this.posIndex + 1 : this.posIndex; //Normalize
if(this.sorter){
this.sorter.moved = 0;
this.sorter.endMove();
}
if(this.cDim){
this.posIsLastEl = this.cDim.length!==0 && this.posMethod=='after' && this.posIndex==this.cDim.length;
this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) :
this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) :
(!this.posIsLastEl && this.cDim[this.posIndex] ? $(this.cDim[this.posIndex][5]).parent() : $(this.outsideElem) ));
this.posTargetModel = this.posTargetEl.data("model");
this.posTargetCollection = this.posTargetEl.data("model-comp");
}
},
/**
* During event
* @param {Object} e Event
* */
selectingPosition: function(e)
{
this.isPointed = true;
if(!this.wp){
this.$wp = this.$wrapper;
this.wp = this.$wp[0];
}
var wpO = this.$wp.offset();
this.wpT = wpO.top;
this.wpL = wpO.left;
this.wpScT = this.$wp.scrollTop();
this.wpScL = this.$wp.scrollLeft();
if(!this.$plh)
this.createPositionPlaceholder();
this.rY = (e.pageY - this.wpT) + this.wpScT;
this.rX = (e.pageX - this.wpL) + this.wpScL;
this.entered(e);
this.updatePosition(this.rX, this.rY);
var actualPos = this.posIndex + ':' + this.posMethod; //save globally the new index
if(!this.lastPos || (this.lastPos != actualPos)){ //If there is a significant changes with mouse
this.updatePositionPlaceholder(this.posIndex, this.posMethod);
this.lastPos = actualPos;
}
},
/**
* Search where to put placeholder
* @param {Integer} posX X position of the mouse
* @param {Integer} posY Y position of the mouse
*
* @retun void
* */
updatePosition: function( posX, posY ){
this.posMethod = "before";
this.posIndex = 0;
var leftLimit = 0, xLimit = 0, dimRight = 0, yLimit = 0, xCenter = 0, yCenter = 0, dimDown = 0, dim = 0;
for(var i = 0; i < this.cDim.length; i++){
dim = this.cDim[i];
dimDown = dim[0] + dim[2];
yCenter = dim[0] + (dim[2] / 2); //Horizontal center
xCenter = dim[1] + (dim[3] / 2); //Vertical center
dimRight = dim[1] + dim[3];
if( (xLimit && dim[1] > xLimit) || (yLimit && yCenter > yLimit) ||
(leftLimit && dimRight < leftLimit)) //No need with this one if over the limit
continue;
if(!dim[4]){ //If it's not inFlow (like float element)
if( posY < dimDown)
yLimit = dimDown;
if( posX < xCenter){ //If mouse lefter than center
xLimit = xCenter;
this.posMethod = "before";
}else{
leftLimit = xCenter;
this.posMethod = "after";
}
this.posIndex = i;
}else{
this.posIndex = i;
if( posY < yCenter ){ //If mouse upper than center
this.posMethod = "before"; //Should place helper before
break; //No need to continue under inFlow element
}else
this.posMethod = "after";
}
}
if(this.posIndex == (this.cDim.length) && this.posMethod == 'after' ){
this.posIndex--;
}
},
/**
* Updates the position of the placeholder
* @param {Integer} index Index of the nearest child
* @param {String} method Before or after position
*
* @return void
* */
updatePositionPlaceholder: function(index, method){
var t = 0, l = 0, w = 0, h = 0,
marg = 2,
un = 'px',
margI = 5,
plh = this.$plh[0];
if( this.cDim[index] ){
var elDim = this.cDim[index];
if(!elDim[4]){
w = 'auto';
t = elDim[0] + marg;
h = elDim[2] - (marg * 2) + un;
l = (method == 'before') ? (elDim[1] - marg) : (elDim[1] + elDim[3] - marg);
}else{
w = elDim[3] + un;
t = (method == 'before') ? (elDim[0] - marg) : (elDim[0] + elDim[2] - marg);
l = elDim[1];
h = 'auto';
}
//t -= this.wpScT;
//l -= this.wpScL;
}else{
if(this.$targetEl){
var trg = this.$targetEl[0],
$eO = this.$targetEl.offset();
t = $eO.top - this.wpT + this.wpScT + margI;
l = $eO.left - this.wpL + this.wpScL + margI;
w = (parseInt(trg.offsetWidth) - margI * 2) + un;
h = 'auto';
}
}
plh.style.top = t + un;
plh.style.left = l + un;
if(w)
plh.style.width = w;
if(h)
plh.style.height = h;
},
/**
* Track inside which element pointer entered
* @param {Object} e Event
*
* @return void
* */
entered: function(e){
if( (!this.outsideElem || this.outsideElem != e.target) ){ //If I'm in the new element
this.outsideElem = e.target; //Set the element in which it's actually inside
this.$targetEl = $(e.target);
$(this.outsideElem).on('mouseleave',this.itemLeft);
this.cDim = this.getChildrenDim();
this.dimT = this.getTargetDim(e);
if( this.nearToBorders(e) && (e.target.parentNode!=this.wp.parentNode) ) //Avoid flickering
this.cDim = this.getChildrenDim(e.target.parentNode);
}else if( this.nearToBorders(e) && (e.target.parentNode!=this.wp.parentNode) ){ //Near to borders and parent is not the canvas
this.cDim = this.getChildrenDim(e.target.parentNode);
}else if( !(this.nearToBorders(e)) ){
this.cDim = this.getChildrenDim();
}
},
/**
* Check if pointer is near to the borders of the target
* @param {Object} e Event
*
* @return {Integer}
* */
nearToBorders: function(e){
var m = 7; //Limit in pixels for be near
if(!this.dimT)
return;
var dimT = this.dimT;
if(dimT[2] < 40)
m = 5;
if( ((dimT[0] + m) > this.rY) || (this.rY > (dimT[0] + dimT[2] - m)) ||
((dimT[1] + m) > this.rX) || (this.rX > (dimT[1] + dimT[3] - m)) ) //Check if the pointer is near
return 1;
else
return 0;
},
/**
* Check if pointer is near to the float component
*
* @return {Integer}
* */
nearToFloat: function()
{
var index = this.posIndex;
var isLastEl = this.posIsLastEl;
if(this.cDim.length !== 0 && (
(!isLastEl && !this.cDim[index][4]) ||
(this.cDim[index-1] && !this.cDim[index-1][4]) ||
(isLastEl && !this.cDim[index-1][4]) ) )
return 1;
else
return 0;
},
/**
* Returns dimension of the taget
* @param {Object} e Event
*
* @return {Array}
* */
getTargetDim: function(e)
{
var elT = e.target,
$el = $(elT);
return [ elT.offsetTop, elT.offsetLeft, $el.outerHeight(), $el.outerWidth() ];
},
/**
* Returns children and their dimensions of the target element,
* excluding text nodes and the move placeholder
* @param {Object} el Element
*
* @return {Array}
* */
getChildrenDim: function(el)
{
var dim = [];
var elToPars = el || this.outsideElem;
var isInFlow = this.isInFlow; //Assign method for make it work inside $.each
var $this = this; //Store context
$(elToPars.childNodes).each(function(){
var $el = $(this);
if(this.nodeName != '#text' && !$el.data('helper') ){ //Ignore text nodes and helpers
dim.push( [ this.offsetTop, this.offsetLeft, $el.outerHeight(), $el.outerWidth(), isInFlow($this, this), this ] );
}
});
return dim;
},
/**
* Track when I go ouside of the element (basically when the target changes)
* @param {Object} e Event
*
* @return void
* */
itemLeft: function(e)
{
$(this.outsideElem).off('mouseleave',this.itemLeft);
this.outsideElem = null;
this.$targetEl = null;
this.lastPos = null;
},
/**
* Returns true if the elements is in flow, or better is not in flow where
* for example the component is with float:left
* @param {Object} $this Context
* @param {Object} elm Element
*
* @return {Boolean}
* */
isInFlow: function($this, elm)
{
var $elm = $(elm), ch = -1;
if(!$elm.length)
return false;
if( ($elm.height() < ch) || !$this.okProps($elm) )
return false;
return true;
},
/**
* Returns true only if the element follow the standard flow
* @param {Object} $elm Element
*
* @return {Boolean}
* */
okProps: function($elm)
{
if ($elm.css('float')!=='none')
return false;
switch($elm.css('position')) {
case 'static': case 'relative': break;
default: return false;
}
switch ($elm.css('display')) {
case 'block': case 'list-item': case 'table': return true;
}
return false;
/**
* Enabel select position
* @private
*/
enable: function() {
this.startSelectPosition();
},
/**
* Removes position placeholder
*
* @param void
* */
removePositionPlaceholder: function()
{
if(this.$plh && this.$plh.length)
this.$plh.remove();
this.$plh = null;
* Check if the pointer is near to the float component
* @param {number} index
* @param {string} method
* @param {Array<Array>} dims
* @return {Boolean}
* @private
* */
nearFloat: function(index, method, dims) {
var i = index || 0;
var m = method || 'before';
var len = dims.length;
var isLast = len !== 0 && m == 'after' && i == len;
if(len !== 0 && (
(!isLast && !dims[i][4]) ||
(dims[i-1] && !dims[i-1][4]) ||
(isLast && !dims[i-1][4]) ) )
return 1;
return 0;
},
/* Run method */
run: function(){
run: function() {
this.enable();
},
/* Stop method */
stop: function(){
this.removePositionPlaceholder();
stop: function() {
this.stopSelectPosition();
this.$wrapper.css('cursor','');//changes back aspect of the cursor
this.$wrapper.unbind();//removes all attached events
this.$wrapper.css('cursor','');
this.$wrapper.unbind();
}
};
});

18
src/commands/view/SwitchVisibility.js

@ -1,17 +1,13 @@
define(function() {
/**
* @class SwitchVisibility
* */
return {
run: function()
{
this.$canvas.addClass(this.pfx + 'dashed');
run: function(ed) {
ed.Canvas.getBody().className = this.ppfx + 'dashed';
},
stop: function()
{
this.$canvas.removeClass(this.pfx + 'dashed');
stop: function(ed) {
ed.Canvas.getBody().className = "";
}
};
});

15
src/commands/view/TextComponent.js

@ -2,13 +2,14 @@ define(['backbone', './CreateComponent'],
function(Backbone, CreateComponent) {
/**
* @class TextComponent
* @private
* */
return _.extend({}, CreateComponent, {
/**
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
*
* @private
* */
beforeDraw: function(component){
component.type = 'text';
@ -16,11 +17,11 @@ define(['backbone', './CreateComponent'],
component.style = {};
component.style.padding = '10px';
},
/**
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
*
* @private
* */
afterDraw: function(model){
if(!model || !model.set)
@ -29,6 +30,6 @@ define(['backbone', './CreateComponent'],
if(this.sender)
this.sender.set('active', false);
},
});
});

15
src/config/require-config.js

@ -33,18 +33,27 @@ require.config({
},
packages : [
{ name: 'GrapesJS', location: 'grapesjs' },
{ name: 'Abstract', location: 'domain_abstract' },
{ name: 'Editor', location: 'editor', },
{ name: 'AssetManager', location: 'asset_manager', },
{ name: 'BlockManager', location: 'block_manager', },
{ name: 'TraitManager', location: 'trait_manager', },
{ name: 'StyleManager', location: 'style_manager', },
{ name: 'ClassManager', location: 'class_manager', },
{ name: 'DeviceManager', location: 'device_manager', },
{ name: 'StorageManager', location: 'storage_manager', },
{ name: 'PluginManager', location: 'plugin_manager', },
{ name: 'Navigator', location: 'navigator', },
{ name: 'DomComponents', location: 'dom_components', },
{ name: 'RichTextEditor', location: 'rich_text_editor', },
{ name: 'SelectorManager', location: 'selector_manager', },
{ name: 'ModalDialog', location: 'modal_dialog', },
{ name: 'CodeManager', location: 'code_manager', },
{ name: 'CssComposer', location: 'css_composer', },
{ name: 'Commands', location: 'commands', },
{ name: 'Canvas', location: 'canvas', },
{ name: 'Panels', location: 'panels', }
{ name: 'Panels', location: 'panels', },
{ name: 'Parser', location: 'parser', },
{ name: 'Utils', location: 'utils', }
]
});
});

2
src/css_composer/config/config.js

@ -8,7 +8,7 @@ define(function () {
'staticRules': '',
// Default CSS style
'defaults': [],
rules: [],
};
});

302
src/css_composer/main.js

@ -1,106 +1,258 @@
/**
* * [add](#add)
* * [get](#get)
* * [getAll](#getall)
* * [load](#load)
* * [store](#store)
*
* This module contains and manage CSS rules for the template inside the canvas
* Before using the methods you should get first the module from the editor instance, in this way:
*
* ```js
* var cssComposer = editor.CssComposer;
* ```
*
* @module CssComposer
* @param {Object} config Configurations
* @param {string|Array<Object>} [config.rules=[]] CSS string or an array of rule objects
* @example
* ...
* CssComposer: {
* rules: '.myClass{ color: red}',
* }
*/
define(function(require) {
/**
* @class CssComposer
* @param {Object} config Configurations
*
* */
var CssComposer = function(config)
{
var c = config || {},
def = require('./config/config'),
CssRule = require('./model/CssRule'),
CssRules = require('./model/CssRules'),
Selectors = require('./model/Selectors'),
CssRulesView = require('./view/CssRulesView');
for (var name in def) {
if (!(name in c))
c[name] = def[name];
}
var rules = new CssRules(c.defaults, c),
rulesView = new CssRulesView({
collection: rules,
config: c,
});
return function() {
var c = {},
defaults = require('./config/config'),
CssRule = require('./model/CssRule'),
CssRules = require('./model/CssRules'),
Selectors = require('./model/Selectors'),
CssRulesView = require('./view/CssRulesView');
var rules, rulesView;
return {
Selectors: Selectors,
/**
* Create new rule and return it. Don't add it to the collection
* @param {Array} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
*
* @return {Object}
* Name of the module
* @type {String}
* @private
*/
name: 'CssComposer',
/**
* Mandatory for the storage manager
* @type {String}
* @private
*/
storageKey: function(){
var keys = [];
var smc = (c.stm && c.stm.getConfig()) || {};
if(smc.storeCss)
keys.push('css');
if(smc.storeStyles)
keys.push('styles');
return keys;
},
/**
* Initializes module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @private
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
var elStyle = (c.em && c.em.config.style) || '';
c.rules = elStyle || c.rules;
c.sm = c.em; // TODO Refactor
rules = new CssRules([], c);
rules.add(c.rules);
rulesView = new CssRulesView({
collection: rules,
config: c,
});
return this;
},
/**
* On load callback
* @private
*/
onLoad: function(){
if(c.stm && c.stm.getConfig().autoload)
this.load();
if(c.stm && c.stm.isAutosave())
c.em.listenRules(this.getAll());
},
/**
* Load data from the passed object, if the object is empty will try to fetch them
* autonomously from the storage manager.
* The fetched data will be added to the collection
* @param {Object} data Object of data to load
* @return {Object} Loaded rules
*/
load: function(data){
var d = data || '';
if(!d && c.stm)
d = c.em.getCacheLoad();
var obj = '';
if(d.style){
try{
obj = JSON.parse(d.style);
}catch(err){}
}else if(d.css)
obj = c.em.get('Parser').parseCss(d.css);
if(obj)
rules.reset(obj);
return obj;
},
/**
* Store data to the selected storage
* @param {Boolean} noStore If true, won't store
* @return {Object} Data to store
*/
store: function(noStore){
if(!c.stm)
return;
var obj = {};
var keys = this.storageKey();
if(keys.indexOf('css') >= 0)
obj.css = c.em.getCss();
if(keys.indexOf('styles') >= 0)
obj.styles = JSON.stringify(rules);
if(!noStore)
c.stm.store(obj);
return obj;
},
/**
* Add new rule to the collection, if not yet exists with the same selectors
* @param {Array<Selector>} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
* @return {Model}
* @example
* var sm = editor.SelectorManager;
* var sel1 = sm.add('myClass1');
* var sel2 = sm.add('myClass2');
* var rule = cssComposer.add([sel1, sel2], 'hover');
* rule.set('style', {
* width: '100px',
* color: '#fff',
* });
* */
newRule: function(selectors, state, width) {
add: function(selectors, state, width) {
var s = state || '';
var w = width || '';
var rule = new CssRule({
state: s,
maxWidth: w,
});
rule.get('selectors').add(selectors);
return rule;
var rule = this.get(selectors, s, w);
if(rule)
return rule;
else{
rule = new CssRule({
state: s,
maxWidth: w,
});
rule.get('selectors').add(selectors);
rules.add(rule);
return rule;
}
},
/**
* Add new rule to the collection if not yet exists
* @param {Object} rule
*
* @return {Object}
* Get the rule
* @param {Array<Selector>} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
* @return {Model|null}
* @example
* var sm = editor.SelectorManager;
* var sel1 = sm.add('myClass1');
* var sel2 = sm.add('myClass2');
* var rule = cssComposer.get([sel1, sel2], 'hover');
* // Update the style
* rule.set('style', {
* width: '300px',
* color: '#000',
* });
* */
addRule: function(rule){
var models = rule.get('selectors').models;
var r = this.getRule(models, rule.get('state'), rule.get('maxWidth'));
if(!r)
r = rules.add(rule);
return r;
get: function(selectors, state, width) {
var rule = null;
rules.each(function(m){
if(rule)
return;
if(m.compare(selectors, state, width))
rule = m;
});
return rule;
},
/**
* Get class by its name
* @param {Array} selectors Array of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
*
* @return {Object|null}
* Get the collection of rules
* @return {Collection}
* */
getRule : function(selectors, state, width) {
fRule = null;
rules.each(function(rule){
if(fRule)
return;
if(rule.compare(selectors, state, width))
fRule = rule;
}, this);
return fRule;
getAll: function() {
return rules;
},
/**
* Get collection of css rules
*
* @return {Object}
* */
getRules : function() {
return rules;
* Add a raw collection of rule objects
* This method overrides styles, in case, of already defined rule
* @param {Array<Object>} data Array of rule objects
* @return {Array<Model>}
* @private
*/
addCollection: function(data){
var result = [];
var d = data instanceof Array ? data : [data];
for(var i = 0, l = d.length; i < l; i++){
var rule = d[i] || {};
if(!rule.selectors)
continue;
var sm = c.em && c.em.get('SelectorManager');
if(!sm)
console.warn('Selector Manager not found');
var sl = rule.selectors;
var sels = sl instanceof Array ? sl : [sl];
var newSels = [];
for(var j = 0, le = sels.length; j < le; j++){
var selec = sm.add(sels[j]);
newSels.push(selec);
}
var model = this.add(newSels, rule.state, rule.width);
model.set('style', rule.style || {});
result.push(model);
}
return result;
},
/**
* Render block of CSS rules
*
* @return {Object}
* Render the block of CSS rules
* @return {HTMLElement}
* @private
*/
render: function(){
render: function() {
return rulesView.render().el;
}
};
};
return CssComposer;
});
});

19
src/css_composer/model/CssRule.js

@ -1,8 +1,5 @@
define(['backbone', './Selectors'],
function (Backbone, Selectors) {
/**
* @class CssRule
* */
return Backbone.Model.extend({
defaults: {
@ -26,7 +23,7 @@ define(['backbone', './Selectors'],
if(this.sm.get){
var slct = [];
for(var i = 0; i < this.slct.length; i++)
slct.push(this.sm.get('ClassManager').addClass(this.slct[i].name));
slct.push(this.sm.get('SelectorManager').add(this.slct[i].name || this.slct[i]));
this.slct = slct;
}
@ -38,15 +35,23 @@ define(['backbone', './Selectors'],
* @param {Object} selectors Collection of selectors
* @param {String} state Css rule state
* @param {String} width For which device this style is oriented
*
* @return {Boolean}
* @private
*/
compare: function(selectors, state, width){
var st = state || '';
var wd = width || '';
var cId = 'cid';
var a1 = _.pluck(selectors.models || selectors, cId);
var a2 = _.pluck(this.get('selectors').models, cId);
//var a1 = _.pluck(selectors.models || selectors, cId);
//var a2 = _.pluck(this.get('selectors').models, cId);
if(!(selectors instanceof Array) && !selectors.models)
selectors = [selectors];
var a1 = _.map((selectors.models || selectors), function(model) {
return model.get('name');
});
var a2 = _.map(this.get('selectors').models, function(model) {
return model.get('name');
});
var f = false;
if(a1.length !== a2.length)

13
src/css_composer/model/CssRules.js

@ -1,12 +1,13 @@
define(['backbone','./CssRule'],
function (Backbone, CssRule) {
/**
* @class CssRules
* */
return Backbone.Collection.extend({
initialize: function(models, opt){
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
this.model = function(attrs, options) {
var model;
@ -23,5 +24,11 @@ define(['backbone','./CssRule'],
},
add: function(models, opt){
if(typeof models === 'string')
models = this.editor.get('Parser').parseCss(models);
return Backbone.Collection.prototype.add.apply(this, [models, opt]);
},
});
});

6
src/css_composer/model/Selectors.js

@ -1,9 +1,5 @@
define([ 'backbone', 'require'],
function (Backbone, require) {
/**
* @class Selectors
* */
return Backbone.Collection.extend({
initialize: function(models, opt){
@ -15,7 +11,7 @@ define([ 'backbone', 'require'],
default:
if(!this.ClassTag)
this.ClassTag = require("ClassManager/model/ClassTag");
this.ClassTag = require("SelectorManager/model/Selector");
model = new this.ClassTag(attrs, opts);
}

35
src/css_composer/view/CssRuleView.js

@ -1,8 +1,5 @@
define(['backbone'],
function (Backbone) {
/**
* @class CssRuleView
* */
return Backbone.View.extend({
tagName: 'style',
@ -10,12 +7,25 @@ define(['backbone'],
initialize: function(o) {
this.config = o.config || {};
this.listenTo(this.model, 'change:style', this.render);
this.listenTo(this.model, 'change:state', this.render);
this.listenTo(this.model, 'destroy remove', this.remove);
this.listenTo(this.model, 'change:maxWidth', this.render);
this.listenTo(this.model.get('selectors'), 'change', this.selChanged);
},
/**
* Triggered when some selector is changed
* @private
*/
selChanged: function(){
this.selStr = this.renderSelectors();
this.render();
},
/**
* Returns string of selectors
*
* @return {String}
* @private
*/
renderSelectors: function(){
var sel = [];
@ -27,8 +37,8 @@ define(['backbone'],
/**
* Returns string of properties
*
* @return {String}
* @private
*/
renderProperties: function(){
var sel = [],
@ -41,13 +51,22 @@ define(['backbone'],
render : function(){
var block = '',
o = '';
selStr = '';
o = '';
if(!this.selStr)
this.selStr = this.renderSelectors();
var prpStr = this.renderProperties();
if(this.selStr)
var stateStr = this.model.get('state');
var width = this.model.get('maxWidth');
if(this.selStr){
stateStr = stateStr ? ':' + stateStr : '';
block = prpStr !== '' ? '{' + prpStr + '}' : '';
o = this.selStr && block ? this.selStr + block : '';
}
o = this.selStr && block ? this.selStr + stateStr + block : '';
if(width && o)
o = '@media (max-width: ' + width + '){' + o + '}';
this.$el.html(o);
return this;
},

6
src/css_composer/view/CssRulesView.js

@ -1,8 +1,5 @@
define(['backbone','./CssRuleView'],
function (Backbone, CssRuleView) {
/**
* @class CssRulesView
* */
return Backbone.View.extend({
initialize: function(o) {
@ -16,6 +13,7 @@ define(['backbone','./CssRuleView'],
/**
* Add to collection
* @param {Object} model
* @private
* */
addTo: function(model){
//console.log('Added');
@ -26,8 +24,8 @@ define(['backbone','./CssRuleView'],
* Add new object to collection
* @param {Object} model
* @param {Object} fragmentEl
*
* @return {Object}
* @private
* */
addToCollection: function(model, fragmentEl){
var fragment = fragmentEl || null;

1072
src/demo.js

File diff suppressed because it is too large

9
src/device_manager/config/config.js

@ -0,0 +1,9 @@
define(function () {
return {
devices: [],
deviceLabel: 'Device',
};
});

115
src/device_manager/main.js

@ -0,0 +1,115 @@
/**
* * [add](#add)
* * [get](#get)
* * [getAll](#getall)
*
* Before using methods you should get first the module from the editor instance, in this way:
*
* ```js
* var deviceManager = editor.DeviceManager;
* ```
*
* @module DeviceManager
*/
define(function(require) {
return function() {
var c = {},
defaults = require('./config/config'),
Devices = require('./model/Devices'),
DevicesView = require('./view/DevicesView');
var devices, view;
return {
/**
* Name of the module
* @type {String}
* @private
*/
name: 'DeviceManager',
/**
* Initialize module. Automatically called with a new instance of the editor
* @param {Object} config Configurations
* @param {Array<Object>} [config.devices=[]] Default devices
* @example
* ...
* {
* devices: [
* {name: 'Desktop', width: ''}
* {name: 'Tablet', width: '991px'}
* ],
* }
* ...
* @return {this}
*/
init: function(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
devices = new Devices(c.devices);
view = new DevicesView({
collection: devices,
config: c
});
return this;
},
/**
* Add new device to the collection. URLs are supposed to be unique
* @param {string} name Device name
* @param {string} width Width of the device
* @param {Object} opts Custom options
* @return {Device} Added device
* @example
* deviceManager.add('Tablet', '900px');
*/
add: function(name, width, opts){
var obj = opts || {};
obj.name = name;
obj.width = width;
return devices.add(obj);
},
/**
* Return device by name
* @param {string} name Name of the device
* @example
* var device = deviceManager.get('Tablet');
* console.log(JSON.stringify(device));
* // {name: 'Tablet', width: '900px'}
*/
get: function(name){
return devices.get(name);
},
/**
* Return all devices
* @return {Collection}
* @example
* var devices = deviceManager.getAll();
* console.log(JSON.stringify(devices));
* // [{name: 'Desktop', width: ''}, ...]
*/
getAll: function(){
return devices;
},
/**
* Render devices
* @return {string} HTML string
* @private
*/
render: function(){
return view.render().el;
},
};
};
});

14
src/device_manager/model/Device.js

@ -0,0 +1,14 @@
define(['backbone'],
function(Backbone){
return Backbone.Model.extend({
idAttribute: 'name',
defaults :{
name: '',
width: '',
},
});
});

9
src/device_manager/model/Devices.js

@ -0,0 +1,9 @@
define(['backbone','./Device'],
function (Backbone, Device) {
return Backbone.Collection.extend({
model: Device,
});
});

10
src/device_manager/template/devices.html

@ -0,0 +1,10 @@
<div class="<%= ppfx %>device-label"><%= deviceLabel %></div>
<div class="<%= ppfx %>field <%= ppfx %>select">
<span id="<%= ppfx %>input-holder">
<select class="<%= ppfx %>devices"></select>
</span>
<div class="<%= ppfx %>sel-arrow">
<div class="<%= ppfx %>d-s-arrow"></div>
</div>
</div>
<button style="display:none" class="<%= ppfx %>add-trasp">+</button>

82
src/device_manager/view/DevicesView.js

@ -0,0 +1,82 @@
define(['backbone', 'text!./../template/devices.html'],
function(Backbone, devicesTemplate) {
return Backbone.View.extend({
template: _.template(devicesTemplate),
events: {
'change': 'updateDevice'
},
initialize: function(o) {
this.config = o.config || {};
this.em = this.config.em;
this.ppfx = this.config.pStylePrefix || '';
this.events['click .' + this.ppfx + 'add-trasp'] = this.startAdd;
this.listenTo(this.em, 'change:device', this.updateSelect);
this.delegateEvents();
},
/**
* Start adding new device
* @return {[type]} [description]
* @private
*/
startAdd: function(){},
/**
* Update device of the editor
* @private
*/
updateDevice: function(){
var em = this.em;
if(em){
var devEl = this.devicesEl;
var val = devEl ? devEl.val() : '';
em.set('device', val);
}
},
/**
* Update select value on device update
* @private
*/
updateSelect: function(){
var em = this.em;
var devEl = this.devicesEl;
if(em && em.getDeviceModel && devEl){
var device = em.getDeviceModel();
var name = device ? device.get('name') : '';
devEl.val(name);
}
},
/**
* Return devices options
* @return {string} String of options
* @private
*/
getOptions: function(){
var result = '';
this.collection.each(function(device){
var name = device.get('name');
result += '<option value="' + name+ '">' + name + '</option>';
});
return result;
},
render: function() {
var pfx = this.ppfx;
this.$el.html(this.template({
ppfx: pfx,
deviceLabel: this.config.deviceLabel
}));
this.devicesEl = this.$el.find('.' + pfx + 'devices');
this.devicesEl.append(this.getOptions());
this.el.className = pfx + 'devices-c';
return this;
},
});
});

11
src/dom_components/config/config.js

@ -9,22 +9,23 @@ define(function () {
removable : false,
copyable : false,
stylable : ['background','background-color','background-image', 'background-repeat','background-attachment','background-position'],
movable : false,
draggable : false,
badgable : false,
components: [],
},
// Could be used for default components
defaults : [],
components: [],
rte : {},
em : {},
rte: {},
// Class for new image component
imageCompClass : 'fa fa-picture-o',
// Open assets manager on create of image component
oAssetsOnCreate : true,
// List of void elements
voidElements: ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],
};
});

311
src/dom_components/main.js

@ -1,89 +1,288 @@
/**
*
* * [getWrapper](#getwrapper)
* * [getComponents](#getcomponents)
* * [addComponent](#addcomponent)
* * [clear](#clear)
* * [load](#load)
* * [store](#store)
* * [render](#render)
*
* With this module is possible to manage components inside the canvas.
* Before using methods you should get first the module from the editor instance, in this way:
*
* ```js
* var domComponents = editor.DomComponents;
* ```
*
* @module DomComponents
* @param {Object} config Configurations
* @param {string|Array<Object>} [config.components=[]] HTML string or an array of possible components
* @example
* ...
* domComponents: {
* components: '<div>Hello world!</div>',
* }
* // Or
* domComponents: {
* components: [
* { tagName: 'span', style: {color: 'red'}, content: 'Hello'},
* { style: {width: '100px', content: 'world!'}}
* ],
* }
* ...
*/
define(function(require) {
/**
* @class Components
* @param {Object} Configurations
*
* @return {Object}
* */
function Components(config)
{
var c = config || {},
defaults = require('./config/config'),
Component = require('./model/Component'),
ComponentText = require('./model/ComponentText'),
ComponentImage = require('./model/ComponentImage'),
ComponentView = require('./view/ComponentView'),
ComponentImageView = require('./view/ComponentImageView'),
return function (){
var c = {},
defaults = require('./config/config'),
Component = require('./model/Component'),
ComponentText = require('./model/ComponentText'),
ComponentImage = require('./model/ComponentImage'),
ComponentLink = require('./model/ComponentLink'),
ComponentView = require('./view/ComponentView'),
ComponentImageView = require('./view/ComponentImageView'),
ComponentTextView = require('./view/ComponentTextView');
ComponentLinkView = require('./view/ComponentLinkView');
var component, componentView;
// Set default options
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
return {
if(!c.wrapper.attributes)
c.wrapper.attributes = {};
c.wrapper.attributes.id = 'wrapper';
/**
* Name of the module
* @type {String}
* @private
*/
name: 'DomComponents',
// If there is no components try to append defaults
if(!c.wrapper.components.length && c.defaults.length){
c.wrapper.components = c.defaults;
}
/**
* Mandatory for the storage manager
* @type {String}
* @private
*/
storageKey: function(){
var keys = [];
var smc = (c.stm && c.stm.getConfig()) || {};
if(smc.storeHtml)
keys.push('html');
if(smc.storeComponents)
keys.push('components');
return keys;
},
if(!c.wrapper.style)
c.wrapper.style = {};
/**
* Initialize module. Called on a new instance of the editor with configurations passed
* inside 'domComponents' field
* @param {Object} config Configurations
* @private
*/
init: function(config) {
c = config || {};
if(c.em)
c.components = c.em.config.components || c.components;
c.wrapper.style.position = 'relative';
this.component = new Component(c.wrapper, { sm: c.em });
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
var obj = {
model: this.component,
config: c,
};
var ppfx = c.pStylePrefix;
if(ppfx)
c.stylePrefix = ppfx + c.stylePrefix;
// Load dependencies
if(c.em){
c.rte = c.em.get('rte') || '';
c.modal = c.em.get('Modal') || '';
c.am = c.em.get('AssetManager') || '';
}
component = new Component(c.wrapper, { sm: c.em, config: c });
component.set({ attributes: {id: 'wrapper'}});
component.get('components').add(c.components);
componentView = new ComponentView({
model: component,
config: c,
});
return this;
},
/**
* On load callback
* @private
*/
onLoad: function(){
if(c.stm && c.stm.getConfig().autoload)
this.load();
if(c.stm && c.stm.isAutosave()){
c.em.initUndoManager();
c.em.initChildrenComp(this.getWrapper());
}
},
/**
* Load components from the passed object, if the object is empty will try to fetch them
* autonomously from the selected storage
* The fetched data will be added to the collection
* @param {Object} data Object of data to load
* @return {Object} Loaded data
*/
load: function(data){
var d = data || '';
if(!d && c.stm)
d = c.em.getCacheLoad();
var obj = '';
if(d.components){
try{
obj = JSON.parse(d.components);
}catch(err){}
}else if(d.html)
obj = d.html;
this.c = c;
this.ComponentView = new ComponentView(obj);
}
if(obj)
this.getComponents().reset(obj);
return obj;
},
Components.prototype = {
/**
* Store components on the selected storage
* @param {Boolean} noStore If true, won't store
* @return {Object} Data to store
*/
store: function(noStore){
if(!c.stm)
return;
var obj = {};
var keys = this.storageKey();
if(keys.indexOf('html') >= 0)
obj.html = c.em.getHtml();
if(keys.indexOf('components') >= 0)
obj.components = JSON.stringify(c.em.getComponents());
if(!noStore)
c.stm.store(obj);
return obj;
},
/**
* Returns main wrapper which will contain all new components
*
* Returns privately the main wrapper
* @return {Object}
* @private
*/
getComponent : function(){
return this.component;
return component;
},
/**
* Returns main wrapper which will contain all new components
*
* @return {Object}
* Returns root component inside the canvas. Something like <body> inside HTML page
* The wrapper doesn't differ from the original Component Model
* @return {Component} Root Component
* @example
* // Change background of the wrapper and set some attribute
* var wrapper = domComponents.getWrapper();
* wrapper.set('style', {'background-color': 'red'});
* wrapper.set('attributes', {'title': 'Hello!'});
*/
getWrapper: function(){
return this.getComponent();
},
/**
* Returns children from the wrapper
*
* @return {Object}
* Returns wrapper's children collection. Once you have the collection you can
* add other Components(Models) inside. Each component can have several nested
* components inside and you can nest them as more as you wish.
* @return {Components} Collection of components
* @example
* // Let's add some component
* var wrapperChildren = domComponents.getComponents();
* var comp1 = wrapperChildren.add({
* style: { 'background-color': 'red'}
* });
* var comp2 = wrapperChildren.add({
* tagName: 'span',
* attributes: { title: 'Hello!'}
* });
* // Now let's add an other one inside first component
* // First we have to get the collection inside. Each
* // component has 'components' property
* var comp1Children = comp1.get('components');
* // Procede as before. You could also add multiple objects
* comp1Children.add([
* { style: { 'background-color': 'blue'}},
* { style: { height: '100px', width: '100px'}}
* ]);
* // Remove comp2
* wrapperChildren.remove(comp2);
*/
getComponents: function(){
return this.getWrapper().get('components');
},
/**
* Render and returns wrapper
*
* @return {Object}
* Add new components to the wrapper's children. It's the same
* as 'domComponents.getComponents().add(...)'
* @param {Object|Component|Array<Object>} component Component/s to add
* @param {string} [component.tagName='div'] Tag name
* @param {string} [component.type=''] Type of the component. Available: ''(default), 'text', 'image'
* @param {boolean} [component.removable=true] If component is removable
* @param {boolean} [component.draggable=true] If is possible to move the component around the structure
* @param {boolean} [component.droppable=true] If is possible to drop inside other components
* @param {boolean} [component.badgable=true] If the badge is visible when the component is selected
* @param {boolean} [component.stylable=true] If is possible to style component
* @param {boolean} [component.copyable=true] If is possible to copy&paste the component
* @param {string} [component.content=''] String inside component
* @param {Object} [component.style={}] Style object
* @param {Object} [component.attributes={}] Attribute object
* @return {Component|Array<Component>} Component/s added
* @example
* // Example of a new component with some extra property
* var comp1 = domComponents.addComponent({
* tagName: 'div',
* removable: true, // Can't remove it
* draggable: true, // Can't move it
* copyable: true, // Disable copy/past
* content: 'Content text', // Text inside component
* style: { color: 'red'},
* attributes: { title: 'here' }
* });
*/
render : function(){
return this.ComponentView.render().el;
addComponent: function(component){
return this.getComponents().add(component);
},
};
return Components;
});
/**
* Render and returns wrapper element with all components inside.
* Once the wrapper is rendered, and it's what happens when you init the editor,
* the all new components will be added automatically and property changes are all
* updated immediately
* @return {HTMLElement}
*/
render: function(){
return componentView.render().el;
},
/**
* Remove all components
* @return {this}
*/
clear: function(){
var c = this.getComponents();
for(var i = 0, len = c.length; i < len; i++)
c.pop();
return this;
},
/**
* Set components
* @param {Object|string} components HTML string or components model
* @return {this}
* @private
*/
setComponents: function(components){
this.clear().addComponent(components);
},
};
};
});

90
src/dom_components/model/Component.js

@ -1,50 +1,59 @@
define(['backbone','./Components', 'ClassManager/model/ClassTags'],
function (Backbone, Components, ClassTags) {
/**
* @class Component
* */
define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitManager/model/Traits'],
function (Backbone, Components, Selectors, Traits) {
return Backbone.Model.extend({
defaults: {
tagName : 'div',
type : '',
editable : false,
removable : true,
movable : true,
droppable : true,
badgable : true,
stylable : true,
copyable : true,
status : '',
previousModel : '',
content : '',
style : {},
attributes : {},
tagName: 'div',
type: '',
editable: false,
removable: true,
draggable: true,
droppable: true,
badgable: true,
stylable: true,
copyable: true,
void: false,
state: '',
status: '',
previousModel: '',
content: '',
style: {},
attributes: {},
traits: ['id', 'title'],
},
initialize: function(o, opt) {
// Check void elements
if(opt && opt.config && opt.config.voidElements.indexOf(this.get('tagName')) >= 0)
this.set('void', true);
this.sm = opt ? opt.sm || {} : {};
this.config = o || {};
this.defaultC = this.config.components || [];
this.defaultCl = this.normalizeClasses(this.config.classes || []);
this.components = new Components(this.defaultC, opt);
this.set('components', this.components);
this.set('classes', new ClassTags(this.defaultCl));
this.set('classes', new Selectors(this.defaultCl));
var traits = new Traits();
traits.setTarget(this);
traits.add(this.get('traits'));
this.set('traits', traits);
},
/**
* Normalize input classes from array to array of objects
* @param {Array} arr
*
* @return {Array}
* @private
*/
normalizeClasses: function(arr){
normalizeClasses: function(arr) {
var res = [];
if(!this.sm.get)
return;
var clm = this.sm.get('ClassManager');
var clm = this.sm.get('SelectorManager');
if(!clm)
return;
@ -56,7 +65,7 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
else
name = val.name;
var model = clm.addClass(name);
var model = clm.add(name);
res.push(model);
});
return res;
@ -64,22 +73,29 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
/**
* Override original clone method
* @private
*/
clone: function()
{
clone: function() {
var attr = _.clone(this.attributes),
comp = this.get('components'),
traits = this.get('traits'),
cls = this.get('classes');
attr.components = [];
attr.classes = [];
attr.classes = [];
attr.traits = [];
if(comp.length){
comp.each(function(md,i){
attr.components[i] = md.clone();
comp.each(function(md,i) {
attr.components[i] = md.clone();
});
}
if(traits.length){
traits.each(function(md, i) {
attr.traits[i] = md.clone();
});
}
if(cls.length){
cls.each(function(md,i){
attr.classes[i] = md.get('name');
cls.each(function(md,i) {
attr.classes[i] = md.get('name');
});
}
attr.status = '';
@ -90,12 +106,16 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
* Get name of the component
*
* @return {String}
* @private
* */
getName: function(){
getName: function() {
if(!this.name){
var id = this.cid.replace(/\D/g,''),
type = this.get('type');
this.name = type.charAt(0).toUpperCase() + type.slice(1) + 'Box' + id;
var id = this.cid.replace(/\D/g,''),
type = this.get('type');
var tag = this.get('tagName');
tag = tag == 'div' ? 'box' : tag;
tag = type ? type : tag;
this.name = tag.charAt(0).toUpperCase() + tag.slice(1) + ' ' + id;
}
return this.name;
},

17
src/dom_components/model/ComponentImage.js

@ -1,14 +1,13 @@
define(['./Component'],
define(['./Component'],
function (Component) {
/**
* @class ComponentImage
* */
return Component.extend({
return Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
src : '',
droppable : false,
src: '',
droppable: false,
traits: ['alt'],
}),
});
});

13
src/dom_components/model/ComponentLink.js

@ -0,0 +1,13 @@
define(['./Component'],
function (Component) {
return Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
tagName: 'a',
droppable: false,
traits: ['title', 'href', 'target'],
}),
});
});

12
src/dom_components/model/ComponentText.js

@ -1,14 +1,12 @@
define(['./Component'],
define(['./Component'],
function (Component) {
/**
* @class ComponentText
* */
return Component.extend({
return Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
content : '',
droppable : false,
}),
});
});

47
src/dom_components/model/Components.js

@ -1,19 +1,27 @@
define([ 'backbone', 'require'],
function (Backbone, require) {
/**
* @class Components
* */
return Backbone.Collection.extend({
initialize: function(models, opt){
this.on('add', this.onAdd);
this.config = opt && opt.config ? opt.config : null;
// Inject editor
if(opt && opt.sm)
this.editor = opt.sm;
this.model = function(attrs, options) {
var model;
if(!options.sm && opt && opt.sm)
options.sm = opt.sm;
if(opt && opt.config)
options.config = opt.config;
switch(attrs.type){
case 'text':
@ -21,6 +29,12 @@ define([ 'backbone', 'require'],
this.mComponentText = require("./ComponentText");
model = new this.mComponentText(attrs, options);
break;
case 'link':
if(!this.mComponentLink)
this.mComponentLink = require("./ComponentLink");
model = new this.mComponentLink(attrs, options);
break;
case 'image':
if(!this.mComponentImage)
@ -40,5 +54,32 @@ define([ 'backbone', 'require'],
},
add: function(models, opt){
if(typeof models === 'string'){
var parsed = this.editor.get('Parser').parseHtml(models);
models = parsed.html;
var cssc = this.editor.get('CssComposer');
if(parsed.css && cssc){
var added = cssc.addCollection(parsed.css);
}
}
return Backbone.Collection.prototype.add.apply(this, [models, opt]);
},
onAdd: function(model, c, opts){
var style = model.get('style');
if(!_.isEmpty(style) && this.editor){
var cssC = this.editor.get('CssComposer');
var newClass = this.editor.get('SelectorManager').add(model.cid);
model.set({style:{}});
model.get('classes').add(newClass);
var rule = cssC.add(newClass);
rule.set('style', style);
}
},
});
});

41
src/dom_components/view/ComponentImageView.js

@ -1,8 +1,5 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
/**
* @class ComponentImageView
* */
return ComponentView.extend({
@ -14,34 +11,34 @@ define(['backbone', './ComponentView'],
initialize: function(o){
ComponentView.prototype.initialize.apply(this, arguments);
this.listenTo( this.model, 'change:src', this.updateSrc);
this.listenTo( this.model, 'dblclick', this.openModal);
this.classEmpty = this.config.stylePrefix + 'image-placeholder ' + this.config.imageCompClass;
if(!this.model.get('src'))
this.$el.attr('class', this.classEmpty);
this.listenTo( this.model, 'change:src', this.updateSrc);
this.listenTo( this.model, 'dblclick active', this.openModal);
this.classEmpty = this.ppfx + 'plh-image';
if(this.config.modal)
this.modal = this.config.modal;
this.modal = this.config.modal;
if(this.config.am)
this.am = this.config.am;
this.am = this.config.am;
},
/**
* Update src attribute
*
* @return void
* @private
* */
updateSrc: function(){
this.$el.attr('src',this.model.get("src"));
updateSrc: function() {
var src = this.model.get("src");
this.$el.attr('src', src);
if(!src)
this.$el.addClass(this.classEmpty);
else
this.$el.removeClass(this.classEmpty);
},
/**
* Open dialog for image changing
* @param {Object} e Event
*
* @return void
* @private
* */
openModal: function(e){
var that = this;
@ -49,9 +46,9 @@ define(['backbone', './ComponentView'],
this.modal.setTitle('Select image');
this.modal.setContent(this.am.render(1));
this.am.setTarget(this.model);
this.modal.show();
this.modal.open();
this.am.onSelect(function(){
that.modal.hide();
that.modal.close();
that.am.setTarget(null);
});
}
@ -60,6 +57,12 @@ define(['backbone', './ComponentView'],
render: function() {
this.updateAttributes();
this.updateClasses();
if(!this.model.get('src'))
this.$el.attr('class', this.classEmpty);
// Avoid strange behaviours while try to drag
this.$el.attr('onmousedown', 'return false');
return this;
},
});

15
src/dom_components/view/ComponentLinkView.js

@ -0,0 +1,15 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
return ComponentView.extend({
events: {
'click': 'onClick',
},
onClick: function(e) {
e.preventDefault();
},
});
});

65
src/dom_components/view/ComponentTextView.js

@ -1,8 +1,5 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
/**
* @class ComponentTextView
* */
return ComponentView.extend({
@ -13,43 +10,37 @@ define(['backbone', './ComponentView'],
initialize: function(o){
ComponentView.prototype.initialize.apply(this, arguments);
_.bindAll(this,'disableEditing');
this.listenTo( this.model, 'focus', this.enableEditing);
if(this.config.rte){
this.rte = this.config.rte;
}
this.listenTo(this.model, 'focus active', this.enableEditing);
this.rte = this.config.rte || '';
},
/**
* Enable this component to be editable,
* load also the mini toolbar for quick editing
* @param Event
* Enable the component to be editable
* @param {Event} e
* @private
* */
enableEditing: function(e){
if(this.rte){
var $e = this.config.em.get('$editor');
if(!this.$wrapper && $e.length)
this.$wrapper = $e.find('#'+this.config.wrapperId);
this.rte.bind(this, this.$wrapper);
}
$(document).on('mousedown', this.disableEditing); //Close edit mode
this.$el.on('mousedown', this.disablePropagation); //Avoid closing edit mode on component click
if(this.rte)
this.rte.attach(this);
this.toggleEvents(1);
},
/**
* Disable this component to be editable
* @param Event
* @param {Event}
* @private
* */
disableEditing: function(e){
if(this.rte){
this.rte.unbind(this);
}
$(document).off('mousedown', this.disableEditing);
this.$el.off('mousedown',this.disablePropagation);
if(this.rte)
this.rte.detach(this);
this.toggleEvents();
this.updateContents();
},
/** Isolate disable propagation method
* @param Event
/**
* Isolate disable propagation method
* @param {Event}
* @private
* */
disablePropagation: function(e){
e.stopPropagation();
@ -57,17 +48,29 @@ define(['backbone', './ComponentView'],
/**
* Update contents of the element
*
* @return void
* @private
**/
updateContents : function(){
this.model.set('content', this.$el.html());
updateContents: function(){
this.model.set('content', this.el.innerHTML);
},
/**
* Enable/Disable events
* @param {Boolean} enable
*/
toggleEvents: function(enable) {
var method = enable ? 'on' : 'off';
// The ownerDocument is from the frame
var elDocs = [this.el.ownerDocument, document, this.rte];
$(elDocs)[method]('mousedown', this.disableEditing);
// Avoid closing edit mode on component click
this.$el[method]('mousedown', this.disablePropagation);
},
render: function() {
this.updateAttributes();
this.updateClasses();
this.$el.html(this.model.get('content'));
this.el.innerHTML = this.model.get('content');
return this;
},
});

88
src/dom_components/view/ComponentView.js

@ -1,31 +1,31 @@
define(['backbone', './ComponentsView'],
function (Backbone, ComponentsView) {
/**
* @class ComponentView
* */
return Backbone.View.extend({
className : function(){ //load classes from model
className : function(){
return this.getClasses();
},
tagName: function(){ //load tagName from model
tagName: function(){
return this.model.get('tagName');
},
initialize: function(opt){
this.config = opt.config;
this.pfx = this.config.stylePrefix;
this.config = opt.config || {};
this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || '';
this.components = this.model.get('components');
this.attr = this.model.get("attributes");
this.classe = this.attr.class || [];
this.attr = this.model.get("attributes");
this.classe = this.attr.class || [];
this.listenTo(this.model, 'destroy remove', this.remove);
this.listenTo(this.model, 'change:style', this.updateStyle);
this.listenTo(this.model, 'change:attributes', this.updateAttributes);
this.listenTo(this.model, 'change:status', this.updateStatus);
this.listenTo(this.model, 'change:state', this.updateState);
this.listenTo(this.model.get('classes'), 'add remove change', this.updateClasses);
this.$el.data("model", this.model);
this.$el.data("model-comp", this.components);
this.$el.data("collection", this.components);
if(this.model.get('classes').length)
this.importClasses();
@ -33,25 +33,42 @@ define(['backbone', './ComponentsView'],
/**
* Import, if possible, classes inside main container
* @private
* */
importClasses: function(){
var clm = this.config.em.get('ClassManager');
var clm = this.config.em.get('SelectorManager');
if(clm){
this.model.get('classes').each(function(m){
clm.addClass(m.get('name'));
clm.add(m.get('name'));
});
}
},
/**
* Fires on state update. If the state is not empty will add a helper class
* @param {Event} e
* @private
* */
updateState: function(e){
var cl = 'hc-state';
var state = this.model.get('state');
if(state){
this.$el.addClass(cl);
}else{
this.$el.removeClass(cl);
}
},
/**
* Update item on status change
* @param Event
* @param {Event} e
* @private
* */
updateStatus: function(e)
{
var s = this.model.get('status'),
pfx = this.pfx;
updateStatus: function(e){
var s = this.model.get('status'),
pfx = this.pfx;
switch(s) {
case 'selected':
this.$el.addClass(pfx + 'selected');
@ -68,9 +85,10 @@ define(['backbone', './ComponentsView'],
* This method is called before initialize
*
* @return {Array}|null
* @private
* */
getClasses: function(){
var attr = this.model.get("attributes"),
var attr = this.model.get("attributes"),
classes = attr['class'] || [];
if(classes.length){
return classes.join(" ");
@ -80,12 +98,11 @@ define(['backbone', './ComponentsView'],
/**
* Update attributes
*
* @return void
* @private
* */
updateAttributes: function(){
var attributes = {},
attr = this.model.get("attributes");
var attributes = {},
attr = this.model.get("attributes");
for(var key in attr) {
if(attr.hasOwnProperty(key))
attributes[key] = attr[key];
@ -94,15 +111,17 @@ define(['backbone', './ComponentsView'],
if(this.model.get("src"))
attributes.src = this.model.get("src");
attributes.style = this.getStyleString();
var styleStr = this.getStyleString();
if(styleStr)
attributes.style = styleStr;
this.$el.attr(attributes);
},
/**
* Update style attribute
*
* @return void
* @private
* */
updateStyle: function(){
this.$el.attr('style', this.getStyleString());
@ -110,12 +129,12 @@ define(['backbone', './ComponentsView'],
/**
* Return style string
*
* @return {String}
* @return {string}
* @private
* */
getStyleString: function(){
var style = '';
this.style = this.model.get('style');
this.style = this.model.get('style');
for(var key in this.style) {
if(this.style.hasOwnProperty(key))
style += key + ':' + this.style[key] + ';';
@ -126,6 +145,7 @@ define(['backbone', './ComponentsView'],
/**
* Update classe attribute
* @private
* */
updateClasses: function(){
var str = '';
@ -133,8 +153,12 @@ define(['backbone', './ComponentsView'],
this.model.get('classes').each(function(model){
str += model.get('name') + ' ';
});
str = str.trim();
this.$el.attr('class',str.trim());
if(str)
this.$el.attr('class', str);
else
this.$el.removeAttr('class');
// Regenerate status class
this.updateStatus();
@ -143,6 +167,7 @@ define(['backbone', './ComponentsView'],
/**
* Reply to event call
* @param object Event that generated the request
* @private
* */
eventCall: function(event){
event.viewResponse = this;
@ -153,10 +178,9 @@ define(['backbone', './ComponentsView'],
this.updateClasses();
this.$el.html(this.model.get('content'));
var view = new ComponentsView({
collection : this.components,
config : this.config,
collection: this.components,
config: this.config,
});
this.$components = view;
// With childNodes lets avoid wrapping 'div'
this.$el.append(view.render(this.$el).el.childNodes);

41
src/dom_components/view/ComponentsView.js

@ -1,41 +1,41 @@
define(['backbone','require'],
function(Backbone, require) {
/**
* @class ComponentsView
* */
return Backbone.View.extend({
initialize: function(o) {
this.config = o.config;
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
},
/**
* Add to collection
* @param {Object} Model
*
*
* @return void
* @private
* */
addTo: function(model){
var i = this.collection.indexOf(model);
this.addToCollection(model, null, i);
},
/**
* Add new object to collection
* @param {Object} Model
* @param {Object} Fragment collection
* @param {Integer} Index of append
*
*
* @return {Object} Object rendered
* @private
* */
addToCollection: function(model, fragmentEl, index){
if(!this.compView)
this.compView = require('./ComponentView');
var fragment = fragmentEl || null,
viewObject = this.compView;
switch(model.get('type')){
case 'text':
if(!this.compViewText)
@ -47,14 +47,19 @@ function(Backbone, require) {
this.compViewImage = require('./ComponentImageView');
viewObject = this.compViewImage;
break;
case 'link':
if(!this.compViewLink)
this.compViewLink = require('./ComponentLinkView');
viewObject = this.compViewLink;
break;
}
var view = new viewObject({
model : model,
var view = new viewObject({
model : model,
config : this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild(rendered);
}else{
@ -76,10 +81,10 @@ function(Backbone, require) {
p.append(rendered);
}
}
return rendered;
},
render: function($p) {
var fragment = document.createDocumentFragment();
this.$parent = $p || this.$el;
@ -88,9 +93,9 @@ function(Backbone, require) {
this.addToCollection(model, fragment);
},this);
this.$el.append(fragment);
return this;
}
});
});
});

68
src/domain_abstract/view/DomainViews.js

@ -0,0 +1,68 @@
define(['backbone'],
function(Backbone) {
return Backbone.View.extend({
itemView: '',
// Defines the View per type
itemsView: '',
itemType: 'type',
initialize: function(opts, config) {
this.config = config || {};
},
/**
* Add new model to the collection
* @param {Model} model
* @private
* */
addTo: function(model){
this.add(model);
},
/**
* Render new model inside the view
* @param {Model} model
* @param {Object} fragment Fragment collection
* @private
* */
add: function(model, fragment){
var frag = fragment || null;
var itemView = this.itemView;
if(this.itemsView){
var typeField = model.get(this.itemType);
itemView = this.itemsView[typeField];
}
var view = new itemView({
model: model,
config: this.config
}, this.config);
var rendered = view.render().el;
if(frag)
frag.appendChild(rendered);
else
this.$el.append(rendered);
},
render: function() {
var frag = document.createDocumentFragment();
this.$el.empty();
if(this.collection.length)
this.collection.each(function(model){
this.add(model, frag);
}, this);
this.$el.append(frag);
return this;
},
});
});

181
src/editor/config/config.js

@ -1,72 +1,183 @@
define(function () {
var config = {
var blkStyle = '.blk-row::after{ content: ""; clear: both; display: block;} .blk-row{padding: 10px;}';
return {
// Style prefix
stylePrefix: 'wte-',
stylePrefix: 'gjs-',
// Prefix to use inside local storage name
storagePrefix: 'wte-',
//TEMP
components: '',
// Editor ID. Useful in case of multiple editors on the same page
id : '',
// Enable/Disable possibility to copy(ctrl + c) & paste(ctrl + v) components
copyPaste: true,
// Where render the editor
container : '',
// Enable/Disable undo manager
undoManager: true,
idCanvas : 'canvas',
// Height for the editor container
height: '900px',
idCanvasOverlay : 'canvas-overlay',
// Width for the editor container
width: '100%',
idWrapper : 'wrapper',
// The css that could only be seen (for instance, inside the code viewer)
protectedCss: '*{box-sizing: border-box;}body{margin:0;height:100%}#wrapper{min-height:100%; overflow:auto}',
// Enable/Disable possibility to copy(ctrl + c) & paste(ctrl + v) components
copyPaste : true,
// Default command
defaultCommand: 'select-comp',
// Enable/Disable undo manager
undoManager : true,
// If true render a select of available devices
showDevices: 1,
//Indicates which storage to use. Available: local | remote | none
storageType : 'local',
// Dom element
el: '',
//Configurations for Asset Manager
assetManager : {},
assetManager: {},
//Configurations for Canvas
canvas : {},
canvas: {},
//Configurations for Style Manager
styleManager : {},
styleManager: {},
//Configurations for Layers
layers : {},
layers: {},
//Configurations for Storage Manager
storageManager : {},
storageManager: {},
//Configurations for Rich Text Editor
rte : {},
rte: {},
//Configurations for Components
components : {},
//Configurations for DomComponents
domComponents: {},
//Configurations for Modal Dialog
modal : {},
modal: {},
//Configurations for Code Manager
codeManager : {},
codeManager: {},
//Configurations for Panels
panels : {},
panels: {},
//Configurations for Commands
commands : {},
//Configurations for Class Manager
classManager : {},
commands: {},
//Configurations for Css Composer
cssComposer : {},
cssComposer: {},
//Configurations for Selector Manager
selectorManager: {},
//Configurations for Device Manager
deviceManager: {
'devices': [{
name: 'Desktop',
width: '',
},{
name: 'Tablet',
width: '992px',
},{
name: 'Mobile landscape',
width: '768px',
},{
name: 'Mobile portrait',
width: '480px',
}],
},
//Configurations for Block Manager
blockManager: {
'blocks': [{
id: 'b1',
label: '1 Block',
content: '<div class="blk-row"><div class="blk1"></div></div><style>'+ blkStyle +'.blk1{width: 100%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b1'}
},{
id: 'b2',
label: '2 Blocks',
content: '<div class="blk-row"><div class="blk2"></div><div class="blk2"></div></div><style>'+ blkStyle +'.blk2{float: left;width: 50%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b2'}
},{
id: 'b3',
label: '3 Blocks',
content: '<div class="blk-row"><div class="blk3"></div><div class="blk3"></div><div class="blk3"></div></div><style>'+ blkStyle +'.blk3{float: left;width: 33.3333%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b3'}
},{
id: 'b4',
label: '3/7 Block',
content: '<div class="blk-row"><div class="blk37l"></div><div class="blk37r"></div></div></div><style>'+ blkStyle +'.blk37l{float: left;width: 30%;padding: 10px;min-height: 75px;}.blk37r{float: left;width: 70%;padding: 10px;min-height: 75px;}</style>',
attributes: {class:'gjs-fonts gjs-f-b37'}
},{
id: 'hero',
label: 'Hero section',
content: '<header class="header-banner"> <div class="container-width">'+
'<div class="logo-container"><div class="logo">GrapesJS</div></div>'+
'<nav class="navbar">'+
'<div class="menu-item">BUILDER</div><div class="menu-item">TEMPLATE</div><div class="menu-item">WEB</div>'+
'</nav><div class="clearfix"></div>'+
'<div class="lead-title">Build your templates without coding</div>'+
'<div class="lead-btn">Try it now</div></div></header>',
attributes: {class:'gjs-fonts gjs-f-hero'}
},{
id: 'h1p',
label: 'Text section',
content: '<h1 class="heading">Insert title here</h1><p class="paragraph">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</p>',
attributes: {class:'gjs-fonts gjs-f-h1p'}
},{
id: '3ba',
label: 'Badges',
content: '<div class="badges">'+
'<div class="badge">'+
'<div class="badge-header"></div>'+
'<img class="badge-avatar" src="img/team1.jpg">'+
'<div class="badge-body">'+
'<div class="badge-name">Adam Smith</div><div class="badge-role">CEO</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+
'</div>'+
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span></div>'+
'</div>'+
'<div class="badge">'+
'<div class="badge-header"></div>'+
'<img class="badge-avatar" src="img/team2.jpg">'+
'<div class="badge-body">'+
'<div class="badge-name">John Black</div><div class="badge-role">Software Engineer</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+
'</div>'+
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span></div>'+
'</div>'+
'<div class="badge">'+
'<div class="badge-header"></div>'+
'<img class="badge-avatar" src="img/team3.jpg">'+
'<div class="badge-body">'+
'<div class="badge-name">Jessica White</div><div class="badge-role">Web Designer</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+
'</div>'+
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span>'+
'</div>'+
'</div></div>',
attributes: {class:'gjs-fonts gjs-f-3ba'}
},{
id: 'text',
label: 'Text',
attributes: {class:'gjs-fonts gjs-f-text'},
content: {
type:'text',
content:'Insert your text here',
style: {padding: '10px' },
activeOnRender: 1
},
},{
id: 'image',
label: 'Image',
attributes: {class:'gjs-fonts gjs-f-image'},
content: { type:'image', activeOnRender: 1},
},{
id: 'quo',
label: 'Quote',
content: '<blockquote class="quote">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</blockquote>',
attributes: {class:'gjs-fonts gjs-f-quo'}
}],
},
};
return config;
});
});

415
src/editor/main.js

@ -1,39 +1,402 @@
/**
*
* * [getConfig](#getconfig)
* * [getHtml](#gethtml)
* * [getCss](#getcss)
* * [getComponents](#getcomponents)
* * [setComponents](#setcomponents)
* * [getStyle](#getstyle)
* * [setStyle](#setstyle)
* * [getSelected](#getselected)
* * [setDevice](#setdevice)
* * [getDevice](#getdevice)
* * [runCommand](#runcommand)
* * [stopCommand](#stopcommand)
* * [store](#store)
* * [load](#load)
* * [getContainer](#getcontainer)
* * [on](#on)
* * [trigger](#trigger)
* * [render](#render)
*
* Editor class contains the top level API which you'll probably use to custom the editor or extend it with plugins.
* You get the Editor instance on init method
*
* ```js
* var editor = grapesjs.init({...});
* ```
* Available events
* #run:{commandName}
* #stop:{commandName}
* #load - When the editor is loaded
*
* @module Editor
* @param {Object} config Configurations
* @param {string} config.container='' Selector for the editor container, eg. '#myEditor'
* @param {string|Array<Object>} [config.components=''] HTML string or object of components
* @param {string|Array<Object>} [config.style=''] CSS string or object of rules
* @param {Boolean} [config.fromElement=false] If true, will fetch HTML and CSS from selected container
* @param {Boolean} [config.copyPaste=true] Enable/Disable the possibility to copy(ctrl + c) & paste(ctrl + v) components
* @param {Boolean} [config.undoManager=true] Enable/Disable undo manager
* @param {Boolean} [config.autorender=true] If true renders editor on init
* @param {Boolean} [config.noticeOnUnload=true] Enable/Disable alert message before unload the page
* @param {string} [config.height='900px'] Height for the editor container
* @param {string} [config.width='100%'] Width for the editor container
* @param {Object} [config.storage={}] Storage manager configuration, see the relative documentation
* @param {Object} [config.styleManager={}] Style manager configuration, see the relative documentation
* @param {Object} [config.commands={}] Commands configuration, see the relative documentation
* @param {Object} [config.domComponents={}] Components configuration, see the relative documentation
* @param {Object} [config.panels={}] Panels configuration, see the relative documentation
* @param {Object} [config.showDevices=true] If true render a select of available devices inside style manager panel
* @param {string} [config.defaultCommand='select-comp'] Command to execute when no other command is running
* @param {Array} [config.plugins=[]] Array of plugins to execute on start
* @param {Object} [config.pluginsOpts={}] Custom options for plugins
* @example
* var editor = grapesjs.init({
* container : '#gjs',
* components: '<div class="txt-red">Hello world!</div>',
* style: '.txt-red{color: red}',
* });
*/
define(function (require){
/**
* @class Grapes
* @param {Object} Configurations
*
* @return {Object}
* */
var Grapes = function(config)
{
var c = config || {},
defaults = require('./config/config'),
Editor = require('./model/Editor'),
EditorView = require('./view/EditorView');
var Editor = function(config) {
var c = config || {},
defaults = require('./config/config'),
EditorModel = require('./model/Editor'),
EditorView = require('./view/EditorView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
c.pStylePrefix = c.stylePrefix;
this.editor = new Editor(c);
var obj = {
model : this.editor,
config : c,
};
var em = new EditorModel(c);
this.editorView = new EditorView(obj);
};
var editorView = new EditorView({
model: em,
config: c,
});
Grapes.prototype = {
return {
render : function()
{
return this.editorView.render().$el;
}
/**
* @property {EditorModel}
* @private
*/
editor: em,
/**
* @property {DomComponents}
*/
DomComponents: em.get('DomComponents'),
/**
* @property {CssComposer}
*/
CssComposer: em.get('CssComposer'),
/**
* @property {StorageManager}
*/
StorageManager: em.get('StorageManager'),
/**
* @property {AssetManager}
*/
AssetManager: em.get('AssetManager'),
/**
* @property {BlockManager}
*/
BlockManager: em.get('BlockManager'),
/**
* @property {TraitManager}
*/
TraitManager: em.get('TraitManager'),
/**
* @property {SelectorManager}
*/
SelectorManager: em.get('SelectorManager'),
/**
* @property {CodeManager}
*/
CodeManager: em.get('CodeManager'),
/**
* @property {Commands}
*/
Commands: em.get('Commands'),
/**
* @property {Modal}
*/
Modal: em.get('Modal'),
/**
* @property {Panels}
*/
Panels: em.get('Panels'),
/**
* @property {StyleManager}
*/
StyleManager: em.get('StyleManager'),
/**
* @property {Canvas}
*/
Canvas: em.get('Canvas'),
/**
* @property {UndoManager}
*/
UndoManager: em.get('UndoManager'),
/**
* @property {DeviceManager}
*/
DeviceManager: em.get('DeviceManager'),
/**
* @property {RichTextEditor}
*/
RichTextEditor: em.get('rte'),
/**
* @property {Utils}
*/
Utils: em.get('Utils'),
/**
* @property {Utils}
*/
Config: em.get('Config'),
/**
* Initialize editor model
* @return {this}
* @private
*/
init: function(){
em.init(this);
return this;
},
/**
* Returns configuration object
* @return {Object}
*/
getConfig: function(){
return c;
},
/**
* Returns HTML built inside canvas
* @return {string} HTML string
*/
getHtml: function(){
return em.getHtml();
},
/**
* Returns CSS built inside canvas
* @return {string} CSS string
*/
getCss: function(){
return em.getCss();
},
/**
* Returns components in JSON format object
* @return {Object}
*/
getComponents: function(){
return em.get('DomComponents').getComponents();
},
/**
* Set components inside editor's canvas. This method overrides actual components
* @param {Array<Object>|Object|string} components HTML string or components model
* @return {this}
* @example
* editor.setComponents('<div class="cls">New component</div>');
* // or
* editor.setComponents({
* type: 'text',
* classes:['cls'],
* content: 'New component'
* });
*/
setComponents: function(components){
em.setComponents(components);
return this;
},
/**
* Returns style in JSON format object
* @return {Object}
*/
getStyle: function(){
return em.get('CssComposer').getAll();
},
/**
* Set style inside editor's canvas. This method overrides actual style
* @param {Array<Object>|Object|string} style CSS string or style model
* @return {this}
* @example
* editor.setStyle('.cls{color: red}');
* //or
* editor.setStyle({
* selectors: ['cls']
* style: { color: 'red' }
* });
*/
setStyle: function(style){
em.setStyle(style);
return this;
},
/**
* Returns selected component, if there is one
* @return {grapesjs.Component}
*/
getSelected: function(){
return em.getSelected();
},
/**
* Set device to the editor. If the device exists it will
* change the canvas to the proper width
* @return {this}
* @example
* editor.setDevice('Tablet');
*/
setDevice: function(name){
return em.set('device', name);
},
/**
* Return the actual active device
* @return {string} Device name
* @example
* var device = editor.getDevice();
* console.log(device);
* // 'Tablet'
*/
getDevice: function(){
return em.get('device');
},
/**
* Execute command
* @param {string} id Command ID
* @param {Object} options Custom options
* @example
* editor.runCommand('myCommand', {someValue: 1});
*/
runCommand: function(id, options) {
var command = em.get('Commands').get(id);
if(command){
command.run(this, this, options);
this.trigger('run:' + id);
}
},
/**
* Stop the command if stop method was provided
* @param {string} id Command ID
* @param {Object} options Custom options
* @example
* editor.stopCommand('myCommand', {someValue: 1});
*/
stopCommand: function(id, options) {
var command = em.get('Commands').get(id);
if(command){
command.stop(this, this, options);
this.trigger('stop:' + id);
}
},
/**
* Store data to the current storage
* @return {Object} Stored data
*/
store: function(){
return em.store();
},
/**
* Load data from the current storage
* @return {Object} Stored data
*/
load: function(){
return em.load();
},
/**
* Returns container element. The one which was indicated as 'container' on init method
* @return {HTMLElement}
*/
getContainer: function(){
return c.el;
},
/**
* Attach event
* @param {string} event Event name
* @param {Function} callback Callback function
* @return {this}
*/
on: function(event, callback){
return em.on(event, callback);
},
/**
* Trigger event
* @param {string} event Event to trigger
* @return {this}
*/
trigger: function(event){
return em.trigger(event);
},
/**
* Returns editor element
* @return {HTMLElement}
* @private
*/
getEl: function(){
return editorView.el;
},
/**
* Returns editor model
* @return {Model}
* @private
*/
getModel: function(){
return em;
},
/**
* Render editor
* @return {HTMLElement}
*/
render: function() {
return editorView.render().el;
},
};
};
return Grapes;
});
return Editor;
});

613
src/editor/model/Editor.js

@ -1,90 +1,92 @@
define([
'backbone',
'backboneUndo',
'keymaster',
'AssetManager',
'StorageManager',
'ModalDialog',
'CodeManager',
'CssComposer',
'Commands',
'Canvas',
'RichTextEditor',
'DomComponents',
'ClassManager',
'Panels'],
function(
Backbone,
UndoManager,
Keymaster,
AssetManager,
StorageManager,
ModalDialog,
CodeManager,
CssComposer,
Commands,
Canvas,
RichTextEditor,
DomComponents,
ClassManager,
Panels
){
var deps = ['Utils', 'StorageManager', 'DeviceManager', 'Parser', 'SelectorManager', 'ModalDialog', 'CodeManager', 'Panels',
'RichTextEditor', 'StyleManager', 'AssetManager', 'CssComposer', 'DomComponents', 'Canvas', 'Commands', 'BlockManager', 'TraitManager'];
// r.js do not see deps if I pass them as a variable
// http://stackoverflow.com/questions/27545412/optimization-fails-when-passing-a-variable-with-a-list-of-dependencies-to-define
define(['backbone', 'backboneUndo', 'keymaster', 'Utils', 'StorageManager', 'DeviceManager', 'Parser', 'SelectorManager',
'ModalDialog', 'CodeManager', 'Panels', 'RichTextEditor', 'StyleManager', 'AssetManager', 'CssComposer', 'DomComponents',
'Canvas', 'Commands', 'BlockManager', 'TraitManager'], function(){
return Backbone.Model.extend({
defaults:{
clipboard : null,
selectedComponent : null,
previousModel : null,
changesCount : 0,
defaults: {
clipboard: null,
selectedComponent: null,
previousModel: null,
changesCount: 0,
storables: [],
toLoad: [],
device: '',
},
initialize: function(c)
{
this.config = c;
this.pfx = this.config.storagePrefix;
this.compName = this.pfx + 'components' + this.config.id;
this.rulesName = this.pfx + 'rules' + this.config.id;
initialize: function(c) {
this.config = c;
this.set('Config', c);
this.initStorage();
this.initClassManager();
this.initModal();
this.initAssetManager();
this.initCodeManager();
this.initCommands();
this.initPanels();
this.initRichTextEditor();
this.initComponents();
this.initCanvas();
this.initUndoManager();
this.initCssComposer();
if(c.el && c.fromElement)
this.config.components = c.el.innerHTML;
// Load modules
deps.forEach(function(name){
this.loadModule(name);
}, this);
// Call modules with onLoad callback
this.get('toLoad').forEach(function(M){
M.onLoad();
});
this.initUndoManager(); // Is already called (inside components and css composer)
this.on('change:selectedComponent', this.componentSelected, this);
},
/**
* Initialize Css Composer
* */
initCssComposer: function() {
var cfg = this.config.cssComposer,
df = this.loadRules();
pfx = cfg.stylePrefix || 'css-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
* Load generic module
* @param {String} moduleName Module name
* @return {this}
*/
loadModule: function(moduleName) {
var c = this.config;
var M = new require(moduleName)();
var name = M.name.charAt(0).toLowerCase() + M.name.slice(1);
var cfg = c[name] || c[M.name] || {};
cfg.pStylePrefix = c.pStylePrefix || '';
// Check if module is storable
var sm = this.get('StorageManager');
if(M.storageKey && M.store && M.load && sm){
cfg.stm = sm;
var storables = this.get('storables');
storables.push(M);
this.set('storables', storables);
}
cfg.em = this;
M.init(Object.create(cfg));
if(df)
cfg.defaults = df;
// Bind the module to the editor model if public
if(!M.private)
this.set(M.name, M);
cfg.sm = this;
this.cssc = new CssComposer(cfg);
this.set('CssComposer', this.cssc);
if(M.onLoad)
this.get('toLoad').push(M);
if(this.stm.isAutosave())
this.listenRules(this.cssc.getRules());
return this;
},
/**
* Initialize editor model and set editor instance
* @param {Editor} editor Editor instance
* @return {this}
* @private
*/
init: function(editor){
this.set('Editor', editor);
},
/**
* Listen for new rules
* @param {Object} collection
* @private
*/
listenRules: function(collection) {
this.stopListening(collection, 'add remove', this.listenRule);
@ -97,6 +99,7 @@ define([
/**
* Listen for rule changes
* @param {Object} model
* @private
*/
listenRule: function(model) {
this.stopListening(model, 'change:style', this.ruleUpdated);
@ -108,202 +111,43 @@ define([
* @param {Object} model
* @param {Mixed} val Value
* @param {Object} opt Options
* @private
* */
ruleUpdated: function(model, val, opt) {
var count = this.get('changesCount') + 1,
avSt = opt ? opt.avoidStore : 0;
this.set('changesCount', count);
if(this.stm.isAutosave() && count < this.stm.getChangesBeforeSave())
var stm = this.get('StorageManager');
if(stm.isAutosave() && count < stm.getStepsBeforeSave())
return;
if(!avSt){
this.storeRules();
this.store();
this.set('changesCount', 0);
}
},
/**
* Initialize Class manager
* */
initClassManager: function() {
var cfg = this.config.classManager,
pfx = cfg.stylePrefix || 'clm-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
cfg.target = this;
this.clm = new ClassManager(cfg);
this.set('ClassManager', this.clm);
},
/**
* Initialize components
* */
initComponents: function()
{
var cfg = this.config.components,
comp = this.loadComponents(),
cmpStylePfx = cfg.stylePrefix || 'comp-';
cfg.stylePrefix = this.config.stylePrefix + cmpStylePfx;
if(comp)
cfg.wrapper = comp;
if(this.rte)
cfg.rte = this.rte;
if(this.modal)
cfg.modal = this.modal;
if(this.am)
cfg.am = this.am;
cfg.em = this;
this.cmp = new DomComponents(cfg);
if(this.stm.isAutosave()){
var md = this.cmp.getComponent();
this.updateComponents( md, null, { avoidStore : 1 });
// Call UndoManager here so it's possible to call it also for children inside
this.initUndoManager();
this.initChildrenComp(md);
}
this.set('Components', this.cmp);
},
/**
* Initialize canvas
* */
initCanvas: function()
{
var cfg = this.config.canvas,
pfx = cfg.stylePrefix || 'cv-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
cfg.canvasId = this.config.idCanvas;
this.cv = new Canvas(this.config.canvas);
if(this.cmp)
this.cv.setWrapper(this.cmp);
this.set('Canvas', this.cv);
},
/**
* Initialize rich text editor
* */
initRichTextEditor: function()
{
var cfg = this.config.rte,
rteStylePfx = cfg.stylePrefix || 'rte-';
cfg.stylePrefix = this.config.stylePrefix + rteStylePfx;
this.rte = new RichTextEditor(cfg);
this.set('RichTextEditor', this.rte);
},
/**
* Initialize storage
* */
initStorage: function()
{
this.stm = new StorageManager(this.config.storageManager);
this.stm.loadDefaultProviders().setCurrentProvider(this.config.storageType);
this.set('StorageManager', this.stm);
},
/**
* Initialize asset manager
* */
initAssetManager: function()
{
var cfg = this.config.assetManager,
pfx = cfg.stylePrefix || 'am-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
if(this.stm)
cfg.stm = this.stm;
this.am = new AssetManager(cfg);
this.set('AssetManager', this.am);
},
/**
* Initialize modal
* */
initModal: function()
{
var cfg = this.config.modal,
pfx = cfg.stylePrefix || 'mdl-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
this.modal = new ModalDialog(cfg);
this.modal.render().appendTo('body');
this.set('Modal', this.modal);
},
/**
* Initialize Code Manager
* */
initCodeManager: function()
{
var cfg = this.config.codeManager,
pfx = cfg.stylePrefix || 'cm-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
this.cm = new CodeManager(cfg);
this.cm.loadDefaultGenerators().loadDefaultEditors();
this.set('CodeManager', this.cm);
},
/**
* Initialize Commands
* */
initCommands: function()
{
var cfg = this.config.commands,
pfx = cfg.stylePrefix || 'com-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
cfg.em = this;
cfg.canvasId = this.config.idCanvas;
cfg.wrapperId = this.config.idWrapper;
this.com = new Commands(cfg);
this.com.loadDefaultCommands();
this.set('Commands', this.com);
},
/**
* Initialize Panels
* */
initPanels: function()
{
var cfg = this.config.panels,
pfx = cfg.stylePrefix || 'pn-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
cfg.em = this;
this.pn = new Panels(cfg);
this.pn.addPanel({ id: 'views-container'});
this.set('Panels', this.pn);
},
/**
* Initialize Undo manager
* @private
* */
initUndoManager: function()
{
initUndoManager: function() {
if(this.um)
return;
if(this.cmp && this.config.undoManager){
var that = this;
this.um = new Backbone.UndoManager({
register: [this.cmp.getComponent().get('components')],
var cmp = this.get('DomComponents');
if(cmp && this.config.undoManager){
var that = this;
this.um = new Backbone.UndoManager({
register: [cmp.getComponents(), this.get('CssComposer').getAll()],
track: true
});
this.UndoManager = this.um;
this.set('UndoManager', this.um);
key('⌘+z, ctrl+z', function(){
that.um.undo();
that.um.undo(true);
});
key('⌘+shift+z, ctrl+shift+z', function(){
that.um.redo();
that.um.redo(true);
});
Backbone.UndoManager.removeUndoType("change");
@ -311,7 +155,7 @@ define([
Backbone.UndoManager.addUndoType("change:style", {
"on": function (model, value, opt) {
if(!beforeCache)
beforeCache = model.toJSON();
beforeCache = model.previousAttributes();
if (opt && opt.avoidStore) {
return;
} else {
@ -326,9 +170,13 @@ define([
},
"undo": function (model, bf, af, opt) {
model.set(bf);
// Update also inputs inside Style Manager
that.trigger('change:selectedComponent');
},
"redo": function (model, bf, af, opt) {
model.set(af);
// Update also inputs inside Style Manager
that.trigger('change:selectedComponent');
}
});
}
@ -339,18 +187,19 @@ define([
* @param {Object} model
* @param {Mixed} val Value
* @param {Object} opt Options
* @private
* */
componentsUpdated: function(model, val, opt)
{
componentsUpdated: function(model, val, opt){
var updatedCount = this.get('changesCount') + 1,
avSt = opt ? opt.avoidStore : 0;
this.set('changesCount', updatedCount);
if(this.stm.isAutosave() && updatedCount < this.stm.getChangesBeforeSave()){
var stm = this.get('StorageManager');
if(stm.isAutosave() && updatedCount < stm.getStepsBeforeSave()){
return;
}
if(!avSt){
this.storeComponents();
this.store();
this.set('changesCount', 0);
}
},
@ -360,79 +209,21 @@ define([
* @param {Object} Model
* @param {Mixed} New value
* @param {Object} Options
*
* @private
* */
componentSelected: function(model, val, options)
{
componentSelected: function(model, val, options){
if(!this.get('selectedComponent'))
this.trigger('deselect-comp');
else
this.trigger('select-comp',[model,val,options]);
},
/**
* Load components from storage
*
* @return {Object}
* */
loadComponents: function() {
var result = null;
try{
var r = this.stm.load(this.compName);
if(r)
result = JSON.parse(r);
}catch(err){
console.warn("Error encountered while parsing JSON response");
}
return result;
},
/**
* Save components to storage
*
* @return void
* */
storeComponents: function() {
var wrp = this.cmp.getComponent();
if(wrp && this.cm){
var res = this.cm.getCode(wrp, 'json');
this.stm.store(this.compName, JSON.stringify(res));
}
},
/**
* Load rules from storage
*
* @return {Array}
* */
loadRules: function(){
var result;
try{
var r = this.stm.load(this.rulesName);
if(r)
result = JSON.parse(r);
}catch(err){
console.warn("Load '" + this.rulesName + "':Error encountered while parsing JSON response");
}
return result;
},
/**
* Save rules to storage
* */
storeRules: function() {
var rules = this.cssc.getRules();
if(rules)
this.stm.store(this.rulesName, JSON.stringify(rules));
},
/**
* Triggered when components are updated
* @param {Object} model
* @param {Mixed} val Value
* @param {Object} opt Options
*
* @private
* */
updateComponents: function(model, val, opt) {
var comps = model.get('components'),
@ -463,17 +254,16 @@ define([
/**
* Init stuff like storage for already existing elements
* @param {Object} model
* @private
*/
initChildrenComp: function(model) {
var comps = model.get('components');
if(comps.length){
comps.each(function(md){
this.updateComponents(md, null, { avoidStore : 1 });
this.initChildrenComp(md);
if(this.um)
this.um.register(md);
}, this);
}
this.updateComponents(model, null, { avoidStore : 1 });
comps.each(function(md){
this.initChildrenComp(md);
if(this.um)
this.um.register(md);
}, this);
},
/**
@ -481,14 +271,203 @@ define([
* @param {Object} model
* @param {Mixed} val Value
* @param {Object} opt Options
*
* @private
* */
rmComponents: function(model, val, opt){
var avSt = opt ? opt.avoidStore : 0;
if(!avSt)
this.componentsUpdated();
}
},
/**
* Returns model of the selected component
* @return {Component|null}
* @private
*/
getSelected: function(){
return this.get('selectedComponent');
},
/**
* Set components inside editor's canvas. This method overrides actual components
* @param {Object|string} components HTML string or components model
* @return {this}
* @private
*/
setComponents: function(components){
return this.get('DomComponents').setComponents(components);
},
/**
* Returns components model from the editor's canvas
* @return {Components}
* @private
*/
getComponents: function(){
var cmp = this.get('DomComponents');
var cm = this.get('CodeManager');
if(!cmp || !cm)
return;
var wrp = cmp.getComponent();
return cm.getCode(wrp, 'json');
},
/**
* Set style inside editor's canvas. This method overrides actual style
* @param {Object|string} style CSS string or style model
* @return {this}
* @private
*/
setStyle: function(style){
var rules = this.get('CssComposer').getAll();
for(var i = 0, len = rules.length; i < len; i++)
rules.pop();
rules.add(style);
return this;
},
/**
* Returns rules/style model from the editor's canvas
* @return {Rules}
* @private
*/
getStyle: function(){
return this.get('CssComposer').getAll();
},
/**
* Returns HTML built inside canvas
* @return {string} HTML string
* @private
*/
getHtml: function(){
var cmp = this.get('DomComponents');
var cm = this.get('CodeManager');
if(!cmp || !cm)
return;
var wrp = cmp.getComponent();
return cm.getCode(wrp, 'html');
},
/**
* Returns CSS built inside canvas
* @return {string} CSS string
* @private
*/
getCss: function(){
var cmp = this.get('DomComponents');
var cm = this.get('CodeManager');
var cssc = this.get('CssComposer');
if(!cmp || !cm || !cssc)
return;
var wrp = cmp.getComponent();
return cm.getCode(wrp, 'css', cssc);
},
/**
* Store data to the current storage
* @return {Object} Stored data
* @private
*/
store: function(){
var sm = this.get('StorageManager');
var store = {};
if(!sm)
return;
// Fetch what to store
this.get('storables').forEach(function(m){
var obj = m.store(1);
for(var el in obj)
store[el] = obj[el];
});
sm.store(store);
return store;
},
/**
* Load data from the current storage
* @return {Object} Loaded data
* @private
*/
load: function(){
var result = this.getCacheLoad(1);
this.get('storables').forEach(function(m){
m.load(result);
});
return result;
},
/**
* Returns cached load
* @param {Boolean} force Force to reload
* @return {Object}
* @private
*/
getCacheLoad: function(force){
var f = force ? 1 : 0;
if(this.cacheLoad && !f)
return this.cacheLoad;
var sm = this.get('StorageManager');
var load = [];
if(!sm)
return {};
this.get('storables').forEach(function(m){
var key = m.storageKey;
key = typeof key === 'function' ? key() : key;
keys = key instanceof Array ? key : [key];
keys.forEach(function(k){
load.push(k);
});
});
this.cacheLoad = sm.load(load);
return this.cacheLoad;
},
/**
* Returns device model by name
* @return {Device|null}
*/
getDeviceModel: function(){
var name = this.get('device');
return this.get('DeviceManager').get(name);
},
/**
* Run default command if setted
* @private
*/
runDefault: function(){
var command = this.get('Commands').get(this.config.defaultCommand);
if(!command || this.defaultRunning)
return;
command.stop(this, this);
command.run(this, this);
this.defaultRunning = 1;
},
/**
* Stop default command
* @private
*/
stopDefault: function(){
var command = this.get('Commands').get(this.config.defaultCommand);
if(!command)
return;
command.stop(this, this);
this.defaultRunning = 0;
},
});
});

38
src/editor/view/EditorView.js

@ -1,41 +1,41 @@
define(['backbone'],
function(Backbone){
/**
* @class EditorView
* */
return Backbone.View.extend({
initialize: function() {
this.cv = this.model.get('Canvas');
this.pn = this.model.get('Panels');
this.css = this.model.get('CssComposer');
this.className = this.model.config.stylePrefix + 'editor';
this.conf = this.model.config;
this.className = this.conf.stylePrefix + 'editor';
this.model.on('loaded', function(){
this.pn.active();
this.model.runDefault();
this.model.trigger('load');
}, this);
},
render: function(){
var conf = this.conf;
this.$el.empty();
this.$cont = $(conf.el || ('body ' + conf.container));
this.$cont = $('body ' + this.model.config.container);
this.model.set('$editor', this.$el);
if(conf.width)
this.$cont.css('width', conf.width);
if(this.cv)
this.$el.append(this.cv.render());
if(conf.height)
this.$cont.css('height', conf.height);
if(this.pn)
this.$el.append(this.pn.render());
// Canvas
this.$el.append(this.model.get('Canvas').render());
if(this.css)
this.$el.append(this.css.render());
// Panels
this.$el.append(this.pn.render());
this.$el.attr('class', this.className);
this.$cont.html(this.$el);
if(this.pn)
this.pn.active();
return this;
}
});
});
});

37
src/grapesjs/config/config.js

@ -0,0 +1,37 @@
define(function () {
return {
// If true renders editor on init
autorender: 1,
// Where init the editor
container: '',
// HTML string or object of components
components: '',
// CSS string or object of rules
style: '',
// If true, will fetch HTML and CSS from selected container
fromElement: 0,
// ---
// Enable/Disable the possibility to copy(ctrl + c) & paste(ctrl + v) components
copyPaste: true,
// Enable/Disable undo manager
undoManager: true,
// Show an alert before unload the page
noticeOnUnload: true,
// Storage Manager
storageManager: {},
// Array of plugins to init
plugins: [],
// Custom options for plugins
pluginsOpts: {}
};
});

79
src/grapesjs/main.js

@ -0,0 +1,79 @@
define(function (require) {
return function(config) {
var c = config || {},
defaults = require('./config/config'),
Editor = require('editor/main'),
PluginManager = require('PluginManager');
var plugins = new PluginManager();
var editors = [];
return {
plugins: plugins,
/**
* Initializes an editor based on passed options
* @param {Object} config Configuration object
* @param {string} config.container Selector which indicates where render the editor
* @param {Object|string} config.components='' HTML string or Component model in JSON format
* @param {Object|string} config.style='' CSS string or CSS model in JSON format
* @param {Boolean} [config.fromElement=false] If true, will fetch HTML and CSS from selected container
* @param {Boolean} [config.copyPaste=true] Enable/Disable the possibility to copy(ctrl+c) & paste(ctrl+v) components
* @param {Boolean} [config.undoManager=true] Enable/Disable undo manager
* @param {Array} [config.plugins=[]] Array of plugins to execute on start
* @return {grapesjs.Editor} GrapesJS editor instance
* @example
* var editor = grapesjs.init({
* container: '#myeditor',
* components: '<article class="hello">Hello world</article>',
* style: '.hello{color: red}',
* })
*/
init: function(config) {
var c = config || {};
var els = c.container;
// Set default options
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
if(!els)
throw new Error("'container' is required");
if(c.noticeOnUnload)
window.onbeforeunload = function(e) {
return 1;
};
c.el = document.querySelector(els);
var editor = new Editor(c).init();
// Execute all plugins
var plugs = plugins.getAll();
for (var id in plugs){
// Check if plugin is requested
if(c.plugins.indexOf(id) < 0)
continue;
var opts = c.pluginsOpts[id] || {};
var plug = plugins.get(id);
plug(editor, opts);
}
if(c.autorender)
editor.render();
editors.push(editor);
return editor;
},
};
}();
});

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save