Browse Source

Merge remote-tracking branch 'refs/remotes/artf/dev' into dev

pull/202/head
thecodefish 9 years ago
parent
commit
5819133805
  1. 16
      .eslintrc
  2. 1
      .gitignore
  3. 42
      README.md
  4. 2
      dist/css/grapes.min.css
  5. 6
      dist/grapes.min.js
  6. 307
      index.html
  7. 7
      package.json
  8. 8
      src/asset_manager/config/config.js
  9. 17
      src/asset_manager/index.js
  10. 5
      src/asset_manager/view/AssetsView.js
  11. 6
      src/asset_manager/view/FileUploader.js
  12. 9
      src/block_manager/view/BlockView.js
  13. 14
      src/block_manager/view/BlocksView.js
  14. 28
      src/canvas/config/config.js
  15. 77
      src/canvas/index.js
  16. 77
      src/canvas/view/CanvasView.js
  17. 2
      src/code_manager/model/CssGenerator.js
  18. 15
      src/code_manager/model/JsGenerator.js
  19. 1
      src/commands/index.js
  20. 9
      src/commands/view/SelectComponent.js
  21. 19
      src/css_composer/index.js
  22. 4
      src/css_composer/model/CssRule.js
  23. 12
      src/device_manager/model/Device.js
  24. 21
      src/dom_components/index.js
  25. 91
      src/dom_components/model/Component.js
  26. 20
      src/dom_components/model/ComponentSvg.js
  27. 13
      src/dom_components/model/Components.js
  28. 9
      src/dom_components/view/ComponentSvgView.js
  29. 45
      src/dom_components/view/ComponentView.js
  30. 60
      src/domain_abstract/model/Styleable.js
  31. 10
      src/domain_abstract/ui/InputNumber.js
  32. 170
      src/editor/config/config.js
  33. 46
      src/editor/index.js
  34. 87
      src/editor/model/Editor.js
  35. 4
      src/editor/view/EditorView.js
  36. 21
      src/grapesjs/index.js
  37. 30
      src/navigator/view/ItemView.js
  38. 5
      src/navigator/view/ItemsView.js
  39. 7
      src/selector_manager/view/ClassTagView.js
  40. 5
      src/storage_manager/index.js
  41. 6
      src/storage_manager/model/LocalStorage.js
  42. 18
      src/storage_manager/model/RemoteStorage.js
  43. 21
      src/style_manager/config/config.js
  44. 2
      src/style_manager/model/Layers.js
  45. 1
      src/style_manager/model/Property.js
  46. 13
      src/style_manager/model/PropertyFactory.js
  47. 15
      src/style_manager/view/LayerView.js
  48. 22
      src/style_manager/view/LayersView.js
  49. 2
      src/style_manager/view/PropertyCompositeView.js
  50. 16
      src/style_manager/view/PropertyFileView.js
  51. 3
      src/style_manager/view/PropertyIntegerView.js
  52. 20
      src/style_manager/view/PropertyStackView.js
  53. 205
      src/style_manager/view/PropertyView.js
  54. 23
      src/style_manager/view/SectorsView.js
  55. 8
      src/styles/scss/_gjs_blocks.scss
  56. 454
      src/styles/scss/_gjs_style_manager.scss
  57. 365
      src/styles/scss/main.scss
  58. 339
      src/utils/Sorter.js
  59. 14
      test/helper.js
  60. 4
      test/main.js
  61. 243
      test/specs/selector_manager/e2e/ClassManager.js
  62. 82
      test/specs/selector_manager/index.js
  63. 82
      test/specs/selector_manager/main.js
  64. 76
      test/specs/selector_manager/model/SelectorModels.js
  65. 222
      test/specs/selector_manager/view/ClassTagView.js
  66. 391
      test/specs/selector_manager/view/ClassTagsView.js
  67. 120
      test/specs/storage_manager/index.js
  68. 123
      test/specs/storage_manager/main.js
  69. 223
      test/specs/storage_manager/model/Models.js
  70. 191
      test/specs/style_manager/index.js
  71. 185
      test/specs/style_manager/main.js
  72. 1601
      test/specs/style_manager/model/Models.js
  73. 130
      test/specs/style_manager/view/LayerView.js
  74. 298
      test/specs/style_manager/view/PropertyColorView.js
  75. 452
      test/specs/style_manager/view/PropertyCompositeView.js
  76. 386
      test/specs/style_manager/view/PropertyIntegerView.js
  77. 317
      test/specs/style_manager/view/PropertyRadioView.js
  78. 357
      test/specs/style_manager/view/PropertySelectView.js
  79. 655
      test/specs/style_manager/view/PropertyStackView.js
  80. 427
      test/specs/style_manager/view/PropertyView.js
  81. 181
      test/specs/style_manager/view/SectorView.js
  82. 92
      test/specs/style_manager/view/SectorsView.js
  83. 8
      test/specs/test_utils.js
  84. 23
      test/specs/trait_manager/index.js
  85. 25
      test/specs/trait_manager/main.js
  86. 51
      test/specs/trait_manager/model/TraitsModel.js
  87. 126
      test/specs/trait_manager/view/TraitsView.js
  88. 4
      test/specs/utils/main.js
  89. 457
      yarn.lock

16
.eslintrc

@ -0,0 +1,16 @@
{
"env": {
"browser": true,
"node": true
},
"parserOptions": {
"sourceType": "module"
},
"rules": {
"strict": 0,
"quotes": [0, "single"],
"eol-last": [0],
"no-mixed-requires": [0],
"no-underscore-dangle": [0]
}
}

1
.gitignore

@ -2,7 +2,6 @@
.settings/ .settings/
.sass-cache/ .sass-cache/
.project .project
.eslintrc
npm-debug.log npm-debug.log
style/.sass-cache/ style/.sass-cache/

42
README.md

@ -22,6 +22,23 @@ Newsletter Demo - http://grapesjs.com/demo-newsletter-editor.html
## Table of contents
* [Features](#features)
* [Installation](#installation)
* [Development](#development)
* [Usage](#usage)
* [Documentation](#documentation)
* [API](#api)
* [Testing](#testing)
* [Plugins](#plugins)
* [Sponsors](#sponsors)
* [Support](#support)
* [License](#license)
## Features ## Features
@ -61,7 +78,7 @@ For development purpose you should follow instructions below.
## Development ## Development
GrapesJS uses [Webpack2](https://github.com/webpack/webpack) as a module bundler and [Babel](https://github.com/babel/babel) as a compiler. GrapesJS uses [Webpack](https://github.com/webpack/webpack) as a module bundler and [Babel](https://github.com/babel/babel) as a compiler.
Clone the repository and install all the necessary dependencies Clone the repository and install all the necessary dependencies
@ -159,6 +176,23 @@ $ npm test
## Plugins
### Extensions
* [grapesjs-plugin-export](https://github.com/artf/grapesjs-plugin-export) - Export GrapesJS templates in a zip archive
* [grapesjs-plugin-filestack](https://github.com/artf/grapesjs-plugin-filestack) - Add Filestack uploader in Asset Manager
* [grapesjs-plugin-ckeditor](https://github.com/artf/grapesjs-plugin-ckeditor) - Replaces the built-in RTE with CKEditor
* [grapesjs-aviary](https://github.com/artf/grapesjs-aviary) - Add the Aviary Image Editor
* [grapesjs-blocks-basic](https://github.com/artf/grapesjs-blocks-basic) - Basic set of blocks
* [grapesjs-plugin-forms](https://github.com/artf/grapesjs-plugin-forms) - Set of form components and blocks
* [grapesjs-navbar](https://github.com/artf/grapesjs-navbar) - Simple navbar component
### Presets
* [grapesjs-preset-newsletter](https://github.com/artf/grapesjs-preset-newsletter) - Newsletter Builder
* [grapesjs-mjml](https://github.com/artf/grapesjs-mjml) - Newsletter Builder with MJML components
## Sponsors ## Sponsors
The project is sponsored by The project is sponsored by
@ -171,11 +205,15 @@ The project is sponsored by
## Support ## Support
If you like the project support it with a donation of your choice. If you like the project support it with a donation of your choice or become a backer/sponsor via [Open Collective](https://opencollective.com/grapesjs)
[![PayPalMe](http://grapesjs.com/img/ppme.png)](https://paypal.me/grapesjs) [![PayPalMe](http://grapesjs.com/img/ppme.png)](https://paypal.me/grapesjs)
<a href="https://opencollective.com/grapesjs/sponsors/0/website" target="_blank"><img src="https://opencollective.com/grapesjs/sponsors/0/avatar"></a>
<a href="https://opencollective.com/grapesjs/sponsors/1/website" target="_blank"><img src="https://opencollective.com/grapesjs/sponsors/1/avatar"></a>
<a href="https://opencollective.com/grapesjs/backers/0/website" target="_blank"><img src="https://opencollective.com/grapesjs/backers/0/avatar"></a>
<a href="https://opencollective.com/grapesjs/backers/1/website" target="_blank"><img src="https://opencollective.com/grapesjs/backers/1/avatar"></a>

2
dist/css/grapes.min.css

File diff suppressed because one or more lines are too long

6
dist/grapes.min.js

File diff suppressed because one or more lines are too long

307
index.html

@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>GrapesJS</title> <title>GrapesJS</title>
<link rel="stylesheet" href="dist/css/grapes.min.css"> <link rel="stylesheet" href="dist/css/grapes.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="dist/grapes.min.js"></script> <script src="dist/grapes.min.js"></script>
</head> </head>
@ -774,6 +774,26 @@
width: 500px; width: 500px;
} }
.row {
display: table;
padding: 10px;
width: 100%;
}
.cell {
width: 8%;
display: table-cell;
height: 75px;
}
.cell30 {
width: 30%;
}
.cell70 {
width: 70%;
}
@media (max-width: 768px){ @media (max-width: 768px){
.foot-form-cont{ .foot-form-cont{
width:400px; width:400px;
@ -781,6 +801,11 @@
.foot-form-title{ .foot-form-title{
width: auto; width: auto;
} }
.cell, .cell30, .cell70 {
width: 100%;
display: block;
}
} }
@media (max-width: 480px){ @media (max-width: 480px){
@ -792,6 +817,7 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
var blkStyle = '.blk-row::after{ content: ""; clear: both; display: block;} .blk-row{padding: 10px;}';
var editor = grapesjs.init( var editor = grapesjs.init(
@ -811,63 +837,6 @@
storeStyles: 1, storeStyles: 1,
}, },
/*
components: [{
//script: 'var el = this; setInterval(function(){el.style.marginLeft = Math.random() * 50 +"px";}, 1000)',
script: 'loadScript = function(){console.log("loaded INSIDE", $);}',
style: {
background: 'red',
width:'500px',
height:'100px',
margin: '50px auto',
}
},{
script: 'this.innerHTML= "test1";',
style: {
background: 'blue',
width:'500px',
height:'100px',
margin: '50px auto',
}
},{
script: 'this.innerHTML= "test2";',
style: {
background: 'green',
width:'500px',
height:'100px',
margin: '50px auto',
}
},{
style: {
background: 'yellow',
width:'500px',
height:'100px',
margin: '50px auto',
}
},{
type: 'text',
style:{
width:'100px',
height:'100px',
margin: '50px auto',
},
traits: ['title'],
components: [{
type: 'textnode',
content: 'text node row',
},{
type: 'textnode',
content: ', another text node',
},{
type: 'link',
content: 'someLink',
},{
type: 'textnode',
content: " More text node --- ",
}],
}],
*/
commands: { commands: {
defaults: [{ defaults: [{
id: 'open-github', id: 'open-github',
@ -889,9 +858,9 @@
} }
},{ },{
id: 'clean-all', id: 'clean-all',
run: function(editor, sender){ run: function(editor, sender) {
sender.set('active',false); sender.set('active',false);
if(confirm('Are you sure to clean the canvas?')){ if(confirm('Are you sure to clean the canvas?')) {
var comps = editor.DomComponents.clear(); var comps = editor.DomComponents.clear();
} }
} }
@ -916,6 +885,156 @@
] ]
}, },
blockManager: {
blocks: [{
id: 'b1',
label: '1 Block',
category: 'Basic',
attributes: {class:'gjs-fonts gjs-f-b1'},
content: `<div class="row" data-gjs-droppable=".cell" data-gjs-custom-name="Row">
<div class="cell" data-gjs-draggable=".row" data-gjs-custom-name="Cell"></div>
</div>`
},{
id: 'b2',
label: '2 Blocks',
category: 'Basic',
attributes: {class:'gjs-fonts gjs-f-b2'},
content: `<div class="row" data-gjs-droppable=".cell" data-gjs-custom-name="Row">
<div class="cell" data-gjs-draggable=".row" data-gjs-custom-name="Cell"></div>
<div class="cell" data-gjs-draggable=".row" data-gjs-custom-name="Cell"></div>
</div>`
},{
id: 'b3',
label: '3 Blocks',
category: 'Basic',
attributes: {class:'gjs-fonts gjs-f-b3'},
content: `<div class="row" data-gjs-droppable=".cell" data-gjs-custom-name="Row">
<div class="cell" data-gjs-draggable=".row" data-gjs-custom-name="Cell"></div>
<div class="cell" data-gjs-draggable=".row" data-gjs-custom-name="Cell"></div>
<div class="cell" data-gjs-draggable=".row" data-gjs-custom-name="Cell"></div>
</div>`
},{
id: 'b4',
label: '3/7 Block',
category: 'Basic',
attributes: {class:'gjs-fonts gjs-f-b37'},
content: `<div class="row" data-gjs-droppable=".cell" data-gjs-custom-name="Row">
<div class="cell cell30" data-gjs-draggable=".row" data-gjs-custom-name="Cell"></div>
<div class="cell cell70" data-gjs-draggable=".row" data-gjs-custom-name="Cell"></div>
</div>`,
},{
id: 'hero',
label: 'Hero section',
category: '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',
category: 'Typography',
content: `<div>
<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>
</div>`,
attributes: {class:'gjs-fonts gjs-f-h1p'}
},{
id: '3ba',
label: 'Badges',
category: 'Section',
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'},
category: 'Basic',
content: {
type:'text',
content:'Insert your text here',
style: {padding: '10px' },
activeOnRender: 1
},
},{
id: 'image',
label: 'Image',
category: 'Basic',
attributes: {class:'gjs-fonts gjs-f-image'},
content: {
style: {color: 'black'},
type:'image',
activeOnRender: 1
},
},{
id: 'quo',
label: 'Quote',
category: 'Typography',
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:'fa fa-quote-right'}
},{
id: 'link',
label: 'Link',
category: 'Basic',
attributes: {class:'fa fa-link'},
content: {
type:'link',
content:'Link',
style:{color: '#d983a6'}
},
},{
id: 'map',
label: 'Map',
category: 'Extra',
attributes: {class:'fa fa-map-o'},
content: {
type: 'map',
style: {height: '350px'}
},
},{
id: 'video',
label: 'Video',
category: 'Basic',
attributes: {class:'fa fa-youtube-play'},
content: {
type: 'video',
src: 'img/video2.webm',
style: {
height: '350px',
width: '615px',
}
},
}],
},
styleManager : { styleManager : {
sectors: [{ sectors: [{
@ -1156,22 +1275,58 @@
}]); }]);
var bm = editor.BlockManager; var bm = editor.BlockManager;
bm.add('link-block', { /*
label: 'Link Block', bm.add('link-block', {
attributes: {class:'fa fa-link'}, label: 'Link Block',
category: 'Basic', attributes: {class:'fa fa-link'},
content: { category: 'Basic',
type:'link', content: {
editable: false, type:'link',
droppable: true, editable: false,
style:{ droppable: true,
display: 'inline-block', style:{
padding: '5px', display: 'inline-block',
'min-height': '50px', padding: '5px',
'min-width': '50px' 'min-height': '50px',
} 'min-width': '50px'
}, }
}); },
});*/
var domc = editor.DomComponents;
var defaultType = domc.getType('default');
var defaultModel = defaultType.model;
var defaultView = defaultType.view;
/*
domc.addType('default', {
model: defaultModel.extend({
defaults: Object.assign({}, defaultModel.prototype.defaults, {
traits: [{
name: 'title',
label: 'Título',
placeholder: 'Insira um texto aqui'
}]
}),
}),
});
*/
// Store and load events
editor.on('storage:load', function(e) {
console.log('LOAD ', e);
})
editor.on('storage:store', function(e) {
console.log('STORE ', e);
})
editor.on('styleManager:change:text-shadow', function(view) {
var model = view.model;
let targetValue = view.getTargetValue({ignoreDefault: 1});
let computedValue = view.getComputedValue();
let defaultValue = view.getDefaultValue();
console.log('Style of ', model.get('property'), 'Target: ', targetValue, 'Computed:', computedValue, 'Default:', defaultValue);
})
editor.render(); editor.render();
</script> </script>

7
package.json

@ -1,7 +1,7 @@
{ {
"name": "grapesjs", "name": "grapesjs",
"description": "Free and Open Source Web Builder Framework", "description": "Free and Open Source Web Builder Framework",
"version": "0.8.4", "version": "0.9.8",
"author": "Artur Arseniev", "author": "Artur Arseniev",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"homepage": "http://grapesjs.com", "homepage": "http://grapesjs.com",
@ -27,16 +27,13 @@
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"chai": "^3.5.0", "chai": "^3.5.0",
"documentation": "^4.0.0-beta2", "documentation": "^4.0.0-beta2",
"eslint": "^4.1.1",
"expect": "^1.20.2", "expect": "^1.20.2",
"istanbul": "^0.4.2", "istanbul": "^0.4.2",
"jsdom": "^11.0.0", "jsdom": "^11.0.0",
"mocha": "^3.1.2", "mocha": "^3.1.2",
"node-sass": "^3.4.2", "node-sass": "^3.4.2",
"sinon": "^1.17.6", "sinon": "^1.17.6",
"svg2ttf": "^4.0.1",
"ttf2eot": "^2.0.0",
"ttf2woff": "^2.0.1",
"ttf2woff2": "^2.0.3",
"webpack": "^2.6.1", "webpack": "^2.6.1",
"webpack-dev-server": "^2.4.5" "webpack-dev-server": "^2.4.5"
}, },

8
src/asset_manager/config/config.js

@ -13,4 +13,12 @@ module.exports = {
// Label for the add button // Label for the add button
addBtnText: 'Add image', addBtnText: 'Add image',
// Custom uploadFile function
// @example
// uploadFile: function(e) {
// var files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
// // ...send somewhere
// }
uploadFile: '',
}; };

17
src/asset_manager/index.js

@ -179,11 +179,18 @@ module.exports = () => {
var name = this.storageKey; var name = this.storageKey;
if(!d && c.stm) if(!d && c.stm)
d = c.stm.load(name); d = c.stm.load(name);
var assets = []; var assets = d[name] || [];
try{
assets = JSON.parse(d[name]); if (typeof assets == 'string') {
}catch(err){} try {
this.getAll().add(assets); assets = JSON.parse(d[name]);
} catch(err) {}
}
if (assets && assets.length) {
this.getAll().reset(assets);
}
return assets; return assets;
}, },

5
src/asset_manager/view/AssetsView.js

@ -32,8 +32,9 @@ module.exports = Backbone.View.extend({
this.config = o.config; this.config = o.config;
this.pfx = this.config.stylePrefix || ''; this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || ''; this.ppfx = this.config.pStylePrefix || '';
this.listenTo( this.collection, 'add', this.addToAsset ); this.listenTo(this.collection, 'add', this.addToAsset );
this.listenTo( this.collection, 'deselectAll', this.deselectAll ); this.listenTo(this.collection, 'deselectAll', this.deselectAll);
this.listenTo(this.collection, 'reset', this.render);
this.className = this.pfx + 'assets'; this.className = this.pfx + 'assets';
this.events = {}; this.events = {};

6
src/asset_manager/view/FileUploader.js

@ -21,6 +21,12 @@ module.exports = Backbone.View.extend({
this.uploadId = this.pfx + 'uploadFile'; this.uploadId = this.pfx + 'uploadFile';
this.disabled = !this.config.upload; this.disabled = !this.config.upload;
this.events['change #' + this.uploadId] = 'uploadFile'; this.events['change #' + this.uploadId] = 'uploadFile';
let uploadFile = this.config.uploadFile;
if (uploadFile) {
this.uploadFile = uploadFile.bind(this);
}
this.delegateEvents(); this.delegateEvents();
}, },

9
src/block_manager/view/BlockView.js

@ -19,8 +19,15 @@ module.exports = Backbone.View.extend({
* @private * @private
*/ */
onDrag(e) { onDrag(e) {
if(!this.config.getSorter) //Right or middel click
if (e.button !== 0) {
return; return;
}
if(!this.config.getSorter) {
return;
}
this.config.em.refreshCanvas(); this.config.em.refreshCanvas();
var sorter = this.config.getSorter(); var sorter = this.config.getSorter();
sorter.setDragHelper(this.el, e); sorter.setDragHelper(this.el, e);

14
src/block_manager/view/BlocksView.js

@ -43,6 +43,7 @@ module.exports = Backbone.View.extend({
pfx: this.ppfx, pfx: this.ppfx,
onStart: this.onDrag, onStart: this.onDrag,
onEndMove: this.onDrop, onEndMove: this.onDrop,
onMove: this.onMove,
document: canvas.getFrameEl().contentDocument, document: canvas.getFrameEl().contentDocument,
direction: 'a', direction: 'a',
wmargin: 1, wmargin: 1,
@ -58,8 +59,13 @@ module.exports = Backbone.View.extend({
* Callback when block is on drag * Callback when block is on drag
* @private * @private
*/ */
onDrag() { onDrag(e) {
this.em.stopDefault(); this.em.stopDefault();
this.em.trigger('block:drag:start', e);
},
onMove(e) {
this.em.trigger('block:drag:move', e);
}, },
/** /**
@ -78,6 +84,7 @@ module.exports = Backbone.View.extend({
// Register all its components (eg. for the Undo Manager) // Register all its components (eg. for the Undo Manager)
this.em.initChildrenComp(model); this.em.initChildrenComp(model);
} }
this.em.trigger('block:drag:stop', model);
}, },
/** /**
@ -161,6 +168,9 @@ module.exports = Backbone.View.extend({
render() { render() {
var ppfx = this.ppfx; var ppfx = this.ppfx;
var frag = document.createDocumentFragment(); var frag = document.createDocumentFragment();
this.catsEl = null;
this.blocksEl = null;
this.renderedCategories = [];
this.el.innerHTML = ` this.el.innerHTML = `
<div class="${this.catsClass}"></div> <div class="${this.catsClass}"></div>
<div class="${this.noCatClass}"> <div class="${this.noCatClass}">
@ -173,7 +183,7 @@ module.exports = Backbone.View.extend({
}, this); }, this);
this.append(frag); this.append(frag);
this.$el.addClass(this.blockContClass + 's'); this.$el.addClass(this.blockContClass + 's')
return this; return this;
}, },

28
src/canvas/config/config.js

@ -6,9 +6,31 @@ module.exports = {
rulers: false, rulers: false,
/* /*
* append scripts in head of iframe before renderBody content * Append external scripts in head of the iframe before renderBody content
* need to manually maintain the same scripts in cms's render template * In this case, you have to add them manually later in the final HTML page
* @example
* scripts: [
* 'https://...',
* ]
*/ */
scripts: [] scripts: [],
/*
* Append external styles. This styles won't be added to the final HTML/CSS
* @example
* styles: [
* 'https://...',
* ]
*/
styles: [],
/**
* Add custom badge naming strategy
* @example
* customBadgeLabel: function(ComponentModel) {
* return ComponentModel.getName();
* }
*/
customBadgeLabel: '',
}; };

77
src/canvas/index.js

@ -4,6 +4,7 @@ module.exports = () => {
Canvas = require('./model/Canvas'), Canvas = require('./model/Canvas'),
CanvasView = require('./view/CanvasView'); CanvasView = require('./view/CanvasView');
var canvas; var canvas;
var frameRect;
return { return {
@ -47,9 +48,20 @@ module.exports = () => {
if(cm) if(cm)
this.setWrapper(cm); this.setWrapper(cm);
this.startAutoscroll = this.startAutoscroll.bind(this);
this.stopAutoscroll = this.stopAutoscroll.bind(this);
this.autoscroll = this.autoscroll.bind(this);
return this; return this;
}, },
/**
* Return config object
* @return {Object}
*/
getConfig() {
return c;
},
/** /**
* Add wrapper * Add wrapper
* @param {Object} wrp Wrapper * @param {Object} wrp Wrapper
@ -199,6 +211,18 @@ module.exports = () => {
}; };
}, },
/**
* Set custom badge naming strategy
* @param {Function} f
* @example
* canvas.setCustomBadgeLabel(function(model){
* return ComponentModel.getName();
* });
*/
setCustomBadgeLabel(f) {
c.customBadgeLabel = f;
},
/** /**
* Get element position relative to the canvas * Get element position relative to the canvas
* @param {HTMLElement} el * @param {HTMLElement} el
@ -313,6 +337,59 @@ module.exports = () => {
}; };
}, },
/**
* Start autoscroll
*/
startAutoscroll() {
this.dragging = 1;
let toListen = this.getScrollListeners();
frameRect = CanvasView.getFrameOffset(1);
toListen.on('mousemove', this.autoscroll);
toListen.on('mouseup', this.stopAutoscroll);
},
autoscroll(e) {
e.preventDefault();
if (this.dragging) {
let frameWindow = this.getFrameEl().contentWindow;
let actualTop = frameWindow.document.body.scrollTop;
let nextTop = actualTop;
let clientY = e.clientY;
let limitTop = 50;
let limitBottom = frameRect.height - limitTop;
if (clientY < limitTop) {
nextTop -= (limitTop - clientY);
}
if (clientY > limitBottom) {
nextTop += (clientY - limitBottom);
}
//console.log(`actualTop: ${actualTop} clientY: ${clientY} nextTop: ${nextTop} frameHeigh: ${frameRect.height}`);
frameWindow.scrollTo(0, nextTop);
}
},
/**
* Stop autoscroll
*/
stopAutoscroll() {
this.dragging = 0;
let toListen = this.getScrollListeners();
toListen.off('mousemove', this.autoscroll);
toListen.off('mouseup', this.stopAutoscroll);
},
getScrollListeners() {
if (!this.scrollListeners) {
this.scrollListeners =
$(this.getFrameEl().contentWindow, this.getElement());
}
return this.scrollListeners;
},
/** /**
* Returns wrapper element * Returns wrapper element
* @return {HTMLElement} * @return {HTMLElement}

77
src/canvas/view/CanvasView.js

@ -67,13 +67,50 @@ module.exports = Backbone.View.extend({
var body = this.frame.$el.contents().find('body'); var body = this.frame.$el.contents().find('body');
var cssc = em.get('CssComposer'); var cssc = em.get('CssComposer');
var conf = em.get('Config'); var conf = em.get('Config');
body.append(wrap.render()).append(cssc.render()); var confCanvas = this.config;
var protCss = conf.protectedCss; var protCss = conf.protectedCss;
var externalStyles = '';
confCanvas.styles.forEach((style) => {
externalStyles += `<link rel="stylesheet" href="${style}"/>`;
});
// rgb(255, 202, 111)
const colorWarn = '#ffca6f';
let baseCss = `
* {
box-sizing: border-box;
}
html, body, #wrapper {
min-height: 100%;
}
html {
height: 100%;
}
body {
margin: 0;
height: auto;
background-color: #fff
}
#wrapper {
overflow: auto
}
`;
let layoutCss = `
.${ppfx}comp-selected{
outline: 3px solid #3b97e3 !important
}
.${ppfx}comp-selected-parent{
outline: 2px solid ${colorWarn} !important
}
`;
// I need all this styles to make the editor work properly // I need all this styles to make the editor work properly
var frameCss = '* {box-sizing: border-box;} body{margin:0;height:auto;background-color:#fff} #wrapper{min-height:100%; overflow:auto}' + var frameCss = baseCss +
'.' + ppfx + 'dashed :not([contenteditable]) > *[data-highlightable]{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' + '.' + ppfx + 'dashed :not([contenteditable]) > *[data-highlightable]{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' +
'.' + ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' + layoutCss +
'.' + ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+ '.' + ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+
'.' + ppfx + 'freezed{opacity: 0.5; pointer-events: none}' + '.' + ppfx + 'freezed{opacity: 0.5; pointer-events: none}' +
'.' + ppfx + 'no-pointer{pointer-events: none}' + '.' + ppfx + 'no-pointer{pointer-events: none}' +
@ -84,7 +121,13 @@ module.exports = Backbone.View.extend({
'* ::-webkit-scrollbar {width: 10px}' + '* ::-webkit-scrollbar {width: 10px}' +
(conf.canvasCss || ''); (conf.canvasCss || '');
frameCss += protCss || ''; frameCss += protCss || '';
if (externalStyles) {
body.append(externalStyles);
}
body.append('<style>' + frameCss + '</style>'); body.append('<style>' + frameCss + '</style>');
body.append(wrap.render()).append(cssc.render());
body.append(this.getJsContainer()); body.append(this.getJsContainer());
em.trigger('loaded'); em.trigger('loaded');
this.frame.el.contentWindow.onscroll = this.onFrameScroll; this.frame.el.contentWindow.onscroll = this.onFrameScroll;
@ -113,7 +156,9 @@ module.exports = Backbone.View.extend({
var docBody = el.ownerDocument.body; var docBody = el.ownerDocument.body;
return { return {
top: rect.top + docBody.scrollTop, top: rect.top + docBody.scrollTop,
left: rect.left + docBody.scrollLeft left: rect.left + docBody.scrollLeft,
width: rect.width,
height: rect.height,
}; };
}, },
@ -131,8 +176,8 @@ module.exports = Backbone.View.extend({
* @return {Object} * @return {Object}
* @private * @private
*/ */
getFrameOffset() { getFrameOffset(force = 0) {
if(!this.frmOff) if(!this.frmOff || force)
this.frmOff = this.offset(this.frame.el); this.frmOff = this.offset(this.frame.el);
return this.frmOff; return this.frmOff;
}, },
@ -199,17 +244,19 @@ module.exports = Backbone.View.extend({
this.getJsContainer().append(view.scriptContainer.get(0)); this.getJsContainer().append(view.scriptContainer.get(0));
} }
var id = view.model.cid; var model = view.model;
var script = view.model.get('script'); var id = model.getId();
var scrStr = 'function(){' + script + '}';
scrStr = typeof script == 'function' ? script.toString() : scrStr;
view.el.id = id; view.el.id = id;
view.scriptContainer.html(''); view.scriptContainer.html('');
// In editor, I make use of setTimeout as during the append process of elements
view.scriptContainer.append('<script>' + // those will not be available immediatly, therefore 'item' variable
'var item = document.getElementById("'+id+'");' + view.scriptContainer.append(`<script>
'(' + scrStr + '.bind(item))()</script>'); setTimeout(function() {
var item = document.getElementById('${id}');
if (!item) return;
(function(){${model.getScriptString()}}.bind(item))()
}, 1);
</script>`);
}, },
/** /**

2
src/code_manager/model/CssGenerator.js

@ -24,7 +24,7 @@ module.exports = Backbone.Model.extend({
} }
if(style && Object.keys(style).length !== 0) { if(style && Object.keys(style).length !== 0) {
code += '#' + model.cid + '{'; code += '#' + model.getId() + '{';
for(var prop in style){ for(var prop in style){
if(style.hasOwnProperty(prop)) if(style.hasOwnProperty(prop))
code += prop + ':' + style[prop] + ';'; code += prop + ':' + style[prop] + ';';

15
src/code_manager/model/JsGenerator.js

@ -7,16 +7,14 @@ module.exports = Backbone.Model.extend({
var script = model.get('script'); var script = model.get('script');
var type = model.get('type'); var type = model.get('type');
var comps = model.get('components'); var comps = model.get('components');
var id = model.cid; var id = model.getId();
if (script) { if (script) {
// If the component has scripts we need to expose his ID // If the component has scripts we need to expose his ID
var attr = model.get('attributes'); var attr = model.get('attributes');
attr = _.extend({}, attr, {id}); attr = _.extend({}, attr, {id});
model.set('attributes', attr); model.set('attributes', attr);
var scrStr = model.getScriptString();
var scrStr = 'function(){' + script + '}';
scrStr = typeof script == 'function' ? script.toString() : scrStr;
// If the script was updated, I'll put its code in a separate container // If the script was updated, I'll put its code in a separate container
if (model.get('scriptUpdated')) { if (model.get('scriptUpdated')) {
@ -48,10 +46,11 @@ module.exports = Backbone.Model.extend({
for(var type in this.mapJs) { for(var type in this.mapJs) {
var mapType = this.mapJs[type]; var mapType = this.mapJs[type];
var ids = '#' + mapType.ids.join(', #'); var ids = '#' + mapType.ids.join(', #');
code += 'var items = document.querySelectorAll("'+ids+'");' + code += `
'for (var i = 0, len = items.length; i < len; i++) {'+ var items = document.querySelectorAll('${ids}');
'(' + mapType.code + '.bind(items[i]))();' + for (var i = 0, len = items.length; i < len; i++) {
'}'; (function(){${mapType.code}}.bind(items[i]))();
}`;
} }

1
src/commands/index.js

@ -160,7 +160,6 @@ module.exports = () => {
em.set('selectedComponent', sel); em.set('selectedComponent', sel);
ed.trigger('component:update', sel); ed.trigger('component:update', sel);
dragger && dragger.blur(); dragger && dragger.blur();
console.log('onEnd ', opts);
}; };
const onDrag = (e, opts) => { const onDrag = (e, opts) => {

9
src/commands/view/SelectComponent.js

@ -245,16 +245,21 @@ module.exports = {
* */ * */
updateBadge(el, pos) { updateBadge(el, pos) {
var $el = $(el); var $el = $(el);
var canvas = this.canvas;
var config = canvas.getConfig();
var customeLabel = config.customBadgeLabel;
this.cacheEl = el; this.cacheEl = el;
var model = $el.data("model"); var model = $el.data("model");
if(!model || !model.get('badgable')) if(!model || !model.get('badgable'))
return; return;
var badge = this.getBadge(); var badge = this.getBadge();
badge.innerHTML = model.getName(); var badgeLabel = model.getIcon() + model.getName();
badgeLabel = customeLabel ? customeLabel(model) : badgeLabel;
badge.innerHTML = badgeLabel;
var bStyle = badge.style; var bStyle = badge.style;
var u = 'px'; var u = 'px';
bStyle.display = 'block'; bStyle.display = 'block';
var canvasPos = this.canvas.getCanvasView().getPosition(); var canvasPos = canvas.getCanvasView().getPosition();
var badgeH = badge ? badge.offsetHeight : 0; var badgeH = badge ? badge.offsetHeight : 0;
var badgeW = badge ? badge.offsetWidth : 0; var badgeW = badge ? badge.offsetWidth : 0;
var top = pos.top - badgeH < canvasPos.top ? canvasPos.top : pos.top - badgeH; var top = pos.top - badgeH < canvasPos.top ? canvasPos.top : pos.top - badgeH;

19
src/css_composer/index.js

@ -93,9 +93,6 @@ module.exports = () => {
* @private * @private
*/ */
onLoad() { onLoad() {
if(c.stm && c.stm.getConfig().autoload)
this.load();
if(c.stm && c.stm.isAutosave()) if(c.stm && c.stm.isAutosave())
c.em.listenRules(this.getAll()); c.em.listenRules(this.getAll());
}, },
@ -224,8 +221,7 @@ module.exports = () => {
* @return {Array<Model>} * @return {Array<Model>}
* @private * @private
*/ */
addCollection(data, opts) { addCollection(data, opts = {}) {
var opt = opts || {};
var result = []; var result = [];
var d = data instanceof Array ? data : [data]; var d = data instanceof Array ? data : [data];
@ -245,12 +241,15 @@ module.exports = () => {
newSels.push(selec); newSels.push(selec);
} }
var modelExists = this.get(newSels, rule.state, rule.mediaText, rule);
var model = this.add(newSels, rule.state, rule.mediaText, rule); var model = this.add(newSels, rule.state, rule.mediaText, rule);
if (opt.extend) { var updateStyle = !modelExists || !opts.avoidUpdateStyle;
var newStyle = _.extend({}, model.get('style'), rule.style || {}); const style = rule.style || {};
model.set('style', newStyle);
} else { if (updateStyle) {
model.set('style', rule.style || {}); let styleUpdate = opts.extend ?
Object.assign({}, model.get('style'), style) : style;
model.set('style', styleUpdate);
} }
result.push(model); result.push(model);

4
src/css_composer/model/CssRule.js

@ -1,7 +1,9 @@
import Styleable from 'domain_abstract/model/Styleable';
var Backbone = require('backbone'); var Backbone = require('backbone');
var Selectors = require('./Selectors'); var Selectors = require('./Selectors');
module.exports = Backbone.Model.extend({ module.exports = Backbone.Model.extend(Styleable).extend({
defaults: { defaults: {
// Css selectors // Css selectors

12
src/device_manager/model/Device.js

@ -6,7 +6,19 @@ module.exports = Backbone.Model.extend({
defaults :{ defaults :{
name: '', name: '',
// Width to set for the editor iframe
width: '', width: '',
// The width which will be used in media queries,
// If empty the width will be used
widthMedia: null,
}, },
initialize() {
if (this.get('widthMedia') == null) {
this.set('widthMedia', this.get('width'));
}
}
}); });

21
src/dom_components/index.js

@ -81,6 +81,11 @@ module.exports = () => {
model: require('./model/ComponentScript'), model: require('./model/ComponentScript'),
view: require('./view/ComponentScriptView'), view: require('./view/ComponentScriptView'),
}, },
{
id: 'svg',
model: require('./model/ComponentSvg'),
view: require('./view/ComponentSvgView'),
},
{ {
id: 'textnode', id: 'textnode',
model: require('./model/ComponentTextNode'), model: require('./model/ComponentTextNode'),
@ -187,9 +192,6 @@ module.exports = () => {
* @private * @private
*/ */
onLoad() { onLoad() {
if(c.stm && c.stm.getConfig().autoload)
this.load();
if(c.stm && c.stm.isAutosave()){ if(c.stm && c.stm.isAutosave()){
c.em.initUndoManager(); c.em.initUndoManager();
c.em.initChildrenComp(this.getWrapper()); c.em.initChildrenComp(this.getWrapper());
@ -215,8 +217,12 @@ module.exports = () => {
}else if(d.html) }else if(d.html)
obj = d.html; obj = d.html;
if(obj) if (obj && obj.length) {
this.getComponents().reset(obj); this.clear();
this.getComponents().reset();
this.getComponents().add(obj);
}
return obj; return obj;
}, },
@ -234,8 +240,11 @@ module.exports = () => {
obj.html = c.em.getHtml(); obj.html = c.em.getHtml();
if(keys.indexOf('components') >= 0) if(keys.indexOf('components') >= 0)
obj.components = JSON.stringify(c.em.getComponents()); obj.components = JSON.stringify(c.em.getComponents());
if(!noStore)
if (!noStore) {
c.stm.store(obj); c.stm.store(obj);
}
return obj; return obj;
}, },

91
src/dom_components/model/Component.js

@ -1,9 +1,15 @@
import Styleable from 'domain_abstract/model/Styleable';
var Backbone = require('backbone'); var Backbone = require('backbone');
var Components = require('./Components'); var Components = require('./Components');
var Selectors = require('selector_manager/model/Selectors'); var Selectors = require('selector_manager/model/Selectors');
var Traits = require('trait_manager/model/Traits'); var Traits = require('trait_manager/model/Traits');
module.exports = Backbone.Model.extend({ const escapeRegExp = (str) => {
return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
}
module.exports = Backbone.Model.extend(Styleable).extend({
defaults: { defaults: {
// HTML tag of the component // HTML tag of the component
@ -58,6 +64,9 @@ module.exports = Backbone.Model.extend({
// Content of the component (not escaped) which will be appended before children rendering // Content of the component (not escaped) which will be appended before children rendering
content: '', content: '',
// Component icon, this string will be inserted before the name, eg. '<i class="fa fa-square-o"></i>'
icon: '',
// Component related style // Component related style
style: {}, style: {},
@ -124,6 +133,7 @@ module.exports = Backbone.Model.extend({
} }
}, this); }, this);
this.set('status', '');
this.init(); this.init();
}, },
@ -244,20 +254,25 @@ module.exports = Backbone.Model.extend({
}, },
/** /**
* Get name of the component * Get the name of the component
* @return {string} * @return {string}
* @private
* */ * */
getName() { getName() {
if(!this.name){ let customName = this.get('custom-name');
var id = this.cid.replace(/\D/g,''), let tag = this.get('tagName');
type = this.get('type'); tag = tag == 'div' ? 'box' : tag;
var tag = this.get('tagName'); let name = this.get('type') || tag;
tag = tag == 'div' ? 'box' : tag; name = name.charAt(0).toUpperCase() + name.slice(1);
tag = type ? type : tag; return customName || name;
this.name = tag.charAt(0).toUpperCase() + tag.slice(1); },
}
return this.name; /**
* Get the icon string
* @return {string}
*/
getIcon() {
let icon = this.get('icon');
return icon ? icon + ' ' : '';
}, },
/** /**
@ -269,17 +284,22 @@ module.exports = Backbone.Model.extend({
toHTML(opts) { toHTML(opts) {
var code = ''; var code = '';
var m = this; var m = this;
var tag = m.get('tagName'), var tag = m.get('tagName');
sTag = m.get('void'), var idFound = 0;
attrId = ''; var sTag = m.get('void');
// Build the string of attributes var attrId = '';
var strAttr = ''; var strAttr = '';
var attr = this.getAttrToHTML(); var attr = this.getAttrToHTML();
for(var prop in attr){
for (var prop in attr) {
if (prop == 'id') {
idFound = 1;
}
var val = attr[prop]; var val = attr[prop];
strAttr += typeof val !== undefined && val !== '' ? strAttr += typeof val !== undefined && val !== '' ?
' ' + prop + '="' + val + '"' : ''; ' ' + prop + '="' + val + '"' : '';
} }
// Build the string of classes // Build the string of classes
var strCls = ''; var strCls = '';
m.get('classes').each(m => { m.get('classes').each(m => {
@ -288,9 +308,8 @@ module.exports = Backbone.Model.extend({
strCls = strCls !== '' ? ' class="' + strCls.trim() + '"' : ''; strCls = strCls !== '' ? ' class="' + strCls.trim() + '"' : '';
// If style is not empty I need an ID attached to the component // If style is not empty I need an ID attached to the component
// TODO: need to refactor in case of 'ID Trait' if(!_.isEmpty(m.get('style')) && !idFound)
if(!_.isEmpty(m.get('style'))) attrId = ' id="' + m.getId() + '" ';
attrId = ' id="' + m.cid + '" ';
code += '<' + tag + strCls + attrId + strAttr + (sTag ? '/' : '') + '>' + m.get('content'); code += '<' + tag + strCls + attrId + strAttr + (sTag ? '/' : '') + '>' + m.get('content');
@ -324,6 +343,7 @@ module.exports = Backbone.Model.extend({
toJSON(...args) { toJSON(...args) {
var obj = Backbone.Model.prototype.toJSON.apply(this, args); var obj = Backbone.Model.prototype.toJSON.apply(this, args);
var scriptStr = this.getScriptString(); var scriptStr = this.getScriptString();
delete obj.toolbar;
if (scriptStr) { if (scriptStr) {
obj.script = scriptStr; obj.script = scriptStr;
@ -332,6 +352,15 @@ module.exports = Backbone.Model.extend({
return obj; return obj;
}, },
/**
* Return model id
* @return {string}
*/
getId() {
let attrs = this.get('attributes') || {};
return attrs.id || this.cid;
},
/** /**
* Return script in string format, cleans 'function() {..' from scripts * Return script in string format, cleans 'function() {..' from scripts
* if it's a function * if it's a function
@ -342,14 +371,28 @@ module.exports = Backbone.Model.extend({
getScriptString(script) { getScriptString(script) {
var scr = script || this.get('script'); var scr = script || this.get('script');
// Need to cast script functions to string if (!scr) {
return scr;
}
// Need to convert script functions to strings
if (typeof scr == 'function') { if (typeof scr == 'function') {
var scrStr = scr.toString().trim(); var scrStr = scr.toString().trim();
scrStr = scrStr.replace(/^function\s?\(\)\s?\{/, ''); scrStr = scrStr.replace(/^function[\s\w]*\(\)\s?\{/, '').replace(/\}$/, '');
scrStr = scrStr.replace(/\}$/, ''); scr = scrStr.trim();
scr = scrStr;
} }
var config = this.sm.config || {};
var tagVarStart = escapeRegExp(config.tagVarStart || '{[ ');
var tagVarEnd = escapeRegExp(config.tagVarEnd || ' ]}');
var reg = new RegExp(`${tagVarStart}(\\w+)${tagVarEnd}`, 'g');
scr = scr.replace(reg, (match, v) => {
// If at least one match is found I have to track this change for a
// better optimization inside JS generator
this.scriptUpdated();
return this.attributes[v];
})
return scr; return scr;
} }

20
src/dom_components/model/ComponentSvg.js

@ -0,0 +1,20 @@
var Component = require('./Component');
module.exports = Component.extend({
getName() {
let name = this.get('tagName');
let customName = this.get('custom-name');
name = name.charAt(0).toUpperCase() + name.slice(1);
return customName || name;
},
}, {
isComponent(el) {
if (SVGElement && el instanceof SVGElement) {
return {type: 'svg'};
}
},
});

13
src/dom_components/model/Components.js

@ -47,14 +47,19 @@ module.exports = Backbone.Collection.extend({
}, },
add(models, opt) { add(models, opt = {}) {
if(typeof models === 'string'){ if (typeof models === 'string') {
var parsed = this.editor.get('Parser').parseHtml(models); var parsed = this.editor.get('Parser').parseHtml(models);
models = parsed.html; models = parsed.html;
var cssc = this.editor.get('CssComposer'); var cssc = this.editor.get('CssComposer');
if(parsed.css && cssc){
var added = cssc.addCollection(parsed.css, {extend: 1}); if (parsed.css && cssc) {
var {avoidUpdateStyle} = opt;
var added = cssc.addCollection(parsed.css, {
extend: 1,
avoidUpdateStyle
});
} }
} }

9
src/dom_components/view/ComponentSvgView.js

@ -0,0 +1,9 @@
const ComponentView = require('./ComponentView');
module.exports = ComponentView.extend({
_createElement: function(tagName) {
return document.createElementNS('http://www.w3.org/2000/svg', tagName);
}
});

45
src/dom_components/view/ComponentView.js

@ -100,16 +100,34 @@ module.exports = Backbone.View.extend({
* @private * @private
* */ * */
updateStatus(e) { updateStatus(e) {
var s = this.model.get('status'), var el = this.el;
pfx = this.pfx; var status = this.model.get('status');
switch(s) { var pfx = this.pfx;
var ppfx = this.ppfx;
var selectedCls = pfx + 'selected';
var selectedParentCls = selectedCls + '-parent';
var freezedCls = `${ppfx}freezed`;
var actualCls = el.getAttribute('class') || '';
var cls = '';
switch (status) {
case 'selected': case 'selected':
this.$el.addClass(pfx + 'selected'); cls = `${actualCls} ${selectedCls}`;
break; break;
case 'moving': case 'selected-parent':
break; cls = `${actualCls} ${selectedParentCls}`;
break;
case 'freezed':
cls = `${actualCls} ${freezedCls}`;
break;
default: default:
this.$el.removeClass(pfx + 'selected'); this.$el.removeClass(`${selectedCls} ${selectedParentCls} ${freezedCls}`);
}
cls = cls.trim();
if (cls) {
el.setAttribute('class', cls);
} }
}, },
@ -290,6 +308,10 @@ module.exports = Backbone.View.extend({
* @private * @private
*/ */
updateScript() { updateScript() {
if (!this.model.get('script')) {
return;
}
var em = this.em; var em = this.em;
if(em) { if(em) {
var canvas = em.get('Canvas'); var canvas = em.get('Canvas');
@ -382,12 +404,7 @@ module.exports = Backbone.View.extend({
var container = this.getChildrenContainer(); var container = this.getChildrenContainer();
container.innerHTML = model.get('content'); container.innerHTML = model.get('content');
this.renderChildren(); this.renderChildren();
this.updateScript();
// Render script
if(model.get('script')) {
this.updateScript();
}
return this; return this;
}, },

60
src/domain_abstract/model/Styleable.js

@ -0,0 +1,60 @@
export default {
/**
* To trigger the style change event on models I have to
* pass a new object instance
* @param {Object} prop
* @return {Object}
*/
extendStyle(prop) {
return Object.assign({}, this.getStyle(), prop);
},
/**
* Get style object
* @return {Object}
*/
getStyle() {
return this.get('style');
},
/**
* Set new style object
* @param {Object} prop
* @param {Object} opts
*/
setStyle(prop = {}, opts = {}) {
this.set('style', Object.assign({}, prop), opts);
},
/**
* Add style property
* @param {Object|string} prop
* @param {string} value
* @example
* this.addStyle({color: 'red'});
* this.addStyle('color', 'blue');
*/
addStyle(prop, value = '', opts = {}) {
if (typeof prop == 'string') {
prop = {
prop: value
};
} else {
opts = value || {};
}
prop = this.extendStyle(prop);
this.setStyle(prop, opts);
},
/**
* Remove style property
* @param {string} prop
*/
removeStyle(prop) {
let style = Object.assign({}, this.getStyle());
delete style[prop];
this.setStyle(style);
}
}

10
src/domain_abstract/ui/InputNumber.js

@ -62,6 +62,7 @@ module.exports = Backbone.View.extend({
handleChange(e) { handleChange(e) {
e.stopPropagation(); e.stopPropagation();
this.setValue(this.getInputEl().value); this.setValue(this.getInputEl().value);
this.elementUpdated();
}, },
/** /**
@ -71,6 +72,14 @@ module.exports = Backbone.View.extend({
e.stopPropagation(); e.stopPropagation();
var value = this.getUnitEl().value; var value = this.getUnitEl().value;
this.model.set('unit', value); this.model.set('unit', value);
this.elementUpdated();
},
/**
* Fired when the element of the property is updated
*/
elementUpdated() {
this.model.trigger('el:change');
}, },
/** /**
@ -185,6 +194,7 @@ module.exports = Backbone.View.extend({
var value = this.prValue - 1; var value = this.prValue - 1;
this.model.set('value', value, {avoidStore: 1}) this.model.set('value', value, {avoidStore: 1})
.set('value', value + 1); .set('value', value + 1);
this.elementUpdated();
} }
}, },

170
src/editor/config/config.js

@ -1,4 +1,3 @@
var blkStyle = '.blk-row::after{ content: ""; clear: both; display: block;} .blk-row{padding: 10px;}';
module.exports = { module.exports = {
// Style prefix // Style prefix
stylePrefix: 'gjs-', stylePrefix: 'gjs-',
@ -38,7 +37,7 @@ module.exports = {
width: '100%', width: '100%',
// CSS that could only be seen (for instance, inside the code viewer) // CSS that could only be seen (for instance, inside the code viewer)
protectedCss: '', protectedCss: '*{box-sizing: border-box;} body{margin: 0;}',
// CSS for the iframe which containing the canvas, useful if you need to custom something inside // CSS for the iframe which containing the canvas, useful if you need to custom something inside
// (eg. the style of the selected component) // (eg. the style of the selected component)
@ -59,6 +58,16 @@ module.exports = {
// When enabled, on device change media rules won't be created // When enabled, on device change media rules won't be created
devicePreviewMode: 0, devicePreviewMode: 0,
// THe condition to use for media queries, eg. 'max-width'
// Comes handy for mobile-first cases
mediaCondition: 'max-width',
// Starting tag for variable inside scripts in Components
tagVarStart: '{[ ',
// Ending tag for variable inside scripts in Components
tagVarEnd: ' ]}',
// This option makes available custom component types also for loaded // This option makes available custom component types also for loaded
// elements inside canvas // elements inside canvas
loadCompsOnRender: 1, loadCompsOnRender: 1,
@ -104,18 +113,21 @@ module.exports = {
//Configurations for Device Manager //Configurations for Device Manager
deviceManager: { deviceManager: {
'devices': [{ devices: [{
name: 'Desktop', name: 'Desktop',
width: '', width: '',
},{ },{
name: 'Tablet', name: 'Tablet',
width: '992px', width: '768px',
widthMedia: '992px',
},{ },{
name: 'Mobile landscape', name: 'Mobile landscape',
width: '768px', width: '568px',
widthMedia: '768px',
},{ },{
name: 'Mobile portrait', name: 'Mobile portrait',
width: '480px', width: '320px',
widthMedia: '480px',
}], }],
}, },
@ -156,150 +168,6 @@ module.exports = {
}, },
//Configurations for Block Manager //Configurations for Block Manager
blockManager: { blockManager: {},
blocks: [{
id: 'b1',
label: '1 Block',
category: 'Basic',
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',
category: 'Basic',
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',
category: 'Basic',
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',
category: 'Basic',
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',
category: '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',
category: 'Typography',
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',
category: 'Section',
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'},
category: 'Basic',
content: {
type:'text',
content:'Insert your text here',
style: {padding: '10px' },
activeOnRender: 1
},
},{
id: 'image',
label: 'Image',
category: 'Basic',
attributes: {class:'gjs-fonts gjs-f-image'},
content: {
style: {color: 'black'},
type:'image',
activeOnRender: 1
},
},{
id: 'quo',
label: 'Quote',
category: 'Typography',
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:'fa fa-quote-right'}
},{
id: 'link',
label: 'Link',
category: 'Basic',
attributes: {class:'fa fa-link'},
content: {
type:'link',
content:'Link',
style:{color: '#d983a6'}
},
},{
id: 'map',
label: 'Map',
category: 'Extra',
attributes: {class:'fa fa-map-o'},
content: {
type: 'map',
style: {height: '350px'}
},
},{
id: 'video',
label: 'Video',
category: 'Basic',
attributes: {class:'fa fa-youtube-play'},
content: {
type: 'video',
src: 'img/video2.webm',
style: {
height: '350px',
width: '615px',
}
},
}/*,{
id: 'table',
label: 'Table',
attributes: {class:'fa fa-table'},
content: {
type: 'table',
columns: 3,
rows: 5,
style: {height: '150px', width: '100%'}
},
}*/],
},
}; };

46
src/editor/index.js

@ -19,6 +19,7 @@
* * [getContainer](#getcontainer) * * [getContainer](#getcontainer)
* * [refresh](#refresh) * * [refresh](#refresh)
* * [on](#on) * * [on](#on)
* * [off](#off)
* * [trigger](#trigger) * * [trigger](#trigger)
* * [render](#render) * * [render](#render)
* *
@ -35,6 +36,10 @@
* component:update:{propertyName} - Listen any property change * component:update:{propertyName} - Listen any property change
* component:styleUpdate - Triggered when the style of the component is updated * component:styleUpdate - Triggered when the style of the component is updated
* component:styleUpdate:{propertyName} - Listen for a specific style property change * component:styleUpdate:{propertyName} - Listen for a specific style property change
* styleManager:change - Triggered on style property change from new selected component, the view of the property is passed as an argument to the callback
* styleManager:change:{propertyName} - As above but for a specific style property
* storage:load - Triggered when something was loaded from the storage, loaded object passed as an argumnet
* storage:store - Triggered when something is stored to the storage, stored object passed as an argumnet
* canvasScroll - Triggered when the canvas is scrolled * canvasScroll - Triggered when the canvas is scrolled
* run:{commandName} - Triggered when some command is called to run (eg. editor.runCommand('preview')) * run:{commandName} - Triggered when some command is called to run (eg. editor.runCommand('preview'))
* stop:{commandName} - Triggered when some command is called to stop (eg. editor.stopCommand('preview')) * stop:{commandName} - Triggered when some command is called to stop (eg. editor.stopCommand('preview'))
@ -257,6 +262,10 @@ module.exports = config => {
/** /**
* Add components * Add components
* @param {Array<Object>|Object|string} components HTML string or components model * @param {Array<Object>|Object|string} components HTML string or components model
* @param {Object} opts Options
* @param {Boolean} [opts.avoidUpdateStyle=false] If the HTML string contains styles,
* by default, they will be created and, if already exist, updated. When this option
* is true, styles already created will not be updated.
* @return {Model|Array<Model>} * @return {Model|Array<Model>}
* @example * @example
* editor.addComponents('<div class="cls">New component</div>'); * editor.addComponents('<div class="cls">New component</div>');
@ -267,8 +276,8 @@ module.exports = config => {
* content: 'New component' * content: 'New component'
* }); * });
*/ */
addComponents(components) { addComponents(components, opts) {
return this.getComponents().add(components); return this.getComponents().add(components, opts);
}, },
/** /**
@ -298,12 +307,28 @@ module.exports = config => {
/** /**
* Returns selected component, if there is one * Returns selected component, if there is one
* @return {grapesjs.Component} * @return {Model}
*/ */
getSelected() { getSelected() {
return em.getSelected(); return em.getSelected();
}, },
/**
* Get stylable entity from the selected component.
* If you select the component without classes the entity is the Component
* itself and all changes will go inside its 'style' property. Otherwise,
* if the selected component has one or more classes, the function will
* return the corresponding CSS Rule
* @return {Model}
*/
getSelectedToStyle() {
let selected = em.getSelected();
if (selected) {
return this.StyleManager.getModelToStyle(selected);
}
},
/** /**
* Set device to the editor. If the device exists it will * Set device to the editor. If the device exists it will
* change the canvas to the proper width * change the canvas to the proper width
@ -376,10 +401,11 @@ module.exports = config => {
/** /**
* Load data from the current storage * Load data from the current storage
* @param {Function} clb Callback function
* @return {Object} Stored data * @return {Object} Stored data
*/ */
load() { load(clb) {
return em.load(); return em.load(clb);
}, },
/** /**
@ -448,6 +474,16 @@ module.exports = config => {
return em.on(event, callback); return em.on(event, callback);
}, },
/**
* Detach event
* @param {string} event Event name
* @param {Function} callback Callback function
* @return {this}
*/
off(event, callback) {
return em.off(event, callback);
},
/** /**
* Trigger event * Trigger event
* @param {string} event Event to trigger * @param {string} event Event to trigger

87
src/editor/model/Editor.js

@ -21,6 +21,7 @@ require('trait_manager'),
var Backbone = require('backbone'); var Backbone = require('backbone');
var UndoManager = require('backbone-undo'); var UndoManager = require('backbone-undo');
var key = require('keymaster'); var key = require('keymaster');
var timedInterval;
module.exports = Backbone.Model.extend({ module.exports = Backbone.Model.extend({
@ -53,12 +54,25 @@ module.exports = Backbone.Model.extend({
M.onLoad(); M.onLoad();
}); });
this.loadOnStart();
this.initUndoManager(); this.initUndoManager();
this.on('change:selectedComponent', this.componentSelected, this); this.on('change:selectedComponent', this.componentSelected, this);
this.on('change:changesCount', this.updateBeforeUnload, this); this.on('change:changesCount', this.updateBeforeUnload, this);
}, },
/**
* Load on start if it was requested
* @private
*/
loadOnStart() {
const sm = this.get('StorageManager');
if (sm && sm.getConfig().autoload) {
this.load();
}
},
/** /**
* Set the alert before unload in case it's requested * Set the alert before unload in case it's requested
* and there are unsaved changes * and there are unsaved changes
@ -135,28 +149,33 @@ module.exports = Backbone.Model.extend({
* @private * @private
*/ */
listenRule(model) { listenRule(model) {
this.stopListening(model, 'change:style', this.ruleUpdated); this.stopListening(model, 'change:style', this.componentsUpdated);
this.listenTo(model, 'change:style', this.ruleUpdated); this.listenTo(model, 'change:style', this.componentsUpdated);
}, },
/** /**
* Triggered when rule is updated * Triggered when something in components is changed
* @param {Object} model * @param {Object} model
* @param {Mixed} val Value * @param {Mixed} val Value
* @param {Object} opt Options * @param {Object} opt Options
* @private * @private
* */ * */
ruleUpdated(model, val, opt) { componentsUpdated(model, val, opt) {
var count = this.get('changesCount') + 1, timedInterval && clearInterval(timedInterval);
avSt = opt ? opt.avoidStore : 0; timedInterval = setTimeout(() => {
this.set('changesCount', count); var count = this.get('changesCount') + 1;
var stm = this.get('StorageManager'); var avoidStore = opt ? opt.avoidStore : 0;
if(stm.isAutosave() && count < stm.getStepsBeforeSave()) var stm = this.get('StorageManager');
return; this.set('changesCount', count);
if(!avSt){ if (!stm.isAutosave() || count < stm.getStepsBeforeSave()) {
this.store(); return;
} }
if (!avoidStore) {
this.store();
}
}, 0);
}, },
/** /**
@ -218,27 +237,6 @@ module.exports = Backbone.Model.extend({
} }
}, },
/**
* Triggered when components are updated
* @param {Object} model
* @param {Mixed} val Value
* @param {Object} opt Options
* @private
* */
componentsUpdated(model, val, opt) {
var updatedCount = this.get('changesCount') + 1,
avSt = opt ? opt.avoidStore : 0;
this.set('changesCount', updatedCount);
var stm = this.get('StorageManager');
if(stm.isAutosave() && updatedCount < stm.getStepsBeforeSave()){
return;
}
if(!avSt){
this.store();
}
},
/** /**
* Callback on component selection * Callback on component selection
* @param {Object} Model * @param {Object} Model
@ -428,9 +426,11 @@ module.exports = Backbone.Model.extend({
store[el] = obj[el]; store[el] = obj[el];
}); });
sm.store(store, clb); sm.store(store, () => {
this.set('changesCount', 0); clb && clb();
this.trigger('storage:store', store); this.set('changesCount', 0);
this.trigger('storage:store', store);
});
return store; return store;
}, },
@ -442,7 +442,7 @@ module.exports = Backbone.Model.extend({
* @private * @private
*/ */
load(clb) { load(clb) {
var result = this.getCacheLoad(1); var result = this.getCacheLoad(1, clb);
this.get('storables').forEach(m => { this.get('storables').forEach(m => {
m.load(result); m.load(result);
}); });
@ -452,10 +452,11 @@ module.exports = Backbone.Model.extend({
/** /**
* Returns cached load * Returns cached load
* @param {Boolean} force Force to reload * @param {Boolean} force Force to reload
* @param {Function} clb Callback function
* @return {Object} * @return {Object}
* @private * @private
*/ */
getCacheLoad(force) { getCacheLoad(force, clb) {
var f = force ? 1 : 0; var f = force ? 1 : 0;
if(this.cacheLoad && !f) if(this.cacheLoad && !f)
return this.cacheLoad; return this.cacheLoad;
@ -474,8 +475,10 @@ module.exports = Backbone.Model.extend({
}); });
}); });
this.cacheLoad = sm.load(load); this.cacheLoad = sm.load(load, (loaded) => {
this.trigger('storage:load', this.cacheLoad); clb && clb(loaded);
this.trigger('storage:load', loaded);
});
return this.cacheLoad; return this.cacheLoad;
}, },

4
src/editor/view/EditorView.js

@ -23,8 +23,10 @@ module.exports = Backbone.View.extend({
if (config.clearOnRender) { if (config.clearOnRender) {
dComps.clear(); dComps.clear();
} }
dComps.getComponents().add(config.components); dComps.getComponents().reset(config.components);
model.loadOnStart();
um.clear(); um.clear();
// This will init loaded components
dComps.onLoad(); dComps.onLoad();
} }

21
src/grapesjs/index.js

@ -48,17 +48,18 @@
c.el = document.querySelector(els); c.el = document.querySelector(els);
var editor = new Editor(c).init(); var editor = new Editor(c).init();
// Execute all plugins // Execute plugins
var plugs = plugins.getAll(); var plugs = plugins.getAll();
for (var id in plugs){
// Check if plugin is requested c.plugins.forEach((pluginId) => {
if(c.plugins.indexOf(id) < 0) let plugin = plugins.get(pluginId);
continue;
if (plugin) {
var opts = c.pluginsOpts[id] || {}; plugin(editor, c.pluginsOpts[pluginId] || {});
var plug = plugins.get(id); } else {
plug(editor, opts); console.warn(`Plugin ${pluginId} not found`);
} }
});
if(c.autorender) if(c.autorender)
editor.render(); editor.render();

30
src/navigator/view/ItemView.js

@ -1,4 +1,5 @@
var Backbone = require('backbone'); var Backbone = require('backbone');
var ComponentView = require('dom_components/view/ComponentView');
var ItemsView; var ItemsView;
module.exports = Backbone.View.extend({ module.exports = Backbone.View.extend({
@ -12,6 +13,7 @@ module.exports = Backbone.View.extend({
<div class="<%= prefix %>title <%= addClass %>"> <div class="<%= prefix %>title <%= addClass %>">
<i id="<%= prefix %>caret" class="fa fa-chevron-right <%= caretCls %>"></i> <i id="<%= prefix %>caret" class="fa fa-chevron-right <%= caretCls %>"></i>
<i class="fa fa-pencil <%= editBtnCls %>"></i> <i class="fa fa-pencil <%= editBtnCls %>"></i>
<%= icon %>
<input class="<%= ppfx %>no-app <%= inputNameCls %>" value="<%= title %>" readonly> <input class="<%= ppfx %>no-app <%= inputNameCls %>" value="<%= title %>" readonly>
</div> </div>
</div> </div>
@ -42,7 +44,6 @@ module.exports = Backbone.View.extend({
this.inputNameCls = this.ppfx + 'nav-comp-name'; this.inputNameCls = this.ppfx + 'nav-comp-name';
this.caretCls = this.ppfx + 'nav-item-caret'; this.caretCls = this.ppfx + 'nav-item-caret';
this.titleCls = this.pfx + 'title'; this.titleCls = this.pfx + 'title';
this.customNameProp = 'custom-name';
this.events = {}; this.events = {};
this.events['click > #'+this.pfx+'btn-eye'] = 'toggleVisibility'; this.events['click > #'+this.pfx+'btn-eye'] = 'toggleVisibility';
this.events['click .' + this.caretCls] = 'toggleOpening'; this.events['click .' + this.caretCls] = 'toggleOpening';
@ -76,7 +77,7 @@ module.exports = Backbone.View.extend({
e.stopPropagation(); e.stopPropagation();
var inputName = this.getInputName(); var inputName = this.getInputName();
inputName.readOnly = true; inputName.readOnly = true;
this.model.set(this.customNameProp, inputName.value); this.model.set('custom-name', inputName.value);
}, },
/** /**
@ -174,18 +175,7 @@ module.exports = Backbone.View.extend({
* @param Event * @param Event
* */ * */
updateStatus(e) { updateStatus(e) {
var status = this.model.get('status'); ComponentView.prototype.updateStatus.apply(this, arguments);
var cls = this.pfx + 'selected';
var el = this.$el;
switch(status) {
case 'selected':
el.addClass(cls);
break;
case 'moving':
break;
default:
el.removeClass(cls);
}
}, },
/** /**
@ -265,12 +255,14 @@ module.exports = Backbone.View.extend({
}, },
render() { render() {
let model = this.model;
var pfx = this.pfx; var pfx = this.pfx;
var vis = this.isVisible(); var vis = this.isVisible();
var count = this.countChildren(this.model); var count = this.countChildren(model);
this.$el.html( this.template({ this.$el.html( this.template({
title: this.model.get(this.customNameProp) || this.model.getName(), title: model.getName(),
icon: model.getIcon(),
addClass: (count ? '' : pfx+'no-chld'), addClass: (count ? '' : pfx+'no-chld'),
editBtnCls: this.editBtnCls, editBtnCls: this.editBtnCls,
inputNameCls: this.inputNameCls, inputNameCls: this.inputNameCls,
@ -285,15 +277,15 @@ module.exports = Backbone.View.extend({
if(typeof ItemsView == 'undefined') if(typeof ItemsView == 'undefined')
ItemsView = require('./ItemsView'); ItemsView = require('./ItemsView');
this.$components = new ItemsView({ this.$components = new ItemsView({
collection : this.model.components, collection : model.components,
config: this.config, config: this.config,
sorter: this.sorter, sorter: this.sorter,
opened: this.opt.opened, opened: this.opt.opened,
parent: this.model parent: model
}).render().$el; }).render().$el;
this.$el.find('.'+ pfx +'children').html(this.$components); this.$el.find('.'+ pfx +'children').html(this.$components);
this.$caret = this.$el.find('> .' + pfx + 'title-c > .' + pfx + 'title > #' + pfx + 'caret'); this.$caret = this.$el.find('> .' + pfx + 'title-c > .' + pfx + 'title > #' + pfx + 'caret');
if(!this.model.get('draggable') || !this.config.sortable){ if(!model.get('draggable') || !this.config.sortable){
this.$el.find('> #' + pfx + 'move').detach(); this.$el.find('> #' + pfx + 'move').detach();
} }
if(!vis) if(!vis)

5
src/navigator/view/ItemsView.js

@ -22,6 +22,7 @@ module.exports = Backbone.View.extend({
containerSel: '.' + pfx + 'items', containerSel: '.' + pfx + 'items',
itemSel: '.' + pfx + 'item', itemSel: '.' + pfx + 'item',
ppfx: this.ppfx, ppfx: this.ppfx,
ignoreViewChildren: 1,
pfx, pfx,
nested: 1 nested: 1
}); });
@ -34,6 +35,10 @@ module.exports = Backbone.View.extend({
// For the sorter // For the sorter
this.$el.data('collection', this.collection); this.$el.data('collection', this.collection);
if (this.parent) {
this.$el.data('model', this.parent);
}
}, },
/** /**

7
src/selector_manager/view/ClassTagView.js

@ -94,14 +94,17 @@ module.exports = Backbone.View.extend({
* @private * @private
*/ */
updateStatus() { updateStatus() {
var chkOn = 'fa-check-square-o';
var chkOff = 'fa-square-o';
if(!this.$chk) if(!this.$chk)
this.$chk = this.$el.find('#' + this.pfx + 'checkbox'); this.$chk = this.$el.find('#' + this.pfx + 'checkbox');
if(this.model.get('active')){ if(this.model.get('active')){
this.$chk.removeClass('fa-circle-o').addClass('fa-dot-circle-o'); this.$chk.removeClass(chkOff).addClass(chkOn);
this.$el.removeClass('opac50'); this.$el.removeClass('opac50');
}else{ }else{
this.$chk.removeClass('fa-dot-circle-o').addClass('fa-circle-o'); this.$chk.removeClass(chkOn).addClass(chkOff);
this.$el.addClass('opac50'); this.$el.addClass('opac50');
} }
}, },

5
src/storage_manager/index.js

@ -196,6 +196,7 @@ module.exports = () => {
/** /**
* Load resource from the current storage by keys * Load resource from the current storage by keys
* @param {string|Array<string>} keys Keys to load * @param {string|Array<string>} keys Keys to load
* @param {Function} clb Callback function
* @return {Object|null} Loaded resources * @return {Object|null} Loaded resources
* @example * @example
* var data = storageManager.load(['item1', 'item2']); * var data = storageManager.load(['item1', 'item2']);
@ -203,7 +204,7 @@ module.exports = () => {
* var data2 = storageManager.load('item1'); * var data2 = storageManager.load('item1');
* // data2 -> {item1: value1} * // data2 -> {item1: value1}
* */ * */
load(keys) { load(keys, clb) {
var st = this.get(this.getCurrent()); var st = this.get(this.getCurrent());
var keysF = []; var keysF = [];
var result = {}; var result = {};
@ -214,7 +215,7 @@ module.exports = () => {
for (var i = 0, len = keys.length; i < len; i++) for (var i = 0, len = keys.length; i < len; i++)
keysF.push(c.id + keys[i]); keysF.push(c.id + keys[i]);
var loaded = st ? st.load(keysF) : {}; var loaded = st ? st.load(keysF, clb) : {};
// Restore keys name // Restore keys name
for (var itemKey in loaded){ for (var itemKey in loaded){

6
src/storage_manager/model/LocalStorage.js

@ -23,7 +23,7 @@ module.exports = Backbone.Model.extend({
/** /**
* @private * @private
*/ */
load(keys) { load(keys, clb) {
this.checkStorageEnvironment(); this.checkStorageEnvironment();
var result = {}; var result = {};
@ -33,6 +33,10 @@ module.exports = Backbone.Model.extend({
result[keys[i]] = value; result[keys[i]] = value;
} }
if (typeof clb == 'function') {
clb(result);
}
return result; return result;
}, },

18
src/storage_manager/model/RemoteStorage.js

@ -24,7 +24,7 @@ module.exports = Backbone.Model.extend({
for(var key in params) for(var key in params)
fd[key] = params[key]; fd[key] = params[key];
$.ajax({ let req = $.ajax({
url: this.get('urlStore'), url: this.get('urlStore'),
beforeSend: this.get('beforeSend'), beforeSend: this.get('beforeSend'),
complete: this.get('onComplete'), complete: this.get('onComplete'),
@ -32,7 +32,10 @@ module.exports = Backbone.Model.extend({
dataType: 'json', dataType: 'json',
contentType: this.get('contentTypeJson') ? 'application/json; charset=utf-8': 'x-www-form-urlencoded', contentType: this.get('contentTypeJson') ? 'application/json; charset=utf-8': 'x-www-form-urlencoded',
data: this.get('contentTypeJson') ? JSON.stringify(fd): fd, data: this.get('contentTypeJson') ? JSON.stringify(fd): fd,
}).always(() => { });
// Assign always callback when possible
req && req.always && req.always(() => {
if (typeof clb == 'function') { if (typeof clb == 'function') {
clb(); clb();
} }
@ -42,7 +45,7 @@ module.exports = Backbone.Model.extend({
/** /**
* @private * @private
*/ */
load(keys) { load(keys, clb) {
var result = {}, var result = {},
fd = {}, fd = {},
params = this.get('params'); params = this.get('params');
@ -52,7 +55,7 @@ module.exports = Backbone.Model.extend({
fd.keys = keys; fd.keys = keys;
$.ajax({ let req = $.ajax({
url: this.get('urlLoad'), url: this.get('urlLoad'),
beforeSend: this.get('beforeSend'), beforeSend: this.get('beforeSend'),
complete: this.get('onComplete'), complete: this.get('onComplete'),
@ -62,6 +65,13 @@ module.exports = Backbone.Model.extend({
}).done(d => { }).done(d => {
result = d; result = d;
}); });
// Assign always callback when possible
req && req.always && req.always((res) => {
if (typeof clb == 'function') {
clb(res);
}
});
return result; return result;
}, },

21
src/style_manager/config/config.js

@ -9,4 +9,25 @@ module.exports = {
// Hide the property in case it's not stylable for the // Hide the property in case it's not stylable for the
// selected component (each component has 'stylable' property) // selected component (each component has 'stylable' property)
hideNotStylable: true, hideNotStylable: true,
// Highlight changed properties of the selected component
highlightChanged: true,
// Highlight computed properties of the selected component
highlightComputed: true,
// Show computed properties of the selected component, if this value
// is set to false, highlightComputed will not take effect
showComputed: true,
// Adds the possibility to clear property value from the target style
clearProperties: false,
// Properties which are valid to be shown as computed
// (Identified as inherited properties: https://developer.mozilla.org/en-US/docs/Web/CSS/inheritance)
validComputed: ['border-collapse', 'border-spacing', 'caption-side', 'color', 'cursor', 'direction', 'empty-cells',
'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight', 'font-size-adjust', 'font-stretch', 'font',
'letter-spacing', 'line-height', 'list-style-image', 'list-style-position', 'list-style-type', 'list-style', 'orphans',
'quotes', 'tab-size', 'text-align', 'text-align-last', 'text-decoration-color', 'text-indent', 'text-justify',
'text-shadow', 'text-transform', 'visibility', 'white-space', 'widows', 'word-break', 'word-spacing', 'word-wrap'],
}; };

2
src/style_manager/model/Layers.js

@ -18,6 +18,6 @@ module.exports = Backbone.Collection.extend({
onReset() { onReset() {
this.idx = 1; this.idx = 1;
}, }
}); });

1
src/style_manager/model/Property.js

@ -17,6 +17,7 @@ module.exports = Backbone.Model.extend({
detached: false, detached: false,
visible: true, visible: true,
functionName: '', functionName: '',
status: '',
properties: [], properties: [],
layers: [], layers: [],
list: [], list: [],

13
src/style_manager/model/PropertyFactory.js

@ -26,13 +26,20 @@ module.exports = () => ({
} }
// Fixed values // Fixed values
switch(prop){ switch(prop) {
case 'margin-top': case 'margin-right': case 'margin-bottom': case 'margin-left': case 'margin-top': case 'margin-right': case 'margin-bottom': case 'margin-left':
case 'padding-top': case 'padding-right': case 'padding-bottom': case 'padding-left': case 'padding-top': case 'padding-right': case 'padding-bottom': case 'padding-left':
case 'width': case 'max-width': case 'min-width': case 'width': case 'max-width': case 'min-width':
case 'height': case 'max-height': case 'min-height': case 'height': case 'max-height': case 'min-height':
obj.fixedValues = ['initial', 'inherit', 'auto']; obj.fixedValues = ['initial', 'inherit', 'auto'];
break; break;
case 'font-size':
obj.fixedValues = ['medium', 'xx-small', 'x-small', 'small', 'large',
'x-large', 'xx-large', 'smaller', 'larger', 'length', 'initial', 'inherit'];
break;
case 'letter-spacing': case 'line-height':
obj.fixedValues = ['normal', 'initial', 'inherit'];
break;
} }
// Type // Type
@ -88,9 +95,9 @@ module.exports = () => ({
} }
// Defaults // Defaults
switch(prop){ switch(prop) {
case 'float': case 'background-color': case 'float': case 'background-color':
case 'background-image': case 'background-image': case 'text-shadow':
obj.defaults = 'none'; obj.defaults = 'none';
break; break;
case 'display': case 'display':

15
src/style_manager/view/LayerView.js

@ -19,21 +19,26 @@ module.exports = Backbone.View.extend({
<div style="clear:both"></div>`), <div style="clear:both"></div>`),
initialize(o) { initialize(o) {
let model = this.model;
this.stackModel = o.stackModel || {}; this.stackModel = o.stackModel || {};
this.config = o.config || {}; this.config = o.config || {};
this.pfx = this.config.stylePrefix || ''; this.pfx = this.config.stylePrefix || '';
this.className = this.pfx + 'layer'; this.className = this.pfx + 'layer';
this.sorter = o.sorter || null; this.sorter = o.sorter || null;
this.listenTo(this.model, 'destroy remove', this.remove); this.listenTo(model, 'destroy remove', this.remove);
this.listenTo(this.model, 'change:value', this.valueChanged); this.listenTo(model, 'change:value', this.valueChanged);
this.listenTo(this.model, 'change:props', this.showProps); this.listenTo(model, 'change:props', this.showProps);
this.events['click #' + this.pfx + 'close-layer'] = 'remove'; this.events['click #' + this.pfx + 'close-layer'] = 'remove';
this.events['mousedown > #' + this.pfx + 'move'] = 'initSorter'; this.events['mousedown > #' + this.pfx + 'move'] = 'initSorter';
if( !this.model.get('preview') ){ if (!model.get('preview')) {
this.$el.addClass(this.pfx + 'no-preview'); this.$el.addClass(this.pfx + 'no-preview');
} }
this.$el.data('model', this.model);
// For the sorter
model.view = this;
model.set({droppable: 0, draggable: 1});
this.$el.data('model', model);
this.delegateEvents(); this.delegateEvents();
}, },

22
src/style_manager/view/LayersView.js

@ -9,23 +9,29 @@ module.exports = Backbone.View.extend({
this.preview = o.preview; this.preview = o.preview;
this.pfx = this.config.stylePrefix || ''; this.pfx = this.config.stylePrefix || '';
this.ppfx = this.config.pStylePrefix || ''; this.ppfx = this.config.pStylePrefix || '';
this.className = this.pfx + 'layers ' + this.ppfx + 'field'; let pfx = this.pfx;
this.listenTo( this.collection, 'add', this.addTo); let ppfx = this.ppfx;
this.listenTo( this.collection, 'deselectAll', this.deselectAll ); let collection = this.collection;
this.listenTo( this.collection, 'reset', this.render); this.className = `${pfx}layers ${ppfx}field`;
this.listenTo(collection, 'add', this.addTo);
this.listenTo(collection, 'deselectAll', this.deselectAll );
this.listenTo(collection, 'reset', this.render);
var em = this.config.em || ''; var em = this.config.em || '';
var utils = em ? em.get('Utils') : ''; var utils = em ? em.get('Utils') : '';
this.sorter = utils ? new utils.Sorter({ this.sorter = utils ? new utils.Sorter({
container: this.el, container: this.el,
containerSel: '.' + this.pfx + 'layers', ignoreViewChildren: 1,
itemSel: '.' + this.pfx + 'layer', containerSel: `.${pfx}layers`,
itemSel: `.${pfx}layer`,
pfx: this.config.pStylePrefix, pfx: this.config.pStylePrefix,
}) : ''; }) : '';
this.$el.data('model', {}); // For the Sorter
this.$el.data('collection', this.collection); collection.view = this;
this.$el.data('model', collection);
this.$el.data('collection', collection);
}, },
/** /**

2
src/style_manager/view/PropertyCompositeView.js

@ -99,7 +99,7 @@ module.exports = PropertyView.extend({
this.props.each((prop, index) => { this.props.each((prop, index) => {
str += prop.get('defaults') + prop.get('unit') + ' '; str += prop.get('defaults') + prop.get('unit') + ' ';
}); });
return str.replace(/ +$/,''); return this.model.get('defaults') || str.replace(/ +$/,'');
}, },
/** /**

16
src/style_manager/view/PropertyFileView.js

@ -41,14 +41,15 @@ module.exports = PropertyView.extend({
this.$previewBox = this.$el.find('#' + this.pfx + 'preview-box'); this.$previewBox = this.$el.find('#' + this.pfx + 'preview-box');
} }
if(!this.componentValue || this.componentValue == this.defaultValue)
this.setPreviewView(0);
else
this.setPreviewView(1);
this.setValue(this.componentValue, 0); this.setValue(this.componentValue, 0);
}, },
setValue(value, f) {
PropertyView.prototype.setValue.apply(this, arguments);
this.setPreviewView(value && value != this.getDefaultValue());
this.setPreview(value);
},
/** /**
* Change visibility of the preview box * Change visibility of the preview box
* @param bool Visibility * @param bool Visibility
@ -84,11 +85,6 @@ module.exports = PropertyView.extend({
this.$preview.css('background-image', "url(" + url + ")"); this.$preview.css('background-image', "url(" + url + ")");
}, },
/** @inheritdoc */
setValue(value, f) {
PropertyView.prototype.setValue.apply(this, arguments);
this.setPreview(value);
},
/** @inheritdoc */ /** @inheritdoc */
renderTemplate() { renderTemplate() {

3
src/style_manager/view/PropertyIntegerView.js

@ -6,7 +6,8 @@ module.exports = PropertyView.extend({
initialize(options) { initialize(options) {
PropertyView.prototype.initialize.apply(this, arguments); PropertyView.prototype.initialize.apply(this, arguments);
this.listenTo( this.model ,'change:unit', this.valueChanged); this.listenTo(this.model, 'change:unit', this.valueChanged);
this.listenTo(this.model, 'el:change', this.elementUpdated);
}, },
/** /**

20
src/style_manager/view/PropertyStackView.js

@ -28,18 +28,13 @@ module.exports = PropertyCompositeView.extend({
* so we gonna check all props and fine if there is some differences. * so we gonna check all props and fine if there is some differences.
* */ * */
targetUpdated(...args) { targetUpdated(...args) {
if(!this.model.get('detached')) if (!this.model.get('detached')) {
PropertyCompositeView.prototype.targetUpdated.apply(this, args); PropertyCompositeView.prototype.targetUpdated.apply(this, args);
else { } else {
this.checkVisibility(); this.checkVisibility();
this.refreshLayers();
/*
this.model.get('properties').each(function(prop) {
console.log(prop.get('property'), ' - ', prop.get('value'));
});
*/
} }
this.refreshLayers();
}, },
/** /**
@ -115,7 +110,7 @@ module.exports = PropertyCompositeView.extend({
// If detached the value in this case is stacked, eg. substack-prop: 1px, 2px, 3px... // If detached the value in this case is stacked, eg. substack-prop: 1px, 2px, 3px...
if (this.model.get('detached')) { if (this.model.get('detached')) {
var targetValue = propView.getTargetValue(); var targetValue = propView.getTargetValue({ignoreCustomValue: 1});
var valist = (targetValue + '').split(','); var valist = (targetValue + '').split(',');
result = valist[layerIndex]; result = valist[layerIndex];
result = result ? result.trim() : propView.getDefaultValue(); result = result ? result.trim() : propView.getDefaultValue();
@ -283,7 +278,10 @@ module.exports = PropertyCompositeView.extend({
fieldName = 'values'; fieldName = 'values';
a = this.getLayersFromTarget(); a = this.getLayersFromTarget();
} else { } else {
var v = this.getComponentValue(); //var v = this.getComponentValue();
var v = this.getTargetValue();
var vDef = this.getDefaultValue();
v = v == vDef ? '' : v;
if (v) { if (v) {
// Remove spaces inside functions: // Remove spaces inside functions:
// eg: // eg:

205
src/style_manager/view/PropertyView.js

@ -9,12 +9,15 @@ module.exports = Backbone.View.extend({
templateLabel: _.template(` templateLabel: _.template(`
<div class="<%= pfx %>label"> <div class="<%= pfx %>label">
<div class="<%= pfx %>icon <%= icon %>" title="<%= info %>"> <span class="<%= pfx %>icon <%= icon %>" title="<%= info %>">
<%= label %> <%= label %>
</div> </span>
<b class="<%= pfx %>clear">&Cross;</b>
</div>`), </div>`),
events: {'change': 'valueUpdated'}, events: {
'change': 'valueUpdated'
},
initialize(o) { initialize(o) {
this.config = o.config || {}; this.config = o.config || {};
@ -29,8 +32,9 @@ module.exports = Backbone.View.extend({
this.defaultValue = this.model.get('defaults'); this.defaultValue = this.model.get('defaults');
this.property = this.model.get('property'); this.property = this.model.get('property');
this.input = this.$input = null; this.input = this.$input = null;
this.className = this.pfx + 'property'; const pfx = this.pfx;
this.inputHolderId = '#' + this.pfx + 'input-holder'; this.className = pfx + 'property';
this.inputHolderId = '#' + pfx + 'input-holder';
this.sector = this.model.collection && this.model.collection.sector; this.sector = this.model.collection && this.model.collection.sector;
if(!this.model.get('value')) if(!this.model.get('value'))
@ -41,46 +45,155 @@ module.exports = Backbone.View.extend({
this.listenTo(this.model, 'change:value', this.valueChanged); this.listenTo(this.model, 'change:value', this.valueChanged);
this.listenTo(this.model, 'targetUpdated', this.targetUpdated); this.listenTo(this.model, 'targetUpdated', this.targetUpdated);
this.listenTo(this.model, 'change:visible', this.updateVisibility); this.listenTo(this.model, 'change:visible', this.updateVisibility);
this.listenTo(this.model, 'change:status', this.updateStatus);
this.events[`click .${pfx}clear`] = 'clear';
this.delegateEvents();
},
updateStatus() {
const status = this.model.get('status');
const pfx = this.pfx;
const ppfx = this.ppfx;
const config = this.config;
const updatedCls = `${ppfx}active-color`;
const computedCls = `${ppfx}warn-color`;
const labelEl = this.$el.find(`> .${pfx}label`);
const clearStyle = this.getClearEl().style;
labelEl.removeClass(`${updatedCls} ${computedCls}`);
clearStyle.display = 'none';
switch (status) {
case 'updated':
labelEl.addClass(updatedCls);
if (config.clearProperties) {
clearStyle.display = 'inline';
}
break;
case 'computed':
labelEl.addClass(computedCls);
break;
}
},
/**
* Clear the property
*/
clear() {
const target = this.getTargetModel();
target.removeStyle(this.model.get('property'));
this.targetUpdated();
},
/**
* Get clear element
* @return {HTMLElement}
*/
getClearEl() {
return this.el.querySelector(`.${this.pfx}clear`);
}, },
/** /**
* Returns selected target which should have 'style' property * Returns selected target which should have 'style' property
* @deprecated
* @return {Model|null} * @return {Model|null}
*/ */
getTarget() { getTarget() {
if(this.selectedComponent) return this.propTarget && this.propTarget.model;
return this.selectedComponent; },
return this.propTarget ? this.propTarget.model : null;
/**
* Returns Styleable model
* @return {Model|null}
*/
getTargetModel() {
return this.propTarget && this.propTarget.model;
},
/**
* Returns helper Styleable model
* @return {Model|null}
*/
getHelperModel() {
return this.propTarget && this.propTarget.helper;
}, },
/** /**
* Fired when the input value is updated * Fired when the input value is updated
*/ */
valueUpdated() { valueUpdated() {
if(this.$input) this.model.set('value', this.getInputValue());
this.model.set('value', this.getInputValue()); this.elementUpdated();
}, },
/** /**
* Fired when the target is updated * Fired when the element of the property is updated
*/
elementUpdated() {
this.model.set('status', 'updated');
},
/**
* Fired when the target is changed
* */ * */
targetUpdated() { targetUpdated() {
this.selectedComponent = this.propTarget.model; if (!this.checkVisibility()) {
this.helperComponent = this.propTarget.helper; return;
this.checkVisibility(); }
let value = '';
let status = '';
let targetValue = this.getTargetValue({ignoreDefault: 1});
let defaultValue = this.getDefaultValue();
let computedValue = this.getComputedValue();
const config = this.config;
const em = config.em;
const model = this.model;
if (targetValue) {
value = targetValue;
if (config.highlightChanged) {
status = 'updated';
}
} else if (computedValue && config.showComputed &&
computedValue != defaultValue) {
value = computedValue;
if (config.highlightComputed) {
status = 'computed';
}
} else {
value = defaultValue;
status = '';
}
//value = this.tryFetchFromFunction(value);
this.setValue(value, 1);
this.model.set('status', status);
if (em) {
em.trigger('styleManager:change', this);
em.trigger(`styleManager:change:${model.get('property')}`, this);
}
/*
if(this.getTarget()) { if(this.getTarget()) {
if(!this.sameValue()){ if(!this.sameValue()){
this.renderInputRequest(); this.renderInputRequest();
} }
} }*/
}, },
checkVisibility() { checkVisibility() {
var result = 1;
// Check if need to hide the property // Check if need to hide the property
if (this.config.hideNotStylable) { if (this.config.hideNotStylable) {
if (!this.isTargetStylable() || !this.isComponentStylable()) { if (!this.isTargetStylable() || !this.isComponentStylable()) {
this.hide(); this.hide();
result = 0;
} else { } else {
this.show(); this.show();
} }
@ -89,6 +202,8 @@ module.exports = Backbone.View.extend({
this.sector.trigger('updateVisibility'); this.sector.trigger('updateVisibility');
} }
} }
return result;
}, },
/** /**
@ -104,12 +219,11 @@ module.exports = Backbone.View.extend({
/** /**
* Get the value from the selected component of this property * Get the value from the selected component of this property
*
* @return {String} * @return {String}
* */ * */
getComponentValue() { getComponentValue() {
var propModel = this.model; var propModel = this.model;
var target = this.getTarget(); var target = this.getTargetModel();
if(!target) if(!target)
return; return;
@ -147,22 +261,31 @@ module.exports = Backbone.View.extend({
* @return string * @return string
* @private * @private
*/ */
getTargetValue(opts) { getTargetValue(opts = {}) {
var result; var result;
var opt = opts || {};
var model = this.model; var model = this.model;
var target = this.getTarget(); var target = this.getTargetModel();
var customFetchValue = this.customValue;
if (!target) { if (!target) {
return result; return result;
} }
result = target.get('style')[model.get('property')]; result = target.getStyle()[model.get('property')];
if (!result && !opt.ignoreDefault) { if (!result && !opts.ignoreDefault) {
result = this.getDefaultValue(); result = this.getDefaultValue();
} }
if (typeof customFetchValue == 'function' && !opts.ignoreCustomValue) {
let index = model.collection.indexOf(model);
let customValue = customFetchValue(this, index);
if (customValue) {
result = customValue;
}
}
return result; return result;
}, },
@ -175,6 +298,18 @@ module.exports = Backbone.View.extend({
return this.model.get('defaults'); return this.model.get('defaults');
}, },
/**
* Returns computed value
* @return {String}
* @private
*/
getComputedValue() {
let computed = this.propTarget.computed;
const valid = this.config.validComputed;
const property = this.model.get('property');
return computed && valid.indexOf(property) >= 0 && computed[property];
},
/** /**
* Fetch the string from function type value * Fetch the string from function type value
* @param {String} v Function type value * @param {String} v Function type value
@ -190,9 +325,10 @@ module.exports = Backbone.View.extend({
return value; return value;
} }
var start = value.indexOf("(") + 1; var valueStr = value + '';
var end = value.lastIndexOf(")"); var start = valueStr.indexOf("(") + 1;
return value.substring(start, end); var end = valueStr.lastIndexOf(")");
return valueStr.substring(start, end);
}, },
/** /**
@ -274,8 +410,9 @@ module.exports = Backbone.View.extend({
target.set('style', targetStyle, { avoidStore : avSt}); target.set('style', targetStyle, { avoidStore : avSt});
if(this.helperComponent) // Helper exists when is active a State in Style Manager
this.helperComponent.set('style', targetStyle, { avoidStore : avSt}); let helper = this.getHelperModel();
helper && helper.setStyle(targetStyle, {avoidStore: avSt});
}, },
/** /**
@ -346,12 +483,13 @@ module.exports = Backbone.View.extend({
}, },
renderLabel() { renderLabel() {
this.$el.html( this.templateLabel({ let model = this.model;
pfx : this.pfx, this.$el.html(this.templateLabel({
ppfx : this.ppfx, pfx: this.pfx,
icon : this.model.get('icon'), ppfx: this.ppfx,
info : this.model.get('info'), icon: model.get('icon'),
label : this.model.get('name'), info: model.get('info'),
label: model.get('name'),
})); }));
}, },
@ -409,6 +547,7 @@ module.exports = Backbone.View.extend({
this.renderLabel(); this.renderLabel();
this.renderField(); this.renderField();
this.$el.attr('class', this.className); this.$el.attr('class', this.className);
this.updateStatus();
return this; return this;
}, },

23
src/style_manager/view/SectorsView.js

@ -33,22 +33,31 @@ module.exports = Backbone.View.extend({
* @private * @private
*/ */
targetUpdated() { targetUpdated() {
var el = this.target.get('selectedComponent'); var em = this.target;
var el = em.get('selectedComponent');
if(!el) if(!el)
return; return;
var previewMode = this.target.get('Config').devicePreviewMode; const config = em.get('Config');
var previewMode = config.devicePreviewMode;
var classes = el.get('classes'); var classes = el.get('classes');
var pt = this.propTarget; var pt = this.propTarget;
var device = this.target.getDeviceModel(); var device = em.getDeviceModel();
var state = !previewMode ? el.get('state') : ''; var state = !previewMode ? el.get('state') : '';
var mediaText = device && !previewMode && device.get('width')? var widthMedia = device && device.get('widthMedia');
'(max-width: ' + device.get('width') + ')' : ''; var mediaText = device && !previewMode && widthMedia ?
`(${config.mediaCondition}: ${widthMedia})` : '';
var stateStr = state ? `:${state}` : null;
var view = el.view;
pt.helper = null; pt.helper = null;
if (view) {
pt.computed = window.getComputedStyle(view.el, stateStr);
}
if(classes.length){ if(classes.length){
var cssC = this.target.get('CssComposer'); var cssC = em.get('CssComposer');
var valid = _.filter(classes.models, item => item.get('active')); var valid = _.filter(classes.models, item => item.get('active'));
var iContainer = cssC.get(valid, state, mediaText); var iContainer = cssC.get(valid, state, mediaText);
@ -67,7 +76,7 @@ module.exports = Backbone.View.extend({
// If the state is not empty, there should be a helper rule in play // If the state is not empty, there should be a helper rule in play
// The helper rule will get the same style of the iContainer // The helper rule will get the same style of the iContainer
if(state){ if(state){
var clm = this.target.get('SelectorManager'); var clm = em.get('SelectorManager');
var helperClass = clm.add('hc-state'); var helperClass = clm.add('hc-state');
var helperRule = cssC.get([helperClass]); var helperRule = cssC.get([helperClass]);
if(!helperRule) if(!helperRule)

8
src/styles/scss/_gjs_blocks.scss

@ -47,6 +47,14 @@
} }
} }
.#{$app-prefix}block-svg {
width: 61%;
}
.#{$app-prefix}block-svg-path {
@extend .#{$app-prefix}color-main;
}
.#{$app-prefix}block.fa { .#{$app-prefix}block.fa {
font-size: 2em; font-size: 2em;
line-height: 2em; line-height: 2em;

454
src/styles/scss/_gjs_style_manager.scss

@ -0,0 +1,454 @@
.#{$sm-prefix} {
&clear {
font-size: 1rem;
line-height: 0;
cursor: pointer;
}
}
.#{$sm-prefix}close-btn {
display: block;
font-size: 23px;
position: absolute;
cursor: pointer;
right: 5px;
top: 0;
@include opacity(0.2);
&:hover {
@include opacity(0.7);
}
}
.#{$sm-prefix}header {
font-size: 11px;
font-weight: lighter;
padding: 10px;
}
.#{$sm-prefix}sector {
clear: both;
font-weight: lighter;
text-align: left;
##{$sm-prefix}caret {
padding-right: 5px;
font-size: 11px;
}
&.#{$sm-prefix}open {
@extend .#{$app-prefix}category-open;
}
.#{$sm-prefix}title {
@extend .#{$app-prefix}category-title;
}
.#{$sm-prefix}label {
margin: 5px 5px 2px 0;
}
/* ------------------Field-------------------- */
.#{$sm-prefix}field {
width: 100%;
position: relative;
input,
select {
background-color: transparent;
color: $mainLhlColor;
border: none;
width: 100%;
}
input {
box-sizing: border-box;
}
select {
position: relative;
z-index: 1;
@include appearance(none);
&::-ms-expand {
display: none;
}
}
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 $mainLhlColor;
}
input:focus,
select:focus {
outline: none;
}
.#{$sm-prefix}unit {
position: absolute;
right: 10px;
top: 3px;
font-size: 10px;
color: $mainLhlColor;
cursor: pointer;
}
.#{$clm-prefix}sel-arrow,
.#{$sm-prefix}int-arrows,
.#{$sm-prefix}sel-arrow {
height: 100%;
width: 9px;
position: absolute;
right: 0;
top: 0;
cursor: ns-resize;
}
.#{$sm-prefix}sel-arrow {
cursor: pointer;
}
.#{$clm-prefix}d-s-arrow,
.#{$sm-prefix}d-arrow,
.#{$sm-prefix}d-s-arrow,
.#{$sm-prefix}u-arrow {
position: absolute;
height: 0;
width: 0;
border-left: 3px solid transparent;
border-right: 4px solid transparent;
cursor: pointer;
}
.#{$sm-prefix}u-arrow {
border-bottom: 4px solid $mainLhlColor;
top: 4px;
}
.#{$clm-prefix}d-s-arrow,
.#{$sm-prefix}d-arrow,
.#{$sm-prefix}d-s-arrow {
border-top: 4px solid $mainLhlColor;
bottom: 4px;
}
.#{$clm-prefix}d-s-arrow,
.#{$sm-prefix}d-s-arrow {
bottom: 7px;
}
&.#{$sm-prefix}color,
&.#{$sm-prefix}input,
&.#{$sm-prefix}integer,
&.#{$sm-prefix}list,
&.#{$sm-prefix}select {
background-color: $mainDkColor;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 1px 1px 0 $mainLhColor;
color: $mainLhlColor;
border-radius: 2px;
box-sizing: border-box;
padding: 0 5px;
}
&.#{$sm-prefix}composite {
@extend .#{$app-prefix}color-main;
border-radius: 2px;
}
&.#{$sm-prefix}select {
padding: 0;
}
&.#{$sm-prefix}select select {
height: 20px;
}
&.#{$sm-prefix}select option {
padding: 3px 0;
}
&.#{$sm-prefix}composite {
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.25);
}
&.#{$sm-prefix}list {
width: auto;
padding: 0;
overflow: hidden;
float: left;
input {
display: none;
}
label {
cursor: pointer;
padding: 5px;
display: block;
}
.#{$sm-prefix}radio:checked + label {
background-color: rgba(255, 255, 255, 0.2);
}
.#{$sm-prefix}icon {
background-repeat: no-repeat;
background-position: center;
text-shadow: none;
line-height: normal;
//padding: 5px 19px;
}
}
&.#{$sm-prefix}integer select {
width: auto;
padding: 0;
}
}
/* ------------------END Field-------------------- */
.#{$sm-prefix}list .#{$sm-prefix}el {
float: left;
border-left: 1px solid $mainDkColor;
&:first-child {
border: none;
}
&:hover {
background: $mainDkColor;
}
}
.#{$sm-prefix}properties {
font-size: 11px;
padding: 10px 5px;
}
/* ------------------Property-------------------- */
.#{$sm-prefix}property {
box-sizing: border-box;
float: left;
width: 50%;
margin-bottom: 5px;
padding: 0 5px;
&.#{$sm-prefix}composite,
&.#{$sm-prefix}file,
&.#{$sm-prefix}list,
&.#{$sm-prefix}stack {
width: 100%;
}
.#{$sm-prefix}btn {
background-color: lighten($mainDkColor, 13%);
border-radius: 2px;
box-shadow: 1px 1px 0 lighten($mainDkColor, 2%), 1px 1px 0 lighten($mainDkColor, 17%) inset;
padding: 5px;
position: relative;
text-align: center;
height: auto;
width: 100%;
cursor: pointer;
color: $fontColor;
box-sizing: border-box;
text-shadow: -1px -1px 0 $mainDkColor;
border: none;
@include opacity(0.85);
}
.#{$sm-prefix}btn-c {
box-sizing: border-box;
float: left;
width: 100%;
padding: 0 5px;
}
&.#{$sm-prefix}file ##{$sm-prefix}preview-box {
background-color: $lightBorder;
border-radius: 2px;
margin-top: 5px;
position: relative;
overflow: hidden;
&.#{$sm-prefix}show {
border: 1px solid darken($lightBorder, 1%);
padding: 3px 5px;
}
##{$sm-prefix}close {
@extend .#{$sm-prefix}close-btn;
display: block;
}
}
&.#{$sm-prefix}file .#{$sm-prefix}show ##{$sm-prefix}preview-file {
height: 50px;
}
&.#{$sm-prefix}file ##{$sm-prefix}preview-file {
background-size: auto 100%;
background-repeat: no-repeat;
background-position: center center;
}
.#{$sm-prefix}layers {
margin-top: 5px;
min-height: 30px;
}
.#{$sm-prefix}layer {
background-color: rgba(255, 255, 255, 0.055);
border-radius: 2px;
box-shadow: 1px 1px 0 rgba(0, 0, 0, 0.2), 1px 1px 0 rgba(255, 255, 255, 0.055) inset;
margin: 2px;
padding: 7px;
position: relative;
cursor: pointer;
> ##{$sm-prefix}preview-box {
@extend .checker-bg;
height: 15px;
position: absolute;
right: 27px;
top: 6px;
width: 15px;
}
##{$sm-prefix}preview,
##{$sm-prefix}preview-box {
border-radius: 2px;
}
##{$sm-prefix}close-layer {
display: block;
font-size: 23px;
position: absolute;
cursor: pointer;
right: 5px;
top: 0;
@include opacity(0.5);
&:hover {
@include opacity(0.8);
}
}
> ##{$sm-prefix}preview-box ##{$sm-prefix}preview {
background-color: white;
height: 100%;
width: 100%;
background-size: cover !important;
}
&.#{$sm-prefix}active {
background-color: rgba(255, 255, 255, 0.12);
}
&.#{$sm-prefix}no-preview ##{$sm-prefix}preview-box {
display: none;
}
}
}
##{$sm-prefix}text-shadow ##{$sm-prefix}preview::after {
color: #000;
content: "T";
font-weight: 900;
line-height: 17px;
padding: 0 4px;
}
/* ------------------END Property-------------------- */
.#{$sm-prefix}stack .#{$sm-prefix}properties {
padding-top: 5px;
}
.#{$sm-prefix}stack ##{$sm-prefix}add {
@extend .#{$app-prefix}color-main;
background: none;
border: none;
cursor: pointer;
font-size: 22px;
line-height: 10px;
position: absolute;
right: 0;
top: -20px;
opacity: 0.75;
&:hover {
@include opacity(1);
}
}
.#{$sm-prefix}colorp-c {
@extend .#{$app-prefix}bg-main;
height: 100%;
width: 20px;
position: absolute;
right: 0;
top: 0;
box-sizing: border-box;
border-radius: 2px;
padding: 2px;
.#{$app-prefix}checker-bg {
height: 100%;
width: 100%;
border-radius: 1px;
}
}
.#{$sm-prefix}color-picker {
background-color: $fontColor;
cursor: pointer;
height: 16px;
width: 100%;
margin-top: -16px;
box-shadow: 0 0 1px $mainDkColor;
border-radius: 1px;
}
.#{$sm-prefix}btn-upload ##{$sm-prefix}upload {
left: 0;
top: 0;
position: absolute;
width: 100%;
opacity: 0;
cursor: pointer;
}
.#{$sm-prefix}btn-upload ##{$sm-prefix}label {
padding: 2px 0;
}
}
.#{$sm-prefix}layer > ##{$sm-prefix}move {
@include opacity(0.7);
cursor: move;
font-size: 12px;
float: left;
margin: 0 5px 0 0;
&:hover {
@include opacity(0.9);
}
}

365
src/styles/scss/main.scss

@ -88,7 +88,20 @@ $fontV: 20;//random(1000)
transform: $v; transform: $v;
} }
//.#{$app-prefix}fonts {} $colorActive: #71b7f1;
$colorWarn: #ffca6f;
.#{$app-prefix}active {
&-color {
color: $colorActive;
}
}
.#{$app-prefix}warn {
&-color {
color: $colorWarn;
}
}
.#{$app-prefix}fonts::before { .#{$app-prefix}fonts::before {
display: block; display: block;
@ -149,12 +162,14 @@ $fontV: 20;//random(1000)
background-color: $mainColor; background-color: $mainColor;
} }
.#{$app-prefix}color-main{ .#{$app-prefix}color-main {
color: $fontColor; color: $fontColor;
fill: $fontColor;
} }
.#{$app-prefix}color-active{ .#{$app-prefix}color-active {
color: $fontColorActive; color: $fontColorActive;
fill: $fontColorActive;
} }
.#{$app-prefix}bdrag { .#{$app-prefix}bdrag {
@ -528,6 +543,10 @@ ol.example li.placeholder:before {position: absolute;}
/************* Navigator *************/ /************* Navigator *************/
.#{$nv-prefix}selected-parent {
border: 1px solid $colorYell;
}
.#{$nv-prefix}opac50{ .#{$nv-prefix}opac50{
@include opacity(0.50); @include opacity(0.50);
} }
@ -734,345 +753,9 @@ $lightBorder: rgba(255, 255, 255, 0.05);
/********* Style Manager **********/ /********* Style Manager **********/
.#{$sm-prefix}close-btn{ @import "gjs_style_manager";
display:block;
font-size: 23px;
position: absolute;
cursor: pointer;
right: 5px;
top: 0;
@include opacity(0.2);
&:hover{ @include opacity(0.7); }
}
.#{$sm-prefix}header {
font-size: 11px;
font-weight: lighter;
padding: 10px;
}
.#{$sm-prefix}sector {
clear:both;
font-weight: lighter;
text-align:left;
##{$sm-prefix}caret{
padding-right: 5px;
font-size: 11px;
}
&.#{$sm-prefix}open {
@extend .#{$app-prefix}category-open;
}
.#{$sm-prefix}title {
@extend .#{$app-prefix}category-title;
}
.#{$sm-prefix}label {
margin: 5px 5px 2px 0;
}
/*------------------Field--------------------*/
.#{$sm-prefix}field {
width: 100%;
position:relative;
input, select {
background-color: transparent;
color: $mainLhlColor;
border:none;
width: 100%;
}
input {
box-sizing: border-box;
}
select {
position: relative;
z-index:1;
@include appearance(none);
&::-ms-expand { display: none;}
}
select:-moz-focusring{
color: transparent;
text-shadow: 0 0 0 $mainLhlColor;
}
select:focus, input:focus{
outline: none;
}
.#{$sm-prefix}unit {
position: absolute;
right: 10px; top: 3px;
font-size: 10px;
color: $mainLhlColor;
cursor:pointer;
}
.#{$sm-prefix}int-arrows, .#{$sm-prefix}sel-arrow, .#{$clm-prefix}sel-arrow{
height: 100%; width: 9px;
position: absolute;
right: 0; top: 0;
cursor: ns-resize;
}
.#{$sm-prefix}sel-arrow{ cursor:pointer }
.#{$sm-prefix}u-arrow, .#{$sm-prefix}d-arrow, .#{$sm-prefix}d-s-arrow, .#{$clm-prefix}d-s-arrow{
position: absolute;
height: 0; width: 0;
border-left: 3px solid transparent;
border-right: 4px solid transparent;
cursor:pointer;
}
.#{$sm-prefix}u-arrow {
border-bottom: 4px solid $mainLhlColor;
top: 4px;
}
.#{$sm-prefix}d-arrow, .#{$sm-prefix}d-s-arrow, .#{$clm-prefix}d-s-arrow{
border-top: 4px solid $mainLhlColor;
bottom: 4px;
}
.#{$sm-prefix}d-s-arrow, .#{$clm-prefix}d-s-arrow{ bottom: 7px; }
&.#{$sm-prefix}integer, &.#{$sm-prefix}select, &.#{$sm-prefix}list, &.#{$sm-prefix}color, &.#{$sm-prefix}input {
background-color: $mainDkColor;/*353535*/
border: 1px solid rgba(0, 0, 0, 0.1);/*292929*/
box-shadow: 1px 1px 0 $mainLhColor;/*575757*/
color: $mainLhlColor;
border-radius: 2px;
box-sizing: border-box;
padding: 0 5px;
}
&.#{$sm-prefix}composite{
@extend .#{$app-prefix}color-main;
border-radius: 2px;
}
&.#{$sm-prefix}select{ padding:0; }
&.#{$sm-prefix}select select{ height: 20px; }
&.#{$sm-prefix}select option { padding: 3px 0;}
&.#{$sm-prefix}composite{
background-color: rgba(0,0,0,0.1);
border: 1px solid rgba(0, 0, 0, 0.25);
}
&.#{$sm-prefix}list{
width:auto;
padding:0;
overflow: hidden;
float:left;
input{ display:none; }
label{ cursor:pointer; padding: 5px; display:block;}
.#{$sm-prefix}radio:checked + label{
background-color: rgba(255, 255, 255, 0.2);/*5b5b5b*/
}
.#{$sm-prefix}icon{
background-repeat: no-repeat;
background-position:center;
text-shadow: none;
line-height: normal;
//padding: 5px 19px;
}
}
&.#{$sm-prefix}integer select{ width:auto; padding: 0;}
} /********* Blocks **********/
/*------------------END Field--------------------*/
.#{$sm-prefix}list .#{$sm-prefix}el{
float:left;
border-left: 1px solid $mainDkColor;
&:first-child{border:none}
&:hover{background: $mainDkColor; }
}
.#{$sm-prefix}properties {
font-size: 11px;
padding: 10px 5px;
}
/*------------------Property--------------------*/
.#{$sm-prefix}property{
box-sizing: border-box;
float:left; width:50%;
margin-bottom: 5px;
padding: 0 5px;
&.#{$sm-prefix}file, &.#{$sm-prefix}composite, &.#{$sm-prefix}stack, &.#{$sm-prefix}list{
width:100%;
}
.#{$sm-prefix}btn{
background-color: lighten($mainDkColor, 13%);/*#5d5d5d*/
border-radius: 2px;
box-shadow: 1px 1px 0 lighten($mainDkColor, 2%), 1px 1px 0 lighten($mainDkColor, 17%) inset;
padding: 5px;
position: relative;
text-align: center;
height: auto; width: 100%;
cursor: pointer;
color: $fontColor;
box-sizing: border-box;
text-shadow: -1px -1px 0 $mainDkColor;
border: none;
@include opacity(0.85);
}
.#{$sm-prefix}btn-c {
box-sizing: border-box;
float: left;
width: 100%;
padding: 0 5px;
}
&.#{$sm-prefix}file ##{$sm-prefix}preview-box {
background-color: $lightBorder;
border-radius: 2px;
margin-top: 5px;
position:relative;
overflow: hidden;
&.#{$sm-prefix}show{
border: 1px solid darken($lightBorder,1%);
padding: 3px 5px;
}
##{$sm-prefix}close{
@extend .#{$sm-prefix}close-btn;
display:block;
}
}
&.#{$sm-prefix}file .#{$sm-prefix}show ##{$sm-prefix}preview-file{ height: 50px;}
&.#{$sm-prefix}file ##{$sm-prefix}preview-file {
background-size: auto 100%;
background-repeat: no-repeat;
background-position: center center;
}
.#{$sm-prefix}layers {
margin-top: 5px;
min-height: 30px;
}
.#{$sm-prefix}layer {
background-color: rgba(255, 255, 255, 0.055);
border-radius: 2px;
box-shadow: 1px 1px 0 rgba(0,0,0,0.2), 1px 1px 0 rgba(255, 255, 255, 0.055) inset;
margin: 2px;
padding: 7px;
position: relative;
cursor: pointer;
> ##{$sm-prefix}preview-box {
@extend .checker-bg;
height: 15px;
position: absolute;
right: 27px;
top: 6px;
width: 15px;
}
##{$sm-prefix}preview-box, ##{$sm-prefix}preview{
border-radius:2px;
}
##{$sm-prefix}close-layer{
display:block;
font-size: 23px;
position: absolute;
cursor: pointer;
right: 5px;
top: 0;
@include opacity(0.5);
&:hover{
@include opacity(0.8);
}
}
> ##{$sm-prefix}preview-box ##{$sm-prefix}preview {
background-color: white;
height: 100%;
width: 100%;
background-size: cover !important;
}
&.#{$sm-prefix}active {
background-color: rgba(255, 255, 255, 0.12);
}
&.#{$sm-prefix}no-preview ##{$sm-prefix}preview-box{
display:none;
}
}
}
##{$sm-prefix}text-shadow ##{$sm-prefix}preview::after {
color: #000;
content: "T";
font-weight: 900;
line-height: 17px;
padding: 0 4px;
}
/*------------------END Property--------------------*/
.#{$sm-prefix}stack .#{$sm-prefix}properties{padding-top: 5px;}
.#{$sm-prefix}stack ##{$sm-prefix}add {
@extend .#{$app-prefix}color-main;
background: none;
border: none;
cursor: pointer;
font-size: 22px;
line-height: 10px;
position: absolute;
right: 0; top: -20px;
opacity: 0.75;
&:hover{ @include opacity(1); }
}
.#{$sm-prefix}colorp-c {
@extend .#{$app-prefix}bg-main;
height: 100%; width: 20px;
position: absolute;
right: 0; top: 0;
box-sizing: border-box;
border-radius: 2px;
padding: 2px;
.#{$app-prefix}checker-bg{
height: 100%; width: 100%;
border-radius: 1px;
}
}
.#{$sm-prefix}color-picker {
background-color: $fontColor;
cursor: pointer;
height: 16px;
width: 100%;
margin-top: -16px;
box-shadow: 0 0 1px $mainDkColor;
border-radius: 1px;
}
.#{$sm-prefix}btn-upload ##{$sm-prefix}upload {
left: 0; top: 0;
position: absolute;
width: 100%;
opacity: 0;
cursor: pointer;
}
.#{$sm-prefix}btn-upload ##{$sm-prefix}label { padding: 2px 0;}
}
.#{$sm-prefix}layer > ##{$sm-prefix}move {
@include opacity(0.7);
cursor: move;
font-size: 12px;
float: left;
margin: 0 5px 0 0;
&:hover{
@include opacity(0.9);
}
}
/********* END Style Manager **********/
@import "gjs_blocks"; @import "gjs_blocks";

339
src/utils/Sorter.js

@ -26,6 +26,8 @@ module.exports = Backbone.View.extend({
this.direction = o.direction || 'v'; // v (vertical), h (horizontal), a (auto) this.direction = o.direction || 'v'; // v (vertical), h (horizontal), a (auto)
this.onMoveClb = o.onMove || ''; this.onMoveClb = o.onMove || '';
this.relative = o.relative || 0; this.relative = o.relative || 0;
this.ignoreViewChildren = o.ignoreViewChildren || 0;
this.ignoreModels = o.ignoreModels || 0;
this.plh = o.placer || ''; this.plh = o.placer || '';
// Frame offset // Frame offset
this.wmargin = o.wmargin || 0; this.wmargin = o.wmargin || 0;
@ -81,11 +83,13 @@ module.exports = Backbone.View.extend({
var sortCls = pfx + 'grabbing'; var sortCls = pfx + 'grabbing';
var emBody = em ? em.get('Canvas').getBody() : ''; var emBody = em ? em.get('Canvas').getBody() : '';
if(active) { if(active) {
em && em.get('Canvas').startAutoscroll();
body.className += ' ' + sortCls; body.className += ' ' + sortCls;
if(em) { if(em) {
emBody.className += ' ' + sortCls; emBody.className += ' ' + sortCls;
} }
} else { } else {
em && em.get('Canvas').stopAutoscroll();
body.className = body.className.replace(sortCls, '').trim(); body.className = body.className.replace(sortCls, '').trim();
if(em) { if(em) {
emBody.className = emBody.className.replace(sortCls, '').trim(); emBody.className = emBody.className.replace(sortCls, '').trim();
@ -108,7 +112,7 @@ module.exports = Backbone.View.extend({
for(var i = 0; i < o.length; i++) { for(var i = 0; i < o.length; i++) {
style += o[i] + ':' + o.getPropertyValue(o[i])+';'; style += o[i] + ':' + o.getPropertyValue(o[i])+';';
} }
clonedEl.style = style; clonedEl.setAttribute('style', style);
clonedEl.className += ' ' + this.pfx + 'bdrag'; clonedEl.className += ' ' + this.pfx + 'bdrag';
document.body.appendChild(clonedEl); document.body.appendChild(clonedEl);
this.dragHelper = clonedEl; this.dragHelper = clonedEl;
@ -132,10 +136,12 @@ module.exports = Backbone.View.extend({
* @param {Event} e * @param {Event} e
*/ */
moveDragHelper(e) { moveDragHelper(e) {
if(!this.dragHelper) { var doc = e.target.ownerDocument;
if(!this.dragHelper || !doc) {
return; return;
} }
var doc = e.target.ownerDocument;
var win = doc.defaultView || doc.parentWindow; var win = doc.defaultView || doc.parentWindow;
var addTop = 0; var addTop = 0;
var addLeft = 0; var addLeft = 0;
@ -216,23 +222,27 @@ module.exports = Backbone.View.extend({
/** /**
* Picking component to move * Picking component to move
* @param {HTMLElement} trg * @param {HTMLElement} src
* */ * */
startSort(trg) { startSort(src) {
this.dropModel = null;
this.moved = 0; this.moved = 0;
this.eV = trg; //this.$document = $([document, trg.ownerDocument]);
if(src && !this.matches(src, this.itemSel + ',' + this.containerSel))
src = this.closest(src, this.itemSel);
if(trg && !this.matches(trg, this.itemSel + ',' + this.containerSel)) this.eV = src;
this.eV = this.closest(trg, this.itemSel);
// Create placeholder if not exists // Create placeholder if not exists
if(!this.plh) { if (!this.plh) {
this.plh = this.createPlaceholder(); this.plh = this.createPlaceholder();
this.getContainerEl().appendChild(this.plh); this.getContainerEl().appendChild(this.plh);
} }
if(this.eV) { if (src) {
this.eV.className += ' ' + this.freezeClass; var srcModel = this.getSourceModel();
srcModel && srcModel.set && srcModel.set('status', 'freezed');
this.$document.on('mouseup', this.endMove); this.$document.on('mouseup', this.endMove);
} }
@ -251,6 +261,60 @@ module.exports = Backbone.View.extend({
this.toggleSortCursor(1); this.toggleSortCursor(1);
}, },
/**
* Get the model from HTMLElement target
* @return {Model|null}
*/
getTargetModel(el) {
let elem = el || this.target;
return $(elem).data('model');
},
/**
* Get the model of the current source element (element to drag)
* @return {Model}
*/
getSourceModel() {
var src = this.eV;
let dropContent = this.dropContent;
let dropModel = this.dropModel;
const em = this.em;
if (dropContent && em) {
if (!dropModel) {
let comps = em.get('DomComponents').getComponents();
let tempModel = comps.add(dropContent, {avoidUpdateStyle: 1});
dropModel = comps.remove(tempModel);
this.dropModel = dropModel instanceof Array ? dropModel[0] : dropModel;
}
return dropModel;
}
if (src) {
return $(src).data('model');
}
},
/**
* Highlight target
* @param {Model|null} model
*/
selectTargetModel(model) {
if (model instanceof Backbone.Collection) {
return;
}
var prevModel = this.targetModel;
if (prevModel) {
prevModel.set('status', '');
}
if (model && model.set) {
model.set('status', 'selected-parent');
this.targetModel = model;
}
},
/** /**
* During move * During move
* @param {Event} e * @param {Event} e
@ -278,6 +342,10 @@ module.exports = Backbone.View.extend({
} }
var dims = this.dimsFromTarget(e.target, rX, rY); var dims = this.dimsFromTarget(e.target, rX, rY);
let targetModel = this.getTargetModel(this.target);
this.selectTargetModel(targetModel);
this.lastDims = dims; this.lastDims = dims;
var pos = this.findPosition(dims, rX, rY); var pos = this.findPosition(dims, rX, rY);
// If there is a significant changes with the pointer // If there is a significant changes with the pointer
@ -361,6 +429,51 @@ module.exports = Backbone.View.extend({
return; return;
}, },
/**
* Check if the target is valid with the actual source
* @param {HTMLElement} trg
* @return {Boolean}
*/
validTarget(trg) {
let srcModel = this.getSourceModel();
let src = srcModel && srcModel.view && srcModel.view.el;
let trgModel = this.getTargetModel(trg);
trg = trgModel && trgModel.view && trgModel.view.el;
let result = {
valid: true,
src,
srcModel,
trg,
trgModel
};
if (!src || !trg) {
result.valid = false;
return result;
}
// Check if the target could accept the source
let droppable = trgModel.get('droppable');
droppable = droppable instanceof Backbone.Collection ? 1 : droppable;
droppable = droppable instanceof Array ? droppable.join(', ') : droppable;
result.dropInfo = droppable;
droppable = typeof droppable === 'string' ? src.matches(droppable) : droppable;
result.droppable = droppable;
// check if the source is draggable in target
let draggable = srcModel.get('draggable');
draggable = draggable instanceof Array ? draggable.join(', ') : draggable;
result.dragInfo = draggable;
draggable = typeof draggable === 'string' ? trg.matches(draggable) : draggable;
result.draggable = draggable;
if (!droppable || !draggable) {
result.valid = false;
}
return result;
},
/** /**
* Get dimensions of nodes relative to the coordinates * Get dimensions of nodes relative to the coordinates
* @param {HTMLElement} target * @param {HTMLElement} target
@ -371,30 +484,39 @@ module.exports = Backbone.View.extend({
dimsFromTarget(target, rX, rY) { dimsFromTarget(target, rX, rY) {
var dims = []; var dims = [];
if (!target) {
return dims;
}
// Select the first valuable target // Select the first valuable target
// TODO: avoid this check for every standard component, if (!target.matches(`${this.itemSel}, ${this.containerSel}`)) {
// which generally is ok
if(!this.matches(target, this.itemSel + ',' + this.containerSel))
target = this.closest(target, this.itemSel); target = this.closest(target, this.itemSel);
}
// If draggable is an array the target will be one of those // If draggable is an array the target will be one of those
if(this.draggable instanceof Array){ if (this.draggable instanceof Array) {
target = this.closest(target, this.draggable.join(',')); target = this.closest(target, this.draggable.join(','));
} }
if(!target) if (!target) {
return dims; return dims;
}
// Check if the target is different from the previous one // Check if the target is different from the previous one
if(this.prevTarget){ if (this.prevTarget && this.prevTarget != target) {
if(this.prevTarget != target){
this.prevTarget = null; this.prevTarget = null;
}
} }
// New target encountered // New target found
if(!this.prevTarget){ if (!this.prevTarget) {
this.targetP = this.closest(target, this.containerSel); this.targetP = this.closest(target, this.containerSel);
// Check if the source is valid with the target
let validResult = this.validTarget(target);
if (!validResult.valid && this.targetP) {
return this.dimsFromTarget(this.targetP, rX, rY);
}
this.prevTarget = target; this.prevTarget = target;
this.prevTargetDim = this.getDim(target); this.prevTargetDim = this.getDim(target);
this.cacheDimsP = this.getChildrenDim(this.targetP); this.cacheDimsP = this.getChildrenDim(this.targetP);
@ -407,13 +529,18 @@ module.exports = Backbone.View.extend({
// Target when I will drop element to sort // Target when I will drop element to sort
this.target = this.prevTarget; this.target = this.prevTarget;
// Generally also on every new target the poiner enters near // Generally also on every new target the poiner enters near
// to borders, so have to to check always // to borders, so have to to check always
if(this.nearBorders(this.prevTargetDim, rX, rY) || if(this.nearBorders(this.prevTargetDim, rX, rY) ||
(!this.nested && !this.cacheDims.length)){ (!this.nested && !this.cacheDims.length)) {
dims = this.cacheDimsP; if (!this.validTarget(this.targetP).valid) {
this.target = this.targetP; return this.dimsFromTarget(this.targetP, rX, rY);
}
dims = this.cacheDimsP;
this.target = this.targetP;
} }
this.lastPos = null; this.lastPos = null;
return dims; return dims;
}, },
@ -428,10 +555,15 @@ module.exports = Backbone.View.extend({
if (this.canvasRelative && this.em) { if (this.canvasRelative && this.em) {
var pos = this.em.get('Canvas').getElementPos(el); var pos = this.em.get('Canvas').getElementPos(el);
top = pos.top; var styles = window.getComputedStyle(el);
left = pos.left; var marginTop = parseFloat(styles['marginTop']);
height = pos.height; var marginBottom = parseFloat(styles['marginBottom']);
width = pos.width; var marginRight = parseFloat(styles['marginRight']);
var marginLeft = parseFloat(styles['marginLeft']);
top = pos.top - marginTop;
left = pos.left - marginLeft;
height = pos.height + marginTop + marginBottom;
width = pos.width + marginLeft + marginRight;
} else { } else {
var o = this.offset(el); var o = this.offset(el);
top = this.relative ? el.offsetTop : o.top - (this.wmargin ? -1 : 1) * this.elT; top = this.relative ? el.offsetTop : o.top - (this.wmargin ? -1 : 1) * this.elT;
@ -450,23 +582,26 @@ module.exports = Backbone.View.extend({
* @param {HTMLELement} el Element root * @param {HTMLELement} el Element root
* @retun {Array} * @retun {Array}
* */ * */
getChildrenDim(elem) { getChildrenDim(trg) {
var dims = []; var dims = [];
if(!elem) if(!trg)
return dims; return dims;
// Get children based on getChildrenContainer // Get children based on getChildrenContainer
var $elem = $(elem); var trgModel = this.getTargetModel(trg);
var elemData = $elem.data('model'); if (trgModel && trgModel.view && !this.ignoreViewChildren) {
if (elemData && elemData.view) { trg = trgModel.view.getChildrenContainer();
elem = elemData.view.getChildrenContainer();
} }
var ch = elem.children; //TODO filter match var ch = trg.children;
for (var i = 0, len = ch.length; i < len; i++) { for (var i = 0, len = ch.length; i < len; i++) {
var el = ch[i]; var el = ch[i];
if(!this.matches(el, this.itemSel))
if (!el.matches(this.itemSel)) {
continue; continue;
}
var dim = this.getDim(el); var dim = this.getDim(el);
var dir = this.direction; var dir = this.direction;
@ -475,12 +610,13 @@ module.exports = Backbone.View.extend({
else if(dir == 'h') else if(dir == 'h')
dir = false; dir = false;
else else
dir = this.isInFlow(el, elem); dir = this.isInFlow(el, trg);
dim.push(dir); dim.push(dir);
dim.push(el); dim.push(el);
dims.push(dim); dims.push(dim);
} }
return dims; return dims;
}, },
@ -623,10 +759,18 @@ module.exports = Backbone.View.extend({
this.$document.off('keydown', this.rollback); this.$document.off('keydown', this.rollback);
this.plh.style.display = 'none'; this.plh.style.display = 'none';
var clsReg = new RegExp('(?:^|\\s)'+this.freezeClass+'(?!\\S)', 'gi'); var clsReg = new RegExp('(?:^|\\s)'+this.freezeClass+'(?!\\S)', 'gi');
if(this.eV) let src = this.eV;
this.eV.className = this.eV.className.replace(clsReg, '');
if (src) {
var srcModel = this.getSourceModel();
if (srcModel && srcModel.set) {
srcModel.set('status', '');
srcModel.set('status', 'selected');
}
}
if(this.moved) if(this.moved)
created = this.move(this.target, this.eV, this.lastPos); created = this.move(this.target, src, this.lastPos);
if(this.plh) if(this.plh)
this.plh.style.display = 'none'; this.plh.style.display = 'none';
@ -634,10 +778,13 @@ module.exports = Backbone.View.extend({
this.onEndMove(created); this.onEndMove(created);
var dragHelper = this.dragHelper; var dragHelper = this.dragHelper;
if(dragHelper) { if(dragHelper) {
dragHelper.remove(); dragHelper.remove();
this.dragHelper = null; this.dragHelper = null;
} }
this.selectTargetModel();
this.toggleSortCursor(); this.toggleSortCursor();
}, },
@ -649,101 +796,63 @@ module.exports = Backbone.View.extend({
* */ * */
move(dst, src, pos) { move(dst, src, pos) {
var em = this.em; var em = this.em;
if (em) em.trigger('component:dragEnd:before', dst, src, pos); em && em.trigger('component:dragEnd:before', dst, src, pos);
var warns = []; var warns = [];
var modelToDrop, modelTemp, created;
var index = pos.index; var index = pos.index;
var model = $(src).data('model'); var modelToDrop, modelTemp, created;
var $dst = $(dst); var validResult = this.validTarget(dst);
var targetModel; var targetCollection = $(dst).data('collection');
var model = validResult.srcModel;
while ($dst.length && !targetModel) { var droppable = validResult.droppable;
targetModel = $dst.data('model'); var draggable = validResult.draggable;
dst = $dst.get(0); var dropInfo = validResult.dropInfo;
var dragInfo = validResult.dragInfo;
if (targetModel && targetModel.view) var dropContent = this.dropContent;
dst = targetModel.view.el; droppable = validResult.trgModel instanceof Backbone.Collection ? 1 : droppable;
if (!targetModel) if (targetCollection && droppable && draggable) {
$dst = $dst.parent();
}
var targetCollection = $dst.data('collection');
// Check if the elemenet is DRAGGABLE to the target
var drag = model && model.get('draggable');
var draggable = typeof drag !== 'undefined' ? drag : 1;
var toDrag = draggable;
if (this.dropContent instanceof Object) {
draggable = this.dropContent.draggable;
draggable = typeof draggable !== 'undefined' ? draggable : 1;
} else if (typeof this.dropContent === 'string' && targetCollection) {
var sandboxOpts = {silent: true};
var sandboxModel = targetCollection.add(this.dropContent, sandboxOpts);
draggable = sandboxModel.get && sandboxModel.get('draggable');
draggable = typeof draggable !== 'undefined' ? draggable : 1;
targetCollection.remove(sandboxModel, sandboxOpts);
}
if(draggable instanceof Array) {
toDrag = draggable.join(', ');
draggable = this.matches(dst, toDrag);
}else if(typeof draggable === 'string') {
toDrag = draggable;
draggable = this.matches(dst, toDrag, 1);
}
// Check if the target could accept the element to be DROPPED inside
var accepted = 1;
var droppable = targetModel && targetModel.get ? targetModel.get('droppable') : 1;
var toDrop = draggable;
if(droppable instanceof Array) {
// When I drag blocks src is the HTMLElement of the block
toDrop = droppable.join(', ');
accepted = this.matches(src, toDrop);
}else if(typeof droppable === 'string') {
toDrop = droppable;
accepted = this.matches(src, toDrop);
}
if(targetCollection && droppable && accepted && draggable) {
index = pos.method === 'after' ? index + 1 : index; index = pos.method === 'after' ? index + 1 : index;
var opts = {at: index, noIncrement: 1}; var opts = {at: index, noIncrement: 1};
if(!this.dropContent){
if (!dropContent) {
modelTemp = targetCollection.add({}, opts); modelTemp = targetCollection.add({}, opts);
if(model)
modelToDrop = model.collection.remove(model);
}else{ if (model) {
modelToDrop = this.dropContent; modelToDrop = model.collection.remove(model);
}
} else {
modelToDrop = dropContent;
opts.silent = false; opts.silent = false;
opts.avoidUpdateStyle = 1;
} }
created = targetCollection.add(modelToDrop, opts); created = targetCollection.add(modelToDrop, opts);
if(!this.dropContent){
if (!dropContent) {
targetCollection.remove(modelTemp); targetCollection.remove(modelTemp);
}else{ } else {
this.dropContent = null; this.dropContent = null;
} }
// This will cause to recalculate children dimensions // This will cause to recalculate children dimensions
this.prevTarget = null; this.prevTarget = null;
} else { } else {
if(!targetCollection){ if (!targetCollection) {
warns.push('target collection not found'); warns.push('Target collection not found');
}
if(!droppable){
warns.push('target is not droppable');
} }
if(!draggable){
warns.push('component not draggable, accepted only by [' + toDrag + ']'); if (!droppable) {
warns.push(`Target is not droppable, accepts [${dropInfo}]`);
} }
if(!accepted){
warns.push('target accepts only [' + toDrop + ']'); if (!draggable) {
warns.push(`Component not draggable, acceptable by [${dragInfo}]`);
} }
console.warn('Invalid target position: ' + warns.join(', ')); console.warn('Invalid target position: ' + warns.join(', '));
} }
if (em) em && em.trigger('component:dragEnd', targetCollection, modelToDrop, warns);
em.trigger('component:dragEnd', targetCollection, modelToDrop, warns);
return created; return created;
}, },

14
test/helper.js

@ -23,6 +23,18 @@ Module.prototype.require = function(name) {
return originalRequire.apply(this, arguments); return originalRequire.apply(this, arguments);
}; };
var localStorage = {
getItem(key) {
return this[key];
},
setItem(key, value) {
this[key] = value;
},
removeItem(key, value) {
delete this[key];
}
};
global.window = window; global.window = window;
global.document = window.document; global.document = window.document;
global.$ = $; global.$ = $;
@ -31,6 +43,8 @@ global.expect = expect;
global.sinon = sinon; global.sinon = sinon;
global.grapesjs = grapesjs; global.grapesjs = grapesjs;
global.Backbone = Backbone; global.Backbone = Backbone;
global.localStorage = localStorage;
global.SVGElement = global.Element;
window.$ = $; window.$ = $;
Backbone.$ = $; Backbone.$ = $;

4
test/main.js

@ -21,4 +21,8 @@ describe('Main', () => {
require(`${path}panels`); require(`${path}panels`);
require(`${path}parser`); require(`${path}parser`);
require(`${path}plugin_manager`); require(`${path}plugin_manager`);
require(`${path}selector_manager`);
require(`${path}storage_manager`);
require(`${path}style_manager`);
require(`${path}trait_manager`);
}); });

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

@ -1,129 +1,120 @@
define((require, exports, module) => { const Selectors = require('selector_manager/model/Selectors');
'use strict'; const ClassTagsView = require('selector_manager/view/ClassTagsView');
var GrapesJS = require('GrapesJS');
var Selectors = require('SelectorManager/model/Selectors'); module.exports = {
var ClassTagsView = require('SelectorManager/view/ClassTagsView'); run() {
describe('E2E tests', () => {
module.exports = {
run() { var instClassTagViewer = ctx => {
describe('E2E tests', () => { var $clm;
var clm = ctx.gjs.editor.get('SelectorManager');
/** if(clm){
* Create tags viewer $clm = new ClassTagsView({
* @param {Object} ctx collection: new Selectors([]),
*/ config: {
var instClassTagViewer = ctx => { em: ctx.gjs.editor
var $clm; },
var clm = ctx.gjs.editor.get('SelectorManager'); }).render();
if(clm){ ctx.$fixture.append($clm.el);
$clm = new ClassTagsView({ }
collection: new Selectors([]), return $clm;
config: { };
em: ctx.gjs.editor
}, before(function () {
}).render(); this.$fixtures = $("#fixtures");
ctx.$fixture.append($clm.el); this.$fixture = $('<div id="SelectorManager-fixture"></div>');
} });
return $clm;
}; beforeEach(function () {
this.gjs = grapesjs.init({
before(function () { stylePrefix: '',
this.$fixtures = $("#fixtures"); storageManager: { autoload: 0, type:'none' },
this.$fixture = $('<div id="SelectorManager-fixture"></div>'); assetManager: {
}); storageType: 'none',
},
beforeEach(function () { container: '#SelectorManager-fixture',
this.gjs = GrapesJS.init({ });
stylePrefix: '', this.$fixture.empty().appendTo(this.$fixtures);
storageManager: { autoload: 0, type:'none' }, this.gjs.render();
assetManager: { });
storageType: 'none',
}, afterEach(function () {
container: '#SelectorManager-fixture', delete this.gjs;
}); });
this.$fixture.empty().appendTo(this.$fixtures);
this.gjs.render(); after(function () {
}); this.$fixture.remove();
});
afterEach(function () {
delete this.gjs; describe('Interaction with Components', () => {
});
beforeEach(function () {
after(function () { this.wrapper = this.gjs.editor.get('DomComponents').getWrapper().get('components');
this.$fixture.remove(); this.$clm = instClassTagViewer(this);
}); });
describe('Interaction with Components', () => { afterEach(function () {
delete this.wrapper;
beforeEach(function () { delete this.$clm;
this.wrapper = this.gjs.editor.get('DomComponents').getWrapper().get('components'); });
this.$clm = instClassTagViewer(this);
}); it('Assign correctly new class to component', function() {
var model = this.wrapper.add({});
afterEach(function () { expect(model.get('classes').length).toEqual(0);
delete this.wrapper; this.gjs.editor.set('selectedComponent', model);
delete this.$clm; this.$clm.addNewTag('test');
}); expect(model.get('classes').length).toEqual(1);
expect(model.get('classes').at(0).get('name')).toEqual('test');
it('Assign correctly new class to component', function() { });
var model = this.wrapper.add({});
model.get('classes').length.should.equal(0); it('Classes from components are correctly imported inside main container', function() {
this.gjs.editor.set('selectedComponent', model); var model = this.wrapper.add([
this.$clm.addNewTag('test'); { classes: ['test11', 'test12', 'test13'] },
model.get('classes').length.should.equal(1); { classes: ['test11', 'test22', 'test22'] },
model.get('classes').at(0).get('name').should.equal('test'); ]);
}); expect(this.gjs.editor.get('SelectorManager').getAll().length).toEqual(4);
});
it('Classes from components are correctly imported inside main container', function() {
var model = this.wrapper.add([ it('Class imported into component is the same model from main container', function() {
{ classes: ['test11', 'test12', 'test13'] }, var model = this.wrapper.add({ classes: ['test1'] });
{ classes: ['test11', 'test22', 'test22'] }, var clModel = model.get('classes').at(0);
]); var clModel2 = this.gjs.editor.get('SelectorManager').getAll().at(0);
this.gjs.editor.get('SelectorManager').getAll().length.should.equal(4); expect(clModel).toEqual(clModel2);
}); });
it('Class imported into component is the same model from main container', function() { it('Can assign only one time the same class on selected component and the class viewer', function() {
var model = this.wrapper.add({ classes: ['test1'] }); var model = this.wrapper.add({});
var clModel = model.get('classes').at(0); this.gjs.editor.set('selectedComponent', model);
var clModel2 = this.gjs.editor.get('SelectorManager').getAll().at(0); this.$clm.addNewTag('test');
clModel.should.deep.equal(clModel2); this.$clm.addNewTag('test');
}); expect(model.get('classes').length).toEqual(1);
expect(model.get('classes').at(0).get('name')).toEqual('test');
it('Can assign only one time the same class on selected component and the class viewer', function() { expect(this.$clm.collection.length).toEqual(1);
var model = this.wrapper.add({}); expect(this.$clm.collection.at(0).get('name')).toEqual('test');
this.gjs.editor.set('selectedComponent', model); });
this.$clm.addNewTag('test');
this.$clm.addNewTag('test'); it('Removing from container removes also from selected component', function() {
model.get('classes').length.should.equal(1); var model = this.wrapper.add({});
model.get('classes').at(0).get('name').should.equal('test'); this.gjs.editor.set('selectedComponent', model);
this.$clm.collection.length.should.equal(1); this.$clm.addNewTag('test');
this.$clm.collection.at(0).get('name').should.equal('test'); this.$clm.getClasses().find('.tag #close').trigger('click')
}); expect(model.get('classes').length).toEqual(0);
});
it('Removing from container removes also from selected component', function() {
var model = this.wrapper.add({}); it("Trigger correctly event on target with new class add", function() {
this.gjs.editor.set('selectedComponent', model); var spy = sinon.spy();
this.$clm.addNewTag('test'); var model = this.wrapper.add({});
this.$clm.getClasses().find('.tag #close').trigger('click') this.gjs.editor.set('selectedComponent', model);
model.get('classes').length.should.equal(0); this.$clm.addNewTag('test');
}); this.gjs.editor.on("targetClassAdded", spy);
this.$clm.addNewTag('test');
it("Trigger correctly event on target with new class add", function() { expect(spy.called).toEqual(false);
var spy = sinon.spy(); this.$clm.addNewTag('test2');
var model = this.wrapper.add({}); expect(spy.called).toEqual(true);
this.gjs.editor.set('selectedComponent', model); });
this.$clm.addNewTag('test');
this.gjs.editor.on("targetClassAdded", spy);
this.$clm.addNewTag('test');
spy.called.should.equal(false);
this.$clm.addNewTag('test2');
spy.called.should.equal(true);
});
});
}); });
}
};
}); });
}
};

82
test/specs/selector_manager/index.js

@ -0,0 +1,82 @@
var SelectorManager = require('selector_manager');
var Models = require('./model/SelectorModels');
var ClassTagView = require('./view/ClassTagView');
var ClassTagsView = require('./view/ClassTagsView');
var e2e = require('./e2e/ClassManager');
describe('SelectorManager', () => {
describe('Main', () => {
var obj;
beforeEach(() => {
obj = new SelectorManager().init();
});
afterEach(() => {
obj = null;
});
it('Object exists', () => {
expect(obj).toExist();
});
it('No selectors inside', () => {
expect(obj.getAll().length).toEqual(0);
});
it('Able to add default selectors', () => {
var cm = new SelectorManager().init({
selectors: ['test1', 'test2', 'test3'],
});
expect(cm.getAll().length).toEqual(3);
});
it('Add new selector', () => {
obj.add('test');
expect(obj.getAll().length).toEqual(1);
});
it('Default new selector is a class type', () => {
obj.add('test');
expect(obj.get('test').get('type')).toEqual('class');
});
it('Check name property', () => {
var name = 'test';
var sel = obj.add(name);
expect(sel.get('name')).toEqual(name);
expect(sel.get('label')).toEqual(name);
});
it('Add 2 selectors', () => {
obj.add('test');
obj.add('test2');
expect(obj.getAll().length).toEqual(2);
});
it('Adding 2 selectors with the same name is not possible', () => {
obj.add('test');
obj.add('test');
expect(obj.getAll().length).toEqual(1);
});
it('Get selector', () => {
var name = 'test';
var sel = obj.add(name);
expect(obj.get(name)).toEqual(sel);
});
it('Get empty class', () => {
expect(obj.get('test')).toEqual(undefined);
});
});
Models.run();
ClassTagView.run();
ClassTagsView.run();
e2e.run();
});

82
test/specs/selector_manager/main.js

@ -1,82 +0,0 @@
define(function(require, exports, module){
'use strict';
var SelectorManager = require('SelectorManager');
var e2e = require('undefined');
describe('SelectorManager', function() {
describe('Main', function() {
var obj;
beforeEach(function () {
obj = new SelectorManager().init();
});
afterEach(function () {
delete obj;
});
it('Object exists', function() {
obj.should.be.exist;
});
it('No selectors inside', function() {
obj.getAll().length.should.equal(0);
});
it('Able to add default selectors', function() {
var cm = new SelectorManager().init({
selectors: ['test1', 'test2', 'test3'],
});
cm.getAll().length.should.equal(3);
});
it('Add new selector', function() {
obj.add('test');
obj.getAll().length.should.equal(1);
});
it('Default new selector is a class type', function() {
obj.add('test');
obj.get('test').get('type').should.equal('class');
});
it('Check name property', function() {
var name = 'test';
var sel = obj.add(name);
sel.get('name').should.equal(name);
sel.get('label').should.equal(name);
});
it('Add 2 selectors', function() {
obj.add('test');
obj.add('test2');
obj.getAll().length.should.equal(2);
});
it('Adding 2 selectors with the same name is not possible', function() {
obj.add('test');
obj.add('test');
obj.getAll().length.should.equal(1);
});
it('Get selector', function() {
var name = 'test';
var sel = obj.add(name);
obj.get(name).should.deep.equal(sel);
});
it('Get empty class', function() {
(obj.get('test') === undefined).should.equal(true);
});
});
Models.run();
ClassTagView.run();
ClassTagsView.run();
e2e.run();
});
});

76
test/specs/selector_manager/model/SelectorModels.js

@ -1,52 +1,50 @@
define(function(require, exports, module){ const Selector = require('selector_manager/model/Selector');
'use strict'; const Selectors = require('selector_manager/model/Selectors');
var Selectors = require('undefined');
module.exports = { module.exports = {
run : function(){ run() {
describe('Selector', function() { describe('Selector', () => {
var obj;
beforeEach(function () { beforeEach(() => {
this.obj = new Selector(); obj = new Selector();
}); });
afterEach(function () {
delete this.obj;
});
it('Has name property', function() {
this.obj.has('name').should.equal(true);
});
it('Has label property', function() { afterEach(() => {
this.obj.has('label').should.equal(true); obj = null;
}); });
it('Has active property', function() { it('Has name property', () => {
this.obj.has('active').should.equal(true); expect(obj.has('name')).toEqual(true);
}); });
it('escapeName test', function() { it('Has label property', () => {
this.obj.escapeName('@Te sT*').should.equal('-Te-sT-'); expect(obj.has('label')).toEqual(true);
}); });
it('Name is corrected at instantiation', function() { it('Has active property', () => {
this.obj = new Selector({ name: '@Te sT*'}); expect(obj.has('active')).toEqual(true);
this.obj.get('name').should.equal('-Te-sT-'); });
});
it('escapeName test', () => {
expect(obj.escapeName('@Te sT*')).toEqual('-Te-sT-');
});
it('Name is corrected at instantiation', () => {
obj = new Selector({ name: '@Te sT*'});
expect(obj.get('name')).toEqual('-Te-sT-');
}); });
describe('Selectors', function() {
it('Creates collection item correctly', function() {
var c = new Selectors();
var m = c.add({});
m.should.be.an.instanceOf(Selector);
});
});
describe('Selectors', () => {
it('Creates collection item correctly', () => {
var c = new Selectors();
var m = c.add({});
expect(m instanceof Selector).toEqual(true);
}); });
}
};
}); });
}
};

222
test/specs/selector_manager/view/ClassTagView.js

@ -1,133 +1,129 @@
define(function(require, exports, module){ const ClassTagView = require('selector_manager/view/ClassTagView');
'use strict'; const Selectors = require('selector_manager/model/Selectors');
var ClassTagView = require('undefined');
var Selectors = require('SelectorManager/model/Selectors'); module.exports = {
run() {
module.exports = { describe('ClassTagView', () => {
run : function(){
describe('ClassTagView', function() { var obj;
var fixture;
var obj; var fixtures;
var fixture; var testLabel;
var fixtures; var coll;
var testLabel;
var coll; before(() => {
fixtures = $("#fixtures");
before(function () { fixture = $('<div class="classtag-fixture"></div>');
fixtures = $("#fixtures"); });
fixture = $('<div class="classtag-fixture"></div>');
});
beforeEach(function () { beforeEach(() => {
coll = new Selectors(); coll = new Selectors();
testLabel = 'TestLabel'; testLabel = 'TestLabel';
var model = coll.add({ var model = coll.add({
name: 'test', name: 'test',
label: testLabel, label: testLabel,
}); });
obj = new ClassTagView({ obj = new ClassTagView({
config : {}, config : {},
model: model, model,
coll: coll coll
}); });
obj.target = { get:function(){} }; obj.target = { get() {} };
_.extend(obj.target, Backbone.Events); _.extend(obj.target, Backbone.Events);
fixture.empty().appendTo(fixtures); fixture.empty().appendTo(fixtures);
fixture.html(obj.render().el); fixture.html(obj.render().el);
}); });
afterEach(function () { afterEach(() => {
delete obj.model; obj.model = null;
}); });
after(function () { after(() => {
fixture.remove(); fixture.remove();
}); });
it('Object exists', function() { it('Object exists', () => {
ClassTagView.should.be.exist; expect(ClassTagView).toExist();
}); });
it('Not empty', () => {
var $el = obj.$el;
expect($el.html()).toExist();
});
it('Not empty', () => {
var $el = obj.$el;
expect($el.html()).toContain(testLabel);
});
describe('Should be rendered correctly', () => {
it('Not empty', function() { it('Has close button', () => {
var $el = obj.$el; var $el = obj.$el;
$el.html().should.not.be.empty; expect($el.find('#close')[0]).toExist();
}); });
it('Has checkbox', () => {
it('Not empty', function() {
var $el = obj.$el; var $el = obj.$el;
$el.html().should.contain(testLabel); expect($el.find('#checkbox')[0]).toExist();
}); });
it('Has label', () => {
describe('Should be rendered correctly', function() { var $el = obj.$el;
expect($el.find('#tag-label')[0]).toExist();
it('Has close button', function() {
var $el = obj.$el;
$el.find('#close').should.have.property(0);
});
it('Has checkbox', function() {
var $el = obj.$el;
$el.find('#checkbox').should.have.property(0);
});
it('Has label', function() {
var $el = obj.$el;
$el.find('#tag-label').should.have.property(0);
});
}); });
it('Could be removed', function() { });
var spy = sinon.spy();
obj.config.target = { get:function(){} };
sinon.stub(obj.config.target, 'get').returns(0);
obj.$el.find('#close').trigger('click');
fixture.html().should.be.empty;
});
it('On remove triggers event', function() { it('Could be removed', () => {
var spy = sinon.spy(); var spy = sinon.spy();
sinon.stub(obj.target, 'get').returns(0); obj.config.target = { get() {} };
obj.target.on("targetClassRemoved", spy); sinon.stub(obj.config.target, 'get').returns(0);
obj.$el.find('#close').trigger('click'); obj.$el.find('#close').trigger('click');
spy.called.should.equal(true); expect(fixture.html()).toNotExist();
}); });
it('Checkbox toggles status', function() { it('On remove triggers event', () => {
var spy = sinon.spy(); var spy = sinon.spy();
obj.model.on("change:active", spy); sinon.stub(obj.target, 'get').returns(0);
obj.model.set('active', true); obj.target.on("targetClassRemoved", spy);
obj.$el.find('#checkbox').trigger('click'); obj.$el.find('#close').trigger('click');
obj.model.get('active').should.equal(false); expect(spy.called).toEqual(true);
spy.called.should.equal(true); });
});
it('On toggle triggers event', function() { it('Checkbox toggles status', () => {
var spy = sinon.spy(); var spy = sinon.spy();
sinon.stub(obj.target, 'get').returns(0); obj.model.on("change:active", spy);
obj.target.on("targetClassUpdated", spy); obj.model.set('active', true);
obj.$el.find('#checkbox').trigger('click'); obj.$el.find('#checkbox').trigger('click');
spy.called.should.equal(true); expect(obj.model.get('active')).toEqual(false);
}); expect(spy.called).toEqual(true);
});
it('Label input is disabled', function() { it('On toggle triggers event', () => {
var inputProp = obj.inputProp; var spy = sinon.spy();
obj.$labelInput.prop(inputProp).should.equal(true); sinon.stub(obj.target, 'get').returns(0);
}); obj.target.on("targetClassUpdated", spy);
obj.$el.find('#checkbox').trigger('click');
expect(spy.called).toEqual(true);
});
it('On double click label input is enable', function() { it('Label input is disabled', () => {
var inputProp = obj.inputProp; var inputProp = obj.inputProp;
obj.$el.find('#tag-label').trigger('dblclick'); expect(obj.$labelInput.prop(inputProp)).toEqual(true);
obj.$labelInput.prop(inputProp).should.equal(false); });
});
it('On blur label input turns back disabled', function() { it('On double click label input is enable', () => {
var inputProp = obj.inputProp; var inputProp = obj.inputProp;
obj.$el.find('#tag-label').trigger('dblclick'); obj.$el.find('#tag-label').trigger('dblclick');
obj.endEditTag(); expect(obj.$labelInput.prop(inputProp)).toEqual(false);
obj.$labelInput.prop(inputProp).should.equal(true); });
});
it('On blur label input turns back disabled', () => {
var inputProp = obj.inputProp;
obj.$el.find('#tag-label').trigger('dblclick');
obj.endEditTag();
expect(obj.$labelInput.prop(inputProp)).toEqual(true);
}); });
}
};
}); });
}
};

391
test/specs/selector_manager/view/ClassTagsView.js

@ -1,196 +1,195 @@
define(function(require, exports, module){ const ClassTagsView = require('selector_manager/view/ClassTagsView');
'use strict'; const Selectors = require('selector_manager/model/Selectors');
var ClassTagsView = require('undefined');
var Selectors = require('SelectorManager/model/Selectors'); module.exports = {
run() {
module.exports = { describe('ClassTagsView', () => {
run : function(){
describe('ClassTagsView', function() { var view;
var fixture;
var view; var fixtures;
var fixture; var testLabel;
var fixtures; var coll;
var testLabel; var target;
var coll;
var target; before(() => {
fixtures = $("#fixtures");
before(function () { fixture = $('<div class="classtag-fixture"></div>');
fixtures = $("#fixtures"); });
fixture = $('<div class="classtag-fixture"></div>');
}); beforeEach(function () {
target = { get() {} };
beforeEach(function () { coll = new Selectors();
target = { get: function(){} }; _.extend(target, Backbone.Events);
coll = new Selectors();
_.extend(target, Backbone.Events); view = new ClassTagsView({
config : { em: target },
view = new ClassTagsView({ collection: coll
config : { em: target }, });
collection: coll
}); this.targetStub = {
add(v) { return {name: v}; }
this.targetStub = { };
add: function(v){ return {name: v}; }
}; this.compTargetStub = {
get() { return { add() {} };}
this.compTargetStub = { };
get: function(){ return { add: function(){} }}
}; fixture.empty().appendTo(fixtures);
fixture.html(view.render().el);
fixture.empty().appendTo(fixtures); this.btnAdd = view.$el.find('#' + view.addBtnId);
fixture.html(view.render().el); this.input = view.$el.find('input#' + view.newInputId);
this.btnAdd = view.$el.find('#' + view.addBtnId); this.$tags = fixture.find('#tags-c');
this.input = view.$el.find('input#' + view.newInputId); this.$states = fixture.find('#states');
this.$tags = fixture.find('#tags-c'); this.$statesC = fixture.find('#input-c');
this.$states = fixture.find('#states'); });
this.$statesC = fixture.find('#input-c');
}); afterEach(() => {
delete view.collection;
afterEach(function () { });
delete view.collection;
}); after(() => {
fixture.remove();
after(function () { });
fixture.remove();
}); it('Object exists', () => {
expect(ClassTagsView).toExist();
it('Object exists', function() { });
ClassTagsView.should.be.exist;
}); it('Not tags inside', function() {
expect(this.$tags.html()).toEqual('');
it('Not tags inside', function() { });
this.$tags.html().should.equal('');
}); it('Add new tag triggers correct method', () => {
sinon.stub(view, "addToClasses");
it('Add new tag triggers correct method', function() { coll.add({ name: 'test' });
sinon.stub(view, "addToClasses"); expect(view.addToClasses.calledOnce).toEqual(true);
coll.add({ name: 'test' }); });
view.addToClasses.calledOnce.should.equal(true);
}); it('Start new tag creation', function() {
this.btnAdd.click();
it('Start new tag creation', function() { expect(this.btnAdd.css('display')).toEqual('none');
this.btnAdd.click(); expect(this.input.css('display')).toNotEqual('none');
(this.btnAdd.css('display') == 'none').should.equal(true); });
(this.input.css('display') !== 'none').should.equal(true);
}); it.skip('Stop tag creation', function() {
this.btnAdd.click();
it('Stop tag creation', function() { this.input.val('test')
this.btnAdd.click(); this.input.blur();
this.input.val('test') //(this.btnAdd.css('display') !== 'none').should.equal(true);
this.input.blur(); //(this.input.css('display') == 'none').should.equal(true);
(this.btnAdd.css('display') !== 'none').should.equal(true); //this.input.val().should.equal('');
(this.input.css('display') == 'none').should.equal(true); expect(this.btnAdd.css('display')).toNotEqual('none');
this.input.val().should.equal(''); expect(this.input.css('display')).toEqual('none');
}); expect(this.input.val()).toEqual('');
});
it('Check keyup of ESC on input', function() {
this.btnAdd.click(); it('Check keyup of ESC on input', function() {
sinon.stub(view, "addNewTag"); this.btnAdd.click();
this.input.trigger({ sinon.stub(view, "addNewTag");
type: 'keyup', this.input.trigger({
keyCode: 13 type: 'keyup',
}); keyCode: 13
view.addNewTag.calledOnce.should.equal(true); });
}); expect(view.addNewTag.calledOnce).toEqual(true);
});
it('Check keyup on ENTER on input', function() {
this.btnAdd.click(); it('Check keyup on ENTER on input', function() {
sinon.stub(view, "endNewTag"); this.btnAdd.click();
this.input.trigger({ sinon.stub(view, "endNewTag");
type: 'keyup', this.input.trigger({
keyCode: 27 type: 'keyup',
}); keyCode: 27
view.endNewTag.calledOnce.should.equal(true); });
}); expect(view.endNewTag.calledOnce).toEqual(true);
});
it('Collection changes on update of target', function() {
coll.add({ name: 'test' }); it('Collection changes on update of target', () => {
target.trigger('change:selectedComponent'); coll.add({ name: 'test' });
coll.length.should.equal(0); target.trigger('change:selectedComponent');
}); expect(coll.length).toEqual(0);
});
it('Collection reacts on reset', function() {
coll.add([{ name: 'test1' }, { name: 'test2' }]); it('Collection reacts on reset', () => {
sinon.stub(view, "addToClasses"); coll.add([{ name: 'test1' }, { name: 'test2' }]);
coll.trigger('reset'); sinon.stub(view, "addToClasses");
view.addToClasses.calledTwice.should.equal(true); coll.trigger('reset');
}); expect(view.addToClasses.calledTwice).toEqual(true);
});
it("Don't accept empty tags", function() {
view.addNewTag(''); it("Don't accept empty tags", function() {
this.$tags.html().should.equal(''); view.addNewTag('');
}); expect(this.$tags.html()).toEqual('');
});
it("Accept new tags", function() {
sinon.stub(target, "get").returns(this.targetStub); it("Accept new tags", function() {
view.compTarget = this.compTargetStub; sinon.stub(target, "get").returns(this.targetStub);
view.addNewTag('test'); view.compTarget = this.compTargetStub;
view.compTarget = this.compTargetStub; view.addNewTag('test');
view.addNewTag('test2'); view.compTarget = this.compTargetStub;
this.$tags.children().length.should.equal(2); view.addNewTag('test2');
}); expect(this.$tags.children().length).toEqual(2);
});
it("New tag correctly added", function() {
coll.add({ label: 'test' }); it("New tag correctly added", function() {
this.$tags.children().first().find('#tag-label input').val().should.equal('test'); coll.add({ label: 'test' });
}); expect(this.$tags.children().first().find('#tag-label input').val()).toEqual('test');
});
it("States are hidden in case no tags", function() {
view.updateStateVis(); it("States are hidden in case no tags", function() {
this.$statesC.css('display').should.equal('none'); view.updateStateVis();
}); expect(this.$statesC.css('display')).toEqual('none');
});
it("States are visible in case of more tags inside", function() {
coll.add({ label: 'test' }); it("States are visible in case of more tags inside", function() {
view.updateStateVis(); coll.add({ label: 'test' });
this.$statesC.css('display').should.equal('block'); view.updateStateVis();
}); expect(this.$statesC.css('display')).toEqual('block');
});
it("Update state visibility on new tag", function() {
sinon.stub(view, "updateStateVis"); it("Update state visibility on new tag", function() {
sinon.stub(target, "get").returns(this.targetStub); sinon.stub(view, "updateStateVis");
view.compTarget = this.compTargetStub; sinon.stub(target, "get").returns(this.targetStub);
view.addNewTag('test'); view.compTarget = this.compTargetStub;
view.updateStateVis.called.should.equal(true); view.addNewTag('test');
}); expect(view.updateStateVis.called).toEqual(true);
});
it("Update state visibility on removing of the tag", function() {
sinon.stub(target, "get").returns(this.targetStub); it("Update state visibility on removing of the tag", function() {
view.compTarget = this.compTargetStub; sinon.stub(target, "get").returns(this.targetStub);
view.addNewTag('test'); view.compTarget = this.compTargetStub;
sinon.stub(view, "updateStateVis"); view.addNewTag('test');
coll.remove(coll.at(0)); sinon.stub(view, "updateStateVis");
view.updateStateVis.calledOnce.should.equal(true); coll.remove(coll.at(0));
}); expect(view.updateStateVis.calledOnce).toEqual(true);
});
it("Output correctly state options", function() {
var view = new ClassTagsView({ it("Output correctly state options", () => {
config : { var view = new ClassTagsView({
em: target, config : {
states: [ { name: 'testName', label: 'testLabel' } ], em: target,
}, states: [ { name: 'testName', label: 'testLabel' } ],
collection: coll },
}); collection: coll
view.getStateOptions().should.equal('<option value="testName">testLabel</option>'); });
}); expect(view.getStateOptions()).toEqual('<option value="testName">testLabel</option>');
});
describe('Should be rendered correctly', function() {
it('Has label', function() { describe('Should be rendered correctly', () => {
view.$el.find('#label').should.have.property(0); it('Has label', () => {
}); expect(view.$el.find('#label')[0]).toExist();
it('Has tags container', function() { });
view.$el.find('#tags-c').should.have.property(0); it('Has tags container', () => {
}); expect(view.$el.find('#tags-c')[0]).toExist();
it('Has add button', function() { });
view.$el.find('#add-tag').should.have.property(0); it('Has add button', () => {
}); expect(view.$el.find('#add-tag')[0]).toExist();
it('Has states input', function() { });
view.$el.find('#states').should.have.property(0); it('Has states input', () => {
}); expect(view.$el.find('#states')[0]).toExist();
}); });
});
});
} });
}; }
};
});

120
test/specs/storage_manager/index.js

@ -0,0 +1,120 @@
const StorageManager = require('storage_manager');
const Models = require('./model/Models');
describe('Storage Manager', () => {
describe('Main', () => {
var obj;
beforeEach(() => {
obj = new StorageManager().init();
});
afterEach(() => {
obj = null;
});
it('Object exists', () => {
expect(StorageManager).toExist();
});
it('Autosave is active by default', () => {
expect(obj.isAutosave()).toEqual(true);
});
it('Change autosave', () => {
obj.setAutosave(0);
expect(obj.isAutosave()).toEqual(false);
});
it('Steps before save are set as default', () => {
expect(obj.getStepsBeforeSave()).toEqual(1);
});
it('Change steps before save', () => {
obj.setStepsBeforeSave(5);
expect(obj.getStepsBeforeSave()).toEqual(5);
});
it('No storages inside', () => {
expect(obj.getStorages()).toEqual({});
});
it('Add and get new storage', () => {
obj.add('test', 'gen');
expect(obj.get('test')).toEqual('gen');
});
it('LocalStorage is set as default', () => {
expect(obj.getCurrent()).toEqual('local');
});
it('Change storage type', () => {
obj.setCurrent('remote');
expect(obj.getCurrent()).toEqual('remote');
});
it('Store do not execute if empty', () => {
expect(obj.store({item:'test'})).toEqual(null);
});
it('Load do not execute if empty', () => {
expect(obj.load(['item'])).toEqual({});
});
it('Load default storages ', () => {
obj.loadDefaultProviders();
expect(obj.get('local')).toExist();
expect(obj.get('remote')).toExist();
expect(obj.get('test')).toNotExist();
});
describe('With custom storage', () => {
var storeValue;
var storageId = 'testStorage';
var storage = {
store(data) {
storeValue = data;
},
load(keys) {
return storeValue;
},
};
beforeEach(() => {
storeValue = [];
obj = new StorageManager().init({
type: storageId,
});
obj.add(storageId, storage);
});
afterEach(() => {
obj = null;
});
it('Store and load data', () => {
var data = {
item: 'testData',
item2: 'testData2',
};
var data2 = {};
var id = obj.getConfig().id;
data2[id + 'item'] = 'testData';
data2[id + 'item2'] = 'testData2';
obj.store(data);
var load = obj.load(['item', 'item2']);
expect(storeValue).toEqual(data2);
expect(load).toEqual(data);
});
});
});
Models.run();
});

123
test/specs/storage_manager/main.js

@ -1,123 +0,0 @@
define(function(require, exports, module){
'use strict';
var StorageManager = require('StorageManager');
var Models = require('undefined');
describe('Storage Manager', function() {
describe('Main', function() {
var obj;
beforeEach(function () {
obj = new StorageManager().init();
});
afterEach(function () {
delete obj;
});
it('Object exists', function() {
StorageManager.should.be.exist;
});
it('Autosave is active by default', function() {
obj.isAutosave().should.equal(true);
});
it('Change autosave', function() {
obj.setAutosave(0);
obj.isAutosave().should.equal(false);
});
it('Steps before save are set as default', function() {
obj.getStepsBeforeSave().should.equal(1);
});
it('Change steps before save', function() {
obj.setStepsBeforeSave(5);
obj.getStepsBeforeSave().should.equal(5);
});
it('No storages inside', function() {
obj.getStorages().should.be.empty;
});
it('Add and get new storage', function() {
obj.add('test', 'gen');
obj.get('test').should.equal('gen');
});
it('LocalStorage is set as default', function() {
obj.getCurrent().should.equal('local');
});
it('Change storage type', function() {
obj.setCurrent('remote');
obj.getCurrent().should.equal('remote');
});
it('Store do not execute if empty', function() {
(obj.store({item:'test'}) === null).should.equal(true);
});
it('Load do not execute if empty', function() {
obj.load(['item']).should.be.empty;
});
it('Load default storages ', function() {
obj.loadDefaultProviders();
obj.get('local').should.not.be.empty;
obj.get('remote').should.not.be.empty;
(obj.get('test') === null).should.equal(true);
});
describe('With custom storage', function() {
var storeValue;
var storageId = 'testStorage';
var storage = {
store: function(data){
storeValue = data;
},
load: function(keys){
return storeValue;
},
};
beforeEach(function () {
storeValue = [];
obj = new StorageManager().init({
type: storageId,
});
obj.add(storageId, storage);
});
afterEach(function () {
delete obj;
});
it('Store and load data', function() {
var data = {
item: 'testData',
item2: 'testData2',
};
var data2 = {};
var id = obj.getConfig().id;
data2[id + 'item'] = 'testData';
data2[id + 'item2'] = 'testData2';
obj.store(data);
var load = obj.load(['item', 'item2']);
storeValue.should.deep.equal(data2);
load.should.deep.equal(data);
});
});
});
Models.run();
});
});

223
test/specs/storage_manager/model/Models.js

@ -1,119 +1,116 @@
define(function(require, exports, module){ const LocalStorage = require('storage_manager/model/LocalStorage');
'use strict'; const RemoteStorage = require('storage_manager/model/RemoteStorage');
var RemoteStorage = require('undefined');
module.exports = {
module.exports = { run() {
run : function(){
describe('LocalStorage', () => {
describe('LocalStorage', function() {
var obj;
var obj; var itemName = 'testItem';
var itemName = 'testItem'; var data = {
var data = { 'item1': 'value1',
'item1': 'value1', 'item2': 'value2',
'item2': 'value2', };
};
beforeEach(() => {
beforeEach(function () { obj = new LocalStorage();
obj = new LocalStorage(); });
});
afterEach(() => {
afterEach(function () { obj = null;
delete obj; });
});
it('Store and load items', () => {
it('Store and load items', function() { obj.store(data);
obj.store(data); var result = obj.load(['item1', 'item2']);
var result = obj.load(['item1', 'item2']); expect(result).toEqual(data);
result.should.deep.equal(data); });
});
it('Store, update and load items', () => {
it('Store, update and load items', function() { obj.store(data);
obj.store(data); obj.store({item3: 'value3'});
obj.store({item3: 'value3'}); obj.store({item2: 'value22'});
obj.store({item2: 'value22'}); var result = obj.load(['item1', 'item2', 'item3']);
var result = obj.load(['item1', 'item2', 'item3']); expect(result).toEqual({
result.should.deep.equal({ 'item1': 'value1',
'item1': 'value1', 'item2': 'value22',
'item2': 'value22', 'item3': 'value3',
'item3': 'value3', });
}); });
});
it('Remove items', () => {
it('Remove items', function() { var items = ['item1', 'item2', 'item3'];
var items = ['item1', 'item2', 'item3']; obj.store(data);
obj.store(data); obj.remove(items);
obj.remove(items); expect(obj.load(items)).toEqual({});
obj.load(items).should.be.empty; });
});
});
describe('RemoteStorage', () => {
var obj;
var itemName = 'testItem';
var endpointStore = 'testStoreEndpoint';
var endpointLoad = 'testLoadEndpoint';
var params = { test: 'testValue' };
var storageOptions;
var data;
beforeEach(() => {
data = {
'item1': 'value1',
'item2': 'value2',
};
storageOptions = {
urlStore: endpointStore,
urlLoad: endpointLoad,
params,
};
obj = new RemoteStorage(storageOptions);
});
afterEach(() => {
$.ajax.restore();
obj = null;
});
// Stubbing will not return the original object so
// .always will not work
it.skip('Store data', () => {
sinon.stub($, "ajax");
for(var k in params)
data[k] = params[k];
obj.store(data);
$.ajax.calledWithMatch({
url: endpointStore,
data,
}).should.equal(true);
});
it('Load data', () => {
sinon.stub($, "ajax").returns({
done() {}
}); });
var dt = {};
var keys = ['item1', 'item2'];
obj.load(keys);
dt.keys = keys;
describe('RemoteStorage', function() { for(var k in params)
dt[k] = params[k];
var obj;
var itemName = 'testItem';
var endpointStore = 'testStoreEndpoint';
var endpointLoad = 'testLoadEndpoint';
var params = { test: 'testValue' };
var storageOptions;
var data;
beforeEach(function () {
data = {
'item1': 'value1',
'item2': 'value2',
};
storageOptions = {
urlStore: endpointStore,
urlLoad: endpointLoad,
params: params,
};
obj = new RemoteStorage(storageOptions);
});
afterEach(function () {
$.ajax.restore();
delete obj;
});
// Stubbing will not return the original object so
// .always will not work
it.skip('Store data', function() {
sinon.stub($, "ajax");
for(var k in params)
data[k] = params[k];
obj.store(data);
$.ajax.calledWithMatch({
url: endpointStore,
data: data,
}).should.equal(true);
});
it('Load data', function() {
sinon.stub($, "ajax").returns({
done: function(){}
});
var dt = {};
var keys = ['item1', 'item2'];
obj.load(keys);
dt.keys = keys;
for(var k in params)
dt[k] = params[k];
$.ajax.calledWithMatch({
url: endpointLoad,
data: dt
}).should.equal(true);
});
}); expect($.ajax.calledWithMatch({
url: endpointLoad,
data: dt
})).toEqual(true);
});
} });
}; }
}); };

191
test/specs/style_manager/index.js

@ -0,0 +1,191 @@
const StyleManager = require('style_manager');
const Models = require('./model/Models');
const SectorView = require('./view/SectorView');
const SectorsView = require('./view/SectorsView');
const PropertyView = require('./view/PropertyView');
const PropertySelectView = require('./view/PropertySelectView');
const PropertyRadioView = require('./view/PropertyRadioView');
const PropertyIntegerView = require('./view/PropertyIntegerView');
const PropertyColorView = require('./view/PropertyColorView');
const PropertyCompositeView = require('./view/PropertyCompositeView');
const PropertyStackView = require('./view/PropertyStackView');
const LayerView = require('./view/LayerView');
describe('StyleManager', () => {
describe('Main', () => {
var obj;
beforeEach(() => {
obj = new StyleManager().init({
sectors: []
});
});
afterEach(() => {
obj = null;
});
it('Object exists', () => {
expect(obj).toExist();
});
it('No sectors', () => {
expect(obj.getSectors().length).toEqual(0);
});
it('Add sector', () => {
obj.addSector('test', {
name: 'Test name'
});
var sector = obj.getSectors().at(0);
expect(obj.getSectors().length).toEqual(1);
expect(sector.get('id')).toEqual('test');
expect(sector.get('name')).toEqual('Test name');
});
it('Add sectors', () => {
obj.addSector('test', {});
obj.addSector('test2', {});
expect(obj.getSectors().length).toEqual(2);
});
it("Can't create more than one sector with the same id", () => {
var sect1 = obj.addSector('test', {});
var sect2 = obj.addSector('test', {});
expect(obj.getSectors().length).toEqual(1);
expect(sect1).toEqual(sect2);
});
it('Get inexistent sector', () => {
expect(obj.getSector('test')).toEqual(null);
});
it('Get sector', () => {
var sect1 = obj.addSector('test', { name: 'Test' });
var sect2 = obj.getSector('test');
expect(sect1).toEqual(sect2);
});
it('Add property to inexistent sector', () => {
expect(obj.addProperty('test', {})).toEqual(null);
});
it('Add property', () => {
obj.addSector('test', {});
expect(obj.addProperty('test', {})).toExist();
expect(obj.getProperties('test').length).toEqual(1);
});
it('Check added property', () => {
obj.addSector('test', {});
var prop = obj.addProperty('test', {
'name': 'test',
});
expect(prop.get('name')).toEqual('test');
});
it('Add properties', () => {
obj.addSector('test', {});
obj.addProperty('test', [{}, {}]);
expect(obj.getProperties('test').length).toEqual(2);
});
it('Get property from inexistent sector', () => {
expect(obj.getProperty('test', 'test-prop')).toEqual(null);
});
it("Can't get properties without proper name", () => {
obj.addSector('test', {});
obj.addProperty('test', [{}, {}]);
expect(obj.getProperty('test', 'test-prop')).toEqual([]);
});
it("Get property with proper name", () => {
obj.addSector('test', {});
var prop1 = obj.addProperty('test', {property: 'test-prop'});
var prop2 = obj.getProperty('test', 'test-prop');
expect(prop1).toEqual(prop2);
});
it("Get properties with proper name", () => {
obj.addSector('test', {});
var prop1 = obj.addProperty('test',[
{property: 'test-prop'},
{property: 'test-prop'}
]);
expect(obj.getProperty('test', 'test-prop').length).toEqual(2);
});
it('Get inexistent properties', () => {
expect(obj.getProperties('test')).toEqual(null);
expect(obj.getProperties()).toEqual(null);
});
it('Renders correctly', () => {
expect(obj.render()).toExist();
});
describe('Init with configuration', () => {
beforeEach(() => {
obj = new StyleManager().init({
sectors: [{
id: 'dim',
name: 'Dimension',
properties: [{
name: 'Width',
property: 'width',
},{
name: 'Height',
property: 'height',
}],
},{
id: 'pos',
name: 'position',
properties: [{
name: 'Width',
property: 'width',
}],
}],
});
});
afterEach(() => {
obj = null;
});
it('Sectors added', () => {
expect(obj.getSectors().length).toEqual(2);
var sect1 = obj.getSector('dim');
expect(sect1.get('name')).toEqual('Dimension');
});
it('Properties added', () => {
var sect1 = obj.getSector('dim');
var sect2 = obj.getSector('pos');
expect(sect1.get('properties').length).toEqual(2);
expect(sect2.get('properties').length).toEqual(1);
});
it('Property is correct', () => {
var prop1 = obj.getProperty('dim', 'width');
expect(prop1.get('name')).toEqual('Width');
});
});
Models.run();
SectorView.run();
SectorsView.run();
PropertyView.run();
PropertySelectView.run();
PropertyRadioView.run();
PropertyIntegerView.run();
PropertyColorView.run();
PropertyCompositeView.run();
PropertyStackView.run();
LayerView.run();
});
});

185
test/specs/style_manager/main.js

@ -1,185 +0,0 @@
define(function(require, exports, module){
'use strict';
var StyleManager = require('StyleManager');
var LayerView = require('undefined');
describe('StyleManager', function() {
describe('Main', function() {
var obj;
beforeEach(function () {
obj = new StyleManager().init({
sectors: []
});
});
afterEach(function () {
delete obj;
});
it('Object exists', function() {
obj.should.be.ok;
});
it('No sectors', function() {
obj.getSectors().length.should.equal(0);
});
it('Add sector', function() {
obj.addSector('test', {
name: 'Test name'
});
obj.getSectors().length.should.equal(1);
var sector = obj.getSectors().at(0);
sector.get('id').should.equal('test');
sector.get('name').should.equal('Test name');
});
it('Add sectors', function() {
obj.addSector('test', {});
obj.addSector('test2', {});
obj.getSectors().length.should.equal(2);
});
it("Can't create more than one sector with the same id", function() {
var sect1 = obj.addSector('test', {});
var sect2 = obj.addSector('test', {});
obj.getSectors().length.should.equal(1);
sect1.should.deep.equal(sect2);
});
it('Get inexistent sector', function() {
(obj.getSector('test') == null).should.equal(true);
});
it('Get sector', function() {
var sect1 = obj.addSector('test', { name: 'Test' });
var sect2 = obj.getSector('test');
sect1.should.deep.equal(sect2);
});
it('Add property to inexistent sector', function() {
(obj.addProperty('test', {}) == null).should.equal(true);
});
it('Add property', function() {
obj.addSector('test', {});
(obj.addProperty('test', {}) == null).should.equal(false);
obj.getProperties('test').length.should.equal(1);
});
it('Check added property', function() {
obj.addSector('test', {});
var prop = obj.addProperty('test', {
'name': 'test',
});
prop.get('name').should.equal('test');
});
it('Add properties', function() {
obj.addSector('test', {});
obj.addProperty('test', [{}, {}]);
obj.getProperties('test').length.should.equal(2);
});
it('Get property from inexistent sector', function() {
(obj.getProperty('test', 'test-prop') == null).should.equal(true);
});
it("Can't get properties without proper name", function() {
obj.addSector('test', {});
obj.addProperty('test', [{}, {}]);
obj.getProperty('test', 'test-prop').should.be.empty;
});
it("Get property with proper name", function() {
obj.addSector('test', {});
var prop1 = obj.addProperty('test', {property: 'test-prop'});
var prop2 = obj.getProperty('test', 'test-prop');
prop1.should.deep.equal(prop2);
});
it("Get properties with proper name", function() {
obj.addSector('test', {});
var prop1 = obj.addProperty('test',[
{property: 'test-prop'},
{property: 'test-prop'}
]);
obj.getProperty('test', 'test-prop').length.should.equal(2);
});
it('Get inexistent properties', function() {
(obj.getProperties('test') == null).should.equal(true);
(obj.getProperties() == null).should.equal(true);
});
it('Renders correctly', function() {
obj.render().should.be.ok;
});
describe('Init with configuration', function() {
beforeEach(function () {
obj = new StyleManager().init({
sectors: [{
id: 'dim',
name: 'Dimension',
properties: [{
name: 'Width',
property: 'width',
},{
name: 'Height',
property: 'height',
}],
},{
id: 'pos',
name: 'position',
properties: [{
name: 'Width',
property: 'width',
}],
}],
});
});
afterEach(function () {
delete obj;
});
it('Sectors added', function() {
obj.getSectors().length.should.equal(2);
var sect1 = obj.getSector('dim');
sect1.get('name').should.equal('Dimension');
});
it('Properties added', function() {
var sect1 = obj.getSector('dim');
var sect2 = obj.getSector('pos');
sect1.get('properties').length.should.equal(2);
sect2.get('properties').length.should.equal(1);
});
it('Property is correct', function() {
var prop1 = obj.getProperty('dim', 'width');
prop1.get('name').should.equal('Width');
});
});
Models.run();
SectorView.run();
SectorsView.run();
PropertyView.run();
PropertySelectView.run();
PropertyRadioView.run();
PropertyIntegerView.run();
PropertyColorView.run();
PropertyCompositeView.run();
PropertyStackView.run();
LayerView.run();
});
});
});

1601
test/specs/style_manager/model/Models.js

File diff suppressed because it is too large

130
test/specs/style_manager/view/LayerView.js

@ -1,81 +1,77 @@
define(function(require, exports, module){ const LayerView = require('style_manager/view/LayerView');
'use strict'; const Layers = require('style_manager/model/Layers');
var LayerView = require('undefined');
var Layers = require('StyleManager/model/Layers');
module.exports = { module.exports = {
run : function(){ run() {
describe('LayerView', function() { describe('LayerView', () => {
var component; var component;
var $fixtures; var $fixtures;
var $fixture; var $fixture;
var target; var target;
var model; var model;
var view; var view;
before(function () { before(() => {
$fixtures = $("#fixtures"); $fixtures = $("#fixtures");
$fixture = $('<div class="layer-fixture"></div>'); $fixture = $('<div class="layer-fixture"></div>');
}); });
beforeEach(function () {
var coll = new Layers();
model = coll.add({});
view = new LayerView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () { beforeEach(() => {
view.remove(); var coll = new Layers();
}); model = coll.add({});
view = new LayerView({
model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
after(function () { afterEach(() => {
$fixture.remove(); view.remove();
delete component; });
delete view;
delete model;
});
it('Rendered correctly', function() { after(() => {
var layer = view.el; $fixture.remove();
$fixture.get(0).querySelector('.layer').should.be.ok; component = null;
layer.querySelector('#label').should.be.ok; view = null;
layer.querySelector('#close-layer').should.be.ok; model = null;
layer.querySelector('#inputs').should.be.ok; });
layer.querySelector('#inputs').innerHTML.should.be.empty;
layer.querySelector('#preview').should.be.ok;
});
it('getIndex returns default value', function() { it('Rendered correctly', () => {
view.getIndex().should.equal(0); var layer = view.el;
}); expect($fixture.get(0).querySelector('.layer')).toExist();
expect(layer.querySelector('#label')).toExist();
expect(layer.querySelector('#close-layer')).toExist();
expect(layer.querySelector('#inputs')).toExist();
expect(layer.querySelector('#inputs').innerHTML).toNotExist();
expect(layer.querySelector('#preview')).toExist();
});
it('No preview', function() { it('getIndex returns default value', () => {
var style = view.el.querySelector('#preview').style; expect(view.getIndex()).toEqual(0);
style.cssText.should.be.empty; });
});
it('Changes on value trigger onPreview', function() { it('No preview', () => {
var called = 0; var style = view.el.querySelector('#preview').style;
view.onPreview = function(){called = 1}; expect(style.cssText).toNotExist();
view.model.set('preview', true); });
view.model.set('value', 'test');
called.should.equal(1);
});
it('Update props', function() { it('Changes on value trigger onPreview', () => {
view.model.set('props', $('<div>')); var called = 0;
view.el.querySelector('#inputs').innerHTML.should.not.be.empty; view.onPreview = () => {called = 1};
(view.model.get('props') === null).should.equal(true); view.model.set('preview', true);
}); view.model.set('value', 'test');
expect(called).toEqual(1);
});
it('Update props', () => {
view.model.set('props', $('<div>'));
expect(view.el.querySelector('#inputs').innerHTML).toExist();
expect(view.model.get('props')).toEqual(null);
}); });
}
};
}); });
}
};

298
test/specs/style_manager/view/PropertyColorView.js

@ -1,166 +1,166 @@
define(function(require, exports, module){ const PropertyColorView = require('style_manager/view/PropertyColorView');
'use strict'; const Property = require('style_manager/model/Property');
var PropertyColorView = require('undefined'); const Component = require('dom_components/model/Component');
var Property = require('StyleManager/model/Property');
var Component = require('DomComponents/model/Component'); module.exports = {
run() {
module.exports = {
run : function(){ describe('PropertyColorView', () => {
describe('PropertyColorView', function() { var component;
var $fixtures;
var component; var $fixture;
var $fixtures; var target;
var $fixture; var model;
var target; var view;
var model; var propTarget;
var view; var propName = 'testprop';
var propName = 'testprop'; var propValue = '#fff';
var propValue = '#fff'; var defValue = 'test2value';
var defValue = 'test2value';
before(() => {
before(function () { $.fn.spectrum = () => {};
$.fn.spectrum = function(){}; $fixtures = $("#fixtures");
$fixtures = $("#fixtures"); $fixture = $('<div class="sm-fixture"></div>');
$fixture = $('<div class="sm-fixture"></div>'); });
});
beforeEach(function () { beforeEach(() => {
target = new Component(); target = new Component();
component = new Component(); component = new Component();
model = new Property({ model = new Property({
type: 'color', type: 'color',
property: propName property: propName
}); });
view = new PropertyColorView({ propTarget = Object.assign({}, Backbone.Events);
model: model propTarget.model = component;
}); view = new PropertyColorView({
$fixture.empty().appendTo($fixtures); model,
$fixture.html(view.render().el); propTarget
}); });
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () { afterEach(() => {
//view.remove(); // strange errors ??? //view.remove(); // strange errors ???
}); });
after(function () { after(() => {
$fixture.remove(); $fixture.remove();
delete component; component = null;
delete view; view = null;
delete model; model = null;
}); });
it('Rendered correctly', function() { it('Rendered correctly', () => {
var prop = view.el; var prop = view.el;
$fixture.get(0).querySelector('.property').should.be.ok; expect($fixture.get(0).querySelector('.property')).toExist();
prop.querySelector('.label').should.be.ok; expect(prop.querySelector('.label')).toExist();
prop.querySelector('.field').should.be.ok; expect(prop.querySelector('.field')).toExist();
}); });
it('Inputs rendered', function() { it('Inputs rendered', () => {
var prop = view.el; var prop = view.el;
prop.querySelector('input[type=text]').should.be.ok; expect(prop.querySelector('input[type=text]')).toExist();
prop.querySelector('.field-color-picker').should.be.ok; expect(prop.querySelector('.field-color-picker')).toExist();
}); });
it('Inputs should exist', function() { it('Inputs should exist', () => {
view.$input.should.be.ok; expect(view.$input).toExist();
view.$color.should.be.ok; expect(view.$color).toExist();
}); });
it('Input value is empty', function() { it('Input value is empty', () => {
view.model.get('value').should.be.empty; expect(view.model.get('value')).toNotExist();
view.$input.val().should.be.empty; expect(view.$input.val()).toNotExist();
}); });
it('Update model on setValue', function() { it('Update model on setValue', () => {
view.setValue(propValue); view.setValue(propValue);
view.model.get('value').should.equal(propValue); expect(view.model.get('value')).toEqual(propValue);
view.$input.val().should.equal(propValue); expect(view.$input.val()).toEqual(propValue);
}); });
it('Update model on input change', function() { it('Update model on input change', () => {
view.$input.val(propValue).trigger('change'); view.$input.val(propValue).trigger('change');
view.model.get('value').should.equal(propValue); expect(view.model.get('value')).toEqual(propValue);
}); });
it('Update input on value change', function() { it('Update input on value change', () => {
view.model.set('value', propValue); view.model.set('value', propValue);
view.getInputValue().should.equal(propValue); expect(view.getInputValue()).toEqual(propValue);
}); });
it('Update target on value change', function() { it('Update target on value change', () => {
view.selectedComponent = component; view.selectedComponent = component;
view.model.set('value', propValue); view.model.set('value', propValue);
var compStyle = view.selectedComponent.get('style'); var compStyle = view.selectedComponent.get('style');
var assertStyle = {}; var assertStyle = {};
assertStyle[propName] = propValue; assertStyle[propName] = propValue;
compStyle.should.deep.equal(assertStyle); expect(compStyle).toEqual(assertStyle);
}); });
describe('With target setted', function() { describe('With target setted', () => {
beforeEach(function () {
target.model = component;
view = new PropertyColorView({
model: model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Update value and input on target swap', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal(propValue);
view.getInputValue().should.equal(propValue);
});
it('Update value after multiple swaps', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = '#123123';
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal('#123123');
view.$input.val().should.equal('#123123');
});
})
describe('Init property', function() {
beforeEach(function () {
component = new Component();
model = new Property({
type: 'color',
property: propName,
defaults: propValue,
});
view = new PropertyColorView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', function() {
view.model.get('value').should.equal(propValue);
});
it('Input value is as default', function() {
view.$input.val().should.equal(propValue);
});
beforeEach(() => {
target.model = component;
view = new PropertyColorView({
model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Update value and input on target swap', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual(propValue);
expect(view.getInputValue()).toEqual(propValue);
});
it('Update value after multiple swaps', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = '#123123';
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual('#123123');
expect(view.getInputValue()).toEqual('#123123');
});
})
describe('Init property', () => {
beforeEach(() => {
component = new Component();
model = new Property({
type: 'color',
property: propName,
defaults: propValue,
}); });
view = new PropertyColorView({
model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', () => {
expect(view.model.get('value')).toEqual(propValue);
});
it('Input value is as default', () => {
expect(view.$input.val()).toEqual(propValue);
});
}); });
}
};
}); });
}
};

452
test/specs/style_manager/view/PropertyCompositeView.js

@ -1,242 +1,238 @@
define(function(require, exports, module){ const PropertyCompositeView = require('style_manager/view/PropertyCompositeView');
'use strict'; const Property = require('style_manager/model/Property');
var PropertyCompositeView = require('undefined'); const Component = require('dom_components/model/Component');
var Property = require('StyleManager/model/Property');
var Component = require('DomComponents/model/Component'); module.exports = {
run() {
module.exports = {
run : function(){ describe('PropertyCompositeView', () => {
describe('PropertyCompositeView', function() { var component;
var $fixtures;
var component; var $fixture;
var $fixtures; var target;
var $fixture; var model;
var target; var view;
var model; var propName = 'testprop';
var view; var propValue = 'test1value';
var propName = 'testprop'; var defValue = 'test2value';
var propValue = 'test1value'; var properties = [
var defValue = 'test2value'; {property: 'subprop1'},
var properties = [ {
{property: 'subprop1'}, type: 'integer',
{ property: 'subprop2',
type: 'integer', defaults: 0,
property: 'subprop2', units: ['%', 'px']
defaults: 0, },
units: ['%', 'px'] {
}, type: 'select',
{ property: 'subprop3',
type: 'select', defaults: 'val2',
property: 'subprop3', list: [
defaults: 'val2', {value:'val1'},
list: [ {value:'val2'},
{value:'val1'}, {value:'val3'},
{value:'val2'}, ]
{value:'val3'}, },
] ];
},
]; before(() => {
$fixtures = $("#fixtures");
before(function () { $fixture = $('<div class="sm-fixture"></div>');
$fixtures = $("#fixtures"); });
$fixture = $('<div class="sm-fixture"></div>');
});
beforeEach(function () {
target = new Component();
component = new Component();
target.model = component;
model = new Property({
type: 'composite',
property: propName,
properties: properties
});
view = new PropertyCompositeView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () { beforeEach(() => {
//view.remove(); // strange errors ??? target = new Component();
}); component = new Component();
target.model = component;
model = new Property({
type: 'composite',
property: propName,
properties
});
view = new PropertyCompositeView({
model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
after(function () { afterEach(() => {
$fixture.remove(); //view.remove(); // strange errors ???
delete component; });
delete view;
delete model;
});
it('Rendered correctly', function() { after(() => {
var prop = view.el; $fixture.remove();
$fixture.get(0).querySelector('.property').should.be.ok; component = null;
prop.querySelector('.label').should.be.ok; view = null;
prop.querySelector('.field').should.be.ok; model = null;
}); });
it('Properties rendered', function() { it('Rendered correctly', () => {
var prop = view.el; var prop = view.el;
prop.querySelector('.properties').should.be.ok; expect($fixture.get(0).querySelector('.property')).toExist();
}); expect(prop.querySelector('.label')).toExist();
expect(prop.querySelector('.field')).toExist();
});
it('Properties rendered correctly', function() { it('Properties rendered', () => {
var children = view.el.querySelector('.properties').children; var prop = view.el;
children.length.should.equal(properties.length + 1); expect(prop.querySelector('.properties')).toExist();
children[0].id.should.equal(properties[0].property); });
children[1].id.should.equal(properties[1].property);
children[2].id.should.equal(properties[2].property);
});
it('Props should exist', function() { it('Properties rendered correctly', () => {
view.$props.should.be.ok; var children = view.el.querySelector('.properties').children;
}); expect(children.length).toEqual(properties.length + 1);
expect(children[0].id).toEqual(properties[0].property);
expect(children[1].id).toEqual(properties[1].property);
expect(children[2].id).toEqual(properties[2].property);
});
it('Input value is empty', function() { it('Props should exist', () => {
view.model.get('value').should.be.empty; expect(view.$props).toExist();
}); });
it('Update input on value change', function() { it('Input value is empty', () => {
view.model.set('value', propValue); expect(view.model.get('value')).toNotExist();
view.$input.val().should.equal(propValue); });
});
describe('With target setted', function() { it('Update input on value change', () => {
view.model.set('value', propValue);
var prop2Val; expect(view.$input.val()).toEqual(propValue);
var prop3Val; });
var prop2Unit;
var finalResult;
var $prop1;
var $prop2;
var $prop3;
beforeEach(function () {
model = new Property({
type: 'composite',
property: propName,
properties: properties
});
view = new PropertyCompositeView({
model: model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
prop3Val = properties[2].list[2].value;
prop2Val = properties[1].defaults;
prop2Unit = properties[1].units[0];
finalResult = propValue + ' ' + prop2Val + prop2Unit +' ' + prop3Val;
$prop1 = view.$props.find('#' + properties[0].property + ' input');
$prop2 = view.$props.find('#' + properties[1].property + ' input');
$prop3 = view.$props.find('#' + properties[2].property + ' select');
});
it('Update model on input change', function() {
$prop1.val(propValue).trigger('change');
$prop3.val(prop3Val).trigger('change');
view.model.get('value').should.equal(finalResult);
});
it('Update value on models change', function() {
view.model.get('properties').at(0).set('value', propValue);
view.model.get('properties').at(2).set('value', prop3Val);
view.model.get('value').should.equal(finalResult);
});
it('Update target on value change', function() {
$prop1.val(propValue).trigger('change');
var compStyle = view.getTarget().get('style');
var assertStyle = {};
assertStyle[propName] = propValue + ' 0% val2';
compStyle.should.deep.equal(assertStyle);
});
it('Update target on detached value change', function() {
model = new Property({
type: 'composite',
property: propName,
properties: properties,
detached: true,
});
view = new PropertyCompositeView({
model: model,
propTarget: target
});
$fixture.html(view.render().el);
$prop1 = view.$props.find('#' + properties[0].property + ' input');
$prop1.val(propValue).trigger('change');
var compStyle = view.getTarget().get('style');
var assertStyle = {};
assertStyle[properties[0].property] = $prop1.val();
compStyle.should.deep.equal(assertStyle);
});
it('Update value and input on target swap', function() {
var style = {};
style[propName] = finalResult;
component.set('style', style);
view.propTarget.trigger('update');
$prop1.val().should.equal(propValue);
$prop3.val().should.equal(prop3Val);
});
it('Update value after multiple swaps', function() {
var style = {};
style[propName] = finalResult;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = propValue + '2 ' + prop2Val + '2' + prop2Unit + ' ' + 'val1';
component.set('style', style);
view.propTarget.trigger('update');
$prop1.val().should.equal(propValue + '2');
$prop2.val().should.equal('2');
$prop3.val().should.equal('val1');
});
it('The value is correctly extracted from the composite string', function() {
var style = {};
style[propName] = 'value1 value2 value3 value4';
component.set('style', style);
view.valueOnIndex(2).should.equal('value3');
view.valueOnIndex(0).should.equal('value1');
(view.valueOnIndex(4) === null).should.equal(true);
});
it('Build value from properties', function() {
view.model.get('properties').at(0).set('value', propValue);
view.model.get('properties').at(2).set('value', prop3Val);
view.build().should.equal(finalResult);
});
})
describe('Init property', function() {
beforeEach(function () {
model = new Property({
type: 'composite',
property: propName,
properties: properties,
defaults: defValue,
});
view = new PropertyCompositeView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', function() {
view.model.get('value').should.equal(defValue);
});
describe('With target setted', () => {
var prop2Val;
var prop3Val;
var prop2Unit;
var finalResult;
var $prop1;
var $prop2;
var $prop3;
beforeEach(() => {
model = new Property({
type: 'composite',
property: propName,
properties
}); });
view = new PropertyCompositeView({
model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
prop3Val = properties[2].list[2].value;
prop2Val = properties[1].defaults;
prop2Unit = properties[1].units[0];
finalResult = propValue + ' ' + prop2Val + prop2Unit +' ' + prop3Val;
$prop1 = view.$props.find('#' + properties[0].property + ' input');
$prop2 = view.$props.find('#' + properties[1].property + ' input');
$prop3 = view.$props.find('#' + properties[2].property + ' select');
});
it('Update model on input change', () => {
$prop1.val(propValue).trigger('change');
$prop3.val(prop3Val).trigger('change');
expect(view.model.get('value')).toEqual(finalResult);
});
it('Update value on models change', () => {
view.model.get('properties').at(0).set('value', propValue);
view.model.get('properties').at(2).set('value', prop3Val);
expect(view.model.get('value')).toEqual(finalResult);
});
it('Update target on value change', () => {
$prop1.val(propValue).trigger('change');
var compStyle = view.getTarget().get('style');
var assertStyle = {};
assertStyle[propName] = propValue + ' 0% val2';
expect(compStyle).toEqual(assertStyle);
});
it('Update target on detached value change', () => {
model = new Property({
type: 'composite',
property: propName,
properties,
detached: true,
});
view = new PropertyCompositeView({
model,
propTarget: target
});
$fixture.html(view.render().el);
$prop1 = view.$props.find('#' + properties[0].property + ' input');
$prop1.val(propValue).trigger('change');
var compStyle = view.getTarget().get('style');
var assertStyle = {};
assertStyle[properties[0].property] = $prop1.val();
expect(compStyle).toEqual(assertStyle);
});
it('Update value and input on target swap', () => {
var style = {};
style[propName] = finalResult;
component.set('style', style);
view.propTarget.trigger('update');
expect($prop1.val()).toEqual(propValue);
expect($prop3.val()).toEqual(prop3Val);
});
it('Update value after multiple swaps', () => {
var style = {};
style[propName] = finalResult;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = propValue + '2 ' + prop2Val + '2' + prop2Unit + ' ' + 'val1';
component.set('style', style);
view.propTarget.trigger('update');
expect($prop1.val()).toEqual(propValue + '2');
expect($prop2.val()).toEqual('2');
expect($prop3.val()).toEqual('val1');
});
it('The value is correctly extracted from the composite string', () => {
var style = {};
style[propName] = 'value1 value2 value3 value4';
component.set('style', style);
expect(view.valueOnIndex(2)).toEqual('value3');
expect(view.valueOnIndex(0)).toEqual('value1');
expect(view.valueOnIndex(4)).toEqual(null);
});
it('Build value from properties', () => {
view.model.get('properties').at(0).set('value', propValue);
view.model.get('properties').at(2).set('value', prop3Val);
expect(view.build()).toEqual(finalResult);
});
})
describe('Init property', () => {
beforeEach(() => {
model = new Property({
type: 'composite',
property: propName,
properties,
defaults: defValue,
});
view = new PropertyCompositeView({
model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', () => {
expect(view.model.get('value')).toEqual(defValue);
});
}); });
}
};
}); });
}
};

386
test/specs/style_manager/view/PropertyIntegerView.js

@ -1,211 +1,211 @@
define(function(require, exports, module){ const PropertyIntegerView = require('style_manager/view/PropertyIntegerView');
'use strict'; const Property = require('style_manager/model/Property');
var PropertyIntegerView = require('undefined'); const Component = require('dom_components/model/Component');
var Property = require('StyleManager/model/Property');
var Component = require('DomComponents/model/Component'); module.exports = {
run() {
module.exports = {
run : function(){ describe('PropertyIntegerView', () => {
describe('PropertyIntegerView', function() { var component;
var $fixtures;
var component; var $fixture;
var $fixtures; var target;
var $fixture; var model;
var target; var view;
var model; var propTarget;
var view; var propName = 'testprop';
var propName = 'testprop'; var intValue = '55';
var intValue = '55'; var unitValue = 'px';
var unitValue = 'px'; var propValue = intValue + unitValue;
var propValue = intValue + unitValue; var defValue = 'test2value';
var defValue = 'test2value'; var units = ['px', '%', 'em'];
var units = ['px', '%', 'em']; var minValue = -15;
var minValue = -15; var maxValue = 75;
var maxValue = 75; var unitsElSel = '.field-units select';
var unitsElSel = '.field-units select';
before(() => {
before(function () { $fixtures = $("#fixtures");
$fixtures = $("#fixtures"); $fixture = $('<div class="sm-fixture"></div>');
$fixture = $('<div class="sm-fixture"></div>'); });
});
beforeEach(function () {
target = new Component();
component = new Component();
model = new Property({
type: 'integer',
units: units,
property: propName
});
view = new PropertyIntegerView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () { beforeEach(() => {
//view.remove(); // strange errors ??? target = new Component();
}); component = new Component();
model = new Property({
type: 'integer',
units,
property: propName
});
propTarget = Object.assign({}, Backbone.Events);
propTarget.model = component;
view = new PropertyIntegerView({
model,
propTarget
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
after(function () { afterEach(() => {
$fixture.remove(); //view.remove(); // strange errors ???
delete component; });
delete view;
delete model;
});
it('Rendered correctly', function() { after(() => {
var prop = view.el; $fixture.remove();
$fixture.get(0).querySelector('.property').should.be.ok; component = null;
prop.querySelector('.label').should.be.ok; view = null;
prop.querySelector('.field').should.be.ok; model = null;
}); });
it('Inputs rendered', function() { it('Rendered correctly', () => {
var prop = view.el; var prop = view.el;
prop.querySelector('input[type=text]').should.be.ok; expect($fixture.get(0).querySelector('.property')).toExist();
prop.querySelector(unitsElSel).should.be.ok; expect(prop.querySelector('.label')).toExist();
}); expect(prop.querySelector('.field')).toExist();
});
it('Units rendered', function() { it('Inputs rendered', () => {
var select = view.el.querySelector(unitsElSel); var prop = view.el;
select.children.length.should.equal(units.length); expect(prop.querySelector('input[type=text]')).toExist();
}); expect(prop.querySelector(unitsElSel)).toExist();
});
it('Units rendered correctly', function() { it('Units rendered', () => {
var children = view.el.querySelector(unitsElSel).children; var select = view.el.querySelector(unitsElSel);
children[0].textContent.should.equal(units[0]); expect(select.children.length).toEqual(units.length);
children[1].textContent.should.equal(units[1]); });
children[2].textContent.should.equal(units[2]);
});
it('Inputs should exist', function() { it('Units rendered correctly', () => {
view.$input.should.be.ok; var children = view.el.querySelector(unitsElSel).children;
view.$unit.should.be.ok; expect(children[0].textContent).toEqual(units[0]);
}); expect(children[1].textContent).toEqual(units[1]);
expect(children[2].textContent).toEqual(units[2]);
});
it('Input value is empty', function() { it('Inputs should exist', () => {
view.model.get('value').should.be.empty; expect(view.$input).toExist();
view.model.get('unit').should.equal('px'); expect(view.$unit).toExist();
}); });
it('Update model on setValue', function() { it('Input value is empty', () => {
view.setValue(intValue + unitValue); expect(view.model.get('value')).toNotExist();
view.model.get('value').should.equal(parseFloat(intValue)); expect(view.model.get('unit')).toEqual('px');
view.model.get('unit').should.equal(unitValue); });
view.$input.val().should.equal(intValue);
view.$unit.val().should.equal(unitValue);
});
it('Update model on input change', function() { it('Update model on setValue', () => {
view.$input.val(123).trigger('change'); view.setValue(intValue + unitValue);
view.model.get('value').should.equal(123); expect(view.model.get('value')).toEqual(parseFloat(intValue));
}); expect(view.model.get('unit')).toEqual(unitValue);
expect(view.$input.val()).toEqual(intValue);
expect(view.$unit.val()).toEqual(unitValue);
});
it('Update model on unit change', function() { it('Update model on input change', () => {
view.$unit.val(units[1]).trigger('change'); view.$input.val(123).trigger('change');
view.model.get('unit').should.equal(units[1]); expect(view.model.get('value')).toEqual(123);
}); });
it('Update input on value change', function() { it('Update model on unit change', () => {
view.model.set('value', intValue); view.$unit.val(units[1]).trigger('change');
view.getInputValue().should.equal(intValue); expect(view.model.get('unit')).toEqual(units[1]);
}); });
it('Update target on value change', function() { it('Update input on value change', () => {
view.selectedComponent = component; view.model.set('value', intValue);
view.model.set('value', intValue); expect(view.getInputValue()).toEqual(intValue);
var compStyle = view.selectedComponent.get('style'); });
var assertStyle = {};
assertStyle[propName] = propValue;
compStyle.should.deep.equal(assertStyle);
});
describe('With target setted', function() { it('Update target on value change', () => {
view.selectedComponent = component;
beforeEach(function () { view.model.set('value', intValue);
target.model = component; var compStyle = view.selectedComponent.get('style');
view = new PropertyIntegerView({ var assertStyle = {};
model: model, assertStyle[propName] = propValue;
propTarget: target expect(compStyle).toEqual(assertStyle);
}); });
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Update value and input on target swap', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal(parseFloat(intValue));
view.getInputValue().should.equal(intValue);
});
it('Update value after multiple swaps', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = '20em';
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal(20);
view.model.get('unit').should.equal('em');
view.$input.val().should.equal('20');
view.$unit.val().should.equal('em');
});
})
describe('Init property', function() {
beforeEach(function () {
component = new Component();
model = new Property({
type: 'integer',
units: units,
property: propName,
defaults: intValue,
min: minValue,
max: maxValue,
unit: units[1],
});
view = new PropertyIntegerView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', function() {
view.model.get('value').should.equal(parseInt(intValue));
view.model.get('unit').should.equal(units[1]);
});
it('Input value is as default', function() {
view.$input.val().should.equal(intValue);
view.$unit.val().should.equal(units[1]);
});
it('Input follows min', function() {
view.$input.val(minValue - 50).trigger('change');
view.model.get('value').should.equal(minValue);
view.$input.val().should.equal(minValue + "");
});
it('Input follows max', function() {
view.$input.val(maxValue + 50).trigger('change');
view.model.get('value').should.equal(maxValue);
view.$input.val().should.equal(maxValue + "");
});
}); describe('With target setted', () => {
beforeEach(() => {
target.model = component;
view = new PropertyIntegerView({
model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Update value and input on target swap', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual(parseFloat(intValue));
expect(view.getInputValue()).toEqual(intValue);
});
it('Update value after multiple swaps', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = '20em';
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual(20);
expect(view.model.get('unit')).toEqual('em');
expect(view.$input.val()).toEqual('20');
expect(view.$unit.val()).toEqual('em');
});
})
describe('Init property', () => {
beforeEach(() => {
component = new Component();
model = new Property({
type: 'integer',
units,
property: propName,
defaults: intValue,
min: minValue,
max: maxValue,
unit: units[1],
});
view = new PropertyIntegerView({
model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', () => {
expect(view.model.get('value')).toEqual(parseInt(intValue));
expect(view.model.get('unit')).toEqual(units[1]);
});
it('Input value is as default', () => {
expect(view.$input.val()).toEqual(intValue);
expect(view.$unit.val()).toEqual(units[1]);
});
it('Input follows min', () => {
view.$input.val(minValue - 50).trigger('change');
expect(view.model.get('value')).toEqual(minValue);
expect(view.$input.val()).toEqual(minValue + "");
});
it('Input follows max', () => {
view.$input.val(maxValue + 50).trigger('change');
expect(view.model.get('value')).toEqual(maxValue);
expect(view.$input.val()).toEqual(maxValue + "");
});
}); });
}
};
}); });
}
};

317
test/specs/style_manager/view/PropertyRadioView.js

@ -1,177 +1,176 @@
define(function(require, exports, module){ const PropertyRadioView = require('style_manager/view/PropertyRadioView');
'use strict'; const Property = require('style_manager/model/Property');
var PropertyRadioView = require('undefined'); const Component = require('dom_components/model/Component');
var Property = require('StyleManager/model/Property');
var Component = require('DomComponents/model/Component'); module.exports = {
run() {
module.exports = {
run : function(){ describe('PropertyRadioView', () => {
describe('PropertyRadioView', function() { var component;
var $fixtures;
var component; var $fixture;
var $fixtures; var target;
var $fixture; var model;
var target; var view;
var model; var propTarget;
var view; var propName = 'testprop';
var propName = 'testprop'; var propValue = 'test1value';
var propValue = 'test1value'; var defValue = 'test2value';
var defValue = 'test2value'; var options = [
var options = [ { value: 'test1value', 'title': 'testtitle'},
{ value: 'test1value', 'title': 'testtitle'}, { name: 'test2', value: 'test2value'}
{ name: 'test2', value: 'test2value'} ];
];
before(() => {
before(function () { $fixtures = $("#fixtures");
$fixtures = $("#fixtures"); $fixture = $('<div class="sm-fixture"></div>');
$fixture = $('<div class="sm-fixture"></div>'); });
});
beforeEach(function () { beforeEach(() => {
target = new Component(); target = new Component();
component = new Component(); component = new Component();
model = new Property({ model = new Property({
type: 'radio', type: 'radio',
list: options, list: options,
property: propName property: propName
}); });
view = new PropertyRadioView({ propTarget = Object.assign({}, Backbone.Events);
model: model propTarget.model = component;
}); view = new PropertyRadioView({
$fixture.empty().appendTo($fixtures); model,
$fixture.html(view.render().el); propTarget
}); });
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () { afterEach(() => {
//view.remove(); // strange errors ??? //view.remove(); // strange errors ???
}); });
after(function () { after(() => {
$fixture.remove(); $fixture.remove();
delete component; component = null;
}); });
it('Rendered correctly', function() { it('Rendered correctly', () => {
var prop = view.el; var prop = view.el;
$fixture.get(0).querySelector('.property').should.be.ok; expect($fixture.get(0).querySelector('.property')).toExist();
prop.querySelector('.label').should.be.ok; expect(prop.querySelector('.label')).toExist();
prop.querySelector('.field').should.be.ok; expect(prop.querySelector('.field')).toExist();
}); });
it('Radio rendered', function() { it('Radio rendered', () => {
var prop = view.el; var prop = view.el;
prop.querySelector('input[type=radio]').should.be.ok; expect(prop.querySelector('input[type=radio]')).toExist();
}); });
it('Options rendered', function() { it('Options rendered', () => {
var input = view.el.querySelector('#input-holder'); var input = view.el.querySelector('#input-holder');
input.children.length.should.equal(options.length); expect(input.children.length).toEqual(options.length);
}); });
it('Options rendered correctly', function() { it('Options rendered correctly', () => {
var children = view.el.querySelector('#input-holder').children; var children = view.el.querySelector('#input-holder').children;
children[0].querySelector('label').textContent.should.equal('test1value'); expect(children[0].querySelector('label').textContent).toEqual('test1value');
children[1].querySelector('label').textContent.should.equal('test2'); expect(children[1].querySelector('label').textContent).toEqual('test2');
children[0].querySelector('input').value.should.equal(options[0].value); expect(children[0].querySelector('input').value).toEqual(options[0].value);
children[1].querySelector('input').value.should.equal(options[1].value); expect(children[1].querySelector('input').value).toEqual(options[1].value);
children[0].querySelector('label').getAttribute('title').should.equal(options[0].title); expect(children[0].querySelector('label').getAttribute('title')).toEqual(options[0].title);
(children[1].querySelector('label').getAttribute('title') == null) expect(children[1].querySelector('label').getAttribute('title')).toEqual(null);
.should.equal(true); });
});
it('Input should exist', function() { it('Input should exist', () => {
view.$input.should.be.ok; expect(view.$input).toExist();
}); });
it('Input value is empty', function() { it('Input value is empty', () => {
view.model.get('value').should.be.empty; expect(view.model.get('value')).toNotExist();
}); });
it('Update model on input change', function() { it('Update model on input change', () => {
view.setValue(propValue); view.setValue(propValue);
view.model.get('value').should.equal(propValue); expect(view.model.get('value')).toEqual(propValue);
view.getInputValue().should.equal(propValue); expect(view.getInputValue()).toEqual(propValue);
}); });
it('Update input on value change', function() { it('Update input on value change', () => {
view.model.set('value', propValue); view.model.set('value', propValue);
view.getInputValue().should.equal(propValue); expect(view.getInputValue()).toEqual(propValue);
}); });
it('Update target on value change', function() { it('Update target on value change', () => {
view.selectedComponent = component; view.selectedComponent = component;
view.model.set('value', propValue); view.model.set('value', propValue);
var compStyle = view.selectedComponent.get('style'); var compStyle = view.selectedComponent.get('style');
var assertStyle = {}; var assertStyle = {};
assertStyle[propName] = propValue; assertStyle[propName] = propValue;
compStyle.should.deep.equal(assertStyle); expect(compStyle).toEqual(assertStyle);
}); });
describe('With target setted', function() { describe('With target setted', () => {
beforeEach(function () {
target.model = component;
view = new PropertyRadioView({
model: model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Update value and input on target swap', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal(propValue);
view.getInputValue().should.equal(propValue);
});
it('Update value after multiple swaps', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = 'test2value';
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal('test2value');
view.getInputValue().should.equal('test2value');
});
})
describe('Init property', function() {
beforeEach(function () {
component = new Component();
model = new Property({
type: 'select',
list: options,
defaults: defValue,
property: propName
});
view = new PropertyRadioView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', function() {
view.model.get('value').should.equal(defValue);
});
it('Input value is as default', function() {
view.getInputValue().should.equal(defValue);
});
beforeEach(() => {
target.model = component;
view = new PropertyRadioView({
model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Update value and input on target swap', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual(propValue);
expect(view.getInputValue()).toEqual(propValue);
});
it('Update value after multiple swaps', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = 'test2value';
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual('test2value');
expect(view.getInputValue()).toEqual('test2value');
});
})
describe('Init property', () => {
beforeEach(() => {
component = new Component();
model = new Property({
type: 'select',
list: options,
defaults: defValue,
property: propName
}); });
view = new PropertyRadioView({
model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', () => {
expect(view.model.get('value')).toEqual(defValue);
});
it('Input value is as default', () => {
expect(view.getInputValue()).toEqual(defValue);
});
}); });
}
};
}); });
}
};

357
test/specs/style_manager/view/PropertySelectView.js

@ -1,196 +1,197 @@
define(function(require, exports, module){ const PropertySelectView = require('style_manager/view/PropertySelectView');
'use strict'; const Property = require('style_manager/model/Property');
var PropertySelectView = require('undefined'); const Component = require('dom_components/model/Component');
var Property = require('StyleManager/model/Property');
var Component = require('DomComponents/model/Component'); module.exports = {
run() {
module.exports = {
run : function(){ describe('PropertySelectView', () => {
describe('PropertySelectView', function() { var component;
var $fixtures;
var component; var $fixture;
var $fixtures; var target;
var $fixture; var model;
var target; var view;
var model; var propTarget;
var view; var options;
var propName = 'testprop'; var propName = 'testprop';
var propValue = 'test1value'; var propValue = 'test1value';
var defValue = 'test2value'; var defValue = 'test2value';
var options = [ var options = [
{value: 'test1value', style: 'test:style'}, {value: 'test1value', style: 'test:style'},
{name: 'test2', value: 'test2value'} {name: 'test2', value: 'test2value'}
]; ];
before(function () { before(() => {
$fixtures = $("#fixtures"); $fixtures = $("#fixtures");
$fixture = $('<div class="sm-fixture"></div>'); $fixture = $('<div class="sm-fixture"></div>');
}); });
beforeEach(function () { beforeEach(() => {
target = new Component(); propTarget = Object.assign({}, Backbone.Events);
component = new Component(); target = new Component();
model = new Property({ component = new Component();
type: 'select', model = new Property({
list: options, type: 'select',
property: propName list: options,
}); property: propName
view = new PropertySelectView({ });
model: model propTarget.model = component;
}); view = new PropertySelectView({
$fixture.empty().appendTo($fixtures); model,
$fixture.html(view.render().el); propTarget
}); });
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () { afterEach(() => {
//view.remove(); // strange errors ??? //view.remove(); // strange errors ???
}); });
after(function () { after(() => {
$fixture.remove(); $fixture.remove();
delete component; component = null;
}); });
it('Rendered correctly', function() { it('Rendered correctly', () => {
var prop = view.el; var prop = view.el;
$fixture.get(0).querySelector('.property').should.be.ok; expect($fixture.get(0).querySelector('.property')).toExist();
prop.querySelector('.label').should.be.ok; expect(prop.querySelector('.label')).toExist();
prop.querySelector('.field').should.be.ok; expect(prop.querySelector('.field')).toExist();
}); });
it('Select rendered', function() { it('Select rendered', () => {
var prop = view.el; var prop = view.el;
prop.querySelector('select').should.be.ok; expect(prop.querySelector('select')).toExist();
}); });
it('Options rendered', function() { it('Options rendered', () => {
var select = view.el.querySelector('select'); var select = view.el.querySelector('select');
select.children.length.should.equal(options.length); expect(select.children.length).toEqual(options.length);
}); });
it('Options rendered correctly', function() { it('Options rendered correctly', () => {
var select = view.el.querySelector('select'); var select = view.el.querySelector('select');
var children = select.children; var children = select.children;
children[0].value.should.equal(options[0].value); expect(children[0].value).toEqual(options[0].value);
children[1].value.should.equal(options[1].value); expect(children[1].value).toEqual(options[1].value);
children[0].textContent.should.equal(options[0].value); expect(children[0].textContent).toEqual(options[0].value);
children[1].textContent.should.equal(options[1].name); expect(children[1].textContent).toEqual(options[1].name);
children[0].getAttribute('style').should.equal(options[0].style); expect(children[0].getAttribute('style')).toEqual(options[0].style);
(children[1].getAttribute('style') == null).should.equal(true); expect(children[1].getAttribute('style')).toEqual(null);
}); });
it('Input should exist', function() { it('Input should exist', () => {
view.$input.should.be.ok; expect(view.$input).toExist();
}); });
it('Input value is empty', function() { it('Input value is empty', () => {
view.model.get('value').should.be.empty; expect(view.model.get('value')).toNotExist();
}); });
it('Update model on input change', function() { it('Update model on input change', () => {
view.$input.val(propValue).trigger('change'); view.$input.val(propValue).trigger('change');
view.model.get('value').should.equal(propValue); expect(view.model.get('value')).toEqual(propValue);
}); });
it('Update input on value change', function() { it('Update input on value change', () => {
view.model.set('value', propValue); view.model.set('value', propValue);
view.$input.val().should.equal(propValue); expect(view.$input.val()).toEqual(propValue);
}); });
it('Update target on value change', function() { it('Update target on value change', () => {
view.selectedComponent = component; view.selectedComponent = component;
view.model.set('value', propValue); view.model.set('value', propValue);
var compStyle = view.selectedComponent.get('style'); var compStyle = view.selectedComponent.get('style');
var assertStyle = {}; var assertStyle = {};
assertStyle[propName] = propValue; assertStyle[propName] = propValue;
compStyle.should.deep.equal(assertStyle); expect(compStyle).toEqual(assertStyle);
}); });
describe('With target setted', function() { describe('With target setted', () => {
beforeEach(function () {
target.model = component;
view = new PropertySelectView({
model: model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Update value and input on target swap', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal(propValue);
view.$input.val().should.equal(propValue);
});
it('Update value after multiple swaps', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = 'test2value';
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal('test2value');
view.$input.val().should.equal('test2value');
});
})
describe('Init property', function() {
beforeEach(function () {
component = new Component();
model = new Property({
type: 'select',
list: options,
defaults: defValue,
property: propName
});
view = new PropertySelectView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', function() {
view.model.get('value').should.equal(defValue);
});
it('Empty value as default', function() {
options = [
{value: 'test1value', name: 'test1'},
{value: 'test2value', name: 'test2'},
{value: '', name: 'TestDef'}
];
component = new Component();
model = new Property({
type: 'select',
list: options,
defaults: '',
property: 'emptyDefault'
});
view = new PropertySelectView({
model: model
});
$fixture.html(view.render().el);
view.$input.val().should.equal('');
});
it('Input value is as default', function() {
view.$input.val().should.equal(defValue);
});
beforeEach(() => {
target.model = component;
view = new PropertySelectView({
model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Update value and input on target swap', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual(propValue);
expect(view.$input.val()).toEqual(propValue);
});
it('Update value after multiple swaps', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = 'test2value';
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual('test2value');
expect(view.$input.val()).toEqual('test2value');
});
})
describe('Init property', () => {
beforeEach(() => {
component = new Component();
model = new Property({
type: 'select',
list: options,
defaults: defValue,
property: propName
});
view = new PropertySelectView({
model
}); });
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', () => {
expect(view.model.get('value')).toEqual(defValue);
});
it('Empty value as default', () => {
options = [
{value: 'test1value', name: 'test1'},
{value: 'test2value', name: 'test2'},
{value: '', name: 'TestDef'}
];
component = new Component();
model = new Property({
type: 'select',
list: options,
defaults: '',
property: 'emptyDefault'
});
view = new PropertySelectView({
model
});
$fixture.html(view.render().el);
expect(view.$input.val()).toEqual('');
});
it('Input value is as default', () => {
expect(view.$input.val()).toEqual(defValue);
});
}); });
}
};
}); });
}
};

655
test/specs/style_manager/view/PropertyStackView.js

@ -1,350 +1,347 @@
define(function(require, exports, module){ const PropertyStackView = require('style_manager/view/PropertyStackView');
'use strict'; const Property = require('style_manager/model/Property');
var PropertyStackView = require('undefined'); const Component = require('dom_components/model/Component');
var Property = require('StyleManager/model/Property');
var Component = require('DomComponents/model/Component'); module.exports = {
run() {
module.exports = {
run : function(){ describe('PropertyStackView', () => {
describe('PropertyStackView', function() { var component;
var $fixtures;
var component; var $fixture;
var $fixtures; var target;
var $fixture; var model;
var target; var view;
var model; var propName = 'testprop';
var view; var propValue = 'test1value';
var propName = 'testprop'; var defValue = 'test2value';
var propValue = 'test1value'; var layers = [
var defValue = 'test2value'; {value: 'lval1'},
var layers = [ {value: 'lval2 lval22'},
{value: 'lval1'}, {value: 'lval3 lval32 lval33'}
{value: 'lval2 lval22'}, ];
{value: 'lval3 lval32 lval33'} var properties = [
]; {property: 'subprop1'},
var properties = [ {
{property: 'subprop1'}, type: 'integer',
{ property: 'subprop2',
type: 'integer', defaults: 0,
property: 'subprop2', units: ['%', 'px']
defaults: 0, },
units: ['%', 'px'] {
}, type: 'select',
{ property: 'subprop3',
type: 'select', defaults: 'val2',
property: 'subprop3', list: [
defaults: 'val2', {value:'val1'},
list: [ {value:'val2'},
{value:'val1'}, {value:'val3'},
{value:'val2'}, ]
{value:'val3'}, },
] ];
},
]; before(() => {
$fixtures = $("#fixtures");
before(function () { $fixture = $('<div class="sm-fixture"></div>');
$fixtures = $("#fixtures"); });
$fixture = $('<div class="sm-fixture"></div>');
});
beforeEach(function () { beforeEach(() => {
target = new Component(); target = new Component();
component = new Component(); component = new Component();
target.model = component; target.model = component;
model = new Property({ model = new Property({
type: 'stack', type: 'stack',
property: propName, property: propName,
properties: properties properties
}); });
view = new PropertyStackView({ view = new PropertyStackView({
model: model model
}); });
$fixture.empty().appendTo($fixtures); $fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el); $fixture.html(view.render().el);
}); });
afterEach(function () { afterEach(() => {
//view.remove(); // strange errors ??? //view.remove(); // strange errors ???
}); });
after(function () { after(() => {
$fixture.remove(); $fixture.remove();
delete component; component = null;
delete view; view = null;
delete model; model = null;
}); });
it('Rendered correctly', function() { it('Rendered correctly', () => {
var prop = view.el; var prop = view.el;
$fixture.get(0).querySelector('.property').should.be.ok; expect($fixture.get(0).querySelector('.property')).toExist();
prop.querySelector('.label').should.be.ok; expect(prop.querySelector('.label')).toExist();
prop.querySelector('.field').should.be.ok; expect(prop.querySelector('.field')).toExist();
prop.querySelector('#add').should.be.ok; expect(prop.querySelector('#add')).toExist();
}); });
it('Layers rendered', function() { it('Layers rendered', () => {
view.el.querySelector('.layers').should.be.ok; expect(view.el.querySelector('.layers')).toExist();
}); });
it('Layers should exist', function() { it('Layers should exist', () => {
view.$props.should.be.ok; expect(view.$props).toExist();
}); });
it('Layers rendered correctly', function() { it('Layers rendered correctly', () => {
var children = view.$props.get(0).children; var children = view.$props.get(0).children;
children.length.should.equal(properties.length + 1); expect(children.length).toEqual(properties.length + 1);
children[0].id.should.equal(properties[0].property); expect(children[0].id).toEqual(properties[0].property);
children[1].id.should.equal(properties[1].property); expect(children[1].id).toEqual(properties[1].property);
children[2].id.should.equal(properties[2].property); expect(children[2].id).toEqual(properties[2].property);
}); });
it('Input value is empty', function() { it('Input value is empty', () => {
view.model.get('value').should.be.empty; expect(view.model.get('value')).toNotExist();
}); });
it('Layers container is empty', function() { it('Layers container is empty', () => {
var layers = view.el.querySelector('.layers'); var layers = view.el.querySelector('.layers');
layers.innerHTML.should.be.empty; expect(layers.innerHTML).toNotExist();
}); });
describe('With layers', function() { describe('With layers', () => {
beforeEach(function () {
model = new Property({
type: 'stack',
property: propName,
properties: properties,
});
view = new PropertyStackView({
model: model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
model.get('layers').add(layers);
});
it('Layers inserted', function() {
view.getLayers().length.should.equal(layers.length);
});
it('Get value on index', function() {
view.model.set('stackIndex', 1);
view.valueOnIndex(1).should.equal('lval22');
});
it('createValue merges layers', function() {
view.createValue().should.equal('lval1, lval2 lval22, lval3 lval32 lval33');
});
it('Add layer', function() {
view.addLayer();
view.getLayers().length.should.equal(layers.length+1);
});
beforeEach(() => {
model = new Property({
type: 'stack',
property: propName,
properties,
}); });
view = new PropertyStackView({
model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
model.get('layers').add(layers);
});
it('Layers inserted', () => {
expect(view.getLayers().length).toEqual(layers.length);
});
it('Get value on index', () => {
view.model.set('stackIndex', 1);
expect(view.valueOnIndex(1)).toEqual('lval22');
});
it('createValue merges layers', () => {
expect(view.createValue()).toEqual('lval1, lval2 lval22, lval3 lval32 lval33');
});
describe('With target setted', function() { it('Add layer', () => {
view.addLayer();
var prop2Val; expect(view.getLayers().length).toEqual(layers.length + 1);
var prop3Val; });
var prop2Unit;
var finalResult;
var $prop1;
var $prop2;
var $prop3;
beforeEach(function () {
model = new Property({
type: 'stack',
property: propName,
properties: properties
});
view = new PropertyStackView({
model: model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
prop3Val = properties[2].list[2].value;
prop2Val = properties[1].defaults;
prop2Unit = properties[1].units[0];
finalResult = propValue + ' ' + prop2Val + prop2Unit +' ' + prop3Val;
view.addLayer();
$prop1 = view.$props.find('#' + properties[0].property + ' input');
$prop2 = view.$props.find('#' + properties[1].property + ' input');
$prop3 = view.$props.find('#' + properties[2].property + ' select');
});
it('Update model on input change', function() {
$prop1.val(propValue).trigger('change');
$prop3.val(prop3Val).trigger('change');
view.model.get('value').should.equal(finalResult);
});
it('Update value on models change', function() {
view.model.get('properties').at(0).set('value', propValue);
view.model.get('properties').at(2).set('value', prop3Val);
view.model.get('value').should.equal(finalResult);
});
it('Update target on value change', function() {
$prop1.val(propValue).trigger('change');
var compStyle = view.getTarget().get('style');
var assertStyle = {};
assertStyle[propName] = propValue + ' 0% val2';
compStyle.should.deep.equal(assertStyle);
});
it('Update value and input on target swap', function() {
var style = {};
var finalResult2 = 'A B C';
style[propName] = finalResult + ', ' + finalResult2;
component.set('style', style);
view.propTarget.trigger('update');
var layers = view.getLayers();
layers.at(0).get('value').should.equal(finalResult);
layers.at(1).get('value').should.equal(finalResult2);
});
it('Update value after multiple swaps', function() {
var style = {};
var finalResult2 = 'A2 B2 C2';
style[propName] = finalResult;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = finalResult + ', ' + finalResult2;
component.set('style', style);
view.propTarget.trigger('update');
var layers = view.getLayers();
layers.at(0).get('value').should.equal(finalResult);
layers.at(1).get('value').should.equal(finalResult2);
});
it('The value is correctly extracted from the composite string', function() {
var style = {};
style[propName] = 'value1 value2, value3 value4';
component.set('style', style);
view.propTarget.trigger('update');
view.model.set('stackIndex', 1);
view.valueOnIndex(0).should.equal('value3');
view.valueOnIndex(1).should.equal('value4');
(view.valueOnIndex(2) === null).should.equal(true);
});
it('The value is correctly extracted from the string with functions', function() {
var style = {};
style[propName] = 'func(a1a, s2a,d3a) value1 value2, func(4ddb, aAS5b, sS.6b) value3';
component.set('style', style);
view.propTarget.trigger('update');
view.model.set('stackIndex', 1);
view.valueOnIndex(0).should.equal('func(4ddb,aAS5b,sS.6b)');
view.valueOnIndex(1).should.equal('value3');
(view.valueOnIndex(2) === null).should.equal(true);
});
it('Build value from properties', function() {
view.model.get('properties').at(0).set('value', propValue);
view.model.get('properties').at(2).set('value', prop3Val);
view.build().should.equal(finalResult);
});
});
describe('With target setted', () => {
var prop2Val;
var prop3Val;
var prop2Unit;
var finalResult;
var $prop1;
var $prop2;
var $prop3;
beforeEach(() => {
model = new Property({
type: 'stack',
property: propName,
properties
}); });
view = new PropertyStackView({
model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
prop3Val = properties[2].list[2].value;
prop2Val = properties[1].defaults;
prop2Unit = properties[1].units[0];
finalResult = propValue + ' ' + prop2Val + prop2Unit +' ' + prop3Val;
view.addLayer();
$prop1 = view.$props.find('#' + properties[0].property + ' input');
$prop2 = view.$props.find('#' + properties[1].property + ' input');
$prop3 = view.$props.find('#' + properties[2].property + ' select');
});
it('Update model on input change', () => {
$prop1.val(propValue).trigger('change');
$prop3.val(prop3Val).trigger('change');
expect(view.model.get('value')).toEqual(finalResult);
});
it('Update value on models change', () => {
view.model.get('properties').at(0).set('value', propValue);
view.model.get('properties').at(2).set('value', prop3Val);
expect(view.model.get('value')).toEqual(finalResult);
});
it('Update target on value change', () => {
$prop1.val(propValue).trigger('change');
var compStyle = view.getTarget().get('style');
var assertStyle = {};
assertStyle[propName] = propValue + ' 0% val2';
expect(compStyle).toEqual(assertStyle);
});
it('Update value and input on target swap', () => {
var style = {};
var finalResult2 = 'A B C';
style[propName] = finalResult + ', ' + finalResult2;
component.set('style', style);
view.propTarget.trigger('update');
var layers = view.getLayers();
expect(layers.at(0).get('value')).toEqual(finalResult);
expect(layers.at(1).get('value')).toEqual(finalResult2);
});
it('Update value after multiple swaps', () => {
var style = {};
var finalResult2 = 'A2 B2 C2';
style[propName] = finalResult;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = finalResult + ', ' + finalResult2;
component.set('style', style);
view.propTarget.trigger('update');
var layers = view.getLayers();
expect(layers.at(0).get('value')).toEqual(finalResult);
expect(layers.at(1).get('value')).toEqual(finalResult2);
});
it('The value is correctly extracted from the composite string', () => {
var style = {};
style[propName] = 'value1 value2, value3 value4';
component.set('style', style);
view.propTarget.trigger('update');
view.model.set('stackIndex', 1);
expect(view.valueOnIndex(0)).toEqual('value3');
expect(view.valueOnIndex(1)).toEqual('value4');
expect(view.valueOnIndex(2)).toEqual(null);
});
it('The value is correctly extracted from the string with functions', () => {
var style = {};
style[propName] = 'func(a1a, s2a,d3a) value1 value2, func(4ddb, aAS5b, sS.6b) value3';
component.set('style', style);
view.propTarget.trigger('update');
view.model.set('stackIndex', 1);
expect(view.valueOnIndex(0)).toEqual('func(4ddb,aAS5b,sS.6b)');
expect(view.valueOnIndex(1)).toEqual('value3');
expect(view.valueOnIndex(2)).toEqual(null);
});
it('Build value from properties', () => {
view.model.get('properties').at(0).set('value', propValue);
view.model.get('properties').at(2).set('value', prop3Val);
expect(view.build()).toEqual(finalResult);
});
describe('Detached with target setted', function() { });
var prop2Val;
var prop3Val;
var prop2Unit;
var finalResult;
var $prop1;
var $prop2;
var $prop3;
var compStyle = {
subprop1: '1px, 20px, 30px',
subprop2: 'A, B, C',
subprop3: 'W, X, Y',
subprop555: 'T, T, T',
};
beforeEach(function () {
model = new Property({
type: 'stack',
property: propName,
properties: properties,
detached: true,
});
view = new PropertyStackView({
model: model,
propTarget: target
});
$fixture.html(view.render().el);
prop3Val = properties[2].list[2].value;
prop2Val = properties[1].defaults;
prop2Unit = properties[1].units[0];
finalResult = propValue + ' ' + prop2Val + prop2Unit +' ' + prop3Val;
view.addLayer();
$prop1 = view.$props.find('#' + properties[0].property + ' input');
$prop2 = view.$props.find('#' + properties[1].property + ' input');
$prop3 = view.$props.find('#' + properties[2].property + ' select');
});
it('Returns correctly layers array from target', function() {
component.set('style', compStyle);
var result = [{
subprop1: '1px',
subprop2: 'A',
subprop3: 'W',
},{
subprop1: '20px',
subprop2: 'B',
subprop3: 'X',
},{
subprop1: '30px',
subprop2: 'C',
subprop3: 'Y',
}];
view.getLayersFromTarget().should.deep.equal(result);
});
it('Update target on detached value change', function() {
$prop1.val(propValue).trigger('change');
var compStyle = view.getTarget().get('style');
var assertStyle = {};
assertStyle[properties[0].property] = $prop1.val();
assertStyle[properties[1].property] = '0%';
assertStyle[properties[2].property] = properties[2].defaults;
compStyle.should.deep.equal(assertStyle);
});
it('Update value and input on target swap', function() {
var style = {};
component.set('style', compStyle);
view.propTarget.trigger('update');
var layers = view.getLayers();
layers.length.should.equal(3);
layers.at(0).get('values').should.deep.equal({
subprop1: '1px',
subprop2: 'A',
subprop3: 'W',
});
layers.at(1).get('values').should.deep.equal({
subprop1: '20px',
subprop2: 'B',
subprop3: 'X',
});
layers.at(2).get('values').should.deep.equal({
subprop1: '30px',
subprop2: 'C',
subprop3: 'Y',
});
});
describe('Detached with target setted', () => {
var prop2Val;
var prop3Val;
var prop2Unit;
var finalResult;
var $prop1;
var $prop2;
var $prop3;
var compStyle = {
subprop1: '1px, 20px, 30px',
subprop2: 'A, B, C',
subprop3: 'W, X, Y',
subprop555: 'T, T, T',
};
beforeEach(() => {
model = new Property({
type: 'stack',
property: propName,
properties,
detached: true,
});
view = new PropertyStackView({
model,
propTarget: target
});
$fixture.html(view.render().el);
prop3Val = properties[2].list[2].value;
prop2Val = properties[1].defaults;
prop2Unit = properties[1].units[0];
finalResult = propValue + ' ' + prop2Val + prop2Unit +' ' + prop3Val;
view.addLayer();
$prop1 = view.$props.find('#' + properties[0].property + ' input');
$prop2 = view.$props.find('#' + properties[1].property + ' input');
$prop3 = view.$props.find('#' + properties[2].property + ' select');
});
it('Returns correctly layers array from target', () => {
component.set('style', compStyle);
var result = [{
subprop1: '1px',
subprop2: 'A',
subprop3: 'W',
},{
subprop1: '20px',
subprop2: 'B',
subprop3: 'X',
},{
subprop1: '30px',
subprop2: 'C',
subprop3: 'Y',
}];
expect(view.getLayersFromTarget()).toEqual(result);
});
it('Update target on detached value change', () => {
$prop1.val(propValue).trigger('change');
var compStyle = view.getTarget().get('style');
var assertStyle = {};
assertStyle[properties[0].property] = $prop1.val();
assertStyle[properties[1].property] = '0%';
assertStyle[properties[2].property] = properties[2].defaults;
expect(compStyle).toEqual(assertStyle);
});
it('Update value and input on target swap', () => {
var style = {};
component.set('style', compStyle);
view.propTarget.trigger('update');
var layers = view.getLayers();
expect(layers.length).toEqual(3);
expect(layers.at(0).get('values')).toEqual({
subprop1: '1px',
subprop2: 'A',
subprop3: 'W',
});
expect(layers.at(1).get('values')).toEqual({
subprop1: '20px',
subprop2: 'B',
subprop3: 'X',
});
expect(layers.at(2).get('values')).toEqual({
subprop1: '30px',
subprop2: 'C',
subprop3: 'Y',
}); });
});
}); });
}
};
}); });
}
};

427
test/specs/style_manager/view/PropertyView.js

@ -1,238 +1,233 @@
define(function(require, exports, module){ const PropertyView = require('style_manager/view/PropertyView');
'use strict'; const Property = require('style_manager/model/Property');
var PropertyView = require('undefined'); const Component = require('dom_components/model/Component');
var Property = require('StyleManager/model/Property');
var Component = require('DomComponents/model/Component'); module.exports = {
run() {
module.exports = {
run : function(){ describe('PropertyView', () => {
describe('PropertyView', function() { var component;
var $fixtures;
var component; var $fixture;
var $fixtures; var target;
var $fixture; var model;
var target; var view;
var model; var propTarget;
var view; var options;
var propName = 'testprop'; var propName = 'testprop';
var propValue = 'testvalue'; var propValue = 'testvalue';
var defValue = 'testDefault'; var defValue = 'testDefault';
before(function () { before(() => {
$fixtures = $("#fixtures"); $fixtures = $("#fixtures");
$fixture = $('<div class="sm-fixture"></div>'); $fixture = $('<div class="sm-fixture"></div>');
}); });
beforeEach(function () {
target = new Component();
component = new Component();
model = new Property({property: propName});
view = new PropertyView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () { beforeEach(() => {
//view.remove(); // strange errors ??? propTarget = Object.assign({}, Backbone.Events);
}); target = new Component();
component = new Component();
model = new Property({property: propName});
propTarget.model = component;
options = {
model,
propTarget
};
view = new PropertyView(options);
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
after(function () { afterEach(() => {
$fixture.remove(); //view.remove(); // strange errors ???
delete component; });
});
it('Rendered correctly', function() { after(() => {
var prop = view.el; $fixture.remove();
$fixture.get(0).querySelector('.property').should.be.ok; component = null;
prop.querySelector('.label').should.be.ok; });
prop.querySelector('.field').should.be.ok;
});
it('Input should exist', function() { it('Rendered correctly', () => {
view.$input.should.be.ok; var prop = view.el;
}); expect($fixture.get(0).querySelector('.property')).toExist();
expect(prop.querySelector('.label')).toExist();
expect(prop.querySelector('.field')).toExist();
});
it('Input value is empty', function() { it('Input should exist', () => {
view.model.get('value').should.be.empty; expect(view.$input).toExist();
view.$input.val().should.be.empty; });
});
it('Model not change without update trigger', function() { it('Input value is empty', () => {
view.$input.val(propValue); expect(view.model.get('value')).toNotExist();
view.model.get('value').should.be.empty; expect(view.$input.val()).toNotExist();
}); });
// Tests valueUpdated() it('Model not change without update trigger', () => {
it('Update model on input change', function() { view.$input.val(propValue);
view.$input.val(propValue).trigger('change'); expect(view.model.get('value')).toNotExist();
view.model.get('value').should.equal(propValue); });
});
// Tests getValueForTarget() // Tests valueUpdated()
it('Get value for target', function() { it('Update model on input change', () => {
view.model.set('value', propValue); view.$input.val(propValue).trigger('change');
view.getValueForTarget().should.equal(propValue); expect(view.model.get('value')).toEqual(propValue);
}); });
// Tests valueChanged() -> ... // Tests getValueForTarget()
it('Update input on value change', function() { it('Get value for target', () => {
view.model.set('value', propValue); view.model.set('value', propValue);
view.$input.val().should.equal(propValue); expect(view.getValueForTarget()).toEqual(propValue);
}); });
it('Update target on value change', function() { // Tests valueChanged() -> ...
view.selectedComponent = component; it('Update input on value change', () => {
view.model.set('value', propValue); view.model.set('value', propValue);
var compStyle = view.selectedComponent.get('style'); expect(view.$input.val()).toEqual(propValue);
var assertStyle = {}; });
assertStyle[propName] = propValue;
compStyle.should.deep.equal(assertStyle);
});
it('Update target on value change with functionName', function() { it('Update target on value change', () => {
view.selectedComponent = component; view.model.set('value', propValue);
view.model.set('functionName', 'testfunc'); var compStyle = view.getTargetModel().get('style');
view.model.set('value', propValue); var assertStyle = {};
var compStyle = view.selectedComponent.get('style'); assertStyle[propName] = propValue;
var assertStyle = {}; expect(compStyle).toEqual(assertStyle);
assertStyle[propName] = 'testfunc(' + propValue + ')'; });
compStyle.should.deep.equal(assertStyle);
});
it('Clean target from the property if its value is empty', function() { it('Update target on value change with functionName', () => {
view.selectedComponent = component; view.model.set('functionName', 'testfunc');
view.model.set('value', propValue); view.model.set('value', propValue);
view.model.set('value', ''); var compStyle = view.getTargetModel().get('style');
var compStyle = view.selectedComponent.get('style'); var assertStyle = {};
compStyle.should.deep.equal({}); assertStyle[propName] = 'testfunc(' + propValue + ')';
}); expect(compStyle).toEqual(assertStyle);
});
it('Check stylable element', function() { it('Clean target from the property if its value is empty', () => {
view.selectedComponent = component; view.model.set('value', propValue);
view.isTargetStylable().should.equal(true); view.model.set('value', '');
component.set('stylable', false); var compStyle = view.getTargetModel().get('style');
view.isTargetStylable().should.equal(false); expect(compStyle).toEqual({});
component.set('stylable', [propName]); });
view.isTargetStylable().should.equal(true);
component.set('stylable', ['test1', propName]);
view.isTargetStylable().should.equal(true);
component.set('stylable', ['test1', 'test2']);
view.isTargetStylable().should.equal(false);
});
it('Target style is empty without values', function() { it('Check stylable element', () => {
view.selectedComponent = component; expect(view.isTargetStylable()).toEqual(true);
view.getComponentValue().should.be.empty; component.set('stylable', false);
}); expect(view.isTargetStylable()).toEqual(false);
component.set('stylable', [propName]);
expect(view.isTargetStylable()).toEqual(true);
component.set('stylable', ['test1', propName]);
expect(view.isTargetStylable()).toEqual(true);
component.set('stylable', ['test1', 'test2']);
expect(view.isTargetStylable()).toEqual(false);
});
it('Target style is correct', function() { it('Target style is empty without values', () => {
view.selectedComponent = component; expect(view.getComponentValue()).toNotExist();
var style = {}; });
style[propName] = propValue;
component.set('style', style);
view.getComponentValue().should.equal(propValue);
});
it('Target style is empty with an other style', function() { it('Target style is correct', () => {
view.selectedComponent = component; var style = {};
var style = {}; style[propName] = propValue;
style[propName + '2'] = propValue; component.set('style', style);
component.set('style', style); expect(view.getComponentValue()).toEqual(propValue);
view.getComponentValue().should.be.empty; });
});
it('Fetch value from function', function() { it('Target style is empty with an other style', () => {
view.selectedComponent = component; var style = {};
var style = {}; style[propName + '2'] = propValue;
style[propName] = 'testfun(' + propValue + ')'; component.set('style', style);
component.set('style', style); expect(view.getComponentValue()).toNotExist();
view.model.set('functionName', 'testfun'); });
view.getComponentValue().should.equal(propValue);
});
describe('With target setted', function() { it('Fetch value from function', () => {
view.selectedComponent = component;
beforeEach(function () { var style = {};
target.model = component; style[propName] = 'testfun(' + propValue + ')';
view = new PropertyView({ component.set('style', style);
model: model, view.model.set('functionName', 'testfun');
propTarget: target expect(view.getComponentValue()).toEqual(propValue);
}); });
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('updateTargetStyle', function() {
view.updateTargetStyle(propValue);
var style = {};
style[propName] = propValue;
component.get('style').should.deep.equal(style);
});
it('updateTargetStyle with custom property', function() {
view.updateTargetStyle(propValue, propName + '2');
var style = {};
style[propName + '2'] = propValue;
component.get('style').should.deep.equal(style);
});
it('Update value and input on target swap', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal(propValue);
view.$input.val().should.equal(propValue);
});
it('Update value after multiple swaps', function() {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = propValue + '2';
component.set('style', style);
view.propTarget.trigger('update');
view.model.get('value').should.equal(propValue + '2');
view.$input.val().should.equal(propValue + '2');
});
})
describe('Init property', function() {
beforeEach(function () {
component = new Component();
model = new Property({
property: propName,
defaults: defValue
});
view = new PropertyView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', function() {
view.model.get('value').should.equal(defValue);
});
it('Placeholder as default', function() {
view.$input.attr('placeholder').should.equal(defValue);
});
it('Input value is empty', function() {
view.$input.val().should.equal(defValue);
});
}); describe('With target setted', () => {
beforeEach(() => {
target.model = component;
view = new PropertyView({
model,
propTarget: target
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('updateTargetStyle', () => {
view.updateTargetStyle(propValue);
var style = {};
style[propName] = propValue;
expect(component.get('style')).toEqual(style);
});
it('updateTargetStyle with custom property', () => {
view.updateTargetStyle(propValue, propName + '2');
var style = {};
style[propName + '2'] = propValue;
expect(component.get('style')).toEqual(style);
});
it('Update value and input on target swap', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual(propValue);
expect(view.$input.val()).toEqual(propValue);
});
it('Update value after multiple swaps', () => {
var style = {};
style[propName] = propValue;
component.set('style', style);
view.propTarget.trigger('update');
style[propName] = propValue + '2';
component.set('style', style);
view.propTarget.trigger('update');
expect(view.model.get('value')).toEqual(propValue + '2');
expect(view.$input.val()).toEqual(propValue + '2');
});
})
describe('Init property', () => {
beforeEach(() => {
component = new Component();
model = new Property({
property: propName,
defaults: defValue
});
view = new PropertyView({
model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
it('Value as default', () => {
expect(view.model.get('value')).toEqual(defValue);
});
it('Placeholder as default', () => {
expect(view.$input.attr('placeholder')).toEqual(defValue);
});
it('Input value is empty', () => {
expect(view.$input.val()).toEqual(defValue);
});
}); });
}
};
}); });
}
};

181
test/specs/style_manager/view/SectorView.js

@ -1,109 +1,104 @@
define(function(require, exports, module){ const SectorView = require('style_manager/view/SectorView');
'use strict'; const Sector = require('style_manager/model/Sector');
var SectorView = require('undefined');
var Sector = require('StyleManager/model/Sector');
module.exports = { module.exports = {
run : function(){ run() {
describe('SectorView', function() { describe('SectorView', () => {
var $fixtures; var $fixtures;
var $fixture; var $fixture;
var model; var model;
var view; var view;
before(function () { before(() => {
$fixtures = $("#fixtures"); $fixtures = $("#fixtures");
$fixture = $('<div class="sector-fixture"></div>'); $fixture = $('<div class="sector-fixture"></div>');
}); });
beforeEach(function () {
model = new Sector();
view = new SectorView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () { beforeEach(() => {
view.remove(); model = new Sector();
}); view = new SectorView({
model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
after(function () { afterEach(() => {
$fixture.remove(); view.remove();
}); });
it('Rendered correctly', function() { after(() => {
var sector = view.el; $fixture.remove();
sector.should.be.ok; });
sector.querySelector('.title').should.be.ok;
var props = sector.querySelector('.properties');
props.should.be.ok;
sector.classList.contains('open').should.equal(true);
});
it('No properties', function() { it('Rendered correctly', () => {
var props = view.el.querySelector('.properties'); var sector = view.el;
props.innerHTML.should.equal('<div class="clear"></div>'); expect(sector.querySelector('.title')).toExist();
}); var props = sector.querySelector('.properties');
expect(props).toExist();
expect(sector.classList.contains('open')).toEqual(true);
});
it('Update on open', function() { it('No properties', () => {
var sector = view.el; var props = view.el.querySelector('.properties');
var props = sector.querySelector('.properties'); expect(props.innerHTML).toEqual('<div class="clear"></div>');
model.set('open', false); });
sector.classList.contains('open').should.equal(false);
props.style.display.should.equal('none');
});
it('Toggle on click', function() { it('Update on open', () => {
var sector = view.el; var sector = view.el;
view.$el.find('.title').click(); var props = sector.querySelector('.properties');
sector.classList.contains('open').should.equal(false); model.set('open', false);
}); expect(sector.classList.contains('open')).toEqual(false);
expect(props.style.display).toEqual('none');
});
describe('Init with options', function() { it('Toggle on click', () => {
var sector = view.el;
beforeEach(function () { view.$el.find('.title').click();
model = new Sector({ expect(sector.classList.contains('open')).toEqual(false);
open: false, });
name: 'TestName',
properties: [
{type:'integer'},
{type:'integer'},
{type:'integer'},
]
});
view = new SectorView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () {
view.remove();
});
it('Rendered correctly2', function() {
var sector = view.el;
var props = sector.querySelector('.properties');
sector.querySelector('.title').innerHTML.should.contain('TestName');
props.should.be.ok;
sector.classList.contains('open').should.equal(false);
props.style.display.should.equal('none');
});
it('Has properties', function() {
var props = view.el.querySelector('.properties');
props.children.length.should.equal(4); // Last one is 'clear' element
});
describe('Init with options', () => {
beforeEach(() => {
model = new Sector({
open: false,
name: 'TestName',
properties: [
{type:'integer'},
{type:'integer'},
{type:'integer'},
]
});
view = new SectorView({
model
}); });
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(() => {
view.remove();
});
it('Rendered correctly2', () => {
var sector = view.el;
var props = sector.querySelector('.properties');
expect(sector.querySelector('.title').innerHTML).toContain('TestName');
expect(props).toExist();
expect(sector.classList.contains('open')).toEqual(false);
expect(props.style.display).toEqual('none');
});
it('Has properties', () => {
var props = view.el.querySelector('.properties');
expect(props.children.length).toEqual(4); // Last one is 'clear' element
});
}); });
}
};
}); });
}
};

92
test/specs/style_manager/view/SectorsView.js

@ -1,51 +1,47 @@
define(function(require, exports, module){ const SectorsView = require('style_manager/view/SectorsView');
'use strict'; const Sectors = require('style_manager/model/Sectors');
var SectorsView = require('undefined');
var Sectors = require('StyleManager/model/Sectors');
module.exports = {
run : function(){
describe('SectorsView', function() {
var $fixtures;
var $fixture;
var model;
var view;
before(function () {
$fixtures = $("#fixtures");
$fixture = $('<div class="sectors-fixture"></div>');
});
beforeEach(function () {
model = new Sectors([]);
view = new SectorsView({
collection: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () {
view.collection.reset();
});
after(function () {
$fixture.remove();
});
it("Collection is empty", function (){
view.el.innerHTML.should.be.empty;
});
it("Add new sectors", function (){
view.collection.add([{}, {}]);
view.el.children.length.should.equal(2);
});
module.exports = {
run() {
describe('SectorsView', () => {
var $fixtures;
var $fixture;
var model;
var view;
before(() => {
$fixtures = $("#fixtures");
$fixture = $('<div class="sectors-fixture"></div>');
});
beforeEach(() => {
model = new Sectors([]);
view = new SectorsView({
collection: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(() => {
view.collection.reset();
});
after(() => {
$fixture.remove();
});
it("Collection is empty", () => {
expect(view.el.innerHTML).toEqual('');
});
it("Add new sectors", () => {
view.collection.add([{}, {}]);
expect(view.el.children.length).toEqual(2);
}); });
}
};
}); });
}
};

8
test/specs/test_utils.js

@ -1,15 +1,15 @@
module.exports = { module.exports = {
storageMock: function() { storageMock() {
var db = {}; var db = {};
return { return {
id: 'testStorage', id: 'testStorage',
store: function(data){ store(data) {
db = data; db = data;
}, },
load: function(keys){ load(keys) {
return db; return db;
}, },
getDb: function(){ getDb() {
return db; return db;
}, },
}; };

23
test/specs/trait_manager/index.js

@ -0,0 +1,23 @@
const TraitManager = require('trait_manager');
const TraitsModel = require('./model/TraitsModel');
const TraitsView = require('./view/TraitsView');
describe('TraitManager', () => {
describe('Main', () => {
var obj;
beforeEach(() => {
obj = new TraitManager().init();
});
afterEach(() => {
obj = null;
});
});
TraitsModel.run();
TraitsView.run();
});

25
test/specs/trait_manager/main.js

@ -1,25 +0,0 @@
define(function(require, exports, module){
'use strict';
var TraitManager = require('TraitManager');
var TraitsView = require('undefined');
describe('TraitManager', function() {
describe('Main', function() {
var obj;
beforeEach(function () {
obj = new TraitManager().init();
});
afterEach(function () {
delete obj;
});
});
TraitsModel.run();
TraitsView.run();
});
});

51
test/specs/trait_manager/model/TraitsModel.js

@ -1,34 +1,31 @@
define(function(require, exports, module){ const Trait = require('trait_manager/model/Trait');
'use strict'; const Component = require('dom_components/model/Component');
var Trait = require('TraitManager/model/Trait');
var Component = require('DomComponents/model/Component');
module.exports = { module.exports = {
run: function(){ run() {
describe('TraitModels', function() { describe('TraitModels', () => {
var obj; var obj;
var target; var target;
var modelName = 'title'; var modelName = 'title';
beforeEach(function () { beforeEach(() => {
target = new Component(); target = new Component();
obj = new Trait({ obj = new Trait({
name: modelName, name: modelName,
target: target, target,
}); });
}); });
afterEach(function () { afterEach(() => {
delete obj; obj = null;
}); });
it('Object exists', function() { it('Object exists', () => {
Trait.should.be.exist; expect(Trait).toExist();
}); });
}); });
} }
} }
});

126
test/specs/trait_manager/view/TraitsView.js

@ -1,75 +1,73 @@
define(function(require, exports, module){ const Trait = require('trait_manager/model/Trait');
'use strict'; const TraitView = require('trait_manager/view/TraitView');
var Trait = require('undefined'); const Component = require('dom_components/model/Component');
var Component = require('DomComponents/model/Component');
module.exports = { module.exports = {
run: function(){ run() {
describe('TraitView', function() { describe('TraitView', () => {
var obj; var obj;
var model; var model;
var modelName = 'title'; var modelName = 'title';
var target; var target;
beforeEach(function () { beforeEach(() => {
target = new Component(); target = new Component();
model = new Trait({ model = new Trait({
name: modelName, name: modelName,
target: target, target,
}); });
obj = new TraitView({ obj = new TraitView({
model: model, model,
}); });
}); });
afterEach(function () { afterEach(() => {
delete obj; obj = null;
delete model; model = null;
delete target; target = null;
}); });
it('Object exists', function() { it('Object exists', () => {
Trait.should.be.exist; expect(Trait).toExist();
}); });
it('Target has no attributes on init', function() { it('Target has no attributes on init', () => {
target.get('attributes').should.deep.equal({}); expect(target.get('attributes')).toEqual({});
}); });
it('On update of the value updates the target attributes', function() { it('On update of the value updates the target attributes', () => {
model.set('value', 'test'); model.set('value', 'test');
var eq = {}; var eq = {};
eq[modelName] = 'test'; eq[modelName] = 'test';
target.get('attributes').should.deep.equal(eq); expect(target.get('attributes')).toEqual(eq);
}); });
it('Updates on different models do not alter other targets', function() { it('Updates on different models do not alter other targets', () => {
var target1 = new Component(); var target1 = new Component();
var target2 = new Component(); var target2 = new Component();
var model1 = new Trait({ var model1 = new Trait({
name: modelName, name: modelName,
target: target1, target: target1,
}); });
var model2 = new Trait({ var model2 = new Trait({
name: modelName, name: modelName,
target: target2, target: target2,
}); });
var obj1 = new TraitView({model: model1}); var obj1 = new TraitView({model: model1});
var obj2 = new TraitView({model: model2}); var obj2 = new TraitView({model: model2});
model1.set('value', 'test1'); model1.set('value', 'test1');
model2.set('value', 'test2'); model2.set('value', 'test2');
var eq1 = {}; var eq1 = {};
eq1[modelName] = 'test1'; eq1[modelName] = 'test1';
var eq2 = {}; var eq2 = {};
eq2[modelName] = 'test2'; eq2[modelName] = 'test2';
target1.get('attributes').should.deep.equal(eq1); expect(target1.get('attributes')).toEqual(eq1);
target2.get('attributes').should.deep.equal(eq2); expect(target2.get('attributes')).toEqual(eq2);
}); });
}); });
} }
} }
});

4
test/specs/utils/main.js

@ -1,9 +1,9 @@
define(function(require, exports, module){ define((require, exports, module) => {
'use strict'; 'use strict';
var Utils = require('Utils'); var Utils = require('Utils');
var Sorter = require('undefined'); var Sorter = require('undefined');
describe('Utils', function() { describe('Utils', () => {
Sorter.run(); Sorter.run();

457
yarn.lock

@ -40,7 +40,13 @@ acorn-globals@^3.1.0:
dependencies: dependencies:
acorn "^4.0.4" acorn "^4.0.4"
acorn@^3.1.0: acorn-jsx@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
dependencies:
acorn "^3.0.4"
acorn@^3.0.4, acorn@^3.1.0:
version "3.3.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
@ -48,11 +54,11 @@ acorn@^4.0.3, acorn@^4.0.4:
version "4.0.13" version "4.0.13"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
acorn@^5.0.0: acorn@^5.0.0, acorn@^5.0.1:
version "5.0.3" version "5.0.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d"
ajv-keywords@^1.1.1: ajv-keywords@^1.0.0, ajv-keywords@^1.1.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
@ -79,6 +85,10 @@ ansi-escapes@^1.0.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
ansi-escapes@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b"
ansi-html@0.0.5: ansi-html@0.0.5:
version "0.0.5" version "0.0.5"
resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.5.tgz#0dcaa5a081206866bc240a3b773a184ea3b88b64" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.5.tgz#0dcaa5a081206866bc240a3b773a184ea3b88b64"
@ -91,6 +101,10 @@ ansi-regex@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107"
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
ansi-styles@^2.0.1, ansi-styles@^2.2.1: ansi-styles@^2.0.1, ansi-styles@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@ -214,10 +228,6 @@ async@^2.1.2:
dependencies: dependencies:
lodash "^4.14.0" lodash "^4.14.0"
async@~0.2.6:
version "0.2.10"
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
asynckit@^0.4.0: asynckit@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@ -958,6 +968,10 @@ balanced-match@^0.4.1:
version "0.4.2" version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
base64-js@^1.0.2: base64-js@^1.0.2:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
@ -1022,6 +1036,13 @@ brace-expansion@^1.0.0:
balanced-match "^0.4.1" balanced-match "^0.4.1"
concat-map "0.0.1" concat-map "0.0.1"
brace-expansion@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@^1.8.2: braces@^1.8.2:
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
@ -1137,6 +1158,16 @@ bytes@2.4.0:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339"
caller-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
dependencies:
callsites "^0.2.0"
callsites@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
camelcase-keys@^2.0.0: camelcase-keys@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
@ -1179,7 +1210,7 @@ chai@^3.5.0:
deep-eql "^0.1.3" deep-eql "^0.1.3"
type-detect "^1.0.0" type-detect "^1.0.0"
chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1: chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies: dependencies:
@ -1226,12 +1257,26 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
dependencies: dependencies:
inherits "^2.0.1" inherits "^2.0.1"
circular-json@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d"
cli-cursor@^1.0.2: cli-cursor@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
dependencies: dependencies:
restore-cursor "^1.0.1" restore-cursor "^1.0.1"
cli-cursor@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
dependencies:
restore-cursor "^2.0.0"
cli-width@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
cliui@^2.1.0: cliui@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
@ -1325,6 +1370,14 @@ concat-stream@^1.0.0, concat-stream@^1.5.0, concat-stream@~1.5.0:
readable-stream "~2.0.0" readable-stream "~2.0.0"
typedarray "~0.0.5" typedarray "~0.0.5"
concat-stream@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
dependencies:
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
connect-history-api-fallback@^1.3.0: connect-history-api-fallback@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169"
@ -1523,6 +1576,18 @@ defined@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
del@^2.0.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
dependencies:
globby "^5.0.0"
is-path-cwd "^1.0.0"
is-path-in-cwd "^1.0.0"
object-assign "^4.0.1"
pify "^2.0.0"
pinkie-promise "^2.0.0"
rimraf "^2.2.8"
delayed-stream@~1.0.0: delayed-stream@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@ -1595,6 +1660,13 @@ doctrine@^1.1.0:
esutils "^2.0.2" esutils "^2.0.2"
isarray "^1.0.0" isarray "^1.0.0"
doctrine@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
dependencies:
esutils "^2.0.2"
isarray "^1.0.0"
documentation@^4.0.0-beta2: documentation@^4.0.0-beta2:
version "4.0.0-beta9" version "4.0.0-beta9"
resolved "https://registry.yarnpkg.com/documentation/-/documentation-4.0.0-beta9.tgz#8221b25286ce9563c7c9bb7710241255ccd2ee79" resolved "https://registry.yarnpkg.com/documentation/-/documentation-4.0.0-beta9.tgz#8221b25286ce9563c7c9bb7710241255ccd2ee79"
@ -1773,7 +1845,7 @@ escape-html@~1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2: escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@ -1788,14 +1860,87 @@ escodegen@1.8.x, escodegen@^1.6.1:
optionalDependencies: optionalDependencies:
source-map "~0.2.0" source-map "~0.2.0"
eslint-scope@^3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
dependencies:
esrecurse "^4.1.0"
estraverse "^4.1.1"
eslint@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.1.1.tgz#facbdfcfe3e0facd3a8b80dc98c4e6c13ae582df"
dependencies:
babel-code-frame "^6.22.0"
chalk "^1.1.3"
concat-stream "^1.6.0"
debug "^2.6.8"
doctrine "^2.0.0"
eslint-scope "^3.7.1"
espree "^3.4.3"
esquery "^1.0.0"
estraverse "^4.2.0"
esutils "^2.0.2"
file-entry-cache "^2.0.0"
glob "^7.1.2"
globals "^9.17.0"
ignore "^3.3.3"
imurmurhash "^0.1.4"
inquirer "^3.0.6"
is-my-json-valid "^2.16.0"
is-resolvable "^1.0.0"
js-yaml "^3.8.4"
json-stable-stringify "^1.0.1"
levn "^0.3.0"
lodash "^4.17.4"
minimatch "^3.0.2"
mkdirp "^0.5.1"
natural-compare "^1.4.0"
optionator "^0.8.2"
path-is-inside "^1.0.2"
pluralize "^4.0.0"
progress "^2.0.0"
require-uncached "^1.0.3"
strip-json-comments "~2.0.1"
table "^4.0.1"
text-table "~0.2.0"
espree@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374"
dependencies:
acorn "^5.0.1"
acorn-jsx "^3.0.0"
esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1:
version "2.7.3" version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
esprima@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
esquery@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa"
dependencies:
estraverse "^4.0.0"
esrecurse@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163"
dependencies:
estraverse "^4.1.0"
object-assign "^4.0.1"
estraverse@^1.9.1: estraverse@^1.9.1:
version "1.9.3" version "1.9.3"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
esutils@^2.0.0, esutils@^2.0.2: esutils@^2.0.0, esutils@^2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
@ -1895,6 +2040,14 @@ extend@^3.0.0, extend@~3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
external-editor@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972"
dependencies:
iconv-lite "^0.4.17"
jschardet "^1.4.2"
tmp "^0.0.31"
extglob@^0.3.1: extglob@^0.3.1:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
@ -1921,6 +2074,19 @@ faye-websocket@~0.11.0:
dependencies: dependencies:
websocket-driver ">=0.5.1" websocket-driver ">=0.5.1"
figures@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
dependencies:
escape-string-regexp "^1.0.5"
file-entry-cache@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
dependencies:
flat-cache "^1.2.1"
object-assign "^4.0.1"
filename-regex@^2.0.0: filename-regex@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775"
@ -1966,6 +2132,15 @@ first-chunk-stream@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e"
flat-cache@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96"
dependencies:
circular-json "^0.3.1"
del "^2.0.2"
graceful-fs "^4.1.2"
write "^0.2.1"
font-awesome@^4.7.0: font-awesome@^4.7.0:
version "4.7.0" version "4.7.0"
resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
@ -2198,6 +2373,17 @@ glob@^7.0.5, glob@~7.1.1:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
globals-docs@2.2.0: globals-docs@2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/globals-docs/-/globals-docs-2.2.0.tgz#28d9e2937cb9a6bce7e786510a5d4e337d61d3ef" resolved "https://registry.yarnpkg.com/globals-docs/-/globals-docs-2.2.0.tgz#28d9e2937cb9a6bce7e786510a5d4e337d61d3ef"
@ -2206,6 +2392,10 @@ globals@^9.0.0:
version "9.14.0" version "9.14.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034" resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034"
globals@^9.17.0:
version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
globby@^4.0.0: globby@^4.0.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-4.1.0.tgz#080f54549ec1b82a6c60e631fc82e1211dbe95f8" resolved "https://registry.yarnpkg.com/globby/-/globby-4.1.0.tgz#080f54549ec1b82a6c60e631fc82e1211dbe95f8"
@ -2217,6 +2407,17 @@ globby@^4.0.0:
pify "^2.0.0" pify "^2.0.0"
pinkie-promise "^2.0.0" pinkie-promise "^2.0.0"
globby@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
dependencies:
array-union "^1.0.1"
arrify "^1.0.0"
glob "^7.0.3"
object-assign "^4.0.1"
pify "^2.0.0"
pinkie-promise "^2.0.0"
globule@^1.0.0: globule@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/globule/-/globule-1.1.0.tgz#c49352e4dc183d85893ee825385eb994bb6df45f" resolved "https://registry.yarnpkg.com/globule/-/globule-1.1.0.tgz#c49352e4dc183d85893ee825385eb994bb6df45f"
@ -2421,10 +2622,22 @@ iconv-lite@0.4.13:
version "0.4.13" version "0.4.13"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
iconv-lite@^0.4.17:
version "0.4.18"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
ieee754@^1.1.4: ieee754@^1.1.4:
version "1.1.8" version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
ignore@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
in-publish@^2.0.0: in-publish@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51"
@ -2446,7 +2659,7 @@ inflight@^1.0.4:
once "^1.3.0" once "^1.3.0"
wrappy "1" wrappy "1"
inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
@ -2458,6 +2671,25 @@ ini@^1.3.3, ini@~1.3.0:
version "1.3.4" version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
inquirer@^3.0.6:
version "3.1.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.1.1.tgz#87621c4fba4072f48a8dd71c9f9df6f100b2d534"
dependencies:
ansi-escapes "^2.0.0"
chalk "^1.0.0"
cli-cursor "^2.1.0"
cli-width "^2.0.0"
external-editor "^2.0.4"
figures "^2.0.0"
lodash "^4.3.0"
mute-stream "0.0.7"
run-async "^2.2.0"
rx-lite "^4.0.8"
rx-lite-aggregates "^4.0.8"
string-width "^2.0.0"
strip-ansi "^3.0.0"
through "^2.3.6"
interpret@^1.0.0: interpret@^1.0.0:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
@ -2590,6 +2822,10 @@ is-fullwidth-code-point@^1.0.0:
dependencies: dependencies:
number-is-nan "^1.0.0" number-is-nan "^1.0.0"
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
is-generator-function@^1.0.6: is-generator-function@^1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.6.tgz#9e71653cd15fff341c79c4151460a131d31e9fc4" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.6.tgz#9e71653cd15fff341c79c4151460a131d31e9fc4"
@ -2619,6 +2855,15 @@ is-my-json-valid@^2.12.4:
jsonpointer "^4.0.0" jsonpointer "^4.0.0"
xtend "^4.0.0" xtend "^4.0.0"
is-my-json-valid@^2.16.0:
version "2.16.0"
resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693"
dependencies:
generate-function "^2.0.0"
generate-object-property "^1.1.0"
jsonpointer "^4.0.0"
xtend "^4.0.0"
is-number-object@^1.0.3: is-number-object@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799"
@ -2629,6 +2874,22 @@ is-number@^2.0.2, is-number@^2.1.0:
dependencies: dependencies:
kind-of "^3.0.2" kind-of "^3.0.2"
is-path-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
is-path-in-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc"
dependencies:
is-path-inside "^1.0.0"
is-path-inside@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f"
dependencies:
path-is-inside "^1.0.1"
is-posix-bracket@^0.1.0: is-posix-bracket@^0.1.0:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
@ -2637,6 +2898,10 @@ is-primitive@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
is-property@^1.0.0: is-property@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
@ -2653,6 +2918,12 @@ is-relative@^0.2.1:
dependencies: dependencies:
is-unc-path "^0.1.1" is-unc-path "^0.1.1"
is-resolvable@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62"
dependencies:
tryit "^1.0.1"
is-ssh@^1.3.0: is-ssh@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.0.tgz#ebea1169a2614da392a63740366c3ce049d8dff6" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.0.tgz#ebea1169a2614da392a63740366c3ce049d8dff6"
@ -2759,10 +3030,21 @@ js-yaml@3.x, js-yaml@^3.3.1:
argparse "^1.0.7" argparse "^1.0.7"
esprima "^2.6.0" esprima "^2.6.0"
js-yaml@^3.8.4:
version "3.8.4"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6"
dependencies:
argparse "^1.0.7"
esprima "^3.1.1"
jsbn@~0.1.0: jsbn@~0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd"
jschardet@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.4.2.tgz#2aa107f142af4121d145659d44f50830961e699a"
jsdom@^11.0.0: jsdom@^11.0.0:
version "11.0.0" version "11.0.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.0.0.tgz#1ee507cb2c0b16c875002476b1a8557d951353e5" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.0.0.tgz#1ee507cb2c0b16c875002476b1a8557d951353e5"
@ -2873,7 +3155,7 @@ lcid@^1.0.0:
dependencies: dependencies:
invert-kv "^1.0.0" invert-kv "^1.0.0"
levn@~0.3.0: levn@^0.3.0, levn@~0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
dependencies: dependencies:
@ -2978,6 +3260,10 @@ lodash@^4.0.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.2, l
version "4.17.2" version "4.17.2"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.2.tgz#34a3055babe04ce42467b607d700072c7ff6bf42" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.2.tgz#34a3055babe04ce42467b607d700072c7ff6bf42"
lodash@^4.17.4, lodash@^4.3.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
lodash@~4.16.4: lodash@~4.16.4:
version "4.16.6" version "4.16.6"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777"
@ -3134,13 +3420,13 @@ mime-db@~1.25.0:
version "1.25.0" version "1.25.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.25.0.tgz#c18dbd7c73a5dbf6f44a024dc0d165a1e7b1c392" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.25.0.tgz#c18dbd7c73a5dbf6f44a024dc0d165a1e7b1c392"
mime-types@^2.1.12, mime-types@~2.1.7: mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.7:
version "2.1.13" version "2.1.13"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.13.tgz#e07aaa9c6c6b9a7ca3012c69003ad25a39e92a88" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.13.tgz#e07aaa9c6c6b9a7ca3012c69003ad25a39e92a88"
dependencies: dependencies:
mime-db "~1.25.0" mime-db "~1.25.0"
mime-types@~2.1.11, mime-types@~2.1.15: mime-types@~2.1.15:
version "2.1.15" version "2.1.15"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
dependencies: dependencies:
@ -3150,6 +3436,10 @@ mime@1.3.4, mime@^1.3.4:
version "1.3.4" version "1.3.4"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
mimic-fn@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
minimalistic-assert@^1.0.0: minimalistic-assert@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
@ -3164,6 +3454,12 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
dependencies: dependencies:
brace-expansion "^1.0.0" brace-expansion "^1.0.0"
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
brace-expansion "^1.1.7"
minimist@0.0.8, minimist@~0.0.1: minimist@0.0.8, minimist@~0.0.1:
version "0.0.8" version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@ -3225,10 +3521,18 @@ ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
nan@^2.1.0, nan@^2.3.0, nan@^2.3.2: nan@^2.1.0, nan@^2.3.0, nan@^2.3.2:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232"
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
negotiator@0.6.1: negotiator@0.6.1:
version "0.6.1" version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
@ -3434,6 +3738,12 @@ onetime@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
onetime@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
dependencies:
mimic-fn "^1.0.0"
opn@4.0.2: opn@4.0.2:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
@ -3448,7 +3758,7 @@ optimist@^0.6.1:
minimist "~0.0.1" minimist "~0.0.1"
wordwrap "~0.0.2" wordwrap "~0.0.2"
optionator@^0.8.1: optionator@^0.8.1, optionator@^0.8.2:
version "0.8.2" version "0.8.2"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
dependencies: dependencies:
@ -3486,7 +3796,7 @@ os-locale@^1.4.0:
dependencies: dependencies:
lcid "^1.0.0" lcid "^1.0.0"
os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@ -3602,6 +3912,10 @@ path-is-absolute@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
path-is-inside@^1.0.1, path-is-inside@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
path-platform@~0.11.15: path-platform@~0.11.15:
version "0.11.15" version "0.11.15"
resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2"
@ -3654,6 +3968,10 @@ plur@^2.0.0:
dependencies: dependencies:
irregular-plurals "^1.0.0" irregular-plurals "^1.0.0"
pluralize@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762"
pn@^1.0.0: pn@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/pn/-/pn-1.0.0.tgz#1cf5a30b0d806cd18f88fc41a6b5d4ad615b3ba9" resolved "https://registry.yarnpkg.com/pn/-/pn-1.0.0.tgz#1cf5a30b0d806cd18f88fc41a6b5d4ad615b3ba9"
@ -3686,6 +4004,10 @@ process@^0.11.0:
version "0.11.10" version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
progress@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
protocols@^1.1.0, protocols@^1.4.0: protocols@^1.1.0, protocols@^1.4.0:
version "1.4.3" version "1.4.3"
resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.3.tgz#635b1c0785f0b389e8a012df1b1afffda9608b76" resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.3.tgz#635b1c0785f0b389e8a012df1b1afffda9608b76"
@ -3834,7 +4156,7 @@ readable-stream@^2.0.2, readable-stream@~2.0.0:
string_decoder "~0.10.x" string_decoder "~0.10.x"
util-deprecate "~1.0.1" util-deprecate "~1.0.1"
readable-stream@^2.2.6, readable-stream@^2.2.9: readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9:
version "2.2.9" version "2.2.9"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8"
dependencies: dependencies:
@ -4038,10 +4360,21 @@ require-main-filename@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
require-uncached@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
dependencies:
caller-path "^0.1.0"
resolve-from "^1.0.0"
requires-port@1.0.x, requires-port@1.x.x: requires-port@1.0.x, requires-port@1.x.x:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
resolve-from@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
resolve@1.1.7, resolve@1.1.x, resolve@^1.1.3, resolve@^1.1.6: resolve@1.1.7, resolve@1.1.x, resolve@^1.1.3, resolve@^1.1.6:
version "1.1.7" version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
@ -4053,6 +4386,13 @@ restore-cursor@^1.0.1:
exit-hook "^1.0.0" exit-hook "^1.0.0"
onetime "^1.0.0" onetime "^1.0.0"
restore-cursor@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
dependencies:
onetime "^2.0.0"
signal-exit "^3.0.2"
right-align@^0.1.1: right-align@^0.1.1:
version "0.1.3" version "0.1.3"
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
@ -4063,7 +4403,7 @@ rimraf@2:
version "2.2.8" version "2.2.8"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582"
rimraf@~2.5.1, rimraf@~2.5.4: rimraf@^2.2.8, rimraf@~2.5.1, rimraf@~2.5.4:
version "2.5.4" version "2.5.4"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04"
dependencies: dependencies:
@ -4076,6 +4416,22 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^2.0.0" hash-base "^2.0.0"
inherits "^2.0.1" inherits "^2.0.1"
run-async@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
dependencies:
is-promise "^2.1.0"
rx-lite-aggregates@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
dependencies:
rx-lite "*"
rx-lite@*, rx-lite@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
safe-buffer@^5.0.1: safe-buffer@^5.0.1:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
@ -4181,6 +4537,10 @@ signal-exit@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.1.tgz#5a4c884992b63a7acd9badb7894c3ee9cfccad81" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.1.tgz#5a4c884992b63a7acd9badb7894c3ee9cfccad81"
signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
sinon@^1.17.6: sinon@^1.17.6:
version "1.17.6" version "1.17.6"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.6.tgz#a43116db59577c8296356afee13fafc2332e58e1" resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.6.tgz#a43116db59577c8296356afee13fafc2332e58e1"
@ -4194,6 +4554,10 @@ slash@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
sntp@1.x.x: sntp@1.x.x:
version "1.0.9" version "1.0.9"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
@ -4354,6 +4718,13 @@ string-width@^1.0.0, string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^1.0.0" is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0" strip-ansi "^3.0.0"
string-width@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.0.tgz#030664561fc146c9423ec7d978fe2457437fe6d0"
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string_decoder@^0.10.25, string_decoder@~0.10.x: string_decoder@^0.10.25, string_decoder@~0.10.x:
version "0.10.31" version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
@ -4381,6 +4752,12 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
dependencies: dependencies:
ansi-regex "^2.0.0" ansi-regex "^2.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
dependencies:
ansi-regex "^3.0.0"
strip-bom-stream@^1.0.0: strip-bom-stream@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee"
@ -4400,7 +4777,7 @@ strip-indent@^1.0.1:
dependencies: dependencies:
get-stdin "^4.0.1" get-stdin "^4.0.1"
strip-json-comments@^2.0.0: strip-json-comments@^2.0.0, strip-json-comments@~2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@ -4443,6 +4820,17 @@ symbol-tree@^3.2.1:
version "3.2.2" version "3.2.2"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
table@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435"
dependencies:
ajv "^4.7.0"
ajv-keywords "^1.0.0"
chalk "^1.1.1"
lodash "^4.0.0"
slice-ansi "0.0.4"
string-width "^2.0.0"
tapable@^0.2.5, tapable@~0.2.5: tapable@^0.2.5, tapable@~0.2.5:
version "0.2.6" version "0.2.6"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d"
@ -4468,7 +4856,7 @@ tar@^2.0.0, tar@~2.2.1:
fstream "^1.0.2" fstream "^1.0.2"
inherits "2" inherits "2"
text-table@^0.2.0: text-table@^0.2.0, text-table@~0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@ -4493,7 +4881,7 @@ through2@^2.0.0, through2@~2.0.0:
readable-stream "~2.0.0" readable-stream "~2.0.0"
xtend "~4.0.0" xtend "~4.0.0"
"through@>=2.2.7 <3": "through@>=2.2.7 <3", through@^2.3.6:
version "2.3.8" version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@ -4518,6 +4906,12 @@ tmatch@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/tmatch/-/tmatch-2.0.1.tgz#0c56246f33f30da1b8d3d72895abaf16660f38cf" resolved "https://registry.yarnpkg.com/tmatch/-/tmatch-2.0.1.tgz#0c56246f33f30da1b8d3d72895abaf16660f38cf"
tmp@^0.0.31:
version "0.0.31"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
dependencies:
os-tmpdir "~1.0.1"
to-absolute-glob@^0.1.1: to-absolute-glob@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f" resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f"
@ -4568,6 +4962,10 @@ trim@0.0.1, trim@^0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
tryit@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
ttf2eot@^2.0.0: ttf2eot@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ttf2eot/-/ttf2eot-2.0.0.tgz#8e6337a585abd1608a0c84958ab483ce69f6654b" resolved "https://registry.yarnpkg.com/ttf2eot/-/ttf2eot-2.0.0.tgz#8e6337a585abd1608a0c84958ab483ce69f6654b"
@ -4625,20 +5023,11 @@ type-is@~1.6.10, type-is@~1.6.15:
media-typer "0.3.0" media-typer "0.3.0"
mime-types "~2.1.15" mime-types "~2.1.15"
typedarray@~0.0.5: typedarray@^0.0.6, typedarray@~0.0.5:
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
uglify-js@^2.6: uglify-js@^2.6, uglify-js@^2.8.27:
version "2.7.5"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8"
dependencies:
async "~0.2.6"
source-map "~0.5.1"
uglify-to-browserify "~1.0.0"
yargs "~3.10.0"
uglify-js@^2.8.27:
version "2.8.27" version "2.8.27"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.27.tgz#47787f912b0f242e5b984343be8e35e95f694c9c" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.27.tgz#47787f912b0f242e5b984343be8e35e95f694c9c"
dependencies: dependencies:
@ -5032,6 +5421,12 @@ wrappy@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
write@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
dependencies:
mkdirp "^0.5.1"
xml-name-validator@^2.0.1: xml-name-validator@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"

Loading…
Cancel
Save